@@ -152,6 +152,40 @@ namespace
152
152
{}
153
153
};
154
154
155
+ // Combined conditional savepoint and its change marker.
156
+ class CondSavepointAndMarker
157
+ {
158
+ public:
159
+ CondSavepointAndMarker (thread_db* tdbb, jrd_tra* trans, bool cond) :
160
+ m_savepoint (tdbb, trans, cond),
161
+ m_marker (cond ? trans->tra_save_point : nullptr )
162
+ {}
163
+
164
+ ~CondSavepointAndMarker ()
165
+ {
166
+ rollback ();
167
+ }
168
+
169
+ void release ()
170
+ {
171
+ m_marker.done ();
172
+ m_savepoint.release ();
173
+ }
174
+
175
+ void rollback ()
176
+ {
177
+ m_marker.done ();
178
+ m_savepoint.rollback ();
179
+ }
180
+
181
+ private:
182
+ // Prohibit unwanted creation/copying
183
+ CondSavepointAndMarker (const CondSavepointAndMarker&) = delete ;
184
+ CondSavepointAndMarker& operator =(const CondSavepointAndMarker&) = delete ;
185
+
186
+ AutoSavePoint m_savepoint;
187
+ Savepoint::ChangeMarker m_marker;
188
+ };
155
189
} // namespace
156
190
157
191
@@ -2314,7 +2348,7 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
2314
2348
PASS1_limit (dsqlScratch, dsqlRows->length , dsqlRows->skip , rse);
2315
2349
2316
2350
if (dsqlSkipLocked)
2317
- rse->flags |= RseNode::FLAG_WRITELOCK | RseNode:: FLAG_SKIP_LOCKED;
2351
+ rse->flags |= RseNode::FLAG_SKIP_LOCKED;
2318
2352
}
2319
2353
2320
2354
if (dsqlReturning && dsqlScratch->isPsql ())
@@ -2348,6 +2382,7 @@ string EraseNode::internalPrint(NodePrinter& printer) const
2348
2382
NODE_PRINT (printer, dsqlReturning);
2349
2383
NODE_PRINT (printer, dsqlRse);
2350
2384
NODE_PRINT (printer, dsqlContext);
2385
+ NODE_PRINT (printer, dsqlSkipLocked);
2351
2386
NODE_PRINT (printer, statement);
2352
2387
NODE_PRINT (printer, subStatement);
2353
2388
NODE_PRINT (printer, stream);
@@ -2356,6 +2391,8 @@ string EraseNode::internalPrint(NodePrinter& printer) const
2356
2391
return " EraseNode" ;
2357
2392
}
2358
2393
2394
+ // The EraseNode::erase() depends on generated nodes layout in case when
2395
+ // RETURNING specified.
2359
2396
void EraseNode::genBlr (DsqlCompilerScratch* dsqlScratch)
2360
2397
{
2361
2398
std::optional<USHORT> tableNumber;
@@ -2388,7 +2425,6 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
2388
2425
if (dsqlReturning)
2389
2426
{
2390
2427
dsqlScratch->appendUChar (blr_begin);
2391
- dsqlGenReturning (dsqlScratch, dsqlReturning, tableNumber);
2392
2428
}
2393
2429
2394
2430
dsqlScratch->appendUChar (blr_erase);
@@ -2399,6 +2435,8 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
2399
2435
2400
2436
if (dsqlReturning)
2401
2437
{
2438
+ dsqlGenReturning (dsqlScratch, dsqlReturning, tableNumber);
2439
+
2402
2440
dsqlScratch->appendUChar (blr_end);
2403
2441
2404
2442
if (!dsqlScratch->isPsql () && dsqlCursorName.isEmpty ())
@@ -2665,7 +2703,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
2665
2703
2666
2704
if (rpb->rpb_runtime_flags & RPB_refetch)
2667
2705
{
2668
- VIO_refetch_record (tdbb, rpb, transaction, RecordLock::NONE , false );
2706
+ VIO_refetch_record (tdbb, rpb, transaction, false , false );
2669
2707
rpb->rpb_runtime_flags &= ~RPB_refetch;
2670
2708
}
2671
2709
@@ -2674,6 +2712,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
2674
2712
2675
2713
SavepointChangeMarker scMarker (transaction);
2676
2714
2715
+ // Prepare to undo changes by PRE-triggers if record is locked by another
2716
+ // transaction and delete should be skipped.
2717
+ const bool skipLocked = rpb->rpb_stream_flags & RPB_s_skipLocked;
2718
+ CondSavepointAndMarker spPreTriggers (tdbb, transaction,
2719
+ skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_pre_erase );
2720
+
2677
2721
// Handle pre-operation trigger.
2678
2722
preModifyEraseTriggers (tdbb, &relation->rel_pre_erase , whichTrig, rpb, NULL , TRIGGER_DELETE);
2679
2723
@@ -2683,23 +2727,36 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
2683
2727
VirtualTable::erase (tdbb, rpb);
2684
2728
else if (!relation->rel_view_rse )
2685
2729
{
2686
- // VIO_erase returns false if there is an update conflict in Read Consistency
2687
- // transaction. Before returning false it disables statement-level snapshot
2688
- // (via setting req_update_conflict flag) so re-fetch should see new data.
2730
+ // VIO_erase returns false if:
2731
+ // a) there is an update conflict in Read Consistency transaction.
2732
+ // Before returning false it disables statement-level snapshot (via
2733
+ // setting req_update_conflict flag) so re-fetch should see new data.
2734
+ // b) record is locked by another transaction and should be skipped.
2689
2735
2690
2736
if (!VIO_erase (tdbb, rpb, transaction))
2691
2737
{
2738
+ // Record was not deleted, flow control should be passed to the
2739
+ // parent ForNode. Note, If RETURNING clause was specified, then
2740
+ // parent node is CompoundStmtNode, not ForNode. If\when this
2741
+ // will be changed, the code below should be changed accordingly.
2742
+
2743
+ if (skipLocked)
2744
+ return forNode;
2745
+
2746
+ spPreTriggers.release ();
2747
+
2692
2748
forceWriteLock (tdbb, rpb, transaction);
2693
2749
2694
2750
if (!forNode)
2695
2751
restartRequest (request, transaction);
2696
2752
2697
2753
forNode->setWriteLockMode (request);
2698
- return parentStmt ;
2754
+ return forNode ;
2699
2755
}
2700
2756
2701
2757
REPL_erase (tdbb, rpb, transaction);
2702
2758
}
2759
+ spPreTriggers.release ();
2703
2760
2704
2761
// Handle post operation trigger.
2705
2762
if (relation->rel_post_erase && whichTrig != PRE_TRIG)
@@ -7199,6 +7256,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up
7199
7256
}
7200
7257
7201
7258
node->dsqlCursorName = dsqlCursorName;
7259
+ node->dsqlSkipLocked = dsqlSkipLocked;
7202
7260
7203
7261
if (dsqlCursorName.hasData () && dsqlScratch->isPsql ())
7204
7262
{
@@ -7305,7 +7363,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up
7305
7363
PASS1_limit (dsqlScratch, dsqlRows->length , dsqlRows->skip , rse);
7306
7364
7307
7365
if (dsqlSkipLocked)
7308
- rse->flags |= RseNode::FLAG_WRITELOCK | RseNode:: FLAG_SKIP_LOCKED;
7366
+ rse->flags |= RseNode::FLAG_SKIP_LOCKED;
7309
7367
}
7310
7368
7311
7369
node->dsqlReturning = dsqlProcessReturning (dsqlScratch,
@@ -7366,6 +7424,7 @@ string ModifyNode::internalPrint(NodePrinter& printer) const
7366
7424
NODE_PRINT (printer, dsqlRseFlags);
7367
7425
NODE_PRINT (printer, dsqlRse);
7368
7426
NODE_PRINT (printer, dsqlContext);
7427
+ NODE_PRINT (printer, dsqlSkipLocked);
7369
7428
NODE_PRINT (printer, statement);
7370
7429
NODE_PRINT (printer, statement2);
7371
7430
NODE_PRINT (printer, subMod);
@@ -7715,6 +7774,12 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
7715
7774
7716
7775
SavepointChangeMarker scMarker (transaction);
7717
7776
7777
+ // Prepare to undo changed by PRE-triggers if record is locked by another
7778
+ // transaction and update should be skipped.
7779
+ const bool skipLocked = orgRpb->rpb_stream_flags & RPB_s_skipLocked;
7780
+ CondSavepointAndMarker spPreTriggers (tdbb, transaction,
7781
+ skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_pre_modify );
7782
+
7718
7783
preModifyEraseTriggers (tdbb, &relation->rel_pre_modify , whichTrig, orgRpb, newRpb,
7719
7784
TRIGGER_UPDATE);
7720
7785
@@ -7727,24 +7792,31 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
7727
7792
VirtualTable::modify (tdbb, orgRpb, newRpb);
7728
7793
else if (!relation->rel_view_rse )
7729
7794
{
7730
- // VIO_modify returns false if there is an update conflict in Read Consistency
7731
- // transaction. Before returning false it disables statement-level snapshot
7732
- // (via setting req_update_conflict flag) so re-fetch should see new data.
7795
+ // VIO_modify returns false if:
7796
+ // a) there is an update conflict in Read Consistency transaction.
7797
+ // Before returning false it disables statement-level snapshot (via
7798
+ // setting req_update_conflict flag) so re-fetch should see new data.
7799
+ // b) record is locked by another transaction and should be skipped.
7733
7800
7734
7801
if (!VIO_modify (tdbb, orgRpb, newRpb, transaction))
7735
7802
{
7736
- forceWriteLock (tdbb, orgRpb, transaction);
7803
+ if (!skipLocked)
7804
+ {
7805
+ spPreTriggers.release ();
7806
+ forceWriteLock (tdbb, orgRpb, transaction);
7737
7807
7738
- if (!forNode)
7739
- restartRequest (request, transaction);
7808
+ if (!forNode)
7809
+ restartRequest (request, transaction);
7740
7810
7741
- forNode->setWriteLockMode (request);
7811
+ forNode->setWriteLockMode (request);
7812
+ }
7742
7813
return parentStmt;
7743
7814
}
7744
7815
7745
7816
IDX_modify (tdbb, orgRpb, newRpb, transaction);
7746
7817
REPL_modify (tdbb, orgRpb, newRpb, transaction);
7747
7818
}
7819
+ spPreTriggers.release ();
7748
7820
7749
7821
newRpb->rpb_number = orgRpb->rpb_number ;
7750
7822
newRpb->rpb_number .setValid (true );
@@ -7815,7 +7887,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
7815
7887
7816
7888
if (orgRpb->rpb_runtime_flags & RPB_refetch)
7817
7889
{
7818
- VIO_refetch_record (tdbb, orgRpb, transaction, RecordLock::NONE , false );
7890
+ VIO_refetch_record (tdbb, orgRpb, transaction, false , false );
7819
7891
orgRpb->rpb_runtime_flags &= ~RPB_refetch;
7820
7892
}
7821
7893
@@ -11281,13 +11353,13 @@ static void cleanupRpb(thread_db* tdbb, record_param* rpb)
11281
11353
// Try to set write lock on record until success or record exists
11282
11354
static void forceWriteLock (thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
11283
11355
{
11284
- while (VIO_refetch_record (tdbb, rpb, transaction, RecordLock::LOCK , true ))
11356
+ while (VIO_refetch_record (tdbb, rpb, transaction, true , true ))
11285
11357
{
11286
11358
rpb->rpb_runtime_flags &= ~RPB_refetch;
11287
11359
11288
11360
// VIO_writelock returns false if record has been deleted or modified
11289
11361
// by someone else.
11290
- if (VIO_writelock (tdbb, rpb, transaction, false ) == WriteLockResult::LOCKED)
11362
+ if (VIO_writelock (tdbb, rpb, transaction) == WriteLockResult::LOCKED)
11291
11363
break ;
11292
11364
}
11293
11365
}
0 commit comments