Skip to content

Commit c7bd902

Browse files
committed
Rework positional DML because it cannot use additional parameters in standard messages anymore
1 parent 73febec commit c7bd902

File tree

11 files changed

+278
-257
lines changed

11 files changed

+278
-257
lines changed

src/dsql/DsqlBatch.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,6 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, DsqlDmlRequest* req, IMessageMetadat
203203

204204
const auto statement = req->getDsqlStatement();
205205

206-
if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN)
207-
{
208-
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
209-
Arg::Gds(isc_bad_req_handle));
210-
}
211-
212206
switch (statement->getType())
213207
{
214208
case DsqlStatement::TYPE_INSERT:
@@ -665,6 +659,8 @@ Firebird::IBatchCompletionState* DsqlBatch::execute(thread_db* tdbb)
665659
// map message to internal engine format
666660
// Do it one time only to avoid parsing its metadata for every message
667661
m_dsqlRequest->metadataToFormat(m_meta, sendMessage);
662+
// Using of positional DML in batch is strange but not forbidden
663+
m_dsqlRequest->mapCursorKey(tdbb);
668664
bool startRequest = true;
669665

670666
bool isExecBlock = dStmt->getType() == DsqlStatement::TYPE_EXEC_BLOCK;

src/dsql/DsqlCompilerScratch.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ typedef Firebird::Pair<
5151
Firebird::NonPooled<NestConst<ValueListNode>, NestConst<ValueListNode>>> ReturningClause;
5252

5353

54-
// DSQL Compiler scratch block - may be discarded after compilation in the future.
54+
// DSQL Compiler scratch block.
55+
// Contains any kind of objects used during DsqlStatement compilation
56+
// Is deleted with its pool as soon as DsqlStatement is fully formed in prepareStatement()
57+
// or with the statement itself (if the statement reqested it returning true from shouldPreserveScratch())
5558
class DsqlCompilerScratch : public BlrDebugWriter
5659
{
5760
public:
@@ -106,7 +109,8 @@ class DsqlCompilerScratch : public BlrDebugWriter
106109

107110
protected:
108111
// DsqlCompilerScratch should never be destroyed using delete.
109-
// It dies together with it's pool in release_request().
112+
// It dies together with it's pool.
113+
void operator delete(void*) = delete;
110114
~DsqlCompilerScratch()
111115
{
112116
}
@@ -318,6 +322,7 @@ class DsqlCompilerScratch : public BlrDebugWriter
318322
DsqlCompilerScratch* mainScratch = nullptr;
319323
Firebird::NonPooledMap<USHORT, USHORT> outerMessagesMap; // <outer, inner>
320324
Firebird::NonPooledMap<USHORT, USHORT> outerVarsMap; // <outer, inner>
325+
dsql_msg* recordKeyMessage = nullptr; // Side message for positioned DML
321326

322327
private:
323328
Firebird::HalfStaticArray<SelectExprNode*, 4> ctes; // common table expressions

src/dsql/DsqlRequests.cpp

Lines changed: 184 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,59 @@ DsqlRequest::~DsqlRequest()
5656
{
5757
}
5858

59+
void DsqlRequest::releaseRequest(thread_db* tdbb)
60+
{
61+
if (req_timer)
62+
{
63+
req_timer->stop();
64+
req_timer = nullptr;
65+
}
66+
67+
// Prevent new children from appear
68+
if (req_cursor_name.hasData())
69+
req_dbb->dbb_cursors.remove(req_cursor_name);
70+
71+
// If request is parent, orphan the children and release a portion of their requests
72+
73+
for (auto childStatement : cursors)
74+
{
75+
// Without parent request prepared DsqlRequest will throw error on execute which is exactly what one would expect
76+
childStatement->onReferencedCursorClose();
77+
78+
// hvlad: lines below is commented out as
79+
// - child is already unlinked from its parent request
80+
// - we should not free child's sql text until its owner request is alive
81+
// It seems to me we should destroy owner request here, not a child
82+
// statement - as it always was before
83+
84+
//Jrd::ContextPoolHolder context(tdbb, &childStatement->getPool());
85+
//releaseStatement(childStatement);
86+
}
87+
88+
// If the request had an open cursor, close it
89+
90+
if (req_cursor)
91+
DsqlCursor::close(tdbb, req_cursor);
92+
93+
if (req_batch)
94+
{
95+
delete req_batch;
96+
req_batch = nullptr;
97+
}
98+
99+
Jrd::Attachment* att = req_dbb->dbb_attachment;
100+
const bool need_trace_free = req_traced && TraceManager::need_dsql_free(att);
101+
if (need_trace_free)
102+
{
103+
TraceSQLStatementImpl stmt(this, nullptr, nullptr);
104+
TraceManager::event_dsql_free(att, &stmt, DSQL_drop);
105+
}
106+
107+
// If a request has been compiled, release it now
108+
if (getRequest())
109+
EXE_release(tdbb, getRequest());
110+
}
111+
59112
void DsqlRequest::setCursor(thread_db* /*tdbb*/, const TEXT* /*name*/)
60113
{
61114
status_exception::raise(
@@ -166,71 +219,102 @@ void DsqlRequest::destroy(thread_db* tdbb, DsqlRequest* dsqlRequest)
166219
{
167220
SET_TDBB(tdbb);
168221

169-
if (dsqlRequest->req_timer)
222+
// Increase the statement refCount so its pool is not destroyed before the request is gone.
223+
auto dsqlStatement = dsqlRequest->getDsqlStatement();
224+
225+
// Let request to clean itself
226+
try
170227
{
171-
dsqlRequest->req_timer->stop();
172-
dsqlRequest->req_timer = nullptr;
228+
dsqlRequest->releaseRequest(tdbb);
173229
}
174-
175-
// If request is parent, orphan the children and release a portion of their requests
176-
177-
for (auto childStatement : dsqlRequest->cursors)
230+
catch(...)
178231
{
179-
childStatement->addFlags(DsqlStatement::FLAG_ORPHAN);
180-
childStatement->setParentRequest(nullptr);
232+
fb_assert(false);
233+
}
181234

182-
// hvlad: lines below is commented out as
183-
// - child is already unlinked from its parent request
184-
// - we should not free child's sql text until its owner request is alive
185-
// It seems to me we should destroy owner request here, not a child
186-
// statement - as it always was before
235+
// Release the entire request
236+
delete dsqlRequest;
237+
dsqlStatement = nullptr;
238+
}
187239

188-
//Jrd::ContextPoolHolder context(tdbb, &childStatement->getPool());
189-
//releaseStatement(childStatement);
190-
}
240+
// DsqlDmlRequest
191241

192-
// If the request had an open cursor, close it
242+
DsqlDmlRequest::DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlDmlStatement* aStatement)
243+
: DsqlRequest(pool, dbb, aStatement)
244+
{
245+
request = aStatement->getStatement()->findRequest(tdbb);
246+
tdbb->getAttachment()->att_requests.add(request);
193247

194-
if (dsqlRequest->req_cursor)
195-
DsqlCursor::close(tdbb, dsqlRequest->req_cursor);
248+
// If this is positional DML - subscribe to parent cursor as well
196249

197-
if (dsqlRequest->req_batch)
250+
if (aStatement->parentCursorName.hasData())
198251
{
199-
delete dsqlRequest->req_batch;
200-
dsqlRequest->req_batch = nullptr;
201-
}
252+
const auto* const symbol = dbb->dbb_cursors.get(aStatement->parentCursorName.c_str());
202253

203-
Jrd::Attachment* att = dsqlRequest->req_dbb->dbb_attachment;
204-
const bool need_trace_free = dsqlRequest->req_traced && TraceManager::need_dsql_free(att);
205-
if (need_trace_free)
206-
{
207-
TraceSQLStatementImpl stmt(dsqlRequest, nullptr, nullptr);
208-
TraceManager::event_dsql_free(att, &stmt, DSQL_drop);
209-
}
254+
if (!symbol)
255+
{
256+
// cursor is not found
257+
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
258+
Arg::Gds(isc_dsql_cursor_err) <<
259+
Arg::Gds(isc_dsql_cursor_not_found) << aStatement->parentCursorName);
260+
}
210261

211-
if (dsqlRequest->req_cursor_name.hasData())
212-
dsqlRequest->req_dbb->dbb_cursors.remove(dsqlRequest->req_cursor_name);
262+
parentRequest = *symbol;
263+
fb_assert(parentRequest != nullptr);
213264

214-
// If a request has been compiled, release it now
215-
if (dsqlRequest->getRequest())
216-
EXE_release(tdbb, dsqlRequest->getRequest());
265+
// Verify that the cursor is appropriate and updatable
217266

218-
// Increase the statement refCount so its pool is not destroyed before the request is gone.
219-
auto dsqlStatement = dsqlRequest->getDsqlStatement();
267+
if (parentRequest->getDsqlStatement()->getType() != DsqlStatement::TYPE_SELECT_UPD)
268+
{
269+
// cursor is not updatable
270+
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-510) <<
271+
Arg::Gds(isc_dsql_cursor_update_err) << aStatement->parentCursorName);
272+
}
220273

221-
// Release the entire request
222-
delete dsqlRequest;
274+
// Check that it contains this relation name
275+
Request* request = parentRequest->getRequest();
276+
fb_assert(request->req_rpb.getCount() > 0 && request->req_rpb[0].rpb_relation != nullptr);
223277

224-
dsqlStatement = nullptr;
225-
}
278+
const MetaName& relName = request->req_rpb[0].rpb_relation->rel_name;
279+
bool found = false;
280+
for (FB_SIZE_T i = 0; i < request->req_rpb.getCount(); ++i)
281+
{
282+
jrd_rel* relation = request->req_rpb[i].rpb_relation;
226283

227-
// DsqlDmlRequest
284+
if (relation && relation->rel_name == relName)
285+
{
286+
if (found)
287+
{
288+
// Relation is used twice in cursor
289+
ERRD_post(Arg::Gds(isc_dsql_cursor_err)
290+
<< Arg::Gds(isc_dsql_cursor_rel_ambiguous) << relName << aStatement->parentCursorName);
291+
}
292+
parentContext = i;
293+
found = true;
294+
}
295+
}
228296

229-
DsqlDmlRequest::DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement)
230-
: DsqlRequest(pool, dbb, aStatement)
297+
if (!found)
298+
{
299+
// Relation is not in cursor
300+
ERRD_post(Arg::Gds(isc_dsql_cursor_err)
301+
<< Arg::Gds(isc_dsql_cursor_rel_not_found) << relName << aStatement->parentCursorName);
302+
}
303+
parentRequest->cursors.add(this);
304+
}
305+
}
306+
307+
void DsqlDmlRequest::releaseRequest(thread_db* tdbb)
231308
{
232-
request = aStatement->getStatement()->findRequest(tdbb);
233-
tdbb->getAttachment()->att_requests.add(request);
309+
// If request is a child - unsubscribe
310+
if (parentRequest)
311+
{
312+
parentRequest->cursors.findAndRemove(this);
313+
parentRequest = nullptr;
314+
}
315+
316+
// Let ancestor do cleanup as well
317+
DsqlRequest::releaseRequest(tdbb);
234318
}
235319

236320
Statement* DsqlDmlRequest::getStatement() const
@@ -417,12 +501,6 @@ DsqlCursor* DsqlDmlRequest::openCursor(thread_db* tdbb, jrd_tra** traHandle,
417501

418502
Jrd::ContextPoolHolder context(tdbb, &getPool());
419503

420-
if (dsqlStatement->getFlags() & DsqlStatement::FLAG_ORPHAN)
421-
{
422-
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
423-
Arg::Gds(isc_bad_req_handle));
424-
}
425-
426504
// Validate transaction handle
427505

428506
if (!*traHandle)
@@ -590,6 +668,8 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
590668
metadataToFormat(inMetadata, message);
591669
}
592670

671+
mapCursorKey(tdbb);
672+
593673
// we need to set new format before tracing of execution start to let trace
594674
// manager know statement parameters values
595675
TraceDSQLExecute trace(req_dbb->dbb_attachment, this, inMsg);
@@ -792,6 +872,57 @@ void DsqlDmlRequest::metadataToFormat(Firebird::IMessageMetadata* meta, const ds
792872
msg->setFormat(request, newFormat);
793873
}
794874

875+
void DsqlDmlRequest::mapCursorKey(thread_db* tdbb)
876+
{
877+
const auto dsqlStatement = getDsqlStatement();
878+
if (!dsqlStatement->parentCursorName.hasData())
879+
return;
880+
881+
if (!parentRequest) // It has been already closed
882+
{
883+
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
884+
Arg::Gds(isc_dsql_cursor_err) <<
885+
Arg::Gds(isc_dsql_cursor_not_found) << dsqlStatement->parentCursorName);
886+
}
887+
888+
Request* master = parentRequest->getRequest();
889+
fb_assert(request);
890+
fb_assert(master);
891+
892+
record_param& rpb = master->req_rpb[parentContext];
893+
if (!rpb.rpb_number.isValid() || rpb.rpb_number.isBof() || !rpb.rpb_relation)
894+
{
895+
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
896+
Arg::Gds(isc_dsql_cursor_err) <<
897+
Arg::Gds(isc_cursor_not_positioned) << dsqlStatement->parentCursorName);
898+
}
899+
900+
// Assign record key
901+
MessageNode* message = request->getStatement()->getMessage(2);
902+
fb_assert(message);
903+
904+
dsc desc = message->getFormat(request)->fmt_desc[0];
905+
UCHAR* msgBuffer = message->getBuffer(request);
906+
desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address;
907+
908+
RecordNumber::Packed dbKey = {};
909+
dbKey.bid_encode(rpb.rpb_number.getValue() + 1);
910+
dbKey.bid_relation_id = rpb.rpb_relation->rel_id;
911+
912+
dsc parentDesc;
913+
parentDesc.makeDbkey(&dbKey);
914+
915+
MOVD_move(tdbb, &parentDesc, &desc);
916+
917+
// Assign version number
918+
919+
desc = message->getFormat(request)->fmt_desc[1];
920+
desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address;
921+
922+
parentDesc.makeDbkey(&rpb.rpb_transaction_nr);
923+
924+
MOVD_move(tdbb, &parentDesc, &desc);
925+
}
795926

796927
// DsqlDdlRequest
797928

0 commit comments

Comments
 (0)