@@ -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+
59112void 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
236320Statement* 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