47
47
import io .dingodb .meta .entity .IndexTable ;
48
48
import io .dingodb .meta .entity .IndexType ;
49
49
import io .dingodb .meta .entity .Table ;
50
+ import io .dingodb .partition .DingoPartitionServiceProvider ;
51
+ import io .dingodb .partition .PartitionService ;
50
52
import io .dingodb .store .api .StoreInstance ;
51
53
import io .dingodb .store .api .transaction .data .Op ;
52
54
import io .dingodb .store .api .transaction .exception .DuplicateEntryException ;
@@ -89,8 +91,11 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
89
91
KeyValueCodec codec = param .getCodec ();
90
92
boolean isVector = false ;
91
93
boolean isDocument = false ;
94
+ Object [] primaryOldTuple = tuple ;
95
+ Table indexTable = null ;
92
96
if (context .getIndexId () != null ) {
93
- Table indexTable = (Table ) TransactionManager .getIndex (txnId , context .getIndexId ());
97
+ boolean duplicate = param .getUpdateMapping () != null && param .getUpdates () != null ;
98
+ indexTable = (Table ) TransactionManager .getIndex (txnId , context .getIndexId ());
94
99
if (indexTable == null ) {
95
100
LogUtils .error (log , "[ddl] TxnPartInsert get index table null, indexId:{}" , context .getIndexId ());
96
101
return false ;
@@ -133,6 +138,87 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
133
138
codec = CodecService .getDefault ().createKeyValueCodec (
134
139
indexTable .getCodecVersion (), indexTable .version , indexTable .tupleType (), indexTable .keyMapping ()
135
140
);
141
+ // index duplicate key check
142
+ if (duplicate && context .getTablePartId () != null ) {
143
+ Object [] primaryTuple = (Object []) param .getSchema ().convertFrom (primaryOldTuple , ValueConverter .INSTANCE );
144
+ KeyValue primaryKv = wrap (param .getCodec ()::encode ).apply (primaryTuple );
145
+ StoreInstance store = Services .KV_STORE .getInstance (param .getTable ().tableId , context .getTablePartId ());
146
+ KeyValue getPrimaryKv = store .txnGet (txnId .seq , primaryKv .getKey (), param .getLockTimeOut ());
147
+ if (getPrimaryKv != null && getPrimaryKv .getValue () != null ) {
148
+ context .setDuplicateKey (true );
149
+ Object [] getPrimaryTuple = param .getCodec ().decode (getPrimaryKv );
150
+ if (!param .isPessimisticTxn ()) {
151
+ Object defaultVal = null ;
152
+ if (columnIndices .contains (-1 )) {
153
+ Column addColumn = indexTable .getColumns ().stream ()
154
+ .filter (column -> column .getSchemaState () != SchemaState .SCHEMA_PUBLIC )
155
+ .findFirst ().orElse (null );
156
+ if (addColumn != null ) {
157
+ defaultVal = addColumn .getDefaultVal ();
158
+ }
159
+ }
160
+ Object finalDefaultVal = defaultVal ;
161
+ Object [] finalGetPrimaryTuple = getPrimaryTuple ;
162
+ getPrimaryTuple = columnIndices .stream ().map (i -> {
163
+ if (i == -1 ) {
164
+ return finalDefaultVal ;
165
+ }
166
+ return finalGetPrimaryTuple [i ];
167
+ }).toArray ();
168
+ }
169
+ PartitionService ps = PartitionService .getService (
170
+ Optional .ofNullable (indexTable .getPartitionStrategy ())
171
+ .orElse (DingoPartitionServiceProvider .RANGE_FUNC_NAME ));
172
+ KeyValue oldKv = wrap (codec ::encode ).apply (getPrimaryTuple );
173
+ CommonId oldPartId = ps .calcPartId (oldKv .getKey (), MetaService .root ().getRangeDistribution (tableId ));
174
+ StoreInstance tmpLocalStore = Services .LOCAL_STORE .getInstance (context .getIndexId (), oldPartId );
175
+ CodecService .getDefault ().setId (oldKv .getKey (), oldPartId .domain );
176
+ byte [] key = oldKv .getKey ();
177
+ byte [] txnIdByte = txnId .encode ();
178
+ byte [] tableIdByte = tableId .encode ();
179
+ byte [] partIdByte = oldPartId .encode ();
180
+ int len = txnIdByte .length + tableIdByte .length + partIdByte .length ;
181
+ byte [] insertKey = ByteUtils .encode (
182
+ CommonId .CommonType .TXN_CACHE_DATA ,
183
+ oldKv .getKey (),
184
+ Op .PUTIFABSENT .getCode (),
185
+ len ,
186
+ txnIdByte ,
187
+ tableIdByte ,
188
+ partIdByte );
189
+ byte [] deleteKey = Arrays .copyOf (insertKey , insertKey .length );
190
+ deleteKey [deleteKey .length - 2 ] = (byte ) Op .DELETE .getCode ();
191
+ byte [] updateKey = Arrays .copyOf (insertKey , insertKey .length );
192
+ updateKey [updateKey .length - 2 ] = (byte ) Op .PUT .getCode ();
193
+ List <byte []> bytes = new ArrayList <>(3 );
194
+ bytes .add (insertKey );
195
+ bytes .add (deleteKey );
196
+ bytes .add (updateKey );
197
+ List <KeyValue > keyValues = tmpLocalStore .get (bytes );
198
+ if (keyValues != null && !keyValues .isEmpty ()) {
199
+ if (keyValues .size () > 1 ) {
200
+ throw new RuntimeException (txnId + " Key is not existed than two in local store" );
201
+ }
202
+ KeyValue value = keyValues .get (0 );
203
+ byte [] oldKey = value .getKey ();
204
+ if (oldKey [oldKey .length - 2 ] == Op .PUTIFABSENT .getCode ()
205
+ || oldKey [oldKey .length - 2 ] == Op .PUT .getCode ()) {
206
+ boolean unique = index .isUnique ();
207
+ if (unique ) {
208
+ throw new DuplicateEntryException ("Duplicate entry "
209
+ + TransactionUtil .duplicateEntryKey (tableId , key , txnId ) + " for key 'PRIMARY'" );
210
+ }
211
+ if (param .getUpdateMapping () != null && param .getUpdates () != null ) {
212
+ tmpLocalStore .delete (oldKey );
213
+ }
214
+ }
215
+ } else {
216
+ if (context .isDuplicateKey ()) {
217
+ tmpLocalStore .put (new KeyValue (deleteKey , Arrays .copyOf (oldKv .getValue (), oldKv .getValue ().length )));
218
+ }
219
+ }
220
+ }
221
+ }
136
222
}
137
223
Object [] newTuple = (Object []) schema .convertFrom (tuple , ValueConverter .INSTANCE );
138
224
KeyValue keyValue = wrap (codec ::encode ).apply (newTuple );
@@ -172,15 +258,18 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
172
258
if (context .isDuplicateKey ()) {
173
259
// insert into ... on duplicate key update ...
174
260
Pair <KeyValue , Long > pair = generateNewKv (
175
- tuple ,
261
+ primaryOldTuple ,
176
262
param ,
177
263
partId ,
178
264
codec ,
179
265
newTuple ,
180
266
txnIdByte ,
181
267
tableIdByte ,
182
268
partIdByte ,
183
- len );
269
+ len ,
270
+ schema ,
271
+ context ,
272
+ indexTable );
184
273
byte [] extraKey = ByteUtils .encode (
185
274
CommonId .CommonType .TXN_CACHE_EXTRA_DATA ,
186
275
key ,
@@ -252,15 +341,18 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
252
341
Pair <KeyValue , Long > pair = null ;
253
342
if (context .isDuplicateKey ()) {
254
343
pair = generateNewKv (
255
- tuple ,
344
+ primaryOldTuple ,
256
345
param ,
257
346
partId ,
258
347
codec ,
259
348
newTuple ,
260
349
txnIdByte ,
261
350
tableIdByte ,
262
351
partIdByte ,
263
- len );
352
+ len ,
353
+ schema ,
354
+ context ,
355
+ indexTable );
264
356
}
265
357
// extraKeyValue [12_jobId_tableId_partId_a_none, oldValue]
266
358
byte [] extraKey = ByteUtils .encode (
@@ -305,15 +397,18 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
305
397
Pair <KeyValue , Long > pair = null ;
306
398
if (context .isDuplicateKey ()) {
307
399
pair = generateNewKv (
308
- tuple ,
400
+ primaryOldTuple ,
309
401
param ,
310
402
partId ,
311
403
codec ,
312
404
newTuple ,
313
405
txnIdByte ,
314
406
tableIdByte ,
315
407
partIdByte ,
316
- len );
408
+ len ,
409
+ schema ,
410
+ context ,
411
+ indexTable );
317
412
}
318
413
byte [] rollBackKey = ByteUtils .getKeyByOp (
319
414
CommonId .CommonType .TXN_CACHE_RESIDUAL_LOCK , Op .DELETE , dataKey
@@ -390,6 +485,9 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
390
485
context .setDuplicateKey (true );
391
486
oldTuple = codec .decode (oldKv );
392
487
}
488
+ if (oldTuple == null ) {
489
+ oldTuple = newTuple ;
490
+ }
393
491
}
394
492
long num = 1L ;
395
493
if (keyValues != null && !keyValues .isEmpty ()) {
@@ -402,15 +500,18 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
402
500
|| oldKey [oldKey .length - 2 ] == Op .PUT .getCode ()) {
403
501
if (param .getUpdateMapping () != null && param .getUpdates () != null ) {
404
502
pair = generateNewKv (
405
- tuple ,
503
+ primaryOldTuple ,
406
504
param ,
407
505
partId ,
408
506
codec ,
409
507
oldTuple ,
410
508
txnIdByte ,
411
509
tableIdByte ,
412
510
partIdByte ,
413
- len );
511
+ len ,
512
+ schema ,
513
+ context ,
514
+ indexTable );
414
515
op = Op .PUT ;
415
516
} else {
416
517
if (param .isIgnore ()) {
@@ -463,15 +564,18 @@ protected boolean pushTuple(Context context, Object[] tuple, Vertex vertex) {
463
564
}
464
565
if (context .isDuplicateKey ()) {
465
566
pair = generateNewKv (
466
- tuple ,
567
+ primaryOldTuple ,
467
568
param ,
468
569
partId ,
469
570
codec ,
470
571
oldTuple ,
471
572
txnIdByte ,
472
573
tableIdByte ,
473
574
partIdByte ,
474
- len );
575
+ len ,
576
+ schema ,
577
+ context ,
578
+ indexTable );
475
579
} else {
476
580
if (!param .isReplaceInto ()) {
477
581
keyValue .setKey (
@@ -535,21 +639,32 @@ private static Pair<KeyValue, Long> generateNewKv(Object[] tuple,
535
639
byte [] txnIdByte ,
536
640
byte [] tableIdByte ,
537
641
byte [] partIdByte ,
538
- int len ) {
642
+ int len ,
643
+ DingoType schema ,
644
+ Context context ,
645
+ Table indexTable ) {
539
646
KeyValue insertUpKv ;
540
647
TupleMapping mapping = param .getUpdateMapping ();
541
648
List <SqlExpr > updates = param .getUpdates ();
649
+ Object [] revMap = mapping .revMap (tuple );
542
650
long updateNum = 0L ;
543
651
for (int i = 0 ; i < mapping .size (); i ++) {
544
- Object newValue = updates .get (i ).eval (tuple );
545
- int index = mapping .get (i );
652
+ Object newValue = updates .get (i ).eval (revMap );
653
+ if (newValue .equals ("NULL" )) {
654
+ newValue = null ;
655
+ }
656
+ int index ;
657
+ if (indexTable != null ) {
658
+ index = indexTable .mapping ().get (i );
659
+ } else {
660
+ index = mapping .get (i );
661
+ }
546
662
if ((newTuple [index ] == null && newValue != null )
547
663
|| (newTuple [index ] != null && !newTuple [index ].equals (newValue ))) {
548
664
newTuple [index ] = newValue ;
549
665
updateNum ++;
550
666
}
551
667
}
552
- DingoType schema = param .getSchema ();
553
668
Object [] convertTuple = (Object []) schema .convertFrom (newTuple , ValueConverter .INSTANCE );
554
669
KeyValue updateKv = wrap (codec ::encode ).apply (convertTuple );
555
670
CodecService .getDefault ().setId (updateKv .getKey (), partId .domain );
0 commit comments