@@ -8,10 +8,10 @@ import * as Exception from '../exception'
8
8
import * as typeDefinition from './helper/definition'
9
9
import Version from '../version'
10
10
import { Traversable } from '../shared'
11
- import { Mutation , Selector , QueryToken , PredicateProvider , checkPredicate , predicateOperatorNames } from './modules'
11
+ import { Mutation , Selector , QueryToken , PredicateProvider , checkPredicate , predicateOperatorNames , RevertController } from './modules'
12
12
import { dispose , contextTableName , fieldIdentifier , hiddenColName } from './symbols'
13
13
import { forEach , clone , contains , tryCatch , hasOwn , getType , assert , identity , warn , keys as objKeys } from '../utils'
14
- import { createPredicate , createPkClause , mergeTransactionResult , predicatableQuery , lfFactory } from './helper'
14
+ import { createPredicate , createPkClause , predicatableQuery , lfFactory , executor } from './helper'
15
15
import { Relationship , RDBType , DataStoreType , LeafType , StatementType , JoinMode } from '../interface/enum'
16
16
import { Record , Fields , JoinInfo , Query , Predicate } from '../interface'
17
17
import { SchemaDef , ColumnDef , ParsedSchema , Association , ScopedHandler } from '../interface'
@@ -110,57 +110,51 @@ export class Database {
110
110
} )
111
111
}
112
112
113
- insert < T > ( tableName : string , raw : T [ ] ) : Observable < ExecutorResult >
113
+ insert < T > ( tableName : string , raw : T [ ] , revertController ?: RevertController ) : Observable < ExecutorResult >
114
114
115
- insert < T > ( tableName : string , raw : T ) : Observable < ExecutorResult >
115
+ insert < T > ( tableName : string , raw : T , revertController ?: RevertController ) : Observable < ExecutorResult >
116
116
117
- insert < T > ( tableName : string , raw : T | T [ ] ) : Observable < ExecutorResult >
117
+ insert < T > ( tableName : string , raw : T | T [ ] , revertController ?: RevertController ) : Observable < ExecutorResult >
118
118
119
- insert < T > ( tableName : string , raw : T | T [ ] ) : Observable < ExecutorResult > {
120
- return this . database$
121
- . concatMap ( db => {
122
- const schema = this . findSchema ( tableName )
123
- const pk = schema . pk
124
- const columnMapper = schema . mapper
125
- const [ table ] = Database . getTables ( db , tableName )
126
- const muts : Mutation [ ] = [ ]
127
- const entities = clone ( raw )
128
-
129
- const iterator = Array . isArray ( entities ) ? entities : [ entities ]
130
-
131
- iterator . forEach ( ( entity : any ) => {
132
- const mut = new Mutation ( db , table )
133
- const hiddenPayload = Object . create ( null )
134
-
135
- columnMapper . forEach ( ( mapper , key ) => {
136
- // cannot create a hidden column for primary key
137
- if ( ! hasOwn ( entity , key ) || key === pk ) {
138
- return
119
+ insert < T > ( tableName : string , raw : T | T [ ] , revertController ?: RevertController ) : Observable < ExecutorResult > {
120
+ return this . database$ . concatMap ( db => {
121
+ const { queries, contextIds } = this . buildInsertQuery ( db , tableName , raw )
122
+ const schema = this . findSchema ( tableName )
123
+ const pk = schema . pk
124
+ const [ table ] = Database . getTables ( db , tableName )
125
+ return Observable . fromPromise ( executor ( db , queries ) )
126
+ . do ( {
127
+ next : ( ) => {
128
+ if ( revertController ) {
129
+ const equalClause = ( Array . isArray ( raw ) ? raw : [ raw ] )
130
+ . map ( data => ( {
131
+ [ pk ] : data [ pk ]
132
+ } ) )
133
+ const clause = { $or : equalClause }
134
+ const tablesStruct : TablesStruct = {
135
+ [ tableName ] : {
136
+ table, contextName : tableName
137
+ }
138
+ }
139
+ const provider = new PredicateProvider ( tablesStruct , tableName , clause )
140
+ const deleteQuery =
141
+ predicatableQuery ( db , table , provider . getPredicate ( ) , StatementType . Delete )
142
+ revertController . inject (
143
+ db , [ deleteQuery ]
144
+ )
139
145
}
140
-
141
- const val = entity [ key ]
142
- hiddenPayload [ key ] = mapper ( val )
143
- hiddenPayload [ hiddenColName ( key ) ] = val
144
- } )
145
-
146
- mut . patch ( { ...entity , ...hiddenPayload } )
147
- mut . withId ( pk , entity [ pk ] )
148
- muts . push ( mut )
146
+ } ,
147
+ error : ( ) => contextIds . forEach ( id => this . storedIds . delete ( id ) )
149
148
} )
150
-
151
- const { contextIds, queries } = Mutation . aggregate ( db , muts , [ ] )
152
- contextIds . forEach ( id => this . storedIds . add ( id ) )
153
- return this . executor ( db , queries )
154
- . do ( { error : ( ) => contextIds . forEach ( id => this . storedIds . delete ( id ) ) } )
155
- } )
149
+ } )
156
150
}
157
151
158
152
get < T > ( tableName : string , query : Query < T > = { } , mode : JoinMode = JoinMode . imlicit ) : QueryToken < T > {
159
153
const selector$ = this . buildSelector < T > ( tableName , query , mode )
160
154
return new QueryToken < T > ( selector$ )
161
155
}
162
156
163
- update < T > ( tableName : string , clause : Predicate < T > , raw : Partial < T > ) : Observable < ExecutorResult > {
157
+ update < T > ( tableName : string , clause : Predicate < T > , raw : Partial < T > , revertController ?: RevertController ) : Observable < ExecutorResult > {
164
158
const type = getType ( raw )
165
159
if ( type !== 'Object' ) {
166
160
return Observable . throw ( Exception . InvalidType ( [ 'Object' , type ] ) )
@@ -172,7 +166,7 @@ export class Database {
172
166
}
173
167
174
168
return this . database$
175
- . concatMap < any , any > ( db => {
169
+ . concatMap ( db => {
176
170
const entity = clone ( raw )
177
171
const [ table ] = Database . getTables ( db , tableName )
178
172
const columnMapper = schema ! . mapper
@@ -210,42 +204,80 @@ export class Database {
210
204
}
211
205
} )
212
206
213
- return this . executor ( db , [ query ] )
207
+ if ( revertController ) {
208
+ const columns = Object . keys ( raw ) . map ( key => table [ key ] )
209
+ predicatableQuery ( db , table , predicate ! , StatementType . Select , ...columns )
210
+ . exec ( )
211
+ . then ( ( [ value ] ) => {
212
+ const revertQuery = predicatableQuery ( db , table , predicate ! , StatementType . Update )
213
+ forEach ( value , ( val , key ) => {
214
+ const column = table [ key ]
215
+ revertQuery . set ( column , val )
216
+ } )
217
+ revertController . inject (
218
+ db , [ revertQuery ]
219
+ )
220
+ } )
221
+ . catch ( e => {
222
+ revertController . giveup ( )
223
+ warn ( e )
224
+ } )
225
+ }
226
+
227
+ return Observable . fromPromise ( executor ( db , [ query ] ) )
228
+ . do ( {
229
+ error : ( ) => {
230
+ if ( revertController ) {
231
+ revertController . giveup ( )
232
+ }
233
+ }
234
+ } )
214
235
} )
215
236
}
216
237
217
- delete < T > ( tableName : string , clause : Predicate < T > = { } ) : Observable < ExecutorResult > {
238
+ delete < T > ( tableName : string , clause : Predicate < T > = { } , revertController ?: RevertController ) : Observable < ExecutorResult > {
218
239
const [ pk , err ] = tryCatch < string > ( this . findPrimaryKey ) ( tableName )
219
240
if ( err ) {
220
241
return Observable . throw ( err )
221
242
}
222
243
223
- return this . database$
224
- . concatMap ( db => {
225
- const [ table ] = Database . getTables ( db , tableName )
226
- const tables = this . buildTablesStructure ( table )
227
- const column = table [ pk ! ]
228
- const provider = new PredicateProvider ( tables , tableName , clause )
229
- const prefetch =
230
- predicatableQuery ( db , table , provider . getPredicate ( ) , StatementType . Select , column )
231
-
232
- return Observable . fromPromise ( prefetch . exec ( ) )
233
- . concatMap ( ( scopedIds ) => {
234
- const predicate = provider . getPredicate ( )
235
- if ( ! predicate ) {
236
- warn ( `The result of parsed Predicate is null, you are deleting all ${ tableName } Table!` )
237
- }
238
- const query = predicatableQuery ( db , table , predicate , StatementType . Delete )
239
-
240
- scopedIds . forEach ( ( entity : any ) =>
241
- this . storedIds . delete ( fieldIdentifier ( tableName , entity [ pk ! ] ) ) )
244
+ if ( revertController ) {
245
+ if ( ! clause ) {
246
+ throw Exception . clauseMissingError ( )
247
+ }
248
+ }
242
249
243
- return this . executor ( db , [ query ] ) . do ( { error : ( ) => {
244
- scopedIds . forEach ( ( entity : any ) =>
245
- this . storedIds . add ( fieldIdentifier ( tableName , entity [ pk ! ] ) ) )
246
- } } )
247
- } )
248
- } )
250
+ return this . database$ . concatMap ( db => {
251
+ const [ table ] = Database . getTables ( db , tableName )
252
+ const tablesStruct = this . buildTablesStructure ( table )
253
+ const provider = new PredicateProvider ( tablesStruct , tableName , clause )
254
+ const predicate = provider . getPredicate ( )
255
+ if ( ! predicate ) {
256
+ warn ( `The result of parsed Predicate is null, you are deleting all ${ tableName } Tables!` )
257
+ }
258
+ const query = predicatableQuery ( db , table , predicate , StatementType . Delete )
259
+ const columns = revertController ? [ ] as any : [ pk ]
260
+ return Observable . fromPromise (
261
+ this . deletePrefetch ( db , table , provider , columns )
262
+ )
263
+ . concatMap ( scopedIds =>
264
+ Observable . fromPromise ( executor ( db , [ query ] ) )
265
+ . do ( {
266
+ next : ( ) => {
267
+ if ( revertController ) {
268
+ const { queries } = this . buildInsertQuery ( db , tableName , scopedIds )
269
+ revertController . inject ( db , queries )
270
+ }
271
+ scopedIds . forEach ( ( entity : any ) =>
272
+ this . storedIds . delete ( fieldIdentifier ( tableName , entity [ pk ! ] ) ) )
273
+ } ,
274
+ error : ( ) => {
275
+ scopedIds . forEach ( ( entity : any ) =>
276
+ this . storedIds . add ( fieldIdentifier ( tableName , entity [ pk ! ] ) ) )
277
+ }
278
+ } )
279
+ )
280
+ } )
249
281
}
250
282
251
283
upsert < T > ( tableName : string , raw : T ) : Observable < ExecutorResult >
@@ -264,7 +296,7 @@ export class Database {
264
296
const { contextIds, queries } = Mutation . aggregate ( db , insert , update )
265
297
if ( queries . length > 0 ) {
266
298
contextIds . forEach ( id => this . storedIds . add ( id ) )
267
- return this . executor ( db , queries )
299
+ return Observable . fromPromise ( executor ( db , queries ) )
268
300
. do ( { error : ( ) => contextIds . forEach ( id => this . storedIds . delete ( id ) ) } )
269
301
} else {
270
302
return Observable . of ( { result : false , insert : 0 , update : 0 , delete : 0 , select : 0 } )
@@ -289,7 +321,7 @@ export class Database {
289
321
}
290
322
291
323
const queries : lf . query . Builder [ ] = [ ]
292
- const removedIds : any = [ ]
324
+ const removedIds : string [ ] = [ ]
293
325
queries . push ( predicatableQuery ( db , table , predicate ! , StatementType . Delete ) )
294
326
295
327
const prefetch = predicatableQuery ( db , table , predicate ! , StatementType . Select )
@@ -303,10 +335,10 @@ export class Database {
303
335
const scope = this . createScopedHandler < T > ( db , queries , removedIds )
304
336
return disposeHandler ( rootEntities , scope )
305
337
. do ( ( ) => removedIds . forEach ( ( id : string ) => this . storedIds . delete ( id ) ) )
306
- . concatMap ( ( ) => this . executor ( db , queries ) )
338
+ . concatMap ( ( ) => executor ( db , queries ) )
307
339
} else {
308
340
removedIds . forEach ( ( id : string ) => this . storedIds . delete ( id ) )
309
- return this . executor ( db , queries )
341
+ return executor ( db , queries )
310
342
}
311
343
} )
312
344
. do ( { error : ( ) =>
@@ -329,7 +361,7 @@ export class Database {
329
361
db . getSchema ( ) . tables ( ) . map ( t => db . delete ( ) . from ( t ) ) )
330
362
331
363
return this . database$ . concatMap ( db => {
332
- return cleanup . concatMap ( queries => this . executor ( db , queries ) )
364
+ return cleanup . concatMap ( queries => executor ( db , queries ) )
333
365
. do ( ( ) => {
334
366
db . close ( )
335
367
this . schemas . clear ( )
@@ -519,6 +551,67 @@ export class Database {
519
551
}
520
552
}
521
553
554
+ private buildInsertQuery < T > ( db : lf . Database , tableName : string , raw : T | T [ ] ) {
555
+ const schema = this . findSchema ( tableName )
556
+ const pk = schema . pk
557
+ const columnMapper = schema . mapper
558
+ const [ table ] = Database . getTables ( db , tableName )
559
+ const muts : Mutation [ ] = [ ]
560
+ const entities = clone ( raw )
561
+
562
+ const iterator = Array . isArray ( entities ) ? entities : [ entities ]
563
+
564
+ iterator . forEach ( ( entity : any ) => {
565
+ const mut = new Mutation ( db , table )
566
+ const hiddenPayload = Object . create ( null )
567
+
568
+ columnMapper . forEach ( ( mapper , key ) => {
569
+ // cannot create a hidden column for primary key
570
+ if ( ! hasOwn ( entity , key ) || key === pk ) {
571
+ return
572
+ }
573
+
574
+ const val = entity [ key ]
575
+ hiddenPayload [ key ] = mapper ( val )
576
+ hiddenPayload [ hiddenColName ( key ) ] = val
577
+ } )
578
+
579
+ mut . patch ( { ...entity , ...hiddenPayload } )
580
+ mut . withId ( pk , entity [ pk ] )
581
+ muts . push ( mut )
582
+ } )
583
+
584
+ const { contextIds, queries } = Mutation . aggregate ( db , muts , [ ] )
585
+ contextIds . forEach ( id => this . storedIds . add ( id ) )
586
+
587
+ return { queries, contextIds }
588
+ }
589
+
590
+ private deletePrefetch < T > (
591
+ db : lf . Database ,
592
+ table : lf . schema . Table ,
593
+ provider : PredicateProvider < T > ,
594
+ columnNames : string [ ]
595
+ ) {
596
+ let columns : lf . schema . Column [ ]
597
+ // build revert query
598
+ if ( ! columnNames . length ) {
599
+ const tableName = table . getName ( )
600
+ columns = Array . from ( this . findSchema ( tableName ) . columns . keys ( ) )
601
+ . map ( columnName => {
602
+ const column = table [ columnName ]
603
+ const hiddenName = hiddenColName ( columnName )
604
+ const hiddenCol = table [ hiddenName ]
605
+ return hiddenCol || column
606
+ } )
607
+ } else {
608
+ columns = columnNames . map ( columnName => table [ columnName ] )
609
+ }
610
+ const prefetch =
611
+ predicatableQuery ( db , table , provider . getPredicate ( ) , StatementType . Select , ...columns )
612
+ return prefetch . exec ( )
613
+ }
614
+
522
615
// context 用来标记DFS路径中的所有出现过的表,用于解决self-join时的二义性
523
616
// path 用来标记每个查询路径上出现的表,用于解决circular reference
524
617
private traverseQueryFields (
@@ -951,20 +1044,4 @@ export class Database {
951
1044
return newTableName !
952
1045
}
953
1046
954
- private executor ( db : lf . Database , queries : lf . query . Builder [ ] ) {
955
- const tx = db . createTransaction ( )
956
- const handler = {
957
- error : ( ) => warn ( `Execute failed, transaction is already marked for rollback.` )
958
- }
959
-
960
- return Observable . fromPromise ( tx . exec ( queries ) )
961
- . do ( handler )
962
- . map ( ( ret ) => {
963
- return {
964
- result : true ,
965
- ...mergeTransactionResult ( queries , ret )
966
- }
967
- } )
968
- }
969
-
970
1047
}
0 commit comments