From 391e7ef4fcd48838145075e72d29532c3dd00f2b Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 7 Feb 2022 15:52:12 -0300 Subject: [PATCH] Refactor statement and request parts so multiple DSQL requests could be created from a single compiled statement. Pave the ground for compiled statement cache. --- builds/win32/msvc15/engine.vcxproj | 4 + builds/win32/msvc15/engine.vcxproj.filters | 12 + src/dsql/DdlNodes.epp | 12 +- src/dsql/DdlNodes.h | 4 +- src/dsql/DsqlBatch.cpp | 24 +- src/dsql/DsqlBatch.h | 8 +- src/dsql/DsqlCompilerScratch.h | 13 +- src/dsql/DsqlCursor.cpp | 10 +- src/dsql/DsqlCursor.h | 6 +- src/dsql/DsqlRequests.cpp | 1123 ++++++++++++++ src/dsql/DsqlRequests.h | 271 ++++ src/dsql/DsqlStatements.cpp | 298 ++++ src/dsql/DsqlStatements.h | 292 ++++ src/dsql/Nodes.h | 8 +- src/dsql/PackageNodes.epp | 60 +- src/dsql/Parser.cpp | 10 +- src/dsql/Parser.h | 15 +- src/dsql/StmtNodes.cpp | 113 +- src/dsql/StmtNodes.h | 24 +- src/dsql/dsql.cpp | 1557 ++------------------ src/dsql/dsql.h | 321 +--- src/dsql/dsql_proto.h | 14 +- src/dsql/gen.cpp | 20 +- src/dsql/gen_proto.h | 2 +- src/dsql/make_proto.h | 2 +- src/dsql/metd_proto.h | 2 +- src/dsql/parse.y | 25 +- src/jrd/EngineInterface.h | 8 +- src/jrd/JrdStatement.cpp | 3 + src/jrd/PreparedStatement.cpp | 8 +- src/jrd/PreparedStatement.h | 6 +- src/jrd/ResultSet.cpp | 8 +- src/jrd/cmp.cpp | 58 +- src/jrd/cmp_proto.h | 5 +- src/jrd/exe_proto.h | 4 +- src/jrd/extds/InternalDS.cpp | 44 +- src/jrd/jrd.cpp | 73 +- src/jrd/jrd_proto.h | 5 +- src/jrd/opt.cpp | 6 +- src/jrd/opt_proto.h | 2 +- src/jrd/req.h | 4 +- src/jrd/trace/TraceDSQLHelpers.h | 24 +- src/jrd/trace/TraceJrdHelpers.h | 8 +- src/jrd/trace/TraceObjects.cpp | 20 +- src/jrd/trace/TraceObjects.h | 16 +- 45 files changed, 2452 insertions(+), 2100 deletions(-) create mode 100644 src/dsql/DsqlRequests.cpp create mode 100644 src/dsql/DsqlRequests.h create mode 100644 src/dsql/DsqlStatements.cpp create mode 100644 src/dsql/DsqlStatements.h diff --git a/builds/win32/msvc15/engine.vcxproj b/builds/win32/msvc15/engine.vcxproj index 89a580cde6..03e6ee6fd6 100644 --- a/builds/win32/msvc15/engine.vcxproj +++ b/builds/win32/msvc15/engine.vcxproj @@ -41,6 +41,8 @@ + + @@ -186,6 +188,8 @@ + + diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters index aa5308dc3e..43ca7bbb6b 100644 --- a/builds/win32/msvc15/engine.vcxproj.filters +++ b/builds/win32/msvc15/engine.vcxproj.filters @@ -138,6 +138,12 @@ DSQL + + DSQL + + + DSQL + DSQL @@ -545,6 +551,12 @@ Header files + + Header files + + + Header files + Header files diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 8ebcfca04f..2e8f65c333 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -2354,7 +2354,7 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(false); dsqlScratch->appendUChar(blr_end); @@ -2879,7 +2879,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS); bool modified = false; - DsqlCompiledStatement* statement = dsqlScratch->getStatement(); + DsqlStatement* statement = dsqlScratch->getStatement(); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) P IN RDB$PROCEDURES @@ -2973,7 +2973,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch P.RDB$PROCEDURE_TYPE.NULL = FALSE; P.RDB$PROCEDURE_TYPE = (USHORT) - (statement->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ? + (statement->getFlags() & DsqlStatement::FLAG_SELECTABLE ? prc_selectable : prc_executable); } } @@ -3249,7 +3249,7 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(true); dsqlScratch->appendUChar(blr_end); @@ -3780,7 +3780,7 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d // The statement type may have been set incorrectly when parsing // the trigger actions, so reset it to reflect the fact that this // is a data definition statement; also reset the ddl node. - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); } invalid = false; @@ -10742,7 +10742,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd (Arg::Gds(isc_adm_task_denied) << Arg::Gds(isc_miss_prvlg) << "CHANGE_MAPPING_RULES").raise(); if (from) - fromUtf8 = from->toUtf8(dsqlScratch); + fromUtf8 = from->toUtf8(transaction); if (global) { diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 3c07b01ac2..9c874557ae 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -1130,7 +1130,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->getStatement()->setType( - legacy ? DsqlCompiledStatement::TYPE_SET_GENERATOR : DsqlCompiledStatement::TYPE_DDL); + legacy ? DsqlStatement::TYPE_SET_GENERATOR : DsqlStatement::TYPE_DDL); return this; } @@ -2418,7 +2418,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->getStatement()->setType( - create ? DsqlCompiledStatement::TYPE_CREATE_DB : DsqlCompiledStatement::TYPE_DDL); + create ? DsqlStatement::TYPE_CREATE_DB : DsqlStatement::TYPE_DDL); return this; } diff --git a/src/dsql/DsqlBatch.cpp b/src/dsql/DsqlBatch.cpp index 81329807d6..0cdb773400 100644 --- a/src/dsql/DsqlBatch.cpp +++ b/src/dsql/DsqlBatch.cpp @@ -60,7 +60,7 @@ namespace { }; } -DsqlBatch::DsqlBatch(dsql_req* req, const dsql_msg* /*message*/, IMessageMetadata* inMeta, ClumpletReader& pb) +DsqlBatch::DsqlBatch(DsqlDmlRequest* req, const dsql_msg* /*message*/, IMessageMetadata* inMeta, ClumpletReader& pb) : m_request(req), m_batch(NULL), m_meta(inMeta), @@ -183,7 +183,7 @@ void DsqlBatch::setInterfacePtr(JBatch* interfacePtr) throw() m_batch = interfacePtr; } -DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inMetadata, +DsqlBatch* DsqlBatch::open(thread_db* tdbb, DsqlDmlRequest* req, IMessageMetadata* inMetadata, unsigned parLength, const UCHAR* par) { SET_TDBB(tdbb); @@ -205,15 +205,15 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inM // Sanity checks before creating batch - if (!req->req_request) + if (!req->getJrdRequest()) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << Arg::Gds(isc_unprepared_stmt)); } - const DsqlCompiledStatement* statement = req->getStatement(); + const DsqlStatement* statement = req->getStatement(); - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_req_handle)); @@ -221,11 +221,11 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inM switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_INSERT: - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_INSERT: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_EXEC_BLOCK: break; default: @@ -652,7 +652,7 @@ private: // execute request m_request->req_transaction = transaction; - jrd_req* req = m_request->req_request; + jrd_req* req = m_request->getJrdRequest(); fb_assert(req); // prepare completion interface @@ -662,7 +662,7 @@ private: const dsql_msg* message = m_request->getStatement()->getSendMsg(); bool startRequest = true; - bool isExecBlock = m_request->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK; + bool isExecBlock = m_request->getStatement()->getType() == DsqlStatement::TYPE_EXEC_BLOCK; const auto receiveMessage = isExecBlock ? m_request->getStatement()->getReceiveMsg() : nullptr; auto receiveMsgBuffer = isExecBlock ? m_request->req_msg_buffers[receiveMessage->msg_buffer_number] : nullptr; diff --git a/src/dsql/DsqlBatch.h b/src/dsql/DsqlBatch.h index 187fc34e4b..06e9182b64 100644 --- a/src/dsql/DsqlBatch.h +++ b/src/dsql/DsqlBatch.h @@ -41,7 +41,7 @@ class ClumpletReader; namespace Jrd { -class dsql_req; +class DsqlDmlRequest; class dsql_msg; class thread_db; class JBatch; @@ -50,7 +50,7 @@ class Attachment; class DsqlBatch { public: - DsqlBatch(dsql_req* req, const dsql_msg* message, Firebird::IMessageMetadata* inMetadata, + DsqlBatch(DsqlDmlRequest* req, const dsql_msg* message, Firebird::IMessageMetadata* inMetadata, Firebird::ClumpletReader& pb); ~DsqlBatch(); @@ -61,7 +61,7 @@ public: static const ULONG SIZEOF_BLOB_HEAD = sizeof(ISC_QUAD) + 2 * sizeof(ULONG); static const unsigned BLOB_STREAM_ALIGN = 4; - static DsqlBatch* open(thread_db* tdbb, dsql_req* req, Firebird::IMessageMetadata* inMetadata, + static DsqlBatch* open(thread_db* tdbb, DsqlDmlRequest* req, Firebird::IMessageMetadata* inMetadata, unsigned parLength, const UCHAR* par); Attachment* getAttachment() const; @@ -101,7 +101,7 @@ private: m_flags &= ~(1 << bit); } - dsql_req* const m_request; + DsqlDmlRequest* const m_request; JBatch* m_batch; Firebird::IMessageMetadata* m_meta; diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index beb0bb1eb4..a29731e380 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -24,6 +24,7 @@ #include "../jrd/jrd.h" #include "../dsql/dsql.h" +#include "../dsql/DsqlStatements.h" #include "../dsql/BlrDebugWriter.h" #include "../common/classes/array.h" #include "../jrd/MetaName.h" @@ -73,14 +74,13 @@ public: public: DsqlCompilerScratch(MemoryPool& p, dsql_dbb* aDbb, jrd_tra* aTransaction, - DsqlCompiledStatement* aStatement, DsqlCompilerScratch* aMainScratch = NULL) + DsqlStatement* aStatement = nullptr, DsqlCompilerScratch* aMainScratch = nullptr) : BlrDebugWriter(p), dbb(aDbb), transaction(aTransaction), statement(aStatement), flags(0), nestingLevel(0), - ports(p), relation(NULL), mainContext(p), context(&mainContext), @@ -167,14 +167,14 @@ public: transaction = value; } - DsqlCompiledStatement* getStatement() + DsqlStatement* getStatement() const { return statement; } - DsqlCompiledStatement* getStatement() const + void setStatement(DsqlStatement* aStatement) { - return statement; + statement = aStatement; } void putBlrMarkers(ULONG marks); @@ -272,12 +272,11 @@ private: dsql_dbb* dbb; // DSQL attachment jrd_tra* transaction; // Transaction - DsqlCompiledStatement* statement; // Compiled statement + DsqlStatement* statement; // Compiled statement public: unsigned flags; // flags unsigned nestingLevel; // begin...end nesting level - Firebird::Array ports; // Port messages dsql_rel* relation; // relation created by this request (for DDL) DsqlContextStack mainContext; DsqlContextStack* context; diff --git a/src/dsql/DsqlCursor.cpp b/src/dsql/DsqlCursor.cpp index 564a476459..ba238e438b 100644 --- a/src/dsql/DsqlCursor.cpp +++ b/src/dsql/DsqlCursor.cpp @@ -33,7 +33,7 @@ using namespace Jrd; static const char* const SCRATCH = "fb_cursor_"; static const ULONG PREFETCH_SIZE = 65536; // 64 KB -DsqlCursor::DsqlCursor(dsql_req* req, ULONG flags) +DsqlCursor::DsqlCursor(DsqlDmlRequest* req, ULONG flags) : m_request(req), m_message(req->getStatement()->getReceiveMsg()), m_resultSet(NULL), m_flags(flags), m_space(req->getPool(), SCRATCH), @@ -69,10 +69,10 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) if (!cursor) return; - Jrd::Attachment* const attachment = cursor->getAttachment(); - dsql_req* const request = cursor->m_request; + const auto attachment = cursor->getAttachment(); + const auto request = cursor->m_request; - if (request->req_request) + if (request->getJrdRequest()) { ThreadStatusGuard status_vector(tdbb); try @@ -90,7 +90,7 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) TraceManager::event_dsql_free(attachment, &stmt, DSQL_close); } - JRD_unwind_request(tdbb, request->req_request); + JRD_unwind_request(tdbb, request->getJrdRequest()); } catch (Firebird::Exception&) {} // no-op diff --git a/src/dsql/DsqlCursor.h b/src/dsql/DsqlCursor.h index 864a8b3817..75392a47f9 100644 --- a/src/dsql/DsqlCursor.h +++ b/src/dsql/DsqlCursor.h @@ -27,7 +27,7 @@ namespace Jrd { -class dsql_req; +class DsqlDmlRequest; class JResultSet; class DsqlCursor @@ -35,7 +35,7 @@ class DsqlCursor enum State { BOS, POSITIONED, EOS }; public: - DsqlCursor(dsql_req* req, ULONG flags); + DsqlCursor(DsqlDmlRequest* req, ULONG flags); ~DsqlCursor(); jrd_tra* getTransaction() const; @@ -65,7 +65,7 @@ private: int fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position); bool cacheInput(thread_db* tdbb, FB_UINT64 position = MAX_UINT64); - dsql_req* const m_request; + DsqlDmlRequest* const m_request; const dsql_msg* const m_message; JResultSet* m_resultSet; const ULONG m_flags; diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp new file mode 100644 index 0000000000..dec63074ab --- /dev/null +++ b/src/dsql/DsqlRequests.cpp @@ -0,0 +1,1123 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.cpp + */ + +#include "firebird.h" +#include "../dsql/DsqlRequests.h" +#include "../dsql/dsql.h" +#include "../dsql/DsqlBatch.h" +#include "../dsql/Nodes.h" +#include "../jrd/JrdStatement.h" +#include "../jrd/req.h" +#include "../jrd/tra.h" +#include "../jrd/replication/Publisher.h" +#include "../jrd/trace/TraceDSQLHelpers.h" +#include "../jrd/trace/TraceObjects.h" +#include "../dsql/errd_proto.h" +#include "../dsql/movd_proto.h" +#include "../jrd/exe_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +static void checkD(IStatus* st); + + +// DsqlRequest + +DsqlRequest::DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement) + : PermanentStorage(pool), + req_dbb(dbb), + statement(aStatement) +{ +} + +DsqlRequest::~DsqlRequest() +{ +} + +void DsqlRequest::setCursor(thread_db* /*tdbb*/, const TEXT* /*name*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); +} + +void DsqlRequest::setDelayedFormat(thread_db* /*tdbb*/, IMessageMetadata* /*metadata*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); +} + +bool DsqlRequest::fetch(thread_db* /*tdbb*/, UCHAR* /*msgBuffer*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); + + return false; // avoid warning +} + +unsigned int DsqlRequest::getTimeout() +{ + return req_timeout; +} + +unsigned int DsqlRequest::getActualTimeout() +{ + if (req_timer) + return req_timer->getValue(); + + return 0; +} + +void DsqlRequest::setTimeout(unsigned int timeOut) +{ + req_timeout = timeOut; +} + +TimeoutTimer* DsqlRequest::setupTimer(thread_db* tdbb) +{ + auto jrdRequest = getJrdRequest(); + + if (jrdRequest) + { + if (jrdRequest->hasInternalStatement()) + return req_timer; + + jrdRequest->req_timeout = this->req_timeout; + + fb_assert(!jrdRequest->req_caller); + if (jrdRequest->req_caller) + { + if (req_timer) + req_timer->setup(0, 0); + return req_timer; + } + } + + Database* dbb = tdbb->getDatabase(); + Attachment* att = tdbb->getAttachment(); + + ISC_STATUS toutErr = isc_cfg_stmt_timeout; + unsigned int timeOut = dbb->dbb_config->getStatementTimeout() * 1000; + + if (req_timeout) + { + if (!timeOut || req_timeout < timeOut) + { + timeOut = req_timeout; + toutErr = isc_req_stmt_timeout; + } + } + else + { + const unsigned int attTout = att->getStatementTimeout(); + + if (!timeOut || attTout && attTout < timeOut) + { + timeOut = attTout; + toutErr = isc_att_stmt_timeout; + } + } + + if (!req_timer && timeOut) + { + req_timer = FB_NEW TimeoutTimer(); + fb_assert(jrdRequest); + jrdRequest->req_timer = this->req_timer; + } + + if (req_timer) + { + req_timer->setup(timeOut, toutErr); + req_timer->start(); + } + + return req_timer; +} + +// Release a dynamic request. +void DsqlRequest::destroy(thread_db* tdbb, DsqlRequest* request) +{ + SET_TDBB(tdbb); + + if (request->req_timer) + { + request->req_timer->stop(); + request->req_timer = nullptr; + } + + // If request is parent, orphan the children and release a portion of their requests + + for (auto childStatement : request->cursors) + { + childStatement->addFlags(DsqlStatement::FLAG_ORPHAN); + childStatement->setParentRequest(nullptr); + + // hvlad: lines below is commented out as + // - child is already unlinked from its parent request + // - we should not free child's sql text until its owner request is alive + // It seems to me we should destroy owner request here, not a child + // statement - as it always was before + + //Jrd::ContextPoolHolder context(tdbb, &childStatement->getPool()); + //releaseStatement(childStatement); + } + + // If the request had an open cursor, close it + + if (request->req_cursor) + DsqlCursor::close(tdbb, request->req_cursor); + + if (request->req_batch) + { + delete request->req_batch; + request->req_batch = nullptr; + } + + Jrd::Attachment* att = request->req_dbb->dbb_attachment; + const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att); + if (need_trace_free) + { + TraceSQLStatementImpl stmt(request, NULL); + TraceManager::event_dsql_free(att, &stmt, DSQL_drop); + } + + if (request->req_cursor_name.hasData()) + request->req_dbb->dbb_cursors.remove(request->req_cursor_name); + + // If a request has been compiled, release it now + if (request->getJrdRequest()) + EXE_release(tdbb, request->getJrdRequest()); + + // Increase the statement refCount so its pool is not destroyed before the request is gone. + auto statement = request->getStatement(); + + // Release the entire request + delete request; + + statement = nullptr; +} + +// Parse the message of a request. +USHORT DsqlRequest::parseMetadata(IMessageMetadata* meta, const Array& parameters_list) +{ + HalfStaticArray parameters; + + for (FB_SIZE_T i = 0; i < parameters_list.getCount(); ++i) + { + dsql_par* param = parameters_list[i]; + + if (param->par_index) + { + if (param->par_index > parameters.getCount()) + parameters.grow(param->par_index); + fb_assert(!parameters[param->par_index - 1]); + parameters[param->par_index - 1] = param; + } + } + + // If there's no metadata, then the format of the current message buffer + // is identical to the format of the previous one. + + if (!meta) + return parameters.getCount(); + + FbLocalStatus st; + unsigned count = meta->getCount(&st); + checkD(&st); + + unsigned count2 = parameters.getCount(); + + if (count != count2) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_wrong_param_num) <getType(&st, index); + checkD(&st); + unsigned sqlLength = meta->getLength(&st, index); + checkD(&st); + + dsc desc; + desc.dsc_flags = 0; + + unsigned dataOffset, nullOffset, dtype, dlength; + offset = fb_utils::sqlTypeToDsc(offset, sqlType, sqlLength, + &dtype, &dlength, &dataOffset, &nullOffset); + desc.dsc_dtype = dtype; + desc.dsc_length = dlength; + + desc.dsc_scale = meta->getScale(&st, index); + checkD(&st); + desc.dsc_sub_type = meta->getSubType(&st, index); + checkD(&st); + unsigned textType = meta->getCharSet(&st, index); + checkD(&st); + desc.setTextType(textType); + desc.dsc_address = (UCHAR*)(IPTR) dataOffset; + + const dsql_par* const parameter = parameters[index]; + fb_assert(parameter); + + // ASF: Older than 2.5 engine hasn't validating strings in DSQL. After this has been + // implemented in 2.5, selecting a NONE column with UTF-8 attachment charset started + // failing. The real problem is that the client encodes SQL_TEXT/SQL_VARYING using + // blr_text/blr_varying (i.e. with the connection charset). I'm reseting the charset + // here at the server as a way to make older (and not yet changed) client work + // correctly. + if (desc.isText() && desc.getTextType() == ttype_dynamic) + desc.setTextType(ttype_none); + + req_user_descs.put(parameter, desc); + + dsql_par* null = parameter->par_null; + if (null) + { + desc.clear(); + desc.dsc_dtype = dtype_short; + desc.dsc_scale = 0; + desc.dsc_length = sizeof(SSHORT); + desc.dsc_address = (UCHAR*)(IPTR) nullOffset; + + req_user_descs.put(null, desc); + } + } + + return count; +} + + +// DsqlDmlRequest + +DsqlDmlRequest::DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement) + : DsqlRequest(pool, dbb, aStatement), + req_msg_buffers(pool) +{ + // Create the messages buffers + for (auto message : aStatement->getPorts()) + { + // Allocate buffer for message + const ULONG newLen = message->msg_length + FB_DOUBLE_ALIGN - 1; + UCHAR* msgBuffer = FB_NEW_POOL(getPool()) UCHAR[newLen]; + msgBuffer = FB_ALIGN(msgBuffer, FB_DOUBLE_ALIGN); + fb_assert(message->msg_buffer_number == req_msg_buffers.getCount()); + req_msg_buffers.add(msgBuffer); + } + + jrdRequest = aStatement->getJrdStatement()->findRequest(tdbb); + tdbb->getAttachment()->att_requests.add(jrdRequest); +} + +JrdStatement* DsqlDmlRequest::getJrdStatement() const +{ + return jrdRequest ? jrdRequest->getStatement() : nullptr; +} + +// Provide backward-compatibility +void DsqlDmlRequest::setDelayedFormat(thread_db* tdbb, IMessageMetadata* metadata) +{ + if (!needDelayedFormat) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); + } + + needDelayedFormat = false; + delayedFormat = metadata; +} + +// Fetch next record from a dynamic SQL cursor. +bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + const auto statement = getStatement(); + + // if the cursor isn't open, we've got a problem + if (statement->isCursorBased()) + { + if (!req_cursor) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_dsql_cursor_err) << + Arg::Gds(isc_dsql_cursor_not_open)); + } + } + + if (!jrdRequest) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_unprepared_stmt)); + } + + dsql_msg* message = (dsql_msg*) statement->getReceiveMsg(); + + if (delayedFormat && message) + { + parseMetadata(delayedFormat, message->msg_parameters); + delayedFormat = NULL; + } + + // Set up things for tracing this call + Jrd::Attachment* att = req_dbb->dbb_attachment; + TraceDSQLFetch trace(att, this); + + thread_db::TimerGuard timerGuard(tdbb, req_timer, false); + if (req_timer && req_timer->expired()) + tdbb->checkCancelState(); + + UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; + if (!firstRowFetched && needRestarts()) + { + // Note: tra_handle can't be changed by executeReceiveWithRestarts below + // and outMetadata and outMsg in not used there, so passing NULL's is safe. + jrd_tra* tra = req_transaction; + + executeReceiveWithRestarts(tdbb, &tra, NULL, NULL, false, false, true); + fb_assert(tra == req_transaction); + } + else + JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, dsqlMsgBuffer); + + firstRowFetched = true; + + const dsql_par* const eof = statement->getEof(); + const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL; + const bool eofReached = eof && !(*eofPtr); + + if (eofReached) + { + if (req_timer) + req_timer->stop(); + + trace.fetch(true, ITracePlugin::RESULT_SUCCESS); + return false; + } + + if (msgBuffer) + mapInOut(tdbb, true, message, NULL, msgBuffer); + + trace.fetch(false, ITracePlugin::RESULT_SUCCESS); + return true; +} + +// Set a cursor name for a dynamic request. +void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + const size_t MAX_CURSOR_LENGTH = 132 - 1; + string cursor = name; + + if (cursor.hasData() && cursor[0] == '\"') + { + // Quoted cursor names eh? Strip'em. + // Note that "" will be replaced with ". + // The code is very strange, because it doesn't check for "" really + // and thus deletes one isolated " in the middle of the cursor. + for (string::iterator i = cursor.begin(); i < cursor.end(); ++i) + { + if (*i == '\"') + cursor.erase(i); + } + } + else // not quoted name + { + const string::size_type i = cursor.find(' '); + if (i != string::npos) + cursor.resize(i); + + cursor.upper(); + } + + USHORT length = (USHORT) fb_utils::name_length(cursor.c_str()); + + if (!length) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_invalid)); + } + + if (length > MAX_CURSOR_LENGTH) + length = MAX_CURSOR_LENGTH; + + cursor.resize(length); + + // If there already is a different cursor by the same name, bitch + + auto* const* symbol = req_dbb->dbb_cursors.get(cursor); + if (symbol) + { + if (this == *symbol) + return; + + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_redefined) << cursor); + } + + // If there already is a cursor and its name isn't the same, ditto. + // We already know there is no cursor by this name in the hash table + + if (req_cursor && req_cursor_name.hasData()) + { + fb_assert(!symbol); + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_redefined) << req_cursor_name); + } + + if (req_cursor_name.hasData()) + req_dbb->dbb_cursors.remove(req_cursor_name); + req_cursor_name = cursor; + req_dbb->dbb_cursors.put(cursor, this); +} + +// Open a dynamic SQL cursor. +DsqlCursor* DsqlDmlRequest::openCursor(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMeta, const UCHAR* inMsg, IMessageMetadata* outMeta, ULONG flags) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << + Arg::Gds(isc_bad_req_handle)); + } + + // Validate transaction handle + + if (!*traHandle) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << + Arg::Gds(isc_bad_trans_handle)); + } + + // Validate statement type + + if (!statement->isCursorBased()) + Arg::Gds(isc_no_cursor).raise(); + + // Validate cursor or batch being not already open + + if (req_cursor) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)); + } + + if (req_batch) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_batch_open)); + } + + req_transaction = *traHandle; + execute(tdbb, traHandle, inMeta, inMsg, outMeta, NULL, false); + + req_cursor = FB_NEW_POOL(getPool()) DsqlCursor(this, flags); + + return req_cursor; +} + +bool DsqlDmlRequest::needRestarts() +{ + return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency)); +}; + +// Execute a dynamic SQL statement +void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + firstRowFetched = false; + const dsql_msg* message = statement->getSendMsg(); + + if (!message) + JRD_start(tdbb, jrdRequest, req_transaction); + else + { + UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; + JRD_start_and_send(tdbb, jrdRequest, req_transaction, message->msg_number, + message->msg_length, msgBuffer); + } + + // Selectable execute block should get the "proc fetch" flag assigned, + // which ensures that the savepoint stack is preserved while suspending + if (statement->getType() == DsqlStatement::TYPE_SELECT_BLOCK) + jrdRequest->req_flags |= req_proc_fetch; + + // TYPE_EXEC_BLOCK has no outputs so there are no out_msg + // supplied from client side, but TYPE_EXEC_BLOCK requires + // 2-byte message for EOS synchronization + const bool isBlock = (statement->getType() == DsqlStatement::TYPE_EXEC_BLOCK); + + message = statement->getReceiveMsg(); + + if (outMetadata == DELAYED_OUT_FORMAT) + { + needDelayedFormat = true; + outMetadata = NULL; + } + + if (outMetadata && message) + parseMetadata(outMetadata, message->msg_parameters); + + if ((outMsg && message) || isBlock) + { + UCHAR temp_buffer[FB_DOUBLE_ALIGN * 2]; + dsql_msg temp_msg(*getDefaultMemoryPool()); + + // Insure that the metadata for the message is parsed, regardless of + // whether anything is found by the call to receive. + + UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; + + if (!outMetadata && isBlock) + { + message = &temp_msg; + temp_msg.msg_number = 1; + temp_msg.msg_length = 2; + msgBuffer = FB_ALIGN(temp_buffer, FB_DOUBLE_ALIGN); + } + + JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, msgBuffer); + + if (outMsg) + mapInOut(tdbb, true, message, NULL, outMsg); + + // if this is a singleton select, make sure there's in fact one record + + if (singleton) + { + USHORT counter; + + // Create a temp message buffer and try two more receives. + // If both succeed then the first is the next record and the + // second is either another record or the end of record message. + // In either case, there's more than one record. + + UCHAR* message_buffer = (UCHAR*) gds__alloc(message->msg_length); + + ISC_STATUS status = FB_SUCCESS; + FbLocalStatus localStatus; + + for (counter = 0; counter < 2 && !status; counter++) + { + localStatus->init(); + AutoSetRestore autoStatus(&tdbb->tdbb_status_vector, &localStatus); + + try + { + JRD_receive(tdbb, jrdRequest, message->msg_number, + message->msg_length, message_buffer); + status = FB_SUCCESS; + } + catch (Exception&) + { + status = tdbb->tdbb_status_vector->getErrors()[1]; + } + } + + gds__free(message_buffer); + + // two successful receives means more than one record + // a req_sync error on the first pass above means no records + // a non-req_sync error on any of the passes above is an error + + if (!status) + status_exception::raise(Arg::Gds(isc_sing_select_err)); + else if (status == isc_req_sync && counter == 1) + status_exception::raise(Arg::Gds(isc_stream_eof)); + else if (status != isc_req_sync) + status_exception::raise(&localStatus); + } + } + + switch (statement->getType()) + { + case DsqlStatement::TYPE_UPDATE_CURSOR: + if (!jrdRequest->req_records_updated) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << + Arg::Gds(isc_deadlock) << + Arg::Gds(isc_update_conflict)); + } + break; + + case DsqlStatement::TYPE_DELETE_CURSOR: + if (!jrdRequest->req_records_deleted) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << + Arg::Gds(isc_deadlock) << + Arg::Gds(isc_update_conflict)); + } + break; + } +} + +DsqlBatch* DsqlDmlRequest::openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) +{ + return DsqlBatch::open(tdbb, this, inMetadata, parLength, par); +} + +// Execute a dynamic SQL statement with tracing, restart and timeout handler +void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + if (!jrdRequest) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_unprepared_stmt)); + } + + // If there is no data required, just start the request + + const dsql_msg* message = statement->getSendMsg(); + if (message) + mapInOut(tdbb, false, message, inMetadata, NULL, inMsg); + + // we need to mapInOut() before tracing of execution start to let trace + // manager know statement parameters values + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + + // Setup and start timeout timer + const bool have_cursor = statement->isCursorBased() && !singleton; + + setupTimer(tdbb); + thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor); + + if (needRestarts()) + executeReceiveWithRestarts(tdbb, traHandle, outMetadata, outMsg, singleton, true, false); + else { + doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); + } + + trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS); +} + +void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton, bool exec, bool fetch) +{ + jrdRequest->req_flags &= ~req_update_conflict; + int numTries = 0; + const int MAX_RESTARTS = 10; + + while (true) + { + AutoSavePoint savePoint(tdbb, req_transaction); + + // Don't set req_restart_ready flag at last attempt to restart request. + // It allows to raise update conflict error (if any) as usual and + // handle error by PSQL handler. + const ULONG flag = (numTries >= MAX_RESTARTS) ? 0 : req_restart_ready; + AutoSetRestoreFlag restartReady(&jrdRequest->req_flags, flag, true); + try + { + if (exec) + doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); + + if (fetch) + { + fb_assert(statement->isCursorBased()); + + const dsql_msg* message = statement->getReceiveMsg(); + + UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; + JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, dsqlMsgBuffer); + } + } + catch (const status_exception&) + { + if (!(req_transaction->tra_flags & TRA_ex_restart)) + { + jrdRequest->req_flags &= ~req_update_conflict; + throw; + } + } + + if (!(jrdRequest->req_flags & req_update_conflict)) + { + fb_assert((req_transaction->tra_flags & TRA_ex_restart) == 0); + req_transaction->tra_flags &= ~TRA_ex_restart; + +#ifdef DEV_BUILD + if (numTries > 0) + { + string s; + s.printf("restarts = %d", numTries); + + ERRD_post_warning(Arg::Warning(isc_random) << Arg::Str(s)); + } +#endif + savePoint.release(); // everything is ok + break; + } + + fb_assert((req_transaction->tra_flags & TRA_ex_restart) != 0); + + jrdRequest->req_flags &= ~req_update_conflict; + req_transaction->tra_flags &= ~TRA_ex_restart; + fb_utils::init_status(tdbb->tdbb_status_vector); + + // Undo current savepoint but preserve already taken locks. + // Savepoint will be restarted at the next loop iteration. + savePoint.rollback(true); + + numTries++; + if (numTries >= MAX_RESTARTS) + { + gds__log("Update conflict: unable to get a stable set of rows in the source tables\n" + "\tafter %d attempts of restart.\n" + "\tQuery:\n%s\n", numTries, jrdRequest->getStatement()->sqlText->c_str() ); + } + + // When restart we must execute query + exec = true; + } +} + +// Map data from external world into message or from message to external world. +void DsqlDmlRequest::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* message, + IMessageMetadata* meta, UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf) +{ + USHORT count = parseMetadata(meta, message->msg_parameters); + + // Sanity check + + if (count) + { + if (toExternal) + { + if (dsql_msg_buf == NULL) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_no_output_sqlda)); + } + } + else + { + if (in_dsql_msg_buf == NULL) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_no_input_sqlda)); + } + } + } + + USHORT count2 = 0; + + for (FB_SIZE_T i = 0; i < message->msg_parameters.getCount(); ++i) + { + dsql_par* parameter = message->msg_parameters[i]; + + if (parameter->par_index) + { + // Make sure the message given to us is long enough + + dsc desc; + if (!req_user_descs.get(parameter, desc)) + desc.clear(); + + /*** + ULONG length = (IPTR) desc.dsc_address + desc.dsc_length; + if (length > msg_length) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_random) << "Message buffer too short"); + } + ***/ + if (!desc.dsc_dtype) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_datatype_err) << + Arg::Gds(isc_dsql_sqlvar_index) << Arg::Num(parameter->par_index-1)); + } + + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + SSHORT* flag = NULL; + dsql_par* const null_ind = parameter->par_null; + if (null_ind != NULL) + { + dsc userNullDesc; + if (!req_user_descs.get(null_ind, userNullDesc)) + userNullDesc.clear(); + + const ULONG null_offset = (IPTR) userNullDesc.dsc_address; + + /*** + length = null_offset + sizeof(SSHORT); + if (length > msg_length) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) + << Arg::Gds(isc_random) << "Message buffer too short"); + } + ***/ + + dsc nullDesc = null_ind->par_desc; + nullDesc.dsc_address = msgBuffer + (IPTR) nullDesc.dsc_address; + + if (toExternal) + { + flag = reinterpret_cast(dsql_msg_buf + null_offset); + *flag = *reinterpret_cast(nullDesc.dsc_address); + } + else + { + flag = reinterpret_cast(nullDesc.dsc_address); + *flag = *reinterpret_cast(in_dsql_msg_buf + null_offset); + } + } + + const bool notNull = (!flag || *flag >= 0); + + dsc parDesc = parameter->par_desc; + parDesc.dsc_address = msgBuffer + (IPTR) parDesc.dsc_address; + + if (toExternal) + { + desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address; + + if (notNull) + MOVD_move(tdbb, &parDesc, &desc); + else + memset(desc.dsc_address, 0, desc.dsc_length); + } + else if (notNull && !parDesc.isNull()) + { + // Safe cast because desc is used as source only. + desc.dsc_address = const_cast(in_dsql_msg_buf) + (IPTR) desc.dsc_address; + MOVD_move(tdbb, &desc, &parDesc); + } + else + memset(parDesc.dsc_address, 0, parDesc.dsc_length); + + ++count2; + } + } + + if (count != count2) + { + ERRD_post( + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_wrong_param_num) << Arg::Num(count) <getParentDbKey()) && + (parameter = statement->getDbKey())) + { + UCHAR* parentMsgBuffer = statement->getParentRequest() ? + statement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + fb_assert(parentMsgBuffer); + + dsc parentDesc = dbkey->par_desc; + parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; + + dsc desc = parameter->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + MOVD_move(tdbb, &parentDesc, &desc); + + dsql_par* null_ind = parameter->par_null; + if (null_ind != NULL) + { + desc = null_ind->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + SSHORT* flag = (SSHORT*) desc.dsc_address; + *flag = 0; + } + } + + const dsql_par* rec_version; + if (!toExternal && (rec_version = statement->getParentRecVersion()) && + (parameter = statement->getRecVersion())) + { + UCHAR* parentMsgBuffer = statement->getParentRequest() ? + statement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : + NULL; + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + fb_assert(parentMsgBuffer); + + dsc parentDesc = rec_version->par_desc; + parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; + + dsc desc = parameter->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + MOVD_move(tdbb, &parentDesc, &desc); + + dsql_par* null_ind = parameter->par_null; + if (null_ind != NULL) + { + desc = null_ind->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + SSHORT* flag = (SSHORT*) desc.dsc_address; + *flag = 0; + } + } +} + + +// DsqlDdlRequest + +DsqlDdlRequest::DsqlDdlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlCompilerScratch* aInternalScratch, DdlNode* aNode) + : DsqlRequest(pool, dbb, aInternalScratch->getStatement()), + internalScratch(aInternalScratch), + node(aNode) +{ +} + +// Execute a dynamic SQL statement. +void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + + fb_utils::init_status(tdbb->tdbb_status_vector); + + // run all statements under savepoint control + { // scope + AutoSavePoint savePoint(tdbb, req_transaction); + + try + { + AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); + + node->executeDdl(tdbb, internalScratch, req_transaction); + + const bool isInternalRequest = + (internalScratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST); + + if (!isInternalRequest && node->mustBeReplicated()) + REPL_exec_sql(tdbb, req_transaction, getStatement()->getOrgText()); + } + catch (status_exception& ex) + { + DsqlStatement::rethrowDdlException(ex, true, node); + } + + savePoint.release(); // everything is ok + } + + JRD_autocommit_ddl(tdbb, req_transaction); + + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// DsqlTransactionRequest + +DsqlTransactionRequest::DsqlTransactionRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, TransactionNode* aNode) + : DsqlRequest(pool, dbb, aStatement), + node(aNode) +{ + // Don't trace anything except savepoint statements + req_traced = (aStatement->getType() == DsqlStatement::TYPE_SAVEPOINT); +} + +// Execute a dynamic SQL statement. +void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/, + IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/, + bool /*singleton*/) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + node->execute(tdbb, this, traHandle); + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// DsqlSessionManagementStatement + +DsqlSessionManagementStatement::~DsqlSessionManagementStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +void DsqlSessionManagementStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, + ntrace_result_t* /*traceResult*/) +{ + node = Node::doDsqlPass(scratch, node); + + this->scratch = scratch; +} + +DsqlSessionManagementRequest* DsqlSessionManagementStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlSessionManagementRequest(getPool(), dbb, this, node); +} + +// Execute a dynamic SQL statement. +void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + node->execute(tdbb, this, traHandle); + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// Utility functions + +// raise error if one present +static void checkD(IStatus* st) +{ + if (st->getState() & IStatus::STATE_ERRORS) + ERRD_post(Arg::StatusVector(st)); +} diff --git a/src/dsql/DsqlRequests.h b/src/dsql/DsqlRequests.h new file mode 100644 index 0000000000..e08764922c --- /dev/null +++ b/src/dsql/DsqlRequests.h @@ -0,0 +1,271 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.h + */ + +#ifndef DSQL_REQUESTS_H +#define DSQL_REQUESTS_H + +#include "firebird/Interface.h" +#include "../common/StatusArg.h" +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/NestConst.h" +#include "../common/classes/RefCounted.h" +#include "../jrd/jrd.h" + +namespace Jrd { + + +class DdlNode; +class dsql_dbb; +class DsqlStatement; +class DsqlCompilerScratch; +class DsqlCursor; +class DsqlDmlStatement; +class dsql_par; +class jrd_req; +class jrd_tra; +class JrdStatement; +class SessionManagementNode; +class TransactionNode; + + +class DsqlRequest : public Firebird::PermanentStorage +{ +public: + DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement); + virtual ~DsqlRequest(); + +public: + jrd_tra* getTransaction() + { + return req_transaction; + } + + Firebird::RefPtr getStatement() + { + return statement; + } + + virtual JrdStatement* getJrdStatement() const + { + return nullptr; + } + + virtual jrd_req* getJrdRequest() const + { + return nullptr; + } + + virtual bool isDml() const + { + return false; + } + + virtual DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMeta, ULONG flags) + { + Firebird::Arg::Gds(isc_no_cursor).raise(); + } + + virtual DsqlBatch* openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) + { + (Firebird::Arg::Gds(isc_sqlerr) << + Firebird::Arg::Num(-504) << + Firebird::Arg::Gds(isc_unprepared_stmt) + ).raise(); + } + + virtual void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) = 0; + + virtual void setCursor(thread_db* tdbb, const TEXT* name); + + virtual bool fetch(thread_db* tdbb, UCHAR* buffer); + + virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); + + // Get session-level timeout, milliseconds + unsigned int getTimeout(); + + // Set session-level timeout, milliseconds + void setTimeout(unsigned int timeOut); + + // Get actual timeout, milliseconds + unsigned int getActualTimeout(); + + // Evaluate actual timeout value, consider config- and session-level timeout values, + // setup and start timer + TimeoutTimer* setupTimer(thread_db* tdbb); + + USHORT parseMetadata(Firebird::IMessageMetadata* meta, const Firebird::Array& parameters_list); + + static void destroy(thread_db* tdbb, DsqlRequest* request); + +public: + dsql_dbb* req_dbb; // DSQL attachment + Firebird::RefPtr statement; + Firebird::Array cursors{getPool()}; // Cursor update statements + + jrd_tra* req_transaction = nullptr; // JRD transaction + + Firebird::string req_cursor_name{getPool()}; // Cursor name, if any + DsqlCursor* req_cursor = nullptr; // Open cursor, if any + DsqlBatch* req_batch = nullptr; // Active batch, if any + Firebird::NonPooledMap req_user_descs{getPool()}; // SQLDA data type + + Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time + SINT64 req_fetch_elapsed = 0; // Number of clock ticks spent while fetching rows for this request since we reported it last time + SINT64 req_fetch_rowcount = 0; // Total number of rows returned by this request + bool req_traced = false; // request is traced via TraceAPI + +protected: + unsigned int req_timeout = 0; // query timeout in milliseconds, set by the user + Firebird::RefPtr req_timer; // timeout timer +}; + + +class DsqlDmlRequest final : public DsqlRequest +{ +public: + DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement); + + // Reintroduce method to fake covariant return type with RefPtr. + auto getStatement() + { + return Firebird::RefPtr((DsqlDmlStatement*) statement.getPtr()); + } + + JrdStatement* getJrdStatement() const override; + + jrd_req* getJrdRequest() const override + { + return jrdRequest; + } + + bool isDml() const override + { + return true; + } + + DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMeta, ULONG flags) override; + + DsqlBatch* openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) override; + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + + void setCursor(thread_db* tdbb, const TEXT* name) override; + + bool fetch(thread_db* tdbb, UCHAR* buffer) override; + + void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata) override; + + void mapInOut(Jrd::thread_db* tdbb, bool toExternal, const dsql_msg* message, Firebird::IMessageMetadata* meta, + UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf = nullptr); + +private: + // True, if request could be restarted + bool needRestarts(); + + void doExecute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton); + + // [Re]start part of "request restarts" algorithm + void executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton, bool exec, bool fetch); + +public: + Firebird::Array req_msg_buffers; + +private: + Firebird::RefPtr delayedFormat; + jrd_req* jrdRequest = nullptr; + bool needDelayedFormat = false; + bool firstRowFetched = false; +}; + + +class DsqlDdlRequest final : public DsqlRequest +{ +public: + DsqlDdlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlCompilerScratch* aInternalScratch, DdlNode* aNode); + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + DsqlCompilerScratch* internalScratch; + NestConst node; +}; + + +class DsqlTransactionRequest final : public DsqlRequest +{ +public: + DsqlTransactionRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, TransactionNode* aNode); + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + NestConst node; +}; + + +class DsqlSessionManagementRequest final : public DsqlRequest +{ +public: + DsqlSessionManagementRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, + SessionManagementNode* aNode) + : DsqlRequest(pool, dbb, aStatement), + node(aNode) + { + } + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + NestConst node; +}; + + +} // namespace Jrd + +#endif // DSQL_REQUESTS_H diff --git a/src/dsql/DsqlStatements.cpp b/src/dsql/DsqlStatements.cpp new file mode 100644 index 0000000000..cb1e3fc139 --- /dev/null +++ b/src/dsql/DsqlStatements.cpp @@ -0,0 +1,298 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.cpp + */ + +#include "firebird.h" +#include "../dsql/DsqlStatements.h" +#include "../dsql/dsql.h" +#include "../dsql/Nodes.h" +#include "../dsql/DsqlCompilerScratch.h" +#include "../jrd/JrdStatement.h" +#include "../dsql/errd_proto.h" +#include "../dsql/gen_proto.h" +#include "../jrd/cmp_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +// Class DsqlStatement + +// Rethrow an exception with isc_no_meta_update and prefix codes. +void DsqlStatement::rethrowDdlException(status_exception& ex, bool metadataUpdate, DdlNode* node) +{ + Arg::StatusVector newVector; + + if (metadataUpdate) + newVector << Arg::Gds(isc_no_meta_update); + + node->putErrorPrefix(newVector); + + const ISC_STATUS* status = ex.value(); + + if (status[1] == isc_no_meta_update) + status += 2; + + newVector.append(Arg::StatusVector(status)); + + status_exception::raise(newVector); +} + +int DsqlStatement::release() +{ + fb_assert(refCounter.value() > 0); + const int refCnt = --refCounter; + + if (!refCnt) + { + doRelease(); + dsqlAttachment->deletePool(&getPool()); + } + + return refCnt; +} + +void DsqlStatement::doRelease() +{ + setSqlText(nullptr); + setOrgText(nullptr, 0); +} + +void DsqlStatement::setOrgText(const char* ptr, ULONG len) +{ + if (!ptr || !len) + { + orgText = NULL; + return; + } + + const string text(ptr, len); + + if (text == *sqlText) + orgText = sqlText; + else + orgText = FB_NEW_POOL(getPool()) RefString(getPool(), text); +} + + +// DsqlDmlStatement + +void DsqlDmlStatement::doRelease() +{ + if (auto parent = getParentRequest()) + { + FB_SIZE_T pos; + if (parent->cursors.find(this, pos)) + parent->cursors.remove(pos); + } + + if (jrdStatement) + { + thread_db* tdbb = JRD_get_thread_data(); + ThreadStatusGuard status_vector(tdbb); + + try + { + jrdStatement->release(tdbb); + } + catch (Exception&) + {} // no-op + } + + DsqlStatement::doRelease(); +} + +void DsqlDmlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) +{ + { // scope + ContextPoolHolder scratchContext(tdbb, &scratch->getPool()); + node = Node::doDsqlPass(scratch, node); + } + + if (scratch->clientDialect > SQL_DIALECT_V5) + scratch->getStatement()->setBlrVersion(5); + else + scratch->getStatement()->setBlrVersion(4); + + GEN_statement(scratch, node); + + unsigned messageNumber = 0; + + for (auto message : ports) + message->msg_buffer_number = messageNumber++; + + // have the access method compile the statement + +#ifdef DSQL_DEBUG + if (DSQL_debug & 64) + { + dsql_trace("Resulting BLR code for DSQL:"); + gds__trace_raw("Statement:\n"); + gds__trace_raw(getSqlText()->c_str(), getSqlText()->length()); + gds__trace_raw("\nBLR:\n"); + fb_print_blr(scratch->getBlrData().begin(), + (ULONG) scratch->getBlrData().getCount(), + gds__trace_printer, 0, 0); + } +#endif + + FbLocalStatus localStatus; + + // check for warnings + if (tdbb->tdbb_status_vector->getState() & IStatus::STATE_WARNINGS) + { + // save a status vector + fb_utils::copyStatus(&localStatus, tdbb->tdbb_status_vector); + fb_utils::init_status(tdbb->tdbb_status_vector); + } + + ISC_STATUS status = FB_SUCCESS; + + try + { + const auto attachment = scratch->getAttachment()->dbb_attachment; + const auto& blr = scratch->getBlrData(); + const auto& debugData = scratch->getDebugData(); + + jrdStatement = CMP_compile(tdbb, blr.begin(), blr.getCount(), + (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST), + debugData.getCount(), debugData.begin()); + + if (getSqlText()) + jrdStatement->sqlText = getSqlText(); + + fb_assert(jrdStatement->blr.isEmpty()); + + if (attachment->getDebugOptions().getDsqlKeepBlr()) + jrdStatement->blr.insert(0, blr.begin(), blr.getCount()); + } + catch (const Exception&) + { + status = tdbb->tdbb_status_vector->getErrors()[1]; + *traceResult = status == isc_no_priv ? + ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED; + } + + // restore warnings (if there are any) + if (localStatus->getState() & IStatus::STATE_WARNINGS) + { + Arg::StatusVector cur(tdbb->tdbb_status_vector->getWarnings()); + Arg::StatusVector saved(localStatus->getWarnings()); + saved << cur; + + tdbb->tdbb_status_vector->setWarnings2(saved.length(), saved.value()); + } + + // free blr memory + scratch->getBlrData().free(); + + if (status) + status_exception::raise(tdbb->tdbb_status_vector); + + node = NULL; +} + +DsqlDmlRequest* DsqlDmlStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlDmlRequest(tdbb, getPool(), dbb, this); +} + + +// DsqlDdlStatement + +DsqlDdlStatement::~DsqlDdlStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +bool DsqlDdlStatement::mustBeReplicated() const +{ + return node->mustBeReplicated(); +} + +void DsqlDdlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) +{ + Database* const dbb = tdbb->getDatabase(); + + scratch->flags |= DsqlCompilerScratch::FLAG_DDL; + + try + { + node = Node::doDsqlPass(scratch, node); + } + catch (status_exception& ex) + { + rethrowDdlException(ex, false, node); + } + + if (dbb->readOnly()) + ERRD_post(Arg::Gds(isc_read_only_database)); + + // In read-only replica, only replicator is allowed to execute DDL. + // As an exception, not replicated DDL statements are also allowed. + if (dbb->isReplica(REPLICA_READ_ONLY) && + !(tdbb->tdbb_flags & TDBB_replicator) && + node->mustBeReplicated()) + { + ERRD_post(Arg::Gds(isc_read_only_trans)); + } + + const auto dbDialect = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; + + if ((scratch->flags & DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT) && + dbDialect != scratch->clientDialect) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-817) << + Arg::Gds(isc_ddl_not_allowed_by_db_sql_dial) << Arg::Num(dbDialect)); + } + + if (scratch->clientDialect > SQL_DIALECT_V5) + scratch->getStatement()->setBlrVersion(5); + else + scratch->getStatement()->setBlrVersion(4); + + this->scratch = scratch; +} + +DsqlDdlRequest* DsqlDdlStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlDdlRequest(getPool(), dbb, scratch, node); +} + + +// DsqlTransactionStatement + +DsqlTransactionStatement::~DsqlTransactionStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +void DsqlTransactionStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, + ntrace_result_t* /*traceResult*/) +{ + node = Node::doDsqlPass(scratch, node); + + this->scratch = scratch; +} + +DsqlTransactionRequest* DsqlTransactionStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlTransactionRequest(getPool(), dbb, this, node); +} diff --git a/src/dsql/DsqlStatements.h b/src/dsql/DsqlStatements.h new file mode 100644 index 0000000000..1918e3f82b --- /dev/null +++ b/src/dsql/DsqlStatements.h @@ -0,0 +1,292 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.h + */ + +#ifndef DSQL_STATEMENTS_H +#define DSQL_STATEMENTS_H + +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/NestConst.h" +#include "../jrd/jrd.h" +#include "../jrd/ntrace.h" +#include "../dsql/DsqlRequests.h" + +namespace Jrd { + + +class DdlNode; +class dsql_dbb; +class dsql_msg; +class dsql_par; +class DsqlRequest; +class DsqlCompilerScratch; +class JrdStatement; +class SessionManagementNode; +class TransactionNode; + + +// Compiled statement - shared by multiple requests. +class DsqlStatement : public Firebird::PermanentStorage +{ +public: + enum Type // statement type + { + TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, + TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, + TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, + TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT, + TYPE_RETURNING_CURSOR + }; + + // Statement flags. + static const unsigned FLAG_ORPHAN = 0x01; + static const unsigned FLAG_NO_BATCH = 0x02; + //static const unsigned FLAG_BLR_VERSION4 = 0x04; + //static const unsigned FLAG_BLR_VERSION5 = 0x08; + static const unsigned FLAG_SELECTABLE = 0x10; + + static void rethrowDdlException(Firebird::status_exception& ex, bool metadataUpdate, DdlNode* node); + +public: + DsqlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment) + : PermanentStorage(p), + dsqlAttachment(aDsqlAttachment), + type(TYPE_SELECT), + flags(0), + blrVersion(5), + ports(p) + { + } + +protected: + virtual ~DsqlStatement() = default; + +public: + int addRef() + { + return ++refCounter; + } + + int release(); + + bool isCursorBased() const + { + switch (type) + { + case TYPE_SELECT: + case TYPE_SELECT_BLOCK: + case TYPE_SELECT_UPD: + case TYPE_RETURNING_CURSOR: + return true; + } + + return false; + } + + Type getType() const { return type; } + void setType(Type value) { type = value; } + + ULONG getFlags() const { return flags; } + void setFlags(ULONG value) { flags = value; } + void addFlags(ULONG value) { flags |= value; } + + unsigned getBlrVersion() const { return blrVersion; } + void setBlrVersion(unsigned value) { blrVersion = value; } + + Firebird::RefStrPtr& getSqlText() { return sqlText; } + const Firebird::RefStrPtr& getSqlText() const { return sqlText; } + void setSqlText(Firebird::RefString* value) { sqlText = value; } + + void setOrgText(const char* ptr, ULONG len); + const Firebird::string& getOrgText() const { return *orgText; } + + Firebird::Array& getPorts() { return ports; } + + dsql_msg* getSendMsg() { return sendMsg; } + const dsql_msg* getSendMsg() const { return sendMsg; } + void setSendMsg(dsql_msg* value) { sendMsg = value; } + + dsql_msg* getReceiveMsg() { return receiveMsg; } + const dsql_msg* getReceiveMsg() const { return receiveMsg; } + void setReceiveMsg(dsql_msg* value) { receiveMsg = value; } + + dsql_par* getEof() { return eof; } + const dsql_par* getEof() const { return eof; } + void setEof(dsql_par* value) { eof = value; } + +public: + virtual JrdStatement* getJrdStatement() const + { + return nullptr; + } + + virtual bool mustBeReplicated() const + { + return false; + } + + virtual bool shouldPreserveScratch() const + { + return true; + } + + virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) = 0; + virtual DsqlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) = 0; + +protected: + virtual void doRelease(); + +protected: + dsql_dbb* dsqlAttachment; + Type type; // Type of statement + ULONG flags; // generic flag + unsigned blrVersion; + Firebird::RefStrPtr sqlText; + Firebird::RefStrPtr orgText; + Firebird::Array ports; // Port messages + dsql_msg* sendMsg = nullptr; // Message to be sent to start request + dsql_msg* receiveMsg = nullptr; // Per record message to be received + dsql_par* eof = nullptr; // End of file parameter + +private: + Firebird::AtomicCounter refCounter; +}; + + +class DsqlDmlStatement final : public DsqlStatement +{ +public: + DsqlDmlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, StmtNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + +public: + JrdStatement* getJrdStatement() const override + { + return jrdStatement; + } + + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlDmlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + + dsql_par* getDbKey() { return dbKey; } + const dsql_par* getDbKey() const { return dbKey; } + void setDbKey(dsql_par* value) { dbKey = value; } + + dsql_par* getRecVersion() { return recVersion; } + const dsql_par* getRecVersion() const { return recVersion; } + void setRecVersion(dsql_par* value) { recVersion = value; } + + dsql_par* getParentRecVersion() { return parentRecVersion; } + const dsql_par* getParentRecVersion() const { return parentRecVersion; } + void setParentRecVersion(dsql_par* value) { parentRecVersion = value; } + + dsql_par* getParentDbKey() { return parentDbKey; } + const dsql_par* getParentDbKey() const { return parentDbKey; } + void setParentDbKey(dsql_par* value) { parentDbKey = value; } + + DsqlDmlRequest* getParentRequest() const { return parentRequest; } + void setParentRequest(DsqlDmlRequest* value) { parentRequest = value; } + +protected: + void doRelease() override; + +private: + NestConst node; + JrdStatement* jrdStatement = nullptr; + dsql_par* dbKey = nullptr; // Database key for current of + dsql_par* recVersion = nullptr; // Record Version for current of + dsql_par* parentRecVersion = nullptr; // parent record version + dsql_par* parentDbKey = nullptr; // Parent database key for current of + DsqlDmlRequest* parentRequest = nullptr; // Source request, if cursor update +}; + + +class DsqlDdlStatement final : public DsqlStatement +{ +public: + DsqlDdlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, DdlNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlDdlStatement(); + +public: + bool mustBeReplicated() const override; + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlDdlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +class DsqlTransactionStatement final : public DsqlStatement +{ +public: + DsqlTransactionStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, TransactionNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlTransactionStatement(); + +public: + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlTransactionRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +class DsqlSessionManagementStatement final : public DsqlStatement +{ +public: + DsqlSessionManagementStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, SessionManagementNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlSessionManagementStatement(); + +public: + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlSessionManagementRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +} // namespace Jrd + +#endif // DSQL_STATEMENTS_H diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index fbc0277ef8..e2192a7724 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -213,7 +213,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); return this; } @@ -283,7 +283,7 @@ public: return this; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const = 0; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const = 0; }; @@ -300,12 +300,12 @@ public: { Node::dsqlPass(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SESSION_MANAGEMENT); return this; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const = 0; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const = 0; }; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 71d40ac857..be7eed2a61 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -221,21 +221,14 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // items for (unsigned i = 0; i < items->getCount(); ++i) { - DsqlCompiledStatement* itemStatement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); - - DsqlCompilerScratch* itemScratch = (*items)[i].dsqlScratch = - FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), - dsqlScratch->getTransaction(), itemStatement); - - itemScratch->clientDialect = dsqlScratch->clientDialect; - itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; - itemScratch->package = name; + DdlNode* ddlNode; switch ((*items)[i].type) { case CreateAlterPackageNode::Item::FUNCTION: { CreateAlterFunctionNode* const fun = (*items)[i].function; + ddlNode = fun; if (functionNames.exist(fun->name)) { @@ -249,13 +242,13 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) fun->alter = true; fun->package = name; - fun->dsqlPass(itemScratch); break; } case CreateAlterPackageNode::Item::PROCEDURE: { CreateAlterProcedureNode* const proc = (*items)[i].procedure; + ddlNode = proc; if (procedureNames.exist(proc->name)) { @@ -269,10 +262,24 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) proc->alter = true; proc->package = name; - proc->dsqlPass(itemScratch); break; } + + default: + fb_assert(false); } + + auto itemStatement = FB_NEW_POOL(pool) DsqlDdlStatement(pool, dsqlScratch->getAttachment(), ddlNode); + + auto itemScratch = (*items)[i].dsqlScratch = + FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), + dsqlScratch->getTransaction(), itemStatement); + + itemScratch->clientDialect = dsqlScratch->clientDialect; + itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; + itemScratch->package = name; + + ddlNode->dsqlPass(itemScratch); } return DdlNode::dsqlPass(dsqlScratch); @@ -616,21 +623,14 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) for (unsigned j = 0; j < arrays[i]->getCount(); ++j) { - DsqlCompiledStatement* itemStatement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); - - DsqlCompilerScratch* itemScratch = (*arrays[i])[j].dsqlScratch = - FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), - dsqlScratch->getTransaction(), itemStatement); - - itemScratch->clientDialect = dsqlScratch->clientDialect; - itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; - itemScratch->package = name; + DdlNode* ddlNode; switch ((*arrays[i])[j].type) { case CreateAlterPackageNode::Item::FUNCTION: { CreateAlterFunctionNode* const fun = (*arrays[i])[j].function; + ddlNode = fun; if (functionNames[i].exist(fun->name)) { @@ -648,14 +648,13 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (arrays[i] == items) fun->alter = true; - fun->dsqlPass(itemScratch); - break; } case CreateAlterPackageNode::Item::PROCEDURE: { CreateAlterProcedureNode* const proc = (*arrays[i])[j].procedure; + ddlNode = proc; if (procedureNames[i].exist(proc->name)) { @@ -673,11 +672,24 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (arrays[i] == items) proc->alter = true; - proc->dsqlPass(itemScratch); - break; } + + default: + fb_assert(false); } + + auto itemStatement = FB_NEW_POOL(pool) DsqlDdlStatement(pool, dsqlScratch->getAttachment(), ddlNode); + + auto itemScratch = (*arrays[i])[j].dsqlScratch = + FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), + dsqlScratch->getTransaction(), itemStatement); + + itemScratch->clientDialect = dsqlScratch->clientDialect; + itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; + itemScratch->package = name; + + ddlNode->dsqlPass(itemScratch); } } diff --git a/src/dsql/Parser.cpp b/src/dsql/Parser.cpp index bacacc6378..af0f9ab9f4 100644 --- a/src/dsql/Parser.cpp +++ b/src/dsql/Parser.cpp @@ -98,10 +98,10 @@ namespace } -Parser::Parser(thread_db* tdbb, MemoryPool& pool, DsqlCompilerScratch* aScratch, - USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, - SSHORT characterSet) +Parser::Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, + USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT characterSet) : PermanentStorage(pool), + statementPool(aStatementPool), scratch(aScratch), client_dialect(aClientDialect), db_dialect(aDbDialect), @@ -172,7 +172,7 @@ Parser::~Parser() } -dsql_req* Parser::parse() +DsqlStatement* Parser::parse() { if (parseAux() != 0) { @@ -182,7 +182,7 @@ dsql_req* Parser::parse() transformString(lex.start, lex.end - lex.start, transformedString); - return DSQL_parse; + return parsedStatement; } diff --git a/src/dsql/Parser.h b/src/dsql/Parser.h index da561e1dc1..28e22323f5 100644 --- a/src/dsql/Parser.h +++ b/src/dsql/Parser.h @@ -130,13 +130,12 @@ public: static const int MAX_TOKEN_LEN = 256; public: - Parser(thread_db* tdbb, MemoryPool& pool, DsqlCompilerScratch* aScratch, USHORT aClientDialect, - USHORT aDbDialect, const TEXT* string, size_t length, - SSHORT characterSet); + Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, + USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT characterSet); ~Parser(); public: - dsql_req* parse(); + DsqlStatement* parse(); const Firebird::string& getTransformedString() const { @@ -233,11 +232,6 @@ private: return cmpNode; } - MemoryPool& getStatementPool() - { - return scratch->getStatement()->getPool(); - } - void yyReducePosn(YYPOSN& ret, YYPOSN* termPosns, YYSTYPE* termVals, int termNo, int stkPos, int yychar, YYPOSN& yyposn, void*); @@ -376,6 +370,7 @@ private: // end - defined in btyacc_fb.ske private: + MemoryPool* statementPool; DsqlCompilerScratch* scratch; USHORT client_dialect; USHORT db_dialect; @@ -385,7 +380,7 @@ private: Firebird::string transformedString; Firebird::GenericMap > strMarks; bool stmt_ambiguous; - dsql_req* DSQL_parse; + DsqlStatement* parsedStatement; // These value/posn are taken from the lexer YYSTYPE yylval; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 1919a984e7..5b1d4c3594 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -72,8 +72,8 @@ namespace Jrd { template static void dsqlExplodeFields(dsql_rel* relation, Array >& fields, bool includeComputed); -static dsql_par* dsqlFindDbKey(const dsql_req*, const RelationSourceNode*); -static dsql_par* dsqlFindRecordVersion(const dsql_req*, const RelationSourceNode*); +static dsql_par* dsqlFindDbKey(const DsqlDmlStatement*, const RelationSourceNode*); +static dsql_par* dsqlFindRecordVersion(const DsqlDmlStatement*, const RelationSourceNode*); static void dsqlGenEofAssignment(DsqlCompilerScratch* dsqlScratch, SSHORT value); static void dsqlGenReturning(DsqlCompilerScratch* dsqlScratch, ReturningClause* returning, Nullable localTableNumber); @@ -1697,7 +1697,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (prevDecl) dsqlScratch->putSubFunction(this, true); - DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); + auto statement = FB_NEW_POOL(pool) DsqlDmlStatement(pool, dsqlScratch->getAttachment(), dsqlBlock); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) statement->setBlrVersion(5); @@ -1709,7 +1709,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); @@ -1729,7 +1729,7 @@ void DeclareSubFuncNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (!dsqlBlock) // forward decl return; - GEN_request(blockScratch, dsqlBlock); + GEN_statement(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subfunc_decl); dsqlScratch->appendNullString(name.c_str()); @@ -2036,7 +2036,7 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (prevDecl) dsqlScratch->putSubProcedure(this, true); - DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); + auto statement = FB_NEW_POOL(pool) DsqlDmlStatement(pool, dsqlScratch->getAttachment(), dsqlBlock); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) statement->setBlrVersion(5); @@ -2048,7 +2048,7 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); @@ -2068,7 +2068,7 @@ void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (!dsqlBlock) // forward decl return; - GEN_request(blockScratch, dsqlBlock); + GEN_statement(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subproc_decl); dsqlScratch->appendNullString(name.c_str()); @@ -2076,7 +2076,7 @@ void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(SUB_ROUTINE_TYPE_PSQL); dsqlScratch->appendUChar( - blockScratch->getStatement()->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ? 1 : 0); + blockScratch->getStatement()->getFlags() & DsqlStatement::FLAG_SELECTABLE ? 1 : 0); genParameters(dsqlScratch, dsqlBlock->parameters); genParameters(dsqlScratch, dsqlBlock->returns); @@ -2291,7 +2291,7 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? - DsqlCompiledStatement::TYPE_DELETE_CURSOR : DsqlCompiledStatement::TYPE_DELETE); + DsqlStatement::TYPE_DELETE_CURSOR : DsqlStatement::TYPE_DELETE); // Generate record selection expression. @@ -2990,7 +2990,7 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } if (!dsqlScratch->isPsql()) - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_EXEC_PROCEDURE); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE); ExecProcedureNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ExecProcedureNode(dsqlScratch->getPool(), dsqlName); node->dsqlProcedure = procedure; @@ -3112,7 +3112,7 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch) { const dsql_msg* message = NULL; - if (dsqlScratch->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_PROCEDURE) + if (dsqlScratch->getStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE) { if ((message = dsqlScratch->getStatement()->getReceiveMsg())) { @@ -4267,12 +4267,12 @@ const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, Exe ExecBlockNode* ExecBlockNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); if (returns.hasData()) - statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK); + statement->setType(DsqlStatement::TYPE_SELECT_BLOCK); else - statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK); + statement->setType(DsqlStatement::TYPE_EXEC_BLOCK); dsqlScratch->flags |= DsqlCompilerScratch::FLAG_BLOCK; @@ -4404,7 +4404,7 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) } } - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); dsqlScratch->appendUChar(blr_begin); @@ -4503,9 +4503,9 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) stmtNode->genBlr(dsqlScratch); if (returns.hasData()) - statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK); + statement->setType(DsqlStatement::TYPE_SELECT_BLOCK); else - statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK); + statement->setType(DsqlStatement::TYPE_EXEC_BLOCK); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(true); @@ -5969,8 +5969,8 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { // Describe it as TYPE_RETURNING_CURSOR if RETURNING is present or as INSERT otherwise. dsqlScratch->getStatement()->setType(returning ? - DsqlCompiledStatement::TYPE_RETURNING_CURSOR : - DsqlCompiledStatement::TYPE_INSERT); + DsqlStatement::TYPE_RETURNING_CURSOR : + DsqlStatement::TYPE_INSERT); } // Setup the main node. @@ -6530,7 +6530,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up } dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? - DsqlCompiledStatement::TYPE_UPDATE_CURSOR : DsqlCompiledStatement::TYPE_UPDATE); + DsqlStatement::TYPE_UPDATE_CURSOR : DsqlStatement::TYPE_UPDATE); doDsqlPass(dsqlScratch, node->dsqlRelation, relation, false); dsql_ctx* mod_context = dsqlGetContext(node->dsqlRelation); @@ -7461,7 +7461,7 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, { DsqlContextStack::AutoRestore autoContext(*dsqlScratch->context); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_INSERT); const auto node = FB_NEW_POOL(dsqlScratch->getPool()) StoreNode(dsqlScratch->getPool()); node->overrideClause = overrideClause; @@ -8164,8 +8164,8 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (dsqlForUpdate) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SELECT_UPD); - dsqlScratch->getStatement()->addFlags(DsqlCompiledStatement::FLAG_NO_BATCH); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SELECT_UPD); + dsqlScratch->getStatement()->addFlags(DsqlStatement::FLAG_NO_BATCH); } else { @@ -8178,7 +8178,7 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (rseNode->dsqlOrder || rseNode->dsqlDistinct) { dsqlScratch->getStatement()->setFlags( - dsqlScratch->getStatement()->getFlags() & ~DsqlCompiledStatement::FLAG_NO_BATCH); + dsqlScratch->getStatement()->getFlags() & ~DsqlStatement::FLAG_NO_BATCH); } } @@ -8204,7 +8204,7 @@ void SelectNode::genBlr(DsqlCompilerScratch* dsqlScratch) RseNode* const rse = nodeAs(dsqlRse); fb_assert(rse); - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); // Set up parameter for things in the select list. ValueListNode* list = rse->dsqlSelectList; @@ -8532,7 +8532,7 @@ DmlNode* SuspendNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* SuspendNode* SuspendNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); if (dsqlScratch->flags & (DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION)) { @@ -8554,7 +8554,7 @@ SuspendNode* SuspendNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) Arg::Gds(isc_dsql_unsupported_in_auto_trans) << Arg::Str("SUSPEND")); } - statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE); + statement->addFlags(DsqlStatement::FLAG_SELECTABLE); return this; } @@ -8790,7 +8790,7 @@ const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_START_TRANS); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_START_TRANS); // Generate tpb for set transaction. Use blr string of dsqlScratch. // If a value is not specified, default is not stuffed, let the engine handle it. @@ -8869,7 +8869,7 @@ SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratc return this; } -void SetTransactionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const +void SetTransactionNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const { JRD_start_transaction(tdbb, &request->req_transaction, request->req_dbb->dbb_attachment, tpb.getCount(), tpb.begin()); @@ -8906,7 +8906,7 @@ void SetTransactionNode::genTableLock(DsqlCompilerScratch* dsqlScratch, //-------------------- -void SessionResetNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const +void SessionResetNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8917,7 +8917,7 @@ void SessionResetNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** tra //-------------------- -void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetRoleNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8948,7 +8948,7 @@ SetDebugOptionNode::SetDebugOptionNode(MemoryPool& pool, MetaName* aName, ExprNo { } -void SetDebugOptionNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDebugOptionNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); auto& debugOptions = tdbb->getAttachment()->getDebugOptions(); @@ -8984,7 +8984,7 @@ SetDecFloatRoundNode::SetDecFloatRoundNode(MemoryPool& pool, MetaName* name) rndMode = mode->val; } -void SetDecFloatRoundNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDecFloatRoundNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -9004,7 +9004,7 @@ void SetDecFloatTrapsNode::trap(MetaName* name) traps |= trap->val; } -void SetDecFloatTrapsNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDecFloatTrapsNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -9027,7 +9027,7 @@ SessionManagementNode* SetBindNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } -void SetBindNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetBindNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); @@ -9091,7 +9091,7 @@ string SetSessionNode::internalPrint(NodePrinter& printer) const return "SetSessionNode"; } -void SetSessionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetSessionNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { Attachment* att = tdbb->getAttachment(); @@ -9111,7 +9111,7 @@ void SetSessionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*tra //-------------------- -void SetTimeZoneNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetTimeZoneNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { Attachment* const attachment = tdbb->getAttachment(); @@ -9363,9 +9363,9 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) node->modifyNode = nodeAs(node->modifyNode->internalDsqlPass(dsqlScratch, true)); fb_assert(node->modifyNode); - // If RETURNING is present, type is already DsqlCompiledStatement::TYPE_EXEC_PROCEDURE. + // If RETURNING is present, type is already DsqlStatement::TYPE_EXEC_PROCEDURE. if (!returning) - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_INSERT); return SavepointEncloseNode::make(dsqlScratch->getPool(), dsqlScratch, node); } @@ -9439,19 +9439,19 @@ CommitRollbackNode* CommitRollbackNode::dsqlPass(DsqlCompilerScratch* dsqlScratc { case CMD_COMMIT: dsqlScratch->getStatement()->setType(retain ? - DsqlCompiledStatement::TYPE_COMMIT_RETAIN : DsqlCompiledStatement::TYPE_COMMIT); + DsqlStatement::TYPE_COMMIT_RETAIN : DsqlStatement::TYPE_COMMIT); break; case CMD_ROLLBACK: dsqlScratch->getStatement()->setType(retain ? - DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN : DsqlCompiledStatement::TYPE_ROLLBACK); + DsqlStatement::TYPE_ROLLBACK_RETAIN : DsqlStatement::TYPE_ROLLBACK); break; } return this; } -void CommitRollbackNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const +void CommitRollbackNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const { if (retain) { @@ -9499,11 +9499,11 @@ Firebird::string UserSavepointNode::internalPrint(NodePrinter& printer) const UserSavepointNode* UserSavepointNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SAVEPOINT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SAVEPOINT); return this; } -void UserSavepointNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*transaction*/) const +void UserSavepointNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*transaction*/) const { jrd_tra* const transaction = request->req_transaction; fb_assert(!(transaction->tra_flags & TRA_system)); @@ -9610,12 +9610,11 @@ static void dsqlExplodeFields(dsql_rel* relation, Array >& fields, } // Find dbkey for named relation in statement's saved dbkeys. -static dsql_par* dsqlFindDbKey(const dsql_req* request, const RelationSourceNode* relation_name) +static dsql_par* dsqlFindDbKey(const DsqlDmlStatement* statement, const RelationSourceNode* relation_name) { - DEV_BLKCHK(request, dsql_type_req); DEV_BLKCHK(relation_name, dsql_type_nod); - const dsql_msg* message = request->getStatement()->getReceiveMsg(); + const dsql_msg* message = statement->getReceiveMsg(); dsql_par* candidate = NULL; const MetaName& relName = relation_name->dsqlName; @@ -9636,11 +9635,9 @@ static dsql_par* dsqlFindDbKey(const dsql_req* request, const RelationSourceNode } // Find record version for relation in statement's saved record version. -static dsql_par* dsqlFindRecordVersion(const dsql_req* request, const RelationSourceNode* relation_name) +static dsql_par* dsqlFindRecordVersion(const DsqlDmlStatement* statement, const RelationSourceNode* relation_name) { - DEV_BLKCHK(request, dsql_type_req); - - const dsql_msg* message = request->getStatement()->getReceiveMsg(); + const dsql_msg* message = statement->getReceiveMsg(); dsql_par* candidate = NULL; const MetaName& relName = relation_name->dsqlName; @@ -9993,7 +9990,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const // Lookup parent dsqlScratch - dsql_req* const* const symbol = dsqlScratch->getAttachment()->dbb_cursors.get(cursor.c_str()); + const auto* const symbol = dsqlScratch->getAttachment()->dbb_cursors.get(cursor.c_str()); if (!symbol) { @@ -10003,12 +10000,12 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const Arg::Gds(isc_dsql_cursor_not_found) << cursor); } - dsql_req* parent = *symbol; + auto parent = *symbol; // Verify that the cursor is appropriate and updatable - dsql_par* source = dsqlFindDbKey(parent, relation_name); - dsql_par* rv_source = dsqlFindRecordVersion(parent, relation_name); + dsql_par* source = dsqlFindDbKey(parent->getStatement(), relation_name); + dsql_par* rv_source = dsqlFindRecordVersion(parent->getStatement(), relation_name); if (!source || !rv_source) { @@ -10017,7 +10014,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const Arg::Gds(isc_dsql_cursor_update_err) << cursor); } - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + const auto statement = static_cast(dsqlScratch->getStatement()); statement->setParentRequest(parent); statement->setParentDbKey(source); @@ -10293,7 +10290,7 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d if (!dsqlScratch->isPsql()) { dsqlScratch->getStatement()->setType(singleton ? - DsqlCompiledStatement::TYPE_EXEC_PROCEDURE : DsqlCompiledStatement::TYPE_RETURNING_CURSOR); + DsqlStatement::TYPE_EXEC_PROCEDURE : DsqlStatement::TYPE_RETURNING_CURSOR); } return node; diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index d8fd591ca8..e8b3e8250f 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -1473,7 +1473,7 @@ public: // Save and reset the statement type, as SessionManagementNode sets it to TYPE_SESSION_MANAGEMENT but // we are a DML statement. - DsqlCompiledStatement::Type statementType = dsqlScratch->getStatement()->getType(); + DsqlStatement::Type statementType = dsqlScratch->getStatement()->getType(); wrapped->dsqlPass(dsqlScratch); dsqlScratch->getStatement()->setType(statementType); @@ -1566,7 +1566,7 @@ public: } virtual SetTransactionNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; private: void genTableLock(DsqlCompilerScratch* dsqlScratch, const RestrictionOption& tblLock, @@ -1607,7 +1607,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual CommitRollbackNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; private: const Command command; @@ -1637,7 +1637,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual UserSavepointNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; public: const Command command; @@ -1661,7 +1661,7 @@ public: return "SessionResetNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; }; @@ -1693,7 +1693,7 @@ public: return "SetRoleNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: bool trusted; @@ -1714,7 +1714,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; private: Type m_type; @@ -1738,7 +1738,7 @@ public: return "SetDebugOptionNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; private: MetaName name; @@ -1761,7 +1761,7 @@ public: return "SetDecFloatRoundNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: USHORT rndMode; @@ -1787,7 +1787,7 @@ public: return "SetDecFloatTrapsNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; void trap(MetaName* name); @@ -1818,7 +1818,7 @@ public: } virtual SessionManagementNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: dsql_fld* from; @@ -1854,7 +1854,7 @@ public: return "SetTimeZoneNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: Firebird::string str; diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 43661f79ee..f5c67ffe3d 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -57,6 +57,7 @@ #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../yvalve/gds_proto.h" +#include "../jrd/exe_proto.h" #include "../jrd/inf_proto.h" #include "../jrd/ini_proto.h" #include "../jrd/intl_proto.h" @@ -80,30 +81,15 @@ using namespace Jrd; using namespace Firebird; -static ULONG get_request_info(thread_db*, dsql_req*, ULONG, UCHAR*); +static ULONG get_request_info(thread_db*, DsqlRequest*, ULONG, UCHAR*); static dsql_dbb* init(Jrd::thread_db*, Jrd::Attachment*); -static dsql_req* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); -static dsql_req* prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); +static DsqlRequest* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); +static RefPtr prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, + bool, ntrace_result_t* traceResult); static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const); -static void release_statement(DsqlCompiledStatement* statement); -static void sql_info(thread_db*, dsql_req*, ULONG, const UCHAR*, ULONG, UCHAR*); +static void sql_info(thread_db*, DsqlRequest*, ULONG, const UCHAR*, ULONG, UCHAR*); static UCHAR* var_info(const dsql_msg*, const UCHAR*, const UCHAR* const, UCHAR*, const UCHAR* const, USHORT, bool); -static void checkD(IStatus*); - -static inline bool reqTypeWithCursor(DsqlCompiledStatement::Type type) -{ - switch (type) - { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - return true; - } - - return false; -} #ifdef DSQL_DEBUG unsigned DSQL_debug = 0; @@ -131,7 +117,7 @@ dsql_dbb::~dsql_dbb() // Execute a dynamic SQL statement. void DSQL_execute(thread_db* tdbb, jrd_tra** tra_handle, - dsql_req* request, + DsqlRequest* request, IMessageMetadata* in_meta, const UCHAR* in_msg, IMessageMetadata* out_meta, UCHAR* out_msg) { @@ -139,9 +125,9 @@ void DSQL_execute(thread_db* tdbb, Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_req_handle)); @@ -150,20 +136,20 @@ void DSQL_execute(thread_db* tdbb, // Only allow NULL trans_handle if we're starting a transaction or set session properties if (!*tra_handle && - statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) + statement->getType() != DsqlStatement::TYPE_START_TRANS && + statement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); } // A select with a non zero output length is a singleton select - const bool singleton = reqTypeWithCursor(statement->getType()) && out_msg; + const bool singleton = statement->isCursorBased() && out_msg; // If the request is a SELECT or blob statement then this is an open. // Make sure the cursor is not already open. - if (reqTypeWithCursor(statement->getType())) + if (statement->isCursorBased()) { if (request->req_cursor) { @@ -180,154 +166,6 @@ void DSQL_execute(thread_db* tdbb, } -// Open a dynamic SQL cursor. -DsqlCursor* DSQL_open(thread_db* tdbb, - jrd_tra** tra_handle, - dsql_req* request, - IMessageMetadata* in_meta, const UCHAR* in_msg, - IMessageMetadata* out_meta, ULONG flags) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - - const DsqlCompiledStatement* statement = request->getStatement(); - - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_bad_req_handle)); - } - - // Validate transaction handle - - if (!*tra_handle) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_bad_trans_handle)); - } - - // Validate statement type - - if (!reqTypeWithCursor(statement->getType())) - Arg::Gds(isc_no_cursor).raise(); - - // Validate cursor or batch being not already open - - if (request->req_cursor) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_cursor_open_err)); - } - - if (request->req_batch) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_batch_open)); - } - - request->req_transaction = *tra_handle; - request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, NULL, false); - - request->req_cursor = FB_NEW_POOL(request->getPool()) DsqlCursor(request, flags); - return request->req_cursor; -} - - -// Provide backward-compatibility -void DsqlDmlRequest::setDelayedFormat(thread_db* tdbb, IMessageMetadata* metadata) -{ - if (!needDelayedFormat) - { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); - } - - needDelayedFormat = false; - delayedFormat = metadata; -} - - -// Fetch next record from a dynamic SQL cursor. -bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &getPool()); - - const DsqlCompiledStatement* statement = getStatement(); - - // if the cursor isn't open, we've got a problem - if (reqTypeWithCursor(statement->getType())) - { - if (!req_cursor) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_dsql_cursor_err) << - Arg::Gds(isc_dsql_cursor_not_open)); - } - } - - if (!req_request) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_unprepared_stmt)); - } - - dsql_msg* message = (dsql_msg*) statement->getReceiveMsg(); - - if (delayedFormat && message) - { - parseMetadata(delayedFormat, message->msg_parameters); - delayedFormat = NULL; - } - - // Set up things for tracing this call - Jrd::Attachment* att = req_dbb->dbb_attachment; - TraceDSQLFetch trace(att, this); - - thread_db::TimerGuard timerGuard(tdbb, req_timer, false); - if (req_timer && req_timer->expired()) - tdbb->checkCancelState(); - - UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; - if (!firstRowFetched && needRestarts()) - { - // Note: tra_handle can't be changed by executeReceiveWithRestarts below - // and outMetadata and outMsg in not used there, so passing NULL's is safe. - jrd_tra* tra = req_transaction; - - executeReceiveWithRestarts(tdbb, &tra, NULL, NULL, false, false, true); - fb_assert(tra == req_transaction); - } - else - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer); - - firstRowFetched = true; - - const dsql_par* const eof = statement->getEof(); - const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL; - const bool eofReached = eof && !(*eofPtr); - - if (eofReached) - { - if (req_timer) - req_timer->stop(); - - trace.fetch(true, ITracePlugin::RESULT_SUCCESS); - return false; - } - - if (msgBuffer) - mapInOut(tdbb, true, message, NULL, msgBuffer); - - trace.fetch(false, ITracePlugin::RESULT_SUCCESS); - return true; -} - - /** DSQL_free_statement @@ -340,30 +178,25 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) @param option **/ -void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option) +void DSQL_free_statement(thread_db* tdbb, DsqlRequest* request, USHORT option) { SET_TDBB(tdbb); Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); + + fb_assert(!(option & DSQL_unprepare)); // handled in y-valve if (option & DSQL_drop) { // Release everything associated with the request - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } - /* - else if (option & DSQL_unprepare) - { - // Release everything but the request itself - dsql_req::destroy(tdbb, request, false); - } - */ else if (option & DSQL_close) { // Just close the cursor associated with the request - if (reqTypeWithCursor(statement->getType())) + if (statement->isCursorBased()) { if (!request->req_cursor) { @@ -396,7 +229,7 @@ void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option) @param buffer **/ -dsql_req* DSQL_prepare(thread_db* tdbb, +DsqlRequest* DSQL_prepare(thread_db* tdbb, Attachment* attachment, jrd_tra* transaction, ULONG length, const TEXT* string, USHORT dialect, unsigned prepareFlags, Array* items, Array* buffer, @@ -405,7 +238,7 @@ dsql_req* DSQL_prepare(thread_db* tdbb, SET_TDBB(tdbb); dsql_dbb* database = init(tdbb, attachment); - dsql_req* request = NULL; + DsqlRequest* request = NULL; try { @@ -416,8 +249,8 @@ dsql_req* DSQL_prepare(thread_db* tdbb, // Can not prepare a CREATE DATABASE/SCHEMA statement - const DsqlCompiledStatement* statement = request->getStatement(); - if (statement->getType() == DsqlCompiledStatement::TYPE_CREATE_DB) + const DsqlStatement* statement = request->getStatement(); + if (statement->getType() == DsqlStatement::TYPE_CREATE_DB) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-530) << Arg::Gds(isc_dsql_crdb_prepare_err)); @@ -437,89 +270,13 @@ dsql_req* DSQL_prepare(thread_db* tdbb, if (request) { Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } throw; } } -// Set a cursor name for a dynamic request. -void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &getPool()); - - const size_t MAX_CURSOR_LENGTH = 132 - 1; - string cursor = name; - - if (cursor.hasData() && cursor[0] == '\"') - { - // Quoted cursor names eh? Strip'em. - // Note that "" will be replaced with ". - // The code is very strange, because it doesn't check for "" really - // and thus deletes one isolated " in the middle of the cursor. - for (string::iterator i = cursor.begin(); i < cursor.end(); ++i) - { - if (*i == '\"') - cursor.erase(i); - } - } - else // not quoted name - { - const string::size_type i = cursor.find(' '); - if (i != string::npos) - cursor.resize(i); - - cursor.upper(); - } - - USHORT length = (USHORT) fb_utils::name_length(cursor.c_str()); - - if (!length) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_invalid)); - } - - if (length > MAX_CURSOR_LENGTH) - length = MAX_CURSOR_LENGTH; - - cursor.resize(length); - - // If there already is a different cursor by the same name, bitch - - dsql_req* const* symbol = req_dbb->dbb_cursors.get(cursor); - if (symbol) - { - if (this == *symbol) - return; - - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_redefined) << cursor); - } - - // If there already is a cursor and its name isn't the same, ditto. - // We already know there is no cursor by this name in the hash table - - if (req_cursor && req_cursor_name.hasData()) - { - fb_assert(!symbol); - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_redefined) << req_cursor_name); - } - - if (req_cursor_name.hasData()) - req_dbb->dbb_cursors.remove(req_cursor_name); - req_cursor_name = cursor; - req_dbb->dbb_cursors.put(cursor, this); -} - - /** DSQL_sql_info @@ -536,7 +293,7 @@ void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) **/ void DSQL_sql_info(thread_db* tdbb, - dsql_req* request, + DsqlRequest* request, ULONG item_length, const UCHAR* items, ULONG info_length, UCHAR* info) { @@ -558,20 +315,20 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr SET_TDBB(tdbb); dsql_dbb* const database = init(tdbb, attachment); - dsql_req* request = NULL; + DsqlRequest* request = NULL; try { request = prepareRequest(tdbb, database, *tra_handle, length, string, dialect, isInternalRequest); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); // Only allow NULL trans_handle if we're starting a transaction or set session properties if (!*tra_handle && - statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) + statement->getType() != DsqlStatement::TYPE_START_TRANS && + statement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); @@ -580,7 +337,7 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr Jrd::ContextPoolHolder context(tdbb, &request->getPool()); // A select having cursor is a singleton select when executed immediate - const bool singleton = reqTypeWithCursor(statement->getType()); + const bool singleton = statement->isCursorBased(); if (singleton && !(out_msg && out_meta)) { ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << @@ -591,515 +348,20 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } catch (const Exception&) { if (request) { Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } throw; } } -void DsqlDmlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) -{ - { // scope - Jrd::ContextPoolHolder scratchContext(tdbb, &scratch->getPool()); - node = Node::doDsqlPass(scratch, node); - } - - if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); - else - scratch->getStatement()->setBlrVersion(4); - - GEN_request(scratch, node); - - // Create the messages buffers - for (FB_SIZE_T i = 0; i < scratch->ports.getCount(); ++i) - { - dsql_msg* message = scratch->ports[i]; - - // Allocate buffer for message - const ULONG newLen = message->msg_length + FB_DOUBLE_ALIGN - 1; - UCHAR* msgBuffer = FB_NEW_POOL(scratch->getStatement()->getPool()) UCHAR[newLen]; - msgBuffer = FB_ALIGN(msgBuffer, FB_DOUBLE_ALIGN); - message->msg_buffer_number = req_msg_buffers.add(msgBuffer); - } - - // have the access method compile the statement - -#ifdef DSQL_DEBUG - if (DSQL_debug & 64) - { - dsql_trace("Resulting BLR code for DSQL:"); - gds__trace_raw("Statement:\n"); - gds__trace_raw(statement->getSqlText()->c_str(), statement->getSqlText()->length()); - gds__trace_raw("\nBLR:\n"); - fb_print_blr(scratch->getBlrData().begin(), - (ULONG) scratch->getBlrData().getCount(), - gds__trace_printer, 0, 0); - } -#endif - - FbLocalStatus localStatus; - - // check for warnings - if (tdbb->tdbb_status_vector->getState() & IStatus::STATE_WARNINGS) - { - // save a status vector - fb_utils::copyStatus(&localStatus, tdbb->tdbb_status_vector); - fb_utils::init_status(tdbb->tdbb_status_vector); - } - - ISC_STATUS status = FB_SUCCESS; - - try - { - JRD_compile(tdbb, scratch->getAttachment()->dbb_attachment, &req_request, - scratch->getBlrData().getCount(), scratch->getBlrData().begin(), - statement->getSqlText(), - scratch->getDebugData().getCount(), scratch->getDebugData().begin(), - (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST)); - } - catch (const Exception&) - { - status = tdbb->tdbb_status_vector->getErrors()[1]; - *traceResult = status == isc_no_priv ? - ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED; - } - - // restore warnings (if there are any) - if (localStatus->getState() & IStatus::STATE_WARNINGS) - { - Arg::StatusVector cur(tdbb->tdbb_status_vector->getWarnings()); - Arg::StatusVector saved(localStatus->getWarnings()); - saved << cur; - - tdbb->tdbb_status_vector->setWarnings2(saved.length(), saved.value()); - } - - // free blr memory - scratch->getBlrData().free(); - - if (status) - status_exception::raise(tdbb->tdbb_status_vector); - - // We don't need the scratch pool anymore. Tell our caller to delete it. - node = NULL; - *destroyScratchPool = true; -} - -bool DsqlDmlRequest::needRestarts() -{ - return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency)); -}; - -// Execute a dynamic SQL statement -void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - firstRowFetched = false; - const dsql_msg* message = statement->getSendMsg(); - - if (!message) - JRD_start(tdbb, req_request, req_transaction); - else - { - UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_start_and_send(tdbb, req_request, req_transaction, message->msg_number, - message->msg_length, msgBuffer); - } - - // Selectable execute block should get the "proc fetch" flag assigned, - // which ensures that the savepoint stack is preserved while suspending - if (statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK) - req_request->req_flags |= req_proc_fetch; - - // TYPE_EXEC_BLOCK has no outputs so there are no out_msg - // supplied from client side, but TYPE_EXEC_BLOCK requires - // 2-byte message for EOS synchronization - const bool isBlock = (statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK); - - message = statement->getReceiveMsg(); - - if (outMetadata == DELAYED_OUT_FORMAT) - { - needDelayedFormat = true; - outMetadata = NULL; - } - - if (outMetadata && message) - parseMetadata(outMetadata, message->msg_parameters); - - if ((outMsg && message) || isBlock) - { - UCHAR temp_buffer[FB_DOUBLE_ALIGN * 2]; - dsql_msg temp_msg(*getDefaultMemoryPool()); - - // Insure that the metadata for the message is parsed, regardless of - // whether anything is found by the call to receive. - - UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; - - if (!outMetadata && isBlock) - { - message = &temp_msg; - temp_msg.msg_number = 1; - temp_msg.msg_length = 2; - msgBuffer = FB_ALIGN(temp_buffer, FB_DOUBLE_ALIGN); - } - - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, msgBuffer); - - if (outMsg) - mapInOut(tdbb, true, message, NULL, outMsg); - - // if this is a singleton select, make sure there's in fact one record - - if (singleton) - { - USHORT counter; - - // Create a temp message buffer and try two more receives. - // If both succeed then the first is the next record and the - // second is either another record or the end of record message. - // In either case, there's more than one record. - - UCHAR* message_buffer = (UCHAR*) gds__alloc(message->msg_length); - - ISC_STATUS status = FB_SUCCESS; - FbLocalStatus localStatus; - - for (counter = 0; counter < 2 && !status; counter++) - { - localStatus->init(); - AutoSetRestore autoStatus(&tdbb->tdbb_status_vector, &localStatus); - - try - { - JRD_receive(tdbb, req_request, message->msg_number, - message->msg_length, message_buffer); - status = FB_SUCCESS; - } - catch (Exception&) - { - status = tdbb->tdbb_status_vector->getErrors()[1]; - } - } - - gds__free(message_buffer); - - // two successful receives means more than one record - // a req_sync error on the first pass above means no records - // a non-req_sync error on any of the passes above is an error - - if (!status) - status_exception::raise(Arg::Gds(isc_sing_select_err)); - else if (status == isc_req_sync && counter == 1) - status_exception::raise(Arg::Gds(isc_stream_eof)); - else if (status != isc_req_sync) - status_exception::raise(&localStatus); - } - } - - switch (statement->getType()) - { - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: - if (!req_request->req_records_updated) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << - Arg::Gds(isc_deadlock) << - Arg::Gds(isc_update_conflict)); - } - break; - - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: - if (!req_request->req_records_deleted) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << - Arg::Gds(isc_deadlock) << - Arg::Gds(isc_update_conflict)); - } - break; - } -} - -// Execute a dynamic SQL statement with tracing, restart and timeout handler -void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - if (!req_request) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_unprepared_stmt)); - } - - // If there is no data required, just start the request - - const dsql_msg* message = statement->getSendMsg(); - if (message) - mapInOut(tdbb, false, message, inMetadata, NULL, inMsg); - - // we need to mapInOut() before tracing of execution start to let trace - // manager know statement parameters values - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - - // Setup and start timeout timer - const bool have_cursor = reqTypeWithCursor(statement->getType()) && !singleton; - - setupTimer(tdbb); - thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor); - - if (needRestarts()) - executeReceiveWithRestarts(tdbb, traHandle, outMetadata, outMsg, singleton, true, false); - else { - doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); - } - - trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS); -} - -void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton, bool exec, bool fetch) -{ - req_request->req_flags &= ~req_update_conflict; - int numTries = 0; - const int MAX_RESTARTS = 10; - - while (true) - { - AutoSavePoint savePoint(tdbb, req_transaction); - - // Don't set req_restart_ready flag at last attempt to restart request. - // It allows to raise update conflict error (if any) as usual and - // handle error by PSQL handler. - const ULONG flag = (numTries >= MAX_RESTARTS) ? 0 : req_restart_ready; - AutoSetRestoreFlag restartReady(&req_request->req_flags, flag, true); - try - { - if (exec) - doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); - - if (fetch) - { - fb_assert(reqTypeWithCursor(statement->getType())); - - const dsql_msg* message = statement->getReceiveMsg(); - - UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer); - } - } - catch (const status_exception&) - { - if (!(req_transaction->tra_flags & TRA_ex_restart)) - { - req_request->req_flags &= ~req_update_conflict; - throw; - } - } - - if (!(req_request->req_flags & req_update_conflict)) - { - fb_assert((req_transaction->tra_flags & TRA_ex_restart) == 0); - req_transaction->tra_flags &= ~TRA_ex_restart; - -#ifdef DEV_BUILD - if (numTries > 0) - { - string s; - s.printf("restarts = %d", numTries); - - ERRD_post_warning(Arg::Warning(isc_random) << Arg::Str(s)); - } -#endif - savePoint.release(); // everything is ok - break; - } - - fb_assert((req_transaction->tra_flags & TRA_ex_restart) != 0); - - req_request->req_flags &= ~req_update_conflict; - req_transaction->tra_flags &= ~TRA_ex_restart; - fb_utils::init_status(tdbb->tdbb_status_vector); - - // Undo current savepoint but preserve already taken locks. - // Savepoint will be restarted at the next loop iteration. - savePoint.rollback(true); - - numTries++; - if (numTries >= MAX_RESTARTS) - { - gds__log("Update conflict: unable to get a stable set of rows in the source tables\n" - "\tafter %d attempts of restart.\n" - "\tQuery:\n%s\n", numTries, req_request->getStatement()->sqlText->c_str() ); - } - - // When restart we must execute query - exec = true; - } -} - -void DsqlDdlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) -{ - Database* const dbb = tdbb->getDatabase(); - - internalScratch = scratch; - - scratch->flags |= DsqlCompilerScratch::FLAG_DDL; - - try - { - node = Node::doDsqlPass(scratch, node); - } - catch (status_exception& ex) - { - rethrowDdlException(ex, false); - } - - if (dbb->readOnly()) - ERRD_post(Arg::Gds(isc_read_only_database)); - - // In read-only replica, only replicator is allowed to execute DDL. - // As an exception, not replicated DDL statements are also allowed. - if (dbb->isReplica(REPLICA_READ_ONLY) && - !(tdbb->tdbb_flags & TDBB_replicator) && - node->mustBeReplicated()) - { - ERRD_post(Arg::Gds(isc_read_only_trans)); - } - - const auto dbDialect = - (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; - - if ((scratch->flags & DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT) && - dbDialect != scratch->clientDialect) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-817) << - Arg::Gds(isc_ddl_not_allowed_by_db_sql_dial) << Arg::Num(dbDialect)); - } - - if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); - else - scratch->getStatement()->setBlrVersion(4); -} - -// Execute a dynamic SQL statement. -void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - - fb_utils::init_status(tdbb->tdbb_status_vector); - - // run all statements under savepoint control - { // scope - AutoSavePoint savePoint(tdbb, req_transaction); - - try - { - AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); - - node->executeDdl(tdbb, internalScratch, req_transaction); - - const bool isInternalRequest = - (internalScratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST); - - if (!isInternalRequest && node->mustBeReplicated()) - REPL_exec_sql(tdbb, req_transaction, getStatement()->getOrgText()); - } - catch (status_exception& ex) - { - rethrowDdlException(ex, true); - } - - savePoint.release(); // everything is ok - } - - JRD_autocommit_ddl(tdbb, req_transaction); - - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - -bool DsqlDdlRequest::mustBeReplicated() const -{ - return node->mustBeReplicated(); -} - -// Rethrow an exception with isc_no_meta_update and prefix codes. -void DsqlDdlRequest::rethrowDdlException(status_exception& ex, bool metadataUpdate) -{ - Arg::StatusVector newVector; - - if (metadataUpdate) - newVector << Arg::Gds(isc_no_meta_update); - - node->putErrorPrefix(newVector); - - const ISC_STATUS* status = ex.value(); - - if (status[1] == isc_no_meta_update) - status += 2; - - newVector.append(Arg::StatusVector(status)); - - status_exception::raise(newVector); -} - - -void DsqlTransactionRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* /*traceResult*/) -{ - node = Node::doDsqlPass(scratch, node); - - // Don't trace anything except savepoint statements - req_traced = (scratch->getStatement()->getType() == DsqlCompiledStatement::TYPE_SAVEPOINT); -} - -// Execute a dynamic SQL statement. -void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/, - IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/, - bool /*singleton*/) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - node->execute(tdbb, this, traHandle); - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - - -void DsqlSessionManagementRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* /*traceResult*/) -{ - node = Node::doDsqlPass(scratch, node); -} - -// Execute a dynamic SQL statement. -void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - node->execute(tdbb, this, traHandle); - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - - /** get_request_info @@ -1112,16 +374,16 @@ void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, @param buffer **/ -static ULONG get_request_info(thread_db* tdbb, dsql_req* request, ULONG buffer_length, UCHAR* buffer) +static ULONG get_request_info(thread_db* tdbb, DsqlRequest* request, ULONG buffer_length, UCHAR* buffer) { - if (!request->req_request) // DDL + if (!request->getJrdRequest()) // DDL return 0; // get the info for the request from the engine try { - return INF_request_info(request->req_request, sizeof(record_info), record_info, + return INF_request_info(request->getJrdRequest(), sizeof(record_info), record_info, buffer_length, buffer); } catch (Exception&) @@ -1163,348 +425,45 @@ static dsql_dbb* init(thread_db* tdbb, Jrd::Attachment* attachment) } -/** - - mapInOut - - @brief Map data from external world into message or - from message to external world. - - - @param request - @param toExternal - @param message - @param meta - @param dsql_msg_buf - @param in_dsql_msg_buf - - **/ -void dsql_req::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* message, - IMessageMetadata* meta, UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf) -{ - USHORT count = parseMetadata(meta, message->msg_parameters); - - // Sanity check - - if (count) - { - if (toExternal) - { - if (dsql_msg_buf == NULL) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_no_output_sqlda)); - } - } - else - { - if (in_dsql_msg_buf == NULL) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_no_input_sqlda)); - } - } - } - - USHORT count2 = 0; - - for (FB_SIZE_T i = 0; i < message->msg_parameters.getCount(); ++i) - { - dsql_par* parameter = message->msg_parameters[i]; - - if (parameter->par_index) - { - // Make sure the message given to us is long enough - - dsc desc; - if (!req_user_descs.get(parameter, desc)) - desc.clear(); - - /*** - ULONG length = (IPTR) desc.dsc_address + desc.dsc_length; - if (length > msg_length) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_random) << "Message buffer too short"); - } - ***/ - if (!desc.dsc_dtype) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_datatype_err) << - Arg::Gds(isc_dsql_sqlvar_index) << Arg::Num(parameter->par_index-1)); - } - - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - SSHORT* flag = NULL; - dsql_par* const null_ind = parameter->par_null; - if (null_ind != NULL) - { - dsc userNullDesc; - if (!req_user_descs.get(null_ind, userNullDesc)) - userNullDesc.clear(); - - const ULONG null_offset = (IPTR) userNullDesc.dsc_address; - - /*** - length = null_offset + sizeof(SSHORT); - if (length > msg_length) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) - << Arg::Gds(isc_random) << "Message buffer too short"); - } - ***/ - - dsc nullDesc = null_ind->par_desc; - nullDesc.dsc_address = msgBuffer + (IPTR) nullDesc.dsc_address; - - if (toExternal) - { - flag = reinterpret_cast(dsql_msg_buf + null_offset); - *flag = *reinterpret_cast(nullDesc.dsc_address); - } - else - { - flag = reinterpret_cast(nullDesc.dsc_address); - *flag = *reinterpret_cast(in_dsql_msg_buf + null_offset); - } - } - - const bool notNull = (!flag || *flag >= 0); - - dsc parDesc = parameter->par_desc; - parDesc.dsc_address = msgBuffer + (IPTR) parDesc.dsc_address; - - if (toExternal) - { - desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address; - - if (notNull) - MOVD_move(tdbb, &parDesc, &desc); - else - memset(desc.dsc_address, 0, desc.dsc_length); - } - else if (notNull && !parDesc.isNull()) - { - // Safe cast because desc is used as source only. - desc.dsc_address = const_cast(in_dsql_msg_buf) + (IPTR) desc.dsc_address; - MOVD_move(tdbb, &desc, &parDesc); - } - else - memset(parDesc.dsc_address, 0, parDesc.dsc_length); - - ++count2; - } - } - - if (count != count2) - { - ERRD_post( - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_wrong_param_num) << Arg::Num(count) <getParentDbKey()) && - (parameter = statement->getDbKey())) - { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - fb_assert(parentMsgBuffer); - - dsc parentDesc = dbkey->par_desc; - parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; - - dsc desc = parameter->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - MOVD_move(tdbb, &parentDesc, &desc); - - dsql_par* null_ind = parameter->par_null; - if (null_ind != NULL) - { - desc = null_ind->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - SSHORT* flag = (SSHORT*) desc.dsc_address; - *flag = 0; - } - } - - const dsql_par* rec_version; - if (!toExternal && (rec_version = statement->getParentRecVersion()) && - (parameter = statement->getRecVersion())) - { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : - NULL; - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - fb_assert(parentMsgBuffer); - - dsc parentDesc = rec_version->par_desc; - parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; - - dsc desc = parameter->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - MOVD_move(tdbb, &parentDesc, &desc); - - dsql_par* null_ind = parameter->par_null; - if (null_ind != NULL) - { - desc = null_ind->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - SSHORT* flag = (SSHORT*) desc.dsc_address; - *flag = 0; - } - } -} - - -/** - - parseMetadata - - @brief Parse the message of a request. - - - @param request - @param meta - @param parameters_list - - **/ -USHORT dsql_req::parseMetadata(IMessageMetadata* meta, const Array& parameters_list) -{ - HalfStaticArray parameters; - - for (FB_SIZE_T i = 0; i < parameters_list.getCount(); ++i) - { - dsql_par* param = parameters_list[i]; - - if (param->par_index) - { - if (param->par_index > parameters.getCount()) - parameters.grow(param->par_index); - fb_assert(!parameters[param->par_index - 1]); - parameters[param->par_index - 1] = param; - } - } - - // If there's no metadata, then the format of the current message buffer - // is identical to the format of the previous one. - - if (!meta) - return parameters.getCount(); - - FbLocalStatus st; - unsigned count = meta->getCount(&st); - checkD(&st); - - unsigned count2 = parameters.getCount(); - - if (count != count2) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_wrong_param_num) <getType(&st, index); - checkD(&st); - unsigned sqlLength = meta->getLength(&st, index); - checkD(&st); - - dsc desc; - desc.dsc_flags = 0; - - unsigned dataOffset, nullOffset, dtype, dlength; - offset = fb_utils::sqlTypeToDsc(offset, sqlType, sqlLength, - &dtype, &dlength, &dataOffset, &nullOffset); - desc.dsc_dtype = dtype; - desc.dsc_length = dlength; - - desc.dsc_scale = meta->getScale(&st, index); - checkD(&st); - desc.dsc_sub_type = meta->getSubType(&st, index); - checkD(&st); - unsigned textType = meta->getCharSet(&st, index); - checkD(&st); - desc.setTextType(textType); - desc.dsc_address = (UCHAR*)(IPTR) dataOffset; - - const dsql_par* const parameter = parameters[index]; - fb_assert(parameter); - - // ASF: Older than 2.5 engine hasn't validating strings in DSQL. After this has been - // implemented in 2.5, selecting a NONE column with UTF-8 attachment charset started - // failing. The real problem is that the client encodes SQL_TEXT/SQL_VARYING using - // blr_text/blr_varying (i.e. with the connection charset). I'm reseting the charset - // here at the server as a way to make older (and not yet changed) client work - // correctly. - if (desc.isText() && desc.getTextType() == ttype_dynamic) - desc.setTextType(ttype_none); - - req_user_descs.put(parameter, desc); - - dsql_par* null = parameter->par_null; - if (null) - { - desc.clear(); - desc.dsc_dtype = dtype_short; - desc.dsc_scale = 0; - desc.dsc_length = sizeof(SSHORT); - desc.dsc_address = (UCHAR*)(IPTR) nullOffset; - - req_user_descs.put(null, desc); - } - } - - return count; -} - - -// raise error if one present -static void checkD(IStatus* st) -{ - if (st->getState() & IStatus::STATE_ERRORS) - { - ERRD_post(Arg::StatusVector(st)); - } -} - - -// Prepare a request for execution. Return SQL status code. +// Prepare a request for execution. // Note: caller is responsible for pool handling. -static dsql_req* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, +static DsqlRequest* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest) { - return prepareStatement(tdbb, database, transaction, textLength, text, clientDialect, isInternalRequest); + TraceDSQLPrepare trace(database->dbb_attachment, transaction, textLength, text); + + ntrace_result_t traceResult = ITracePlugin::RESULT_SUCCESS; + try + { + auto statement = prepareStatement(tdbb, database, transaction, textLength, text, + clientDialect, isInternalRequest, &traceResult); + + auto request = statement->createRequest(tdbb, database); + + request->req_traced = true; + trace.setStatement(request); + trace.prepare(traceResult); + + return request; + } + catch (const Exception&) + { + trace.prepare(ITracePlugin::RESULT_FAILED); + throw; + } } -// Prepare a statement for execution. Return SQL status code. +// Prepare a statement for execution. // Note: caller is responsible for pool handling. -static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, - ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest) +static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, + ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest, ntrace_result_t* traceResult) { Database* const dbb = tdbb->getDatabase(); if (text && textLength == 0) textLength = static_cast(strlen(text)); - TraceDSQLPrepare trace(database->dbb_attachment, transaction, textLength, text); - if (clientDialect > SQL_DIALECT_CURRENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << @@ -1540,27 +499,19 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* // allocate the statement block, then prepare the statement + MemoryPool* scratchPool = nullptr; + DsqlCompilerScratch* scratch = nullptr; MemoryPool* statementPool = database->createPool(); - MemoryPool* scratchPool = NULL; - dsql_req* request = NULL; + RefPtr statement; Jrd::ContextPoolHolder statementContext(tdbb, statementPool); try { - DsqlCompiledStatement* statement = FB_NEW_POOL(*statementPool) DsqlCompiledStatement(*statementPool); - scratchPool = database->createPool(); if (!transaction) // Useful for session management statements transaction = database->dbb_attachment->getSysTransaction(); - DsqlCompilerScratch* scratch = FB_NEW_POOL(*scratchPool) DsqlCompilerScratch(*scratchPool, database, - transaction, statement); - scratch->clientDialect = clientDialect; - - if (isInternalRequest) - scratch->flags |= DsqlCompilerScratch::FLAG_INTERNAL_REQUEST; - const auto dbDialect = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; @@ -1571,12 +522,19 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* { // scope to delete parser before the scratch pool is gone Jrd::ContextPoolHolder scratchContext(tdbb, scratchPool); - Parser parser(tdbb, *scratchPool, scratch, clientDialect, + scratch = FB_NEW_POOL(*scratchPool) DsqlCompilerScratch(*scratchPool, database, transaction); + scratch->clientDialect = clientDialect; + + if (isInternalRequest) + scratch->flags |= DsqlCompilerScratch::FLAG_INTERNAL_REQUEST; + + Parser parser(tdbb, *scratchPool, statementPool, scratch, clientDialect, dbDialect, text, textLength, charSetId); // Parse the SQL statement. If it croaks, return - request = parser.parse(); - request->liveScratchPool = scratchPool; + statement = parser.parse(); + + scratch->setStatement(statement); if (parser.isStmtAmbiguous()) scratch->flags |= DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT; @@ -1584,10 +542,6 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transformedText = parser.getTransformedString(); } - request->req_dbb = scratch->getAttachment(); - request->req_transaction = scratch->getTransaction(); - request->statement = scratch->getStatement(); - // If the attachment charset is NONE, replace non-ASCII characters by question marks, so // that engine internals doesn't receive non-mappeable data to UTF8. If an attachment // charset is used, validate the string. @@ -1628,50 +582,26 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); + statement->dsqlPass(tdbb, scratch, traceResult); - request->req_traced = true; - trace.setStatement(request); + if (!statement->shouldPreserveScratch()) + database->deletePool(scratchPool); - ntrace_result_t traceResult = ITracePlugin::RESULT_SUCCESS; - try - { - bool destroyScratchPool = false; - request->dsqlPass(tdbb, scratch, &destroyScratchPool, &traceResult); + scratchPool = nullptr; - if (destroyScratchPool) - { - database->deletePool(scratchPool); - request->liveScratchPool = scratchPool = NULL; - } - } - catch (const Exception&) - { - trace.prepare(traceResult); - throw; - } - - if (!isInternalRequest && request->mustBeReplicated()) + if (!isInternalRequest && statement->mustBeReplicated()) statement->setOrgText(text, textLength); - trace.prepare(traceResult); - - return request; + return statement; } catch (const Exception&) { - trace.prepare(ITracePlugin::RESULT_FAILED); - - if (request) - { - request->req_traced = false; - dsql_req::destroy(tdbb, request, true); - } - else - { + if (scratchPool) database->deletePool(scratchPool); + + if (!statement) database->deletePool(statementPool); - } throw; } @@ -1718,261 +648,14 @@ static UCHAR* put_item( UCHAR item, } -// Release a compiled statement. -static void release_statement(DsqlCompiledStatement* statement) -{ - if (statement->getParentRequest()) - { - dsql_req* parent = statement->getParentRequest(); - - FB_SIZE_T pos; - if (parent->cursors.find(statement, pos)) - parent->cursors.remove(pos); - - statement->setParentRequest(NULL); - } - - statement->setSqlText(NULL); - statement->setOrgText(NULL, 0); -} - - -// Class DsqlCompiledStatement - -void DsqlCompiledStatement::setOrgText(const char* ptr, ULONG len) -{ - if (!ptr || !len) - { - orgText = NULL; - return; - } - - const string text(ptr, len); - - if (text == *sqlText) - orgText = sqlText; - else - orgText = FB_NEW_POOL(getPool()) RefString(getPool(), text); -} - - -// Class dsql_req - -dsql_req::dsql_req(MemoryPool& pool) - : req_pool(pool), - statement(NULL), - liveScratchPool(NULL), - cursors(req_pool), - req_dbb(NULL), - req_transaction(NULL), - req_msg_buffers(req_pool), - req_cursor_name(req_pool), - req_cursor(NULL), - req_batch(NULL), - req_user_descs(req_pool), - req_traced(false), - req_timeout(0) -{ -} - -dsql_req::~dsql_req() -{ -} - -void dsql_req::setCursor(thread_db* /*tdbb*/, const TEXT* /*name*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); -} - -void dsql_req::setDelayedFormat(thread_db* /*tdbb*/, IMessageMetadata* /*metadata*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); -} - -bool dsql_req::fetch(thread_db* /*tdbb*/, UCHAR* /*msgBuffer*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); - - return false; // avoid warning -} - -unsigned int dsql_req::getTimeout() -{ - return req_timeout; -} - -unsigned int dsql_req::getActualTimeout() -{ - if (req_timer) - return req_timer->getValue(); - - return 0; -} - -void dsql_req::setTimeout(unsigned int timeOut) -{ - req_timeout = timeOut; -} - -TimeoutTimer* dsql_req::setupTimer(thread_db* tdbb) -{ - if (req_request) - { - if (req_request->hasInternalStatement()) - return req_timer; - - req_request->req_timeout = this->req_timeout; - - fb_assert(!req_request->req_caller); - if (req_request->req_caller) - { - if (req_timer) - req_timer->setup(0, 0); - return req_timer; - } - } - - Database* dbb = tdbb->getDatabase(); - Attachment* att = tdbb->getAttachment(); - - ISC_STATUS toutErr = isc_cfg_stmt_timeout; - unsigned int timeOut = dbb->dbb_config->getStatementTimeout() * 1000; - - if (req_timeout) - { - if (!timeOut || req_timeout < timeOut) - { - timeOut = req_timeout; - toutErr = isc_req_stmt_timeout; - } - } - else - { - const unsigned int attTout = att->getStatementTimeout(); - - if (!timeOut || attTout && attTout < timeOut) - { - timeOut = attTout; - toutErr = isc_att_stmt_timeout; - } - } - - if (!req_timer && timeOut) - { - req_timer = FB_NEW TimeoutTimer(); - req_request->req_timer = this->req_timer; - } - - if (req_timer) - { - req_timer->setup(timeOut, toutErr); - req_timer->start(); - } - - return req_timer; -} - -// Release a dynamic request. -void dsql_req::destroy(thread_db* tdbb, dsql_req* request, bool drop) -{ - SET_TDBB(tdbb); - - if (request->req_timer) - { - request->req_timer->stop(); - request->req_timer = NULL; - } - - // If request is parent, orphan the children and release a portion of their requests - - for (FB_SIZE_T i = 0; i < request->cursors.getCount(); ++i) - { - DsqlCompiledStatement* child = request->cursors[i]; - child->addFlags(DsqlCompiledStatement::FLAG_ORPHAN); - child->setParentRequest(NULL); - - // hvlad: lines below is commented out as - // - child is already unlinked from its parent request - // - we should not free child's sql text until its owner request is alive - // It seems to me we should destroy owner request here, not a child - // statement - as it always was before - - //Jrd::ContextPoolHolder context(tdbb, &child->getPool()); - //release_statement(child); - } - - // If the request had an open cursor, close it - - if (request->req_cursor) - DsqlCursor::close(tdbb, request->req_cursor); - - if (request->req_batch) - { - delete request->req_batch; - request->req_batch = nullptr; - } - - Jrd::Attachment* att = request->req_dbb->dbb_attachment; - const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att); - if (need_trace_free) - { - TraceSQLStatementImpl stmt(request, NULL); - TraceManager::event_dsql_free(att, &stmt, DSQL_drop); - } - request->req_traced = false; - - if (request->req_cursor_name.hasData()) - { - request->req_dbb->dbb_cursors.remove(request->req_cursor_name); - request->req_cursor_name = ""; - } - - // If a request has been compiled, release it now - - if (request->req_request) - { - ThreadStatusGuard status_vector(tdbb); - - try - { - CMP_release(tdbb, request->req_request); - request->req_request = NULL; - } - catch (Exception&) - {} // no-op - } - - const DsqlCompiledStatement* statement = request->getStatement(); - release_statement(const_cast(statement)); - - // Release the entire request if explicitly asked for - - if (drop) - { - request->req_dbb->deletePool(request->liveScratchPool); - request->req_dbb->deletePool(&request->getPool()); - } -} - - // Return as UTF8 -string IntlString::toUtf8(DsqlCompilerScratch* dsqlScratch) const +string IntlString::toUtf8(jrd_tra* transaction) const { CHARSET_ID id = CS_dynamic; if (charset.hasData()) { - const dsql_intlsym* resolved = METD_get_charset(dsqlScratch->getTransaction(), - charset.length(), charset.c_str()); + const dsql_intlsym* resolved = METD_get_charset(transaction, charset.length(), charset.c_str()); if (!resolved) { @@ -2004,7 +687,7 @@ string IntlString::toUtf8(DsqlCompilerScratch* dsqlScratch) const **/ static void sql_info(thread_db* tdbb, - dsql_req* request, + DsqlRequest* request, ULONG item_length, const UCHAR* items, ULONG info_length, @@ -2037,7 +720,7 @@ static void sql_info(thread_db* tdbb, bool messageFound = false; USHORT first_index = 0; - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); while (items < end_items && *items != isc_info_end && info < end_info) { @@ -2065,14 +748,14 @@ static void sql_info(thread_db* tdbb, value = IStatement::FLAG_REPEAT_EXECUTE; switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_CREATE_DB: - case DsqlCompiledStatement::TYPE_DDL: + case DsqlStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_DDL: value &= ~IStatement::FLAG_REPEAT_EXECUTE; break; - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_RETURNING_CURSOR: value |= IStatement::FLAG_HAS_CURSOR; break; } @@ -2085,55 +768,55 @@ static void sql_info(thread_db* tdbb, case isc_info_sql_stmt_type: switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_RETURNING_CURSOR: number = isc_info_sql_stmt_select; break; - case DsqlCompiledStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_UPD: number = isc_info_sql_stmt_select_for_upd; break; - case DsqlCompiledStatement::TYPE_CREATE_DB: - case DsqlCompiledStatement::TYPE_DDL: + case DsqlStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_DDL: number = isc_info_sql_stmt_ddl; break; - case DsqlCompiledStatement::TYPE_COMMIT: - case DsqlCompiledStatement::TYPE_COMMIT_RETAIN: + case DsqlStatement::TYPE_COMMIT: + case DsqlStatement::TYPE_COMMIT_RETAIN: number = isc_info_sql_stmt_commit; break; - case DsqlCompiledStatement::TYPE_ROLLBACK: - case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN: + case DsqlStatement::TYPE_ROLLBACK: + case DsqlStatement::TYPE_ROLLBACK_RETAIN: number = isc_info_sql_stmt_rollback; break; - case DsqlCompiledStatement::TYPE_START_TRANS: + case DsqlStatement::TYPE_START_TRANS: number = isc_info_sql_stmt_start_trans; break; - case DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT: + case DsqlStatement::TYPE_SESSION_MANAGEMENT: number = isc_info_sql_stmt_ddl; // ????????????????? break; - case DsqlCompiledStatement::TYPE_INSERT: + case DsqlStatement::TYPE_INSERT: number = isc_info_sql_stmt_insert; break; - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_UPDATE_CURSOR: number = isc_info_sql_stmt_update; break; - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_DELETE_CURSOR: number = isc_info_sql_stmt_delete; break; - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_EXEC_PROCEDURE: number = isc_info_sql_stmt_exec_procedure; break; - case DsqlCompiledStatement::TYPE_SET_GENERATOR: + case DsqlStatement::TYPE_SET_GENERATOR: number = isc_info_sql_stmt_set_generator; break; - case DsqlCompiledStatement::TYPE_SAVEPOINT: + case DsqlStatement::TYPE_SAVEPOINT: number = isc_info_sql_stmt_savepoint; break; - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_EXEC_BLOCK: number = isc_info_sql_stmt_exec_procedure; break; - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT_BLOCK: number = isc_info_sql_stmt_select; break; default: @@ -2169,7 +852,7 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_batch_fetch: - if (statement->getFlags() & DsqlCompiledStatement::FLAG_NO_BATCH) + if (statement->getFlags() & DsqlStatement::FLAG_NO_BATCH) number = 0; else number = 1; @@ -2206,9 +889,7 @@ static void sql_info(thread_db* tdbb, { const bool detailed = (item == isc_info_sql_explain_plan); string plan = tdbb->getAttachment()->stringToUserCharSet(tdbb, - OPT_get_plan(tdbb, - (request->req_request ? request->req_request->getStatement() : nullptr), - detailed)); + OPT_get_plan(tdbb, request->getJrdStatement(), detailed)); if (plan.hasData()) { @@ -2258,9 +939,9 @@ static void sql_info(thread_db* tdbb, { HalfStaticArray path; - if (request->req_request && request->req_request->getStatement()) + if (request->getJrdStatement()) { - const auto& blr = request->req_request->getStatement()->blr; + const auto& blr = request->getJrdStatement()->blr; if (blr.hasData()) { diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index b3e1e7d6ff..6a3266aba7 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -35,6 +35,7 @@ #define DSQL_DSQL_H #include "../common/classes/array.h" +#include "../common/classes/fb_atomic.h" #include "../common/classes/GenericMap.h" #include "../jrd/MetaName.h" #include "../common/classes/stack.h" @@ -76,6 +77,7 @@ namespace Jrd class Attachment; class Database; class DsqlCompilerScratch; + class DsqlDmlStatement; class DdlNode; class RseNode; class StmtNode; @@ -134,7 +136,7 @@ public: Firebird::GenericMap > > dbb_charsets_by_id; // charsets sorted by charset_id Firebird::GenericMap > > dbb_cursors; // known cursors in database + Firebird::string, DsqlDmlRequest*>>> dbb_cursors; // known cursors in database MemoryPool& dbb_pool; // The current pool for the dbb Attachment* dbb_attachment; @@ -451,321 +453,6 @@ enum intlsym_flags_vals { INTLSYM_dropped = 1 // intlsym has been dropped }; - -// Compiled statement - shared by multiple requests. -class DsqlCompiledStatement : public Firebird::PermanentStorage -{ -public: - enum Type // statement type - { - TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, - TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, - TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, - TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT, - TYPE_RETURNING_CURSOR - }; - - // Statement flags. - static const unsigned FLAG_ORPHAN = 0x01; - static const unsigned FLAG_NO_BATCH = 0x02; - //static const unsigned FLAG_BLR_VERSION4 = 0x04; - //static const unsigned FLAG_BLR_VERSION5 = 0x08; - static const unsigned FLAG_SELECTABLE = 0x10; - -public: - explicit DsqlCompiledStatement(MemoryPool& p) - : PermanentStorage(p), - type(TYPE_SELECT), - flags(0), - blrVersion(5), - sendMsg(NULL), - receiveMsg(NULL), - eof(NULL), - dbKey(NULL), - recVersion(NULL), - parentRecVersion(NULL), - parentDbKey(NULL), - parentRequest(NULL) - { - } - -public: - Type getType() const { return type; } - void setType(Type value) { type = value; } - - ULONG getFlags() const { return flags; } - void setFlags(ULONG value) { flags = value; } - void addFlags(ULONG value) { flags |= value; } - - unsigned getBlrVersion() const { return blrVersion; } - void setBlrVersion(unsigned value) { blrVersion = value; } - - Firebird::RefStrPtr& getSqlText() { return sqlText; } - const Firebird::RefStrPtr& getSqlText() const { return sqlText; } - void setSqlText(Firebird::RefString* value) { sqlText = value; } - - void setOrgText(const char* ptr, ULONG len); - const Firebird::string& getOrgText() const { return *orgText; } - - dsql_msg* getSendMsg() { return sendMsg; } - const dsql_msg* getSendMsg() const { return sendMsg; } - void setSendMsg(dsql_msg* value) { sendMsg = value; } - - dsql_msg* getReceiveMsg() { return receiveMsg; } - const dsql_msg* getReceiveMsg() const { return receiveMsg; } - void setReceiveMsg(dsql_msg* value) { receiveMsg = value; } - - dsql_par* getEof() { return eof; } - const dsql_par* getEof() const { return eof; } - void setEof(dsql_par* value) { eof = value; } - - dsql_par* getDbKey() { return dbKey; } - const dsql_par* getDbKey() const { return dbKey; } - void setDbKey(dsql_par* value) { dbKey = value; } - - dsql_par* getRecVersion() { return recVersion; } - const dsql_par* getRecVersion() const { return recVersion; } - void setRecVersion(dsql_par* value) { recVersion = value; } - - dsql_par* getParentRecVersion() { return parentRecVersion; } - const dsql_par* getParentRecVersion() const { return parentRecVersion; } - void setParentRecVersion(dsql_par* value) { parentRecVersion = value; } - - dsql_par* getParentDbKey() { return parentDbKey; } - const dsql_par* getParentDbKey() const { return parentDbKey; } - void setParentDbKey(dsql_par* value) { parentDbKey = value; } - - dsql_req* getParentRequest() const { return parentRequest; } - void setParentRequest(dsql_req* value) { parentRequest = value; } - -private: - Type type; // Type of statement - ULONG flags; // generic flag - unsigned blrVersion; - Firebird::RefStrPtr sqlText; - Firebird::RefStrPtr orgText; - dsql_msg* sendMsg; // Message to be sent to start request - dsql_msg* receiveMsg; // Per record message to be received - dsql_par* eof; // End of file parameter - dsql_par* dbKey; // Database key for current of - dsql_par* recVersion; // Record Version for current of - dsql_par* parentRecVersion; // parent record version - dsql_par* parentDbKey; // Parent database key for current of - dsql_req* parentRequest; // Source request, if cursor update -}; - -class dsql_req : public pool_alloc -{ -public: - explicit dsql_req(MemoryPool& pool); - -public: - MemoryPool& getPool() - { - return req_pool; - } - - jrd_tra* getTransaction() - { - return req_transaction; - } - - const DsqlCompiledStatement* getStatement() const - { - return statement; - } - - virtual bool mustBeReplicated() const - { - return false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) = 0; - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) = 0; - - virtual void setCursor(thread_db* tdbb, const TEXT* name); - - virtual bool fetch(thread_db* tdbb, UCHAR* buffer); - - virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); - - // Get session-level timeout, milliseconds - unsigned int getTimeout(); - - // Set session-level timeout, milliseconds - void setTimeout(unsigned int timeOut); - - // Get actual timeout, milliseconds - unsigned int getActualTimeout(); - - // Evaluate actual timeout value, consider config- and session-level timeout values, - // setup and start timer - TimeoutTimer* setupTimer(thread_db* tdbb); - - USHORT parseMetadata(Firebird::IMessageMetadata* meta, const Firebird::Array& parameters_list); - void mapInOut(Jrd::thread_db* tdbb, bool toExternal, const dsql_msg* message, Firebird::IMessageMetadata* meta, - UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf = NULL); - - static void destroy(thread_db* tdbb, dsql_req* request, bool drop); - -private: - MemoryPool& req_pool; - -public: - const DsqlCompiledStatement* statement; - MemoryPool* liveScratchPool; - Firebird::Array cursors; // Cursor update statements - - dsql_dbb* req_dbb; // DSQL attachment - jrd_tra* req_transaction; // JRD transaction - jrd_req* req_request; // JRD request - - Firebird::Array req_msg_buffers; - Firebird::string req_cursor_name; // Cursor name, if any - DsqlCursor* req_cursor; // Open cursor, if any - DsqlBatch* req_batch; // Active batch, if any - Firebird::GenericMap > req_user_descs; // SQLDA data type - - Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time - SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time - SINT64 req_fetch_rowcount; // Total number of rows returned by this request - bool req_traced; // request is traced via TraceAPI - -protected: - unsigned int req_timeout; // query timeout in milliseconds, set by the user - Firebird::RefPtr req_timer; // timeout timer - - // Request should never be destroyed using delete. - // It dies together with it's pool in release_request(). - ~dsql_req(); - - // To avoid posix warning about missing public destructor declare - // MemoryPool as friend class. In fact IT releases request memory! - friend class Firebird::MemoryPool; -}; - -class DsqlDmlRequest : public dsql_req -{ -public: - explicit DsqlDmlRequest(MemoryPool& pool, StmtNode* aNode) - : dsql_req(pool), - node(aNode), - needDelayedFormat(false), - firstRowFetched(false) - { - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - virtual void setCursor(thread_db* tdbb, const TEXT* name); - - virtual bool fetch(thread_db* tdbb, UCHAR* buffer); - - virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); - -private: - // True, if request could be restarted - bool needRestarts(); - - void doExecute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - // [Re]start part of "request restarts" algorithm - void executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton, bool exec, bool fetch); - - NestConst node; - Firebird::RefPtr delayedFormat; - bool needDelayedFormat; - bool firstRowFetched; -}; - -class DsqlDdlRequest : public dsql_req -{ -public: - explicit DsqlDdlRequest(MemoryPool& pool, DdlNode* aNode) - : dsql_req(pool), - node(aNode), - internalScratch(NULL) - { - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - virtual bool mustBeReplicated() const; - -private: - // Rethrow an exception with isc_no_meta_update and prefix codes. - void rethrowDdlException(Firebird::status_exception& ex, bool metadataUpdate); - -private: - NestConst node; - DsqlCompilerScratch* internalScratch; -}; - -class DsqlTransactionRequest : public dsql_req -{ -public: - explicit DsqlTransactionRequest(MemoryPool& pool, TransactionNode* aNode) - : dsql_req(pool), - node(aNode) - { - req_traced = false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - -private: - NestConst node; -}; - -class DsqlSessionManagementRequest : public dsql_req -{ -public: - explicit DsqlSessionManagementRequest(MemoryPool& pool, SessionManagementNode* aNode) - : dsql_req(pool), - node(aNode) - { - req_traced = false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - -private: - NestConst node; -}; - //! Implicit (NATURAL and USING) joins class ImplicitJoin : public pool_alloc { @@ -985,7 +672,7 @@ public: s(p) { } - Firebird::string toUtf8(DsqlCompilerScratch*) const; + Firebird::string toUtf8(jrd_tra* transaction) const; const MetaName& getCharSet() const { diff --git a/src/dsql/dsql_proto.h b/src/dsql/dsql_proto.h index dfd44e6d4d..89a6e2b4c3 100644 --- a/src/dsql/dsql_proto.h +++ b/src/dsql/dsql_proto.h @@ -31,21 +31,19 @@ namespace Jrd { class Attachment; class jrd_tra; - class dsql_req; + class DsqlDmlRequest; + class DsqlRequest; } -void DSQL_execute(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::dsql_req*, +void DSQL_execute(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::DsqlRequest*, Firebird::IMessageMetadata*, const UCHAR*, Firebird::IMessageMetadata*, UCHAR*); void DSQL_execute_immediate(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra**, ULONG, const TEXT*, USHORT, Firebird::IMessageMetadata*, const UCHAR*, Firebird::IMessageMetadata*, UCHAR*, bool); -void DSQL_free_statement(Jrd::thread_db*, Jrd::dsql_req*, USHORT); -Jrd::DsqlCursor* DSQL_open(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::dsql_req*, - Firebird::IMessageMetadata*, const UCHAR*, - Firebird::IMessageMetadata*, ULONG); -Jrd::dsql_req* DSQL_prepare(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra*, ULONG, const TEXT*, +void DSQL_free_statement(Jrd::thread_db*, Jrd::DsqlRequest*, USHORT); +Jrd::DsqlRequest* DSQL_prepare(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra*, ULONG, const TEXT*, USHORT, unsigned, Firebird::Array*, Firebird::Array*, bool); -void DSQL_sql_info(Jrd::thread_db*, Jrd::dsql_req*, +void DSQL_sql_info(Jrd::thread_db*, Jrd::DsqlRequest*, ULONG, const UCHAR*, ULONG, UCHAR*); #endif // DSQL_DSQL_PROTO_H diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index d8bbb41689..89aa4c554b 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -220,22 +220,22 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) message->msg_length = offset; - dsqlScratch->ports.add(message); + dsqlScratch->getStatement()->getPorts().add(message); } // Generate complete blr for a dsqlScratch. -void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) +void GEN_statement(DsqlCompilerScratch* scratch, DmlNode* node) { - DsqlCompiledStatement* statement = scratch->getStatement(); + DsqlStatement* statement = scratch->getStatement(); if (statement->getBlrVersion() == 4) scratch->appendUChar(blr_version4); else scratch->appendUChar(blr_version5); - const bool block = statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK || - statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK; + const bool block = statement->getType() == DsqlStatement::TYPE_EXEC_BLOCK || + statement->getType() == DsqlStatement::TYPE_SELECT_BLOCK; // To parse sub-routines messages, they must not have that begin...end pair. // And since it appears to be unnecessary for execute block too, do not generate them. @@ -247,14 +247,14 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_SELECT_BLOCK: node->genBlr(scratch); break; - ///case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + ///case DsqlStatement::TYPE_RETURNING_CURSOR: default: { dsql_msg* message = statement->getSendMsg(); diff --git a/src/dsql/gen_proto.h b/src/dsql/gen_proto.h index 55cf5fb4fe..6301fda05d 100644 --- a/src/dsql/gen_proto.h +++ b/src/dsql/gen_proto.h @@ -35,7 +35,7 @@ void GEN_expr(Jrd::DsqlCompilerScratch*, Jrd::ExprNode*); void GEN_hidden_variables(Jrd::DsqlCompilerScratch* dsqlScratch); void GEN_parameter(Jrd::DsqlCompilerScratch*, const Jrd::dsql_par*); void GEN_port(Jrd::DsqlCompilerScratch*, Jrd::dsql_msg*); -void GEN_request(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*); +void GEN_statement(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*); void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*); void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*); void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*); diff --git a/src/dsql/make_proto.h b/src/dsql/make_proto.h index 5eddf02f5c..d68df999fb 100644 --- a/src/dsql/make_proto.h +++ b/src/dsql/make_proto.h @@ -34,7 +34,7 @@ namespace Jrd { class TypeClause; class dsql_msg; class dsql_par; - class dsql_req; + class DsqlRequest; class DsqlCompilerScratch; class IntlString; class ExprNode; diff --git a/src/dsql/metd_proto.h b/src/dsql/metd_proto.h index b622c18400..fe9faa2c36 100644 --- a/src/dsql/metd_proto.h +++ b/src/dsql/metd_proto.h @@ -34,7 +34,7 @@ namespace Jrd { typedef Firebird::GenericMap MetaNamePairMap; - class dsql_req; + class DsqlRequest; class DsqlCompilerScratch; class jrd_tra; class dsql_intlsym; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 852889bfd5..e9840aa749 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -828,7 +828,7 @@ using namespace Firebird; Jrd::SetTransactionNode::RestrictionOption* setTransactionRestrictionClause; Jrd::DeclareSubProcNode* declareSubProcNode; Jrd::DeclareSubFuncNode* declareSubFuncNode; - Jrd::dsql_req* dsqlReq; + Jrd::DsqlStatement* dsqlStatement; Jrd::CreateAlterUserNode* createAlterUserNode; Jrd::MappingNode* mappingNode; Jrd::MappingNode::OP mappingOp; @@ -848,16 +848,23 @@ using namespace Firebird; // list of possible statements top - : statement { DSQL_parse = $1; } - | statement ';' { DSQL_parse = $1; } + : statement { parsedStatement = $1; } + | statement ';' { parsedStatement = $1; } ; -%type statement +%type statement statement - : dml_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlDmlRequest(getStatementPool(), $1); } - | ddl_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlDdlRequest(getStatementPool(), $1); } - | tra_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlTransactionRequest(getStatementPool(), $1); } - | mng_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlSessionManagementRequest(getStatementPool(), $1); } + : dml_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlDmlStatement(*statementPool, scratch->getAttachment(), $1); } + | ddl_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlDdlStatement(*statementPool, scratch->getAttachment(), $1); } + | tra_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlTransactionStatement(*statementPool, scratch->getAttachment(), $1); } + | mng_statement + { + $$ = FB_NEW_POOL(*statementPool) DsqlSessionManagementStatement( + *statementPool, scratch->getAttachment(), $1); + } ; %type dml_statement @@ -7743,7 +7750,7 @@ sql_string %type utf_string utf_string : sql_string - { $$ = newString($1->toUtf8(scratch)); } + { $$ = newString($1->toUtf8(scratch->getTransaction())); } ; %type signed_short_integer diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index edc19703f5..4bfd0c0308 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -35,7 +35,7 @@ class blb; class jrd_tra; class DsqlCursor; class DsqlBatch; -class dsql_req; +class DsqlRequest; class JrdStatement; class StableAttachmentPart; class Attachment; @@ -305,20 +305,20 @@ public: unsigned parLength, const unsigned char* par) override; public: - JStatement(dsql_req* handle, StableAttachmentPart* sa, Firebird::Array& meta); + JStatement(DsqlRequest* handle, StableAttachmentPart* sa, Firebird::Array& meta); StableAttachmentPart* getAttachment() { return sAtt; } - dsql_req* getHandle() throw() + DsqlRequest* getHandle() throw() { return statement; } private: - dsql_req* statement; + DsqlRequest* statement; Firebird::RefPtr sAtt; Firebird::StatementMetadata metadata; diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/JrdStatement.cpp index 1ce4864d13..6e9ece57ad 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/JrdStatement.cpp @@ -410,6 +410,9 @@ jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) // Create the request. jrd_req* const request = FB_NEW_POOL(*pool) jrd_req(attachment, this, parentStats); + if (level == 0) + pool->setStatsGroup(request->req_memory_stats); + requests[level] = request; return request; diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index e58a309d3d..9f39d76ff0 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -325,7 +325,7 @@ void PreparedStatement::init(thread_db* tdbb, Attachment* attachment, jrd_tra* t request = DSQL_prepare(tdbb, attachment, transaction, text.length(), text.c_str(), dialect, 0, NULL, NULL, isInternalRequest); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); if (statement->getSendMsg()) parseDsqlMessage(statement->getSendMsg(), inValues, inMetadata, inMessage); @@ -348,7 +348,7 @@ void PreparedStatement::setDesc(thread_db* tdbb, unsigned param, const dsc& valu { fb_assert(param > 0); - jrd_req* jrdRequest = getRequest()->req_request; + jrd_req* jrdRequest = getRequest()->getJrdRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( @@ -382,7 +382,7 @@ void PreparedStatement::open(thread_db* tdbb, jrd_tra* transaction) if (builder) builder->moveToStatement(tdbb, this); - DSQL_open(tdbb, &transaction, request, inMetadata, inMessage.begin(), outMetadata, 0); + request->openCursor(tdbb, &transaction, inMetadata, inMessage.begin(), outMetadata, 0); } @@ -400,7 +400,7 @@ ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction unsigned PreparedStatement::executeUpdate(thread_db* tdbb, jrd_tra* transaction) { execute(tdbb, transaction); - return getRequest()->req_request->req_records_updated; + return getRequest()->getJrdRequest()->req_records_updated; } diff --git a/src/jrd/PreparedStatement.h b/src/jrd/PreparedStatement.h index 9c37ae611f..4206b41370 100644 --- a/src/jrd/PreparedStatement.h +++ b/src/jrd/PreparedStatement.h @@ -40,7 +40,7 @@ namespace Jrd { class thread_db; class jrd_tra; class Attachment; -class dsql_req; +class DsqlRequest; class dsql_msg; class ResultSet; @@ -359,7 +359,7 @@ public: int getResultCount() const; - dsql_req* getRequest() + DsqlRequest* getRequest() { return request; } @@ -369,7 +369,7 @@ public: private: const Builder* builder; - dsql_req* request; + DsqlRequest* request; Firebird::Array inValues, outValues; Firebird::RefPtr inMetadata, outMetadata; Firebird::UCharBuffer inMessage, outMessage; diff --git a/src/jrd/ResultSet.cpp b/src/jrd/ResultSet.cpp index 0d22863ce6..bd112fa6bb 100644 --- a/src/jrd/ResultSet.cpp +++ b/src/jrd/ResultSet.cpp @@ -54,14 +54,14 @@ ResultSet::~ResultSet() stmt->resultSet = NULL; - if (stmt->request->getStatement()->getType() != DsqlCompiledStatement::TYPE_EXEC_PROCEDURE) + if (stmt->request->getStatement()->getType() != DsqlStatement::TYPE_EXEC_PROCEDURE) DSQL_free_statement(tdbb, stmt->request, DSQL_close); } bool ResultSet::fetch(thread_db* tdbb) { - if (stmt->request->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_PROCEDURE && + if (stmt->request->getStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE && firstFetchDone) { return false; @@ -103,7 +103,7 @@ Firebird::string ResultSet::getString(thread_db* tdbb, unsigned param) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->req_request; + jrd_req* jrdRequest = stmt->getRequest()->getJrdRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( @@ -131,7 +131,7 @@ void ResultSet::moveDesc(thread_db* tdbb, unsigned param, dsc& desc) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->req_request; + jrd_req* jrdRequest = stmt->getRequest()->getJrdRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index c891e8a54f..ba6c8c4ba3 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -143,38 +143,26 @@ BoolExprNode* CMP_clone_node_opt(thread_db* tdbb, CompilerScratch* csb, BoolExpr return clone; } - -jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool internal_flag, - ULONG dbginfo_length, const UCHAR* dbginfo) +// Compile a statement. +JrdStatement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, + ULONG dbginfoLength, const UCHAR* dbginfo) { -/************************************** - * - * C M P _ c o m p i l e 2 - * - ************************************** - * - * Functional description - * Compile a BLR request. - * - **************************************/ - jrd_req* request = NULL; + JrdStatement* statement = nullptr; SET_TDBB(tdbb); - Jrd::Attachment* const att = tdbb->getAttachment(); + const auto att = tdbb->getAttachment(); // 26.09.2002 Nickolay Samofatov: default memory pool will become statement pool // and will be freed by CMP_release - MemoryPool* const new_pool = att->createPool(); + const auto newPool = att->createPool(); try { - Jrd::ContextPoolHolder context(tdbb, new_pool); + Jrd::ContextPoolHolder context(tdbb, newPool); - CompilerScratch* csb = - PAR_parse(tdbb, blr, blr_length, internal_flag, dbginfo_length, dbginfo); + const auto csb = PAR_parse(tdbb, blr, blrLength, internalFlag, dbginfoLength, dbginfo); - request = JrdStatement::makeRequest(tdbb, csb, internal_flag); - new_pool->setStatsGroup(request->req_memory_stats); + statement = JrdStatement::makeStatement(tdbb, csb, internalFlag); #ifdef CMP_DEBUG if (csb->csb_dump.hasData()) @@ -196,20 +184,40 @@ jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool } #endif - request->getStatement()->verifyAccess(tdbb); + statement->verifyAccess(tdbb); delete csb; } catch (const Firebird::Exception& ex) { ex.stuffException(tdbb->tdbb_status_vector); - if (request) - CMP_release(tdbb, request); + if (statement) + statement->release(tdbb); else - att->deletePool(new_pool); + att->deletePool(newPool); ERR_punt(); } + return statement; +} + +jrd_req* CMP_compile_request(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag) +{ +/************************************** + * + * C M P _ c o m p i l e _ r e q u e s t + * + ************************************** + * + * Functional description + * Compile a BLR request. + * + **************************************/ + SET_TDBB(tdbb); + + auto statement = CMP_compile(tdbb, blr, blrLength, internalFlag, 0, nullptr); + auto request = statement->getRequest(tdbb, 0); + return request; } diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index c95f41c881..a5f3a9b630 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -37,8 +37,9 @@ StreamType* CMP_alloc_map(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType str Jrd::ValueExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); Jrd::BoolExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::BoolExprNode*); Jrd::ValueExprNode* CMP_clone_node(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); -Jrd::jrd_req* CMP_compile2(Jrd::thread_db*, const UCHAR* blr, ULONG blr_length, bool internal_flag, - ULONG = 0, const UCHAR* = NULL); +Jrd::JrdStatement* CMP_compile(Jrd::thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, + ULONG dbginfoLength, const UCHAR* dbginfo); +Jrd::jrd_req* CMP_compile_request(Jrd::thread_db*, const UCHAR* blr, ULONG blrLength, bool internalFlag); Jrd::CompilerScratch::csb_repeat* CMP_csb_element(Jrd::CompilerScratch*, StreamType element); const Jrd::Format* CMP_format(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType); Jrd::IndexLock* CMP_get_index_lock(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); diff --git a/src/jrd/exe_proto.h b/src/jrd/exe_proto.h index ea3dedcc2e..ee60d69e6f 100644 --- a/src/jrd/exe_proto.h +++ b/src/jrd/exe_proto.h @@ -93,7 +93,7 @@ namespace Jrd if (request) return; - request = CMP_compile2(tdbb, blr, blrLength, true); + request = CMP_compile_request(tdbb, blr, blrLength, true); cacheRequest(); } @@ -166,7 +166,7 @@ namespace Jrd if (request) return; - request = CMP_compile2(tdbb, blr, blrLength, true); + request = CMP_compile_request(tdbb, blr, blrLength, true); } jrd_req* operator ->() diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 0ab41a1969..590e2fa931 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -392,7 +392,7 @@ void InternalTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bo else m_transaction->rollback(status); - if (status->getState() & IStatus::STATE_ERRORS) + if (status->getState() & IStatus::STATE_ERRORS) err = status->getErrors()[1]; if (err == isc_cancelled) @@ -504,7 +504,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (status->getState() & IStatus::STATE_ERRORS) raise(&status, tdbb, "JAttachment::prepare", &sql); - const DsqlCompiledStatement* statement = m_request->getHandle()->getStatement(); + const DsqlStatement* statement = m_request->getHandle()->getStatement(); if (statement->getSendMsg()) { @@ -542,33 +542,33 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_BLOCK: m_stmt_selectable = true; break; - case DsqlCompiledStatement::TYPE_START_TRANS: - case DsqlCompiledStatement::TYPE_COMMIT: - case DsqlCompiledStatement::TYPE_ROLLBACK: - case DsqlCompiledStatement::TYPE_COMMIT_RETAIN: - case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN: - case DsqlCompiledStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_START_TRANS: + case DsqlStatement::TYPE_COMMIT: + case DsqlStatement::TYPE_ROLLBACK: + case DsqlStatement::TYPE_COMMIT_RETAIN: + case DsqlStatement::TYPE_ROLLBACK_RETAIN: + case DsqlStatement::TYPE_CREATE_DB: Arg::Gds(isc_eds_expl_tran_ctrl).copyTo(&status); raise(&status, tdbb, "JAttachment::prepare", &sql); break; - case DsqlCompiledStatement::TYPE_INSERT: - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: - case DsqlCompiledStatement::TYPE_DDL: - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: - case DsqlCompiledStatement::TYPE_SET_GENERATOR: - case DsqlCompiledStatement::TYPE_SAVEPOINT: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_INSERT: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_UPDATE_CURSOR: + case DsqlStatement::TYPE_DELETE_CURSOR: + case DsqlStatement::TYPE_DDL: + case DsqlStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_SET_GENERATOR: + case DsqlStatement::TYPE_SAVEPOINT: + case DsqlStatement::TYPE_EXEC_BLOCK: break; } } diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index a65090931b..883b2b14d2 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -345,7 +345,7 @@ JEvents::JEvents(int aId, StableAttachmentPart* sa, Firebird::IEventCallback* aC { } -JStatement::JStatement(dsql_req* handle, StableAttachmentPart* sa, Firebird::Array& meta) +JStatement::JStatement(DsqlRequest* handle, StableAttachmentPart* sa, Firebird::Array& meta) : statement(handle), sAtt(sa), metadata(getPool(), this, sAtt) { metadata.parse(meta.getCount(), meta.begin()); @@ -657,7 +657,7 @@ namespace validateHandle(tdbb, statement->requests[0]->req_attachment); } - inline void validateHandle(thread_db* tdbb, dsql_req* const statement) + inline void validateHandle(thread_db* tdbb, DsqlRequest* const statement) { if (!statement) status_exception::raise(Arg::Gds(isc_bad_req_handle)); @@ -2693,11 +2693,14 @@ JRequest* JAttachment::compileRequest(CheckStatusWrapper* user_status, TraceBlrCompile trace(tdbb, blr_length, blr); try { - jrd_req* request = NULL; - JRD_compile(tdbb, getHandle(), &request, blr_length, blr, RefStrPtr(), 0, NULL, false); - stmt = request->getStatement(); + stmt = CMP_compile(tdbb, blr, blr_length, false, 0, nullptr); - trace.finish(request, ITracePlugin::RESULT_SUCCESS); + const auto attachment = tdbb->getAttachment(); + const auto rootRequest = stmt->getRequest(tdbb, 0); + rootRequest->setAttachment(attachment); + attachment->att_requests.add(rootRequest); + + trace.finish(stmt, ITracePlugin::RESULT_SUCCESS); } catch (const Exception& ex) { @@ -4717,7 +4720,6 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* for (FB_SIZE_T i = 0; i < csb->csb_rpt.getCount(); i++) { - const MessageNode* node = csb->csb_rpt[i].csb_message; if (node) { @@ -5066,7 +5068,7 @@ JResultSet* JStatement::openCursor(CheckStatusWrapper* user_status, ITransaction } } - DsqlCursor* const cursor = DSQL_open(tdbb, &tra, getHandle(), + const auto cursor = getHandle()->openCursor(tdbb, &tra, inMetadata, static_cast(inBuffer), outMetadata, flags); rs = FB_NEW JResultSet(cursor, this); @@ -5542,7 +5544,7 @@ JStatement* JAttachment::prepare(CheckStatusWrapper* user_status, ITransaction* validateHandle(tdbb, tra); check_database(tdbb); - dsql_req* statement = NULL; + DsqlRequest* statement = NULL; try { @@ -5814,7 +5816,7 @@ void JResultSet::setDelayedOutputFormat(CheckStatusWrapper* user_status, Firebir try { - dsql_req* req = statement->getHandle(); + DsqlRequest* req = statement->getHandle(); fb_assert(req); req->setDelayedFormat(tdbb, outMetadata); } @@ -5874,7 +5876,7 @@ unsigned int JStatement::getTimeout(CheckStatusWrapper* user_status) try { - Jrd::dsql_req* req = getHandle(); + Jrd::DsqlRequest* req = getHandle(); return req->getTimeout(); } catch (const Exception& ex) @@ -5904,7 +5906,7 @@ void JStatement::setTimeout(CheckStatusWrapper* user_status, unsigned int timeOu try { - Jrd::dsql_req* req = getHandle(); + Jrd::DsqlRequest* req = getHandle(); req->setTimeout(timeOut); } catch (const Exception& ex) @@ -5946,11 +5948,11 @@ JBatch* JStatement::createBatch(Firebird::CheckStatusWrapper* status, Firebird:: } } - DsqlBatch* const b = DsqlBatch::open(tdbb, getHandle(), inMetadata, parLength, par); + const auto dsqlBatch = getHandle()->openBatch(tdbb, inMetadata, parLength, par); - batch = FB_NEW JBatch(b, this, inMetadata); + batch = FB_NEW JBatch(dsqlBatch, this, inMetadata); batch->addRef(); - b->setInterfacePtr(batch); + dsqlBatch->setInterfacePtr(batch); tdbb->getAttachment()->registerBatch(batch); } catch (const Exception& ex) @@ -9367,47 +9369,6 @@ void JRD_unwind_request(thread_db* tdbb, jrd_req* request) } -void JRD_compile(thread_db* tdbb, - Jrd::Attachment* attachment, - jrd_req** req_handle, - ULONG blr_length, - const UCHAR* blr, - RefStrPtr ref_str, - ULONG dbginfo_length, - const UCHAR* dbginfo, - bool isInternalRequest) -{ -/************************************** - * - * J R D _ c o m p i l e - * - ************************************** - * - * Functional description - * Compile a request passing the SQL text and debug information. - * - **************************************/ - if (*req_handle) - status_exception::raise(Arg::Gds(isc_bad_req_handle)); - - jrd_req* request = CMP_compile2(tdbb, blr, blr_length, isInternalRequest, dbginfo_length, dbginfo); - request->req_attachment = attachment; - attachment->att_requests.add(request); - - JrdStatement* statement = request->getStatement(); - - if (ref_str) - statement->sqlText = ref_str; - - fb_assert(statement->blr.isEmpty()); - - if (attachment->getDebugOptions().getDsqlKeepBlr()) - statement->blr.insert(0, blr, blr_length); - - *req_handle = request; -} - - namespace { class DatabaseDirList : public DirectoryList diff --git a/src/jrd/jrd_proto.h b/src/jrd/jrd_proto.h index b1569e0fba..7f39152a17 100644 --- a/src/jrd/jrd_proto.h +++ b/src/jrd/jrd_proto.h @@ -40,7 +40,7 @@ namespace Jrd { class Service; class thread_db; struct teb; - class dsql_req; + class DsqlRequest; class MetaName; } @@ -71,9 +71,6 @@ void JRD_start_and_send(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_tr void JRD_start_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra** transaction, Jrd::Attachment* attachment, unsigned int tpb_length, const UCHAR* tpb); void JRD_unwind_request(Jrd::thread_db* tdbb, Jrd::jrd_req* request); -void JRD_compile(Jrd::thread_db* tdbb, Jrd::Attachment* attachment, Jrd::jrd_req** req_handle, - ULONG blr_length, const UCHAR* blr, Firebird::RefStrPtr, - ULONG dbginfo_length, const UCHAR* dbginfo, bool isInternalRequest); bool JRD_verify_database_access(const Firebird::PathName&); void JRD_shutdown_attachment(Jrd::Attachment* attachment); void JRD_shutdown_attachments(Jrd::Database* dbb); diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index 546a12464e..09e5b5ecaa 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -455,12 +455,10 @@ string OPT_get_plan(thread_db* tdbb, const JrdStatement* statement, bool detaile if (statement) { - const Array& fors = statement->fors; - - for (FB_SIZE_T i = 0; i < fors.getCount(); i++) + for (const auto& recordSource : statement->fors) { plan += detailed ? "\nSelect Expression" : "\nPLAN "; - fors[i]->print(tdbb, plan, detailed, 0); + recordSource->print(tdbb, plan, detailed, 0); } } diff --git a/src/jrd/opt_proto.h b/src/jrd/opt_proto.h index 233d8ee3ee..3ff42d7dac 100644 --- a/src/jrd/opt_proto.h +++ b/src/jrd/opt_proto.h @@ -30,7 +30,7 @@ #include "../jrd/lls.h" namespace Jrd { - class jrd_req; + class JrdStatement; class jrd_rel; class RecordSource; struct index_desc; diff --git a/src/jrd/req.h b/src/jrd/req.h index aa0736a606..08e5375543 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -382,8 +382,8 @@ public: ULONG req_flags; // misc request flags Savepoint* req_savepoints; // Looper savepoint list Savepoint* req_proc_sav_point; // procedure savepoint list - unsigned int req_timeout; // query timeout in milliseconds, set by the dsql_req::setupTimer - Firebird::RefPtr req_timer; // timeout timer, shared with dsql_req + unsigned int req_timeout; // query timeout in milliseconds, set by the DsqlRequest::setupTimer + Firebird::RefPtr req_timer; // timeout timer, shared with DsqlRequest Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time diff --git a/src/jrd/trace/TraceDSQLHelpers.h b/src/jrd/trace/TraceDSQLHelpers.h index cb4a176975..7d7570dac2 100644 --- a/src/jrd/trace/TraceDSQLHelpers.h +++ b/src/jrd/trace/TraceDSQLHelpers.h @@ -28,6 +28,7 @@ #ifndef JRD_TRACE_DSQL_HELPERS_H #define JRD_TRACE_DSQL_HELPERS_H +#include "../../jrd/trace/TraceManager.h" #include "../../jrd/trace/TraceObjects.h" namespace Jrd { @@ -65,7 +66,7 @@ public: prepare(ITracePlugin::RESULT_FAILED); } - void setStatement(dsql_req* request) + void setStatement(DsqlRequest* request) { m_request = request; } @@ -100,7 +101,7 @@ private: bool m_need_trace; Attachment* m_attachment; jrd_tra* const m_transaction; - dsql_req* m_request; + DsqlRequest* m_request; SINT64 m_start_clock; FB_SIZE_T m_string_len; const TEXT* m_string; @@ -110,7 +111,7 @@ private: class TraceDSQLExecute { public: - TraceDSQLExecute(Attachment* attachment, dsql_req* request) : + TraceDSQLExecute(Attachment* attachment, DsqlRequest* request) : m_attachment(attachment), m_request(request) { @@ -131,11 +132,10 @@ public: fb_assert(!m_request->req_fetch_baseline); m_request->req_fetch_baseline = NULL; - jrd_req* jrd_request = m_request->req_request; - if (jrd_request) + if (auto jrdRequest = m_request->getJrdRequest()) { MemoryPool* pool = MemoryPool::getContextPool(); - m_request->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, jrd_request->req_stats); + m_request->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, jrdRequest->req_stats); } } @@ -152,7 +152,7 @@ public: } TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - m_request->req_request ? &m_request->req_request->req_stats : NULL, + m_request->getJrdRequest() ? &m_request->getJrdRequest()->req_stats : NULL, fb_utils::query_performance_counter() - m_start_clock, m_request->req_fetch_rowcount); @@ -170,19 +170,19 @@ public: private: bool m_need_trace; Attachment* const m_attachment; - dsql_req* const m_request; + DsqlRequest* const m_request; SINT64 m_start_clock; }; class TraceDSQLFetch { public: - TraceDSQLFetch(Attachment* attachment, dsql_req* request) : + TraceDSQLFetch(Attachment* attachment, DsqlRequest* request) : m_attachment(attachment), m_request(request) { m_need_trace = m_request->req_traced && TraceManager::need_dsql_execute(m_attachment) && - m_request->req_request && (m_request->req_request->req_flags & req_active); + m_request->getJrdRequest() && (m_request->getJrdRequest()->req_flags & req_active); if (!m_need_trace) { @@ -212,7 +212,7 @@ public: } TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - &m_request->req_request->req_stats, m_request->req_fetch_elapsed, + &m_request->getJrdRequest()->req_stats, m_request->req_fetch_elapsed, m_request->req_fetch_rowcount); TraceSQLStatementImpl stmt(m_request, stats.getPerf()); @@ -227,7 +227,7 @@ public: private: bool m_need_trace; Attachment* const m_attachment; - dsql_req* const m_request; + DsqlRequest* const m_request; SINT64 m_start_clock; }; diff --git a/src/jrd/trace/TraceJrdHelpers.h b/src/jrd/trace/TraceJrdHelpers.h index eef6e7b16c..5643a48e1d 100644 --- a/src/jrd/trace/TraceJrdHelpers.h +++ b/src/jrd/trace/TraceJrdHelpers.h @@ -392,7 +392,7 @@ public: m_start_clock = fb_utils::query_performance_counter(); } - void finish(jrd_req* request, ntrace_result_t result) + void finish(JrdStatement* statement, ntrace_result_t result) { if (!m_need_trace) return; @@ -406,9 +406,9 @@ public: TraceConnectionImpl conn(m_tdbb->getAttachment()); TraceTransactionImpl tran(m_tdbb->getTransaction()); - if (request) + if (statement) { - TraceBLRStatementImpl stmt(request, NULL); + TraceBLRStatementImpl stmt(statement, NULL); trace_mgr->event_blr_compile(&conn, m_tdbb->getTransaction() ? &tran : NULL, &stmt, m_start_clock, result); } @@ -474,7 +474,7 @@ public: TraceConnectionImpl conn(m_tdbb->getAttachment()); TraceTransactionImpl tran(m_tdbb->getTransaction()); - TraceBLRStatementImpl stmt(m_request, stats.getPerf()); + TraceBLRStatementImpl stmt(m_request->getStatement(), stats.getPerf()); TraceManager* trace_mgr = m_tdbb->getAttachment()->att_trace_manager; trace_mgr->event_blr_execute(&conn, &tran, &stmt, result); diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 9cc507aa1d..56c5099477 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -169,8 +169,8 @@ ISC_INT64 TraceTransactionImpl::getInitialID() ISC_INT64 TraceSQLStatementImpl::getStmtID() { - if (m_stmt->req_request) - return m_stmt->req_request->getRequestId(); + if (m_stmt->getJrdRequest()) + return m_stmt->getJrdRequest()->getRequestId(); return 0; } @@ -211,8 +211,8 @@ void TraceSQLStatementImpl::fillPlan(bool explained) if (m_plan.isEmpty() || m_planExplained != explained) { m_planExplained = explained; - if (m_stmt->req_request) - m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->req_request->getStatement(), m_planExplained); + if (m_stmt->getJrdStatement()) + m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->getJrdStatement(), m_planExplained); } } @@ -234,6 +234,14 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() if (m_descs.getCount() || !m_params) return; + if (!m_stmt->isDml()) + { + fb_assert(false); + return; + } + + const auto dmlRequest = (DsqlDmlRequest*) m_stmt; + USHORT first_index = 0; for (FB_SIZE_T i = 0 ; i < m_params->getCount(); ++i) { @@ -246,7 +254,7 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() if (parameter->par_null) { const UCHAR* msgBuffer = - m_stmt->req_msg_buffers[parameter->par_null->par_message->msg_buffer_number]; + dmlRequest->req_msg_buffers[parameter->par_null->par_message->msg_buffer_number]; if (*(SSHORT*) (msgBuffer + (IPTR) parameter->par_null->par_desc.dsc_address)) null_flag = DSC_null; @@ -263,7 +271,7 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() *desc = parameter->par_desc; desc->dsc_flags |= null_flag; - UCHAR* msgBuffer = m_stmt->req_msg_buffers[parameter->par_message->msg_buffer_number]; + UCHAR* msgBuffer = dmlRequest->req_msg_buffers[parameter->par_message->msg_buffer_number]; desc->dsc_address = msgBuffer + (IPTR) desc->dsc_address; } } diff --git a/src/jrd/trace/TraceObjects.h b/src/jrd/trace/TraceObjects.h index 8b13bca77d..2536d74613 100644 --- a/src/jrd/trace/TraceObjects.h +++ b/src/jrd/trace/TraceObjects.h @@ -146,17 +146,17 @@ private: class TraceBLRStatementImpl : public BLRPrinter { public: - TraceBLRStatementImpl(const jrd_req* stmt, Firebird::PerformanceInfo* perf) : - BLRPrinter(stmt->getStatement()->blr.begin(), stmt->getStatement()->blr.getCount()), + TraceBLRStatementImpl(const JrdStatement* stmt, Firebird::PerformanceInfo* perf) : + BLRPrinter(stmt->blr.begin(), stmt->blr.getCount()), m_stmt(stmt), m_perf(perf) {} - ISC_INT64 getStmtID() { return m_stmt->getRequestId(); } + ISC_INT64 getStmtID() { return m_stmt->getStatementId(); } Firebird::PerformanceInfo* getPerf() { return m_perf; } private: - const jrd_req* const m_stmt; + const JrdStatement* const m_stmt; Firebird::PerformanceInfo* const m_perf; }; @@ -177,7 +177,7 @@ class TraceSQLStatementImpl : public Firebird::AutoIface > { public: - TraceSQLStatementImpl(const dsql_req* stmt, Firebird::PerformanceInfo* perf) : + TraceSQLStatementImpl(DsqlRequest* stmt, Firebird::PerformanceInfo* perf) : m_stmt(stmt), m_perf(perf), m_planExplained(false), @@ -198,7 +198,7 @@ private: public Firebird::AutoIface > { public: - DSQLParamsImpl(Firebird::MemoryPool& pool, const dsql_req* const stmt) : + DSQLParamsImpl(Firebird::MemoryPool& pool, DsqlRequest* const stmt) : m_stmt(stmt), m_params(NULL), m_descs(pool) @@ -215,7 +215,7 @@ private: private: void fillParams(); - const dsql_req* const m_stmt; + DsqlRequest* const m_stmt; const Firebird::Array* m_params; Firebird::HalfStaticArray m_descs; Firebird::string temp_utf8_text; @@ -223,7 +223,7 @@ private: void fillPlan(bool explained); - const dsql_req* const m_stmt; + DsqlRequest* const m_stmt; Firebird::PerformanceInfo* const m_perf; Firebird::string m_plan; bool m_planExplained;