@@ -178,11 +178,11 @@ namespace
178
178
m_savepoint.rollback ();
179
179
}
180
180
181
- private:
182
181
// Prohibit unwanted creation/copying
183
182
CondSavepointAndMarker (const CondSavepointAndMarker&) = delete ;
184
183
CondSavepointAndMarker& operator =(const CondSavepointAndMarker&) = delete ;
185
184
185
+ private:
186
186
AutoSavePoint m_savepoint;
187
187
Savepoint::ChangeMarker m_marker;
188
188
};
@@ -2273,9 +2273,9 @@ const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, Request* request,
2273
2273
// --------------------
2274
2274
2275
2275
2276
- static RegisterNode<EraseNode> regEraseNode ({blr_erase});
2276
+ static RegisterNode<EraseNode> regEraseNode ({blr_erase, blr_erase2 });
2277
2277
2278
- DmlNode* EraseNode::parse (thread_db* /* tdbb*/ , MemoryPool& pool, CompilerScratch* csb, const UCHAR /* blrOp*/ )
2278
+ DmlNode* EraseNode::parse (thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
2279
2279
{
2280
2280
const USHORT n = csb->csb_blr_reader .getByte ();
2281
2281
@@ -2288,6 +2288,9 @@ DmlNode* EraseNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch
2288
2288
if (csb->csb_blr_reader .peekByte () == blr_marks)
2289
2289
node->marks |= PAR_marks (csb);
2290
2290
2291
+ if (blrOp == blr_erase2)
2292
+ node->returningStatement = PAR_parse_stmt (tdbb, csb);
2293
+
2291
2294
return node;
2292
2295
}
2293
2296
@@ -2385,6 +2388,7 @@ string EraseNode::internalPrint(NodePrinter& printer) const
2385
2388
NODE_PRINT (printer, dsqlSkipLocked);
2386
2389
NODE_PRINT (printer, statement);
2387
2390
NODE_PRINT (printer, subStatement);
2391
+ NODE_PRINT (printer, returningStatement);
2388
2392
NODE_PRINT (printer, stream);
2389
2393
NODE_PRINT (printer, marks);
2390
2394
@@ -2397,11 +2401,14 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
2397
2401
{
2398
2402
std::optional<USHORT> tableNumber;
2399
2403
2404
+ const bool skipLocked = dsqlRse && dsqlRse->hasSkipLocked ();
2405
+
2400
2406
if (dsqlReturning && !dsqlScratch->isPsql ())
2401
2407
{
2402
2408
if (dsqlCursorName.isEmpty ())
2403
2409
{
2404
- dsqlScratch->appendUChar (blr_begin);
2410
+ if (!skipLocked)
2411
+ dsqlScratch->appendUChar (blr_begin);
2405
2412
2406
2413
tableNumber = dsqlScratch->localTableNumber ++;
2407
2414
dsqlGenReturningLocalTableDecl (dsqlScratch, tableNumber.value ());
@@ -2422,28 +2429,31 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
2422
2429
2423
2430
const auto * context = dsqlContext ? dsqlContext : dsqlRelation->dsqlContext ;
2424
2431
2425
- if (dsqlReturning)
2432
+ if (dsqlReturning && !skipLocked )
2426
2433
{
2427
2434
dsqlScratch->appendUChar (blr_begin);
2435
+ dsqlGenReturning (dsqlScratch, dsqlReturning, tableNumber);
2428
2436
}
2429
2437
2430
- dsqlScratch->appendUChar (blr_erase);
2438
+ dsqlScratch->appendUChar (dsqlReturning && skipLocked ? blr_erase2 : blr_erase);
2431
2439
GEN_stuff_context (dsqlScratch, context);
2432
2440
2433
2441
if (marks)
2434
2442
dsqlScratch->putBlrMarkers (marks);
2435
2443
2436
2444
if (dsqlReturning)
2437
2445
{
2438
- dsqlGenReturning (dsqlScratch, dsqlReturning, tableNumber);
2439
-
2440
- dsqlScratch->appendUChar (blr_end);
2446
+ if (!skipLocked)
2447
+ dsqlScratch->appendUChar (blr_end);
2448
+ else
2449
+ dsqlGenReturning (dsqlScratch, dsqlReturning, tableNumber);
2441
2450
2442
2451
if (!dsqlScratch->isPsql () && dsqlCursorName.isEmpty ())
2443
2452
{
2444
2453
dsqlGenReturningLocalTableCursor (dsqlScratch, dsqlReturning, tableNumber.value ());
2445
2454
2446
- dsqlScratch->appendUChar (blr_end);
2455
+ if (!skipLocked)
2456
+ dsqlScratch->appendUChar (blr_end);
2447
2457
}
2448
2458
}
2449
2459
}
@@ -2455,6 +2465,9 @@ EraseNode* EraseNode::pass1(thread_db* tdbb, CompilerScratch* csb)
2455
2465
doPass1 (tdbb, csb, statement.getAddress ());
2456
2466
doPass1 (tdbb, csb, subStatement.getAddress ());
2457
2467
2468
+ AutoSetRestore<bool > autoReturningExpr (&csb->csb_returning_expr , true );
2469
+ doPass1 (tdbb, csb, returningStatement.getAddress ());
2470
+
2458
2471
return this ;
2459
2472
}
2460
2473
@@ -2571,6 +2584,7 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod
2571
2584
EraseNode* EraseNode::pass2 (thread_db* tdbb, CompilerScratch* csb)
2572
2585
{
2573
2586
doPass2 (tdbb, csb, statement.getAddress (), this );
2587
+ doPass2 (tdbb, csb, returningStatement.getAddress (), this );
2574
2588
doPass2 (tdbb, csb, subStatement.getAddress (), this );
2575
2589
2576
2590
const jrd_rel* const relation = csb->csb_rpt [stream].csb_relation ;
@@ -2649,6 +2663,8 @@ const StmtNode* EraseNode::execute(thread_db* tdbb, Request* request, ExeState*
2649
2663
// Perform erase operation.
2650
2664
const StmtNode* EraseNode::erase (thread_db* tdbb, Request* request, WhichTrigger whichTrig) const
2651
2665
{
2666
+ impure_state* impure = request->getImpure <impure_state>(impureOffset);
2667
+
2652
2668
jrd_tra* transaction = request->req_transaction ;
2653
2669
record_param* rpb = &request->req_rpb [stream];
2654
2670
jrd_rel* relation = rpb->rpb_relation ;
@@ -2674,6 +2690,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
2674
2690
}
2675
2691
2676
2692
case Request::req_return:
2693
+ if (impure->sta_state == 1 )
2694
+ {
2695
+ impure->sta_state = 0 ;
2696
+ rpb->rpb_number .setValid (false );
2697
+ return parentStmt;
2698
+ }
2677
2699
break ;
2678
2700
2679
2701
default :
@@ -2735,9 +2757,9 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
2735
2757
2736
2758
if (!VIO_erase (tdbb, rpb, transaction))
2737
2759
{
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
2760
+ // Record was not deleted, flow control should be passed to the parent
2761
+ // ForNode. Note, If RETURNING clause was specified and SKIP LOCKED was
2762
+ // not, then parent node is CompoundStmtNode, not ForNode. If\when this
2741
2763
// will be changed, the code below should be changed accordingly.
2742
2764
2743
2765
if (skipLocked)
@@ -2789,6 +2811,13 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
2789
2811
}
2790
2812
}
2791
2813
2814
+ if (returningStatement)
2815
+ {
2816
+ impure->sta_state = 1 ;
2817
+ request->req_operation = Request::req_evaluate;
2818
+ return returningStatement;
2819
+ }
2820
+
2792
2821
rpb->rpb_number .setValid (false );
2793
2822
2794
2823
return parentStmt;
@@ -7774,7 +7803,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
7774
7803
7775
7804
SavepointChangeMarker scMarker (transaction);
7776
7805
7777
- // Prepare to undo changed by PRE-triggers if record is locked by another
7806
+ // Prepare to undo changes by PRE-triggers if record is locked by another
7778
7807
// transaction and update should be skipped.
7779
7808
const bool skipLocked = orgRpb->rpb_stream_flags & RPB_s_skipLocked;
7780
7809
CondSavepointAndMarker spPreTriggers (tdbb, transaction,
0 commit comments