Skip to content

Commit 5b240c4

Browse files
authored
Merge pull request #7872 from FirebirdSQL/work/gh-7810-2
Additional fixes for #7810 (Improve SKIP LOCKED implementation)
2 parents e23f030 + 173f32b commit 5b240c4

File tree

6 files changed

+50
-20
lines changed

6 files changed

+50
-20
lines changed

doc/sql.extensions/README.skip_locked.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ DELETE FROM <sometable>
4646

4747
## Notes
4848

49-
As it happens with subclauses `FIRST`/`SKIP`/`ROWS`/`OFFSET`/`FETCH` record lock
50-
(and "skip locked" check) is done in between of skip (`SKIP`/`ROWS`/`OFFSET`/`FETCH`) and
51-
limit (`FIRST`/`ROWS`/`OFFSET`/`FETCH`) checks.
49+
If statement have both `SKIP LOCKED` and `SKIP`/`ROWS` subclauses, some locked rows
50+
could be skipped before `SKIP`/`ROWS` subclause would account it, thus skipping more
51+
rows than specified in `SKIP`/`ROWS`.
5252

5353
## Examples
5454

src/dsql/StmtNodes.cpp

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ namespace
178178
m_savepoint.rollback();
179179
}
180180

181-
private:
182181
// Prohibit unwanted creation/copying
183182
CondSavepointAndMarker(const CondSavepointAndMarker&) = delete;
184183
CondSavepointAndMarker& operator=(const CondSavepointAndMarker&) = delete;
185184

185+
private:
186186
AutoSavePoint m_savepoint;
187187
Savepoint::ChangeMarker m_marker;
188188
};
@@ -2273,9 +2273,9 @@ const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, Request* request,
22732273
//--------------------
22742274

22752275

2276-
static RegisterNode<EraseNode> regEraseNode({blr_erase});
2276+
static RegisterNode<EraseNode> regEraseNode({blr_erase, blr_erase2});
22772277

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)
22792279
{
22802280
const USHORT n = csb->csb_blr_reader.getByte();
22812281

@@ -2288,6 +2288,9 @@ DmlNode* EraseNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch
22882288
if (csb->csb_blr_reader.peekByte() == blr_marks)
22892289
node->marks |= PAR_marks(csb);
22902290

2291+
if (blrOp == blr_erase2)
2292+
node->returningStatement = PAR_parse_stmt(tdbb, csb);
2293+
22912294
return node;
22922295
}
22932296

@@ -2385,6 +2388,7 @@ string EraseNode::internalPrint(NodePrinter& printer) const
23852388
NODE_PRINT(printer, dsqlSkipLocked);
23862389
NODE_PRINT(printer, statement);
23872390
NODE_PRINT(printer, subStatement);
2391+
NODE_PRINT(printer, returningStatement);
23882392
NODE_PRINT(printer, stream);
23892393
NODE_PRINT(printer, marks);
23902394

@@ -2397,11 +2401,14 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
23972401
{
23982402
std::optional<USHORT> tableNumber;
23992403

2404+
const bool skipLocked = dsqlRse && dsqlRse->hasSkipLocked();
2405+
24002406
if (dsqlReturning && !dsqlScratch->isPsql())
24012407
{
24022408
if (dsqlCursorName.isEmpty())
24032409
{
2404-
dsqlScratch->appendUChar(blr_begin);
2410+
if (!skipLocked)
2411+
dsqlScratch->appendUChar(blr_begin);
24052412

24062413
tableNumber = dsqlScratch->localTableNumber++;
24072414
dsqlGenReturningLocalTableDecl(dsqlScratch, tableNumber.value());
@@ -2422,28 +2429,31 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
24222429

24232430
const auto* context = dsqlContext ? dsqlContext : dsqlRelation->dsqlContext;
24242431

2425-
if (dsqlReturning)
2432+
if (dsqlReturning && !skipLocked)
24262433
{
24272434
dsqlScratch->appendUChar(blr_begin);
2435+
dsqlGenReturning(dsqlScratch, dsqlReturning, tableNumber);
24282436
}
24292437

2430-
dsqlScratch->appendUChar(blr_erase);
2438+
dsqlScratch->appendUChar(dsqlReturning && skipLocked ? blr_erase2 : blr_erase);
24312439
GEN_stuff_context(dsqlScratch, context);
24322440

24332441
if (marks)
24342442
dsqlScratch->putBlrMarkers(marks);
24352443

24362444
if (dsqlReturning)
24372445
{
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);
24412450

24422451
if (!dsqlScratch->isPsql() && dsqlCursorName.isEmpty())
24432452
{
24442453
dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, tableNumber.value());
24452454

2446-
dsqlScratch->appendUChar(blr_end);
2455+
if (!skipLocked)
2456+
dsqlScratch->appendUChar(blr_end);
24472457
}
24482458
}
24492459
}
@@ -2455,6 +2465,9 @@ EraseNode* EraseNode::pass1(thread_db* tdbb, CompilerScratch* csb)
24552465
doPass1(tdbb, csb, statement.getAddress());
24562466
doPass1(tdbb, csb, subStatement.getAddress());
24572467

2468+
AutoSetRestore<bool> autoReturningExpr(&csb->csb_returning_expr, true);
2469+
doPass1(tdbb, csb, returningStatement.getAddress());
2470+
24582471
return this;
24592472
}
24602473

@@ -2571,6 +2584,7 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod
25712584
EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb)
25722585
{
25732586
doPass2(tdbb, csb, statement.getAddress(), this);
2587+
doPass2(tdbb, csb, returningStatement.getAddress(), this);
25742588
doPass2(tdbb, csb, subStatement.getAddress(), this);
25752589

25762590
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*
26492663
// Perform erase operation.
26502664
const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const
26512665
{
2666+
impure_state* impure = request->getImpure<impure_state>(impureOffset);
2667+
26522668
jrd_tra* transaction = request->req_transaction;
26532669
record_param* rpb = &request->req_rpb[stream];
26542670
jrd_rel* relation = rpb->rpb_relation;
@@ -2674,6 +2690,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
26742690
}
26752691

26762692
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+
}
26772699
break;
26782700

26792701
default:
@@ -2735,9 +2757,9 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
27352757

27362758
if (!VIO_erase(tdbb, rpb, transaction))
27372759
{
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
27412763
// will be changed, the code below should be changed accordingly.
27422764

27432765
if (skipLocked)
@@ -2789,6 +2811,13 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
27892811
}
27902812
}
27912813

2814+
if (returningStatement)
2815+
{
2816+
impure->sta_state = 1;
2817+
request->req_operation = Request::req_evaluate;
2818+
return returningStatement;
2819+
}
2820+
27922821
rpb->rpb_number.setValid(false);
27932822

27942823
return parentStmt;
@@ -7774,7 +7803,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
77747803

77757804
SavepointChangeMarker scMarker(transaction);
77767805

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
77787807
// transaction and update should be skipped.
77797808
const bool skipLocked = orgRpb->rpb_stream_flags & RPB_s_skipLocked;
77807809
CondSavepointAndMarker spPreTriggers(tdbb, transaction,

src/dsql/StmtNodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ class EraseNode final : public TypedNode<StmtNode, StmtNode::TYPE_ERASE>
552552
dsql_ctx* dsqlContext = nullptr;
553553
NestConst<StmtNode> statement;
554554
NestConst<StmtNode> subStatement;
555+
NestConst<StmtNode> returningStatement;
555556
NestConst<ForNode> forNode; // parent implicit cursor, if present
556557
StreamType stream = 0;
557558
unsigned marks = 0; // see StmtNode::IUD_MARK_xxx

src/include/firebird/impl/blr.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,7 @@
309309
#define blr_agg_list (unsigned char)170
310310
#define blr_agg_list_distinct (unsigned char)171
311311
#define blr_modify2 (unsigned char)172
312-
313-
// unused codes: 173
312+
#define blr_erase2 (unsigned char)173
314313

315314
/* FB 1.0 specific BLR */
316315

src/jrd/blp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ static const struct
199199
{"agg_list", two}, // 170
200200
{"agg_list_distinct", two},
201201
{"modify2", modify2},
202-
{NULL, NULL},
202+
{"erase2", erase2},
203203
// New BLR in FB1
204204
{"current_role", zero},
205205
{"skip", one},

src/yvalve/gds.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ static const UCHAR
414414
store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0},
415415
marks[] = { op_byte, op_literal, op_line, op_verb, 0},
416416
erase[] = { op_erase, 0},
417+
erase2[] = { op_erase, op_verb, 0},
417418
local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0},
418419
outer_map[] = { op_outer_map, 0 },
419420
in_list[] = { op_verb, op_line, op_word, op_line, op_args, 0},

0 commit comments

Comments
 (0)