From: <asf...@us...> - 2010-11-29 02:17:13
|
Revision: 51995 http://firebird.svn.sourceforge.net/firebird/?rev=51995&view=rev Author: asfernandes Date: 2010-11-29 02:17:04 +0000 (Mon, 29 Nov 2010) Log Message: ----------- Refactored the support for blr_handler, blr_loop, blr_exec_sql, blr_exec_into, blr_exec_stmt, blr_start_savepoint, blr_end_savepoint, blr_store, blr_store2, blr_erase, blr_modify, blr_modify2, blr_exec_proc, blr_exec_proc2, blr_exec_pid, blr_dcl_cursor, blr_cursor_stmt, blr_set_generator, blr_receive, blr_stall, blr_select, blr_block, blr_error_handler, blr_label, blr_leave, blr_continue and the source info node. Modified Paths: -------------- firebird/trunk/builds/win32/msvc10/engine.vcxproj firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters firebird/trunk/builds/win32/msvc7/engine.vcproj firebird/trunk/builds/win32/msvc7/engine_classic.vcproj firebird/trunk/builds/win32/msvc7/engine_embed.vcproj firebird/trunk/builds/win32/msvc8/engine.vcproj firebird/trunk/builds/win32/msvc9/engine.vcproj firebird/trunk/src/dsql/DdlNodes.epp firebird/trunk/src/dsql/Nodes.h firebird/trunk/src/dsql/StmtNodes.cpp firebird/trunk/src/dsql/StmtNodes.h firebird/trunk/src/dsql/Visitors.h firebird/trunk/src/dsql/parse.y firebird/trunk/src/dsql/pass1.cpp firebird/trunk/src/jrd/JrdStatement.cpp firebird/trunk/src/jrd/JrdStatement.h firebird/trunk/src/jrd/cmp.cpp firebird/trunk/src/jrd/cmp_proto.h firebird/trunk/src/jrd/exe.cpp firebird/trunk/src/jrd/exe.h firebird/trunk/src/jrd/exe_proto.h firebird/trunk/src/jrd/extds/ExtDS.cpp firebird/trunk/src/jrd/extds/ExtDS.h firebird/trunk/src/jrd/extds/IscDS.cpp firebird/trunk/src/jrd/extds/IscDS.h firebird/trunk/src/jrd/inf.cpp firebird/trunk/src/jrd/nod.h firebird/trunk/src/jrd/par.cpp firebird/trunk/src/jrd/par_proto.h firebird/trunk/src/misc/blrtable.cpp Removed Paths: ------------- firebird/trunk/src/jrd/execute_statement.cpp firebird/trunk/src/jrd/execute_statement.h Modified: firebird/trunk/builds/win32/msvc10/engine.vcxproj =================================================================== --- firebird/trunk/builds/win32/msvc10/engine.vcxproj 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/builds/win32/msvc10/engine.vcxproj 2010-11-29 02:17:04 UTC (rev 51995) @@ -76,7 +76,6 @@ <ClCompile Include="..\..\..\src\jrd\event.cpp" /> <ClCompile Include="..\..\..\src\jrd\evl.cpp" /> <ClCompile Include="..\..\..\src\jrd\exe.cpp" /> - <ClCompile Include="..\..\..\src\jrd\execute_statement.cpp" /> <ClCompile Include="..\..\..\src\jrd\ext.cpp" /> <ClCompile Include="..\..\..\src\jrd\extds\ExtDS.cpp" /> <ClCompile Include="..\..\..\src\jrd\extds\InternalDS.cpp" /> @@ -237,7 +236,6 @@ <ClInclude Include="..\..\..\src\jrd\evl_proto.h" /> <ClInclude Include="..\..\..\src\jrd\evl_string.h" /> <ClInclude Include="..\..\..\src\jrd\exe.h" /> - <ClInclude Include="..\..\..\src\jrd\execute_statement.h" /> <ClInclude Include="..\..\..\src\jrd\exe_proto.h" /> <ClInclude Include="..\..\..\src\jrd\ext.h" /> <ClInclude Include="..\..\..\src\jrd\extds\ExtDS.h" /> @@ -562,4 +560,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters =================================================================== --- firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters 2010-11-29 02:17:04 UTC (rev 51995) @@ -258,9 +258,6 @@ <ClCompile Include="..\..\..\src\jrd\exe.cpp"> <Filter>JRD files</Filter> </ClCompile> - <ClCompile Include="..\..\..\src\jrd\execute_statement.cpp"> - <Filter>JRD files</Filter> - </ClCompile> <ClCompile Include="..\..\..\src\jrd\ext.cpp"> <Filter>JRD files</Filter> </ClCompile> @@ -740,9 +737,6 @@ <ClInclude Include="..\..\..\src\jrd\exe_proto.h"> <Filter>Header files</Filter> </ClInclude> - <ClInclude Include="..\..\..\src\jrd\execute_statement.h"> - <Filter>Header files</Filter> - </ClInclude> <ClInclude Include="..\..\..\src\jrd\ext.h"> <Filter>Header files</Filter> </ClInclude> @@ -1096,4 +1090,4 @@ <Filter>Resource files</Filter> </ResourceCompile> </ItemGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc7/engine.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc7/engine.vcproj 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/builds/win32/msvc7/engine.vcproj 2010-11-29 02:17:04 UTC (rev 51995) @@ -107,8 +107,6 @@ </File> <File RelativePath="..\..\..\src\jrd\exe.cpp"> </File> - <File RelativePath="..\..\..\src\jrd\execute_statement.cpp"> - </File> <File RelativePath="..\..\..\src\jrd\ext.cpp"> </File> <File RelativePath="..\..\..\src\jrd\extds\ExtDS.cpp"> @@ -371,8 +369,6 @@ </File> <File RelativePath="..\..\..\src\jrd\exe_proto.h"> </File> - <File RelativePath="..\..\..\src\jrd\execute_statement.h"> - </File> <File RelativePath="..\..\..\src\jrd\ext.h"> </File> <File RelativePath="..\..\..\src\jrd\ext_proto.h"> @@ -649,4 +645,4 @@ </Files> <Globals> </Globals> -</VisualStudioProject> \ No newline at end of file +</VisualStudioProject> Modified: firebird/trunk/builds/win32/msvc7/engine_classic.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc7/engine_classic.vcproj 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/builds/win32/msvc7/engine_classic.vcproj 2010-11-29 02:17:04 UTC (rev 51995) @@ -107,8 +107,6 @@ </File> <File RelativePath="..\..\..\src\jrd\exe.cpp"> </File> - <File RelativePath="..\..\..\src\jrd\execute_statement.cpp"> - </File> <File RelativePath="..\..\..\src\jrd\ext.cpp"> </File> <File RelativePath="..\..\..\src\jrd\extds\ExtDS.cpp"> @@ -371,8 +369,6 @@ </File> <File RelativePath="..\..\..\src\jrd\exe_proto.h"> </File> - <File RelativePath="..\..\..\src\jrd\execute_statement.h"> - </File> <File RelativePath="..\..\..\src\jrd\ext.h"> </File> <File RelativePath="..\..\..\src\jrd\ext_proto.h"> @@ -647,4 +643,4 @@ </Files> <Globals> </Globals> -</VisualStudioProject> \ No newline at end of file +</VisualStudioProject> Modified: firebird/trunk/builds/win32/msvc7/engine_embed.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc7/engine_embed.vcproj 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/builds/win32/msvc7/engine_embed.vcproj 2010-11-29 02:17:04 UTC (rev 51995) @@ -107,8 +107,6 @@ </File> <File RelativePath="..\..\..\src\jrd\exe.cpp"> </File> - <File RelativePath="..\..\..\src\jrd\execute_statement.cpp"> - </File> <File RelativePath="..\..\..\src\jrd\ext.cpp"> </File> <File RelativePath="..\..\..\src\jrd\extds\ExtDS.cpp"> @@ -371,8 +369,6 @@ </File> <File RelativePath="..\..\..\src\jrd\exe_proto.h"> </File> - <File RelativePath="..\..\..\src\jrd\execute_statement.h"> - </File> <File RelativePath="..\..\..\src\jrd\ext.h"> </File> <File RelativePath="..\..\..\src\jrd\ext_proto.h"> @@ -647,4 +643,4 @@ </Files> <Globals> </Globals> -</VisualStudioProject> \ No newline at end of file +</VisualStudioProject> Modified: firebird/trunk/builds/win32/msvc8/engine.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc8/engine.vcproj 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/builds/win32/msvc8/engine.vcproj 2010-11-29 02:17:04 UTC (rev 51995) @@ -400,10 +400,6 @@ > </File> <File - RelativePath="..\..\..\src\jrd\execute_statement.cpp" - > - </File> - <File RelativePath="..\..\..\src\jrd\ext.cpp" > </File> @@ -1108,10 +1104,6 @@ > </File> <File - RelativePath="..\..\..\src\jrd\execute_statement.h" - > - </File> - <File RelativePath="..\..\..\src\dsql\ExprNodes.h" > </File> Modified: firebird/trunk/builds/win32/msvc9/engine.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc9/engine.vcproj 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/builds/win32/msvc9/engine.vcproj 2010-11-29 02:17:04 UTC (rev 51995) @@ -400,10 +400,6 @@ > </File> <File - RelativePath="..\..\..\src\jrd\execute_statement.cpp" - > - </File> - <File RelativePath="..\..\..\src\jrd\ext.cpp" > </File> @@ -1108,10 +1104,6 @@ > </File> <File - RelativePath="..\..\..\src\jrd\execute_statement.h" - > - </File> - <File RelativePath="..\..\..\src\dsql\ExprNodes.h" > </File> Modified: firebird/trunk/src/dsql/DdlNodes.epp =================================================================== --- firebird/trunk/src/dsql/DdlNodes.epp 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/src/dsql/DdlNodes.epp 2010-11-29 02:17:04 UTC (rev 51995) @@ -8058,7 +8058,7 @@ bool found = false; - for (; search < end; ++search, replaceFields ? ++replace : NULL) + for (; search < end; ++search, replace += (replaceFields ? 1 : 0)) { const dsql_str* replaceName = NULL; if (replaceFields) Modified: firebird/trunk/src/dsql/Nodes.h =================================================================== --- firebird/trunk/src/dsql/Nodes.h 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/src/dsql/Nodes.h 2010-11-29 02:17:04 UTC (rev 51995) @@ -24,6 +24,7 @@ #define DSQL_NODES_H #include "../common/common.h" +#include "../jrd/jrd.h" #include "../dsql/DsqlCompilerScratch.h" #include "../dsql/node.h" #include "../dsql/Visitors.h" @@ -799,13 +800,129 @@ class StmtNode : public DmlNode { public: - explicit StmtNode(MemoryPool& pool) + enum Type + { + TYPE_BLOCK, + TYPE_CONTINUE_LEAVE, + TYPE_CURSOR_STMT, + TYPE_DECLARE_CURSOR, + TYPE_ERASE, + TYPE_ERROR_HANDLER, + TYPE_EXCEPTION, + TYPE_EXEC_BLOCK, + TYPE_EXEC_PROCEDURE, + TYPE_EXEC_STATEMENT, + TYPE_EXIT, + TYPE_IF, + TYPE_IN_AUTO_TRANS, + TYPE_FOR, + TYPE_HANDLER, + TYPE_LABEL, + TYPE_LOOP, + TYPE_MERGE, + TYPE_MODIFY, + TYPE_POST_EVENT, + TYPE_RECEIVE, + TYPE_RETURN, + TYPE_SAVEPOINT, + TYPE_SAVEPOINT_ENCLOSE, + TYPE_SELECT, + TYPE_SET_GENERATOR, + TYPE_SOURCE_INFO, + TYPE_STALL, + TYPE_STORE, + TYPE_SUSPEND, + TYPE_USER_SAVEPOINT + }; + + enum WhichTrigger + { + ALL_TRIGS = 0, + PRE_TRIG = 1, + POST_TRIG = 2 + }; + + struct ExeState + { + ExeState(thread_db* tdbb) + : oldPool(tdbb->getDefaultPool()), + oldRequest(tdbb->getRequest()), + errorPending(false), + catchDisabled(false), + whichEraseTrig(ALL_TRIGS), + whichStoTrig(ALL_TRIGS), + whichModTrig(ALL_TRIGS), + topNode(NULL), + prevNode(NULL) + { + } + + MemoryPool* oldPool; // Save the old pool to restore on exit. + jrd_req* oldRequest; // Save the old request to restore on exit. + bool errorPending; // Is there an error pending to be handled? + bool catchDisabled; // Catch errors so we can unwind cleanly. + WhichTrigger whichEraseTrig; + WhichTrigger whichStoTrig; + WhichTrigger whichModTrig; + const jrd_nod* topNode; + const jrd_nod* prevNode; + }; + +public: + explicit StmtNode(Type aType, MemoryPool& pool) : DmlNode(pool), + type(aType), node(NULL) { } -public: + template <typename T> T* as() + { + return type == T::TYPE ? static_cast<T*>(this) : NULL; + } + + template <typename T> const T* as() const + { + return type == T::TYPE ? static_cast<const T*>(this) : NULL; + } + + template <typename T> bool is() const + { + return type == T::TYPE; + } + + template <typename T, typename LegacyType> static T* as(LegacyType* node) + { + StmtNode* obj = T::fromLegacy(node); + return obj ? obj->as<T>() : NULL; + } + + template <typename T, typename LegacyType> static const T* as(const LegacyType* node) + { + const StmtNode* obj = T::fromLegacy(node); + return obj ? obj->as<T>() : NULL; + } + + template <typename T, typename LegacyType> static bool is(const LegacyType* node) + { + const StmtNode* obj = T::fromLegacy(node); + return obj ? obj->is<T>() : false; + } + + static const StmtNode* fromLegacy(const StmtNode* node) + { + return node; + } + + static StmtNode* fromLegacy(StmtNode* node) + { + return node; + } + + static StmtNode* fromLegacy(const dsql_nod* node); + static const StmtNode* fromLegacy(const jrd_nod* node); + static StmtNode* fromLegacy(jrd_nod* node); + jrd_nod* getNode() { return node; @@ -820,8 +937,11 @@ { } - virtual const jrd_nod* execute(thread_db* tdbb, jrd_req* request) const = 0; + virtual const jrd_nod* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const = 0; +public: + const Type type; + protected: NestConst<jrd_nod> node; }; @@ -832,8 +952,8 @@ class DsqlOnlyStmtNode : public StmtNode { public: - explicit DsqlOnlyStmtNode(MemoryPool& pool) - : StmtNode(pool) + explicit DsqlOnlyStmtNode(Type aType, MemoryPool& pool) + : StmtNode(aType, pool) { } @@ -850,7 +970,7 @@ return this; } - const jrd_nod* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + const jrd_nod* execute(thread_db* /*tdbb*/, jrd_req* /*request*/, ExeState* /*exeState*/) const { fb_assert(false); return NULL; @@ -859,11 +979,11 @@ // This class (via the make method) does the job that pass1_savepoint does for the legacy nodes. -class SavepointEncloseNode : public DsqlOnlyStmtNode +class SavepointEncloseNode : public TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_SAVEPOINT_ENCLOSE> { public: explicit SavepointEncloseNode(MemoryPool& pool, StmtNode* aStmt) - : DsqlOnlyStmtNode(pool), + : TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_SAVEPOINT_ENCLOSE>(pool), stmt(aStmt) { } Modified: firebird/trunk/src/dsql/StmtNodes.cpp =================================================================== --- firebird/trunk/src/dsql/StmtNodes.cpp 2010-11-28 19:51:03 UTC (rev 51994) +++ firebird/trunk/src/dsql/StmtNodes.cpp 2010-11-29 02:17:04 UTC (rev 51995) @@ -28,14 +28,23 @@ #include "../jrd/blr.h" #include "../jrd/tra.h" #include "../jrd/RecordSourceNodes.h" +#include "../jrd/VirtualTable.h" +#include "../jrd/extds/ExtDS.h" +#include "../jrd/recsrc/RecordSource.h" #include "../jrd/recsrc/Cursor.h" +#include "../jrd/trace/TraceManager.h" +#include "../jrd/trace/TraceJrdHelpers.h" #include "../jrd/cmp_proto.h" #include "../jrd/dfw_proto.h" +#include "../jrd/dpm_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/exe_proto.h" +#include "../jrd/ext_proto.h" +#include "../jrd/idx_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/par_proto.h" +#include "../jrd/rlck_proto.h" #include "../jrd/tra_proto.h" #include "../dsql/ddl_proto.h" #include "../jrd/vio_proto.h" @@ -53,6 +62,28 @@ namespace Jrd { +StmtNode* StmtNode::fromLegacy(const dsql_nod* node) +{ + return node && node->nod_type == Dsql::nod_class_stmtnode ? + reinterpret_cast<StmtNode*>(node->nod_arg[0]) : NULL; +} + +const StmtNode* StmtNode::fromLegacy(const jrd_nod* node) +{ + return node && node->nod_type == nod_class_stmtnode_jrd ? + reinterpret_cast<const StmtNode*>(node->nod_arg[0]) : NULL; +} + +StmtNode* StmtNode::fromLegacy(jrd_nod* node) +{ + return node && node->nod_type == nod_class_stmtnode_jrd ? + reinterpret_cast<StmtNode*>(node->nod_arg[0]) : NULL; +} + + +//-------------------- + + StmtNode* SavepointEncloseNode::make(MemoryPool& pool, DsqlCompilerScratch* dsqlScratch, StmtNode* node) { if (dsqlScratch->errorHandlers) @@ -87,6 +118,1490 @@ //-------------------- +static RegisterNode<BlockNode> regBlockNode(blr_block); + +DmlNode* BlockNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/) +{ + BlockNode* node = FB_NEW(pool) BlockNode(pool); + node->action = PAR_parse_node(tdbb, csb, STATEMENT); + + NodeStack stack; + + while (csb->csb_blr_reader.peekByte() != blr_end) + stack.push(PAR_parse_node(tdbb, csb, STATEMENT)); + + csb->csb_blr_reader.getByte(); // skip blr_end + + node->handlers = PAR_make_list(tdbb, stack); + + return node; +} + +BlockNode* BlockNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void BlockNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "BlockNode"; +} + +void BlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +BlockNode* BlockNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + action = CMP_pass1(tdbb, csb, action); + handlers = CMP_pass1(tdbb, csb, handlers); + return this; +} + +BlockNode* BlockNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + action = CMP_pass2(tdbb, csb, action, node); + handlers = CMP_pass2(tdbb, csb, handlers, node); + + node->nod_impure = CMP_impure(csb, sizeof(SLONG)); + + return this; +} + +const jrd_nod* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +{ + jrd_tra* transaction = request->req_transaction; + jrd_tra* sysTransaction = request->req_attachment->getSysTransaction(); + SLONG count; + + switch (request->req_operation) + { + case jrd_req::req_evaluate: + if (transaction != sysTransaction) + { + VIO_start_save_point(tdbb, transaction); + const Savepoint* save_point = transaction->tra_save_point; + count = save_point->sav_number; + *request->getImpure<SLONG>(node->nod_impure) = count; + } + return action; + + case jrd_req::req_unwind: + { + if (request->req_flags & (req_leave | req_continue_loop)) + { + // Although the req_operation is set to req_unwind, + // it's not an error case if req_leave/req_continue_loop bit is set. + // req_leave/req_continue_loop bit indicates that we hit an EXIT or + // BREAK/LEAVE/CONTINUE statement in the SP/trigger code. + // Do not perform the error handling stuff. + + if (transaction != sysTransaction) + { + count = *request->getImpure<SLONG>(node->nod_impure); + + for (const Savepoint* save_point = transaction->tra_save_point; + save_point && count <= save_point->sav_number; + save_point = transaction->tra_save_point) + { + EXE_verb_cleanup(tdbb, transaction); + } + } + + return node->nod_parent; + } + + if (transaction != sysTransaction) + { + count = *request->getImpure<SLONG>(node->nod_impure); + + // Since there occurred an error (req_unwind), undo all savepoints + // up to, but not including, the savepoint of this block. The + // savepoint of this block will be dealt with below. + + for (const Savepoint* save_point = transaction->tra_save_point; + save_point && count < save_point->sav_number; + save_point = transaction->tra_save_point) + { + ++transaction->tra_save_point->sav_verb_count; + EXE_verb_cleanup(tdbb, transaction); + } + } + + const jrd_nod* temp; + + if (handlers) + { + temp = node->nod_parent; + const jrd_nod* const* ptr = handlers->nod_arg; + + for (const jrd_nod* const* const end = ptr + handlers->nod_count; ptr < end; ++ptr) + { + const ErrorHandlerNode* handlerNode = StmtNode::as<ErrorHandlerNode>(*ptr); + const PsqlException* xcpNode = handlerNode->conditions; + + if (testAndFixupError(tdbb, request, xcpNode)) + { + request->req_operation = jrd_req::req_evaluate; + temp = handlerNode->action; + exeState->errorPending = false; + + // On entering looper exeState->oldRequest etc. are saved. + // On recursive calling we will loose the actual old + // request for that invocation of looper. Avoid this. + + { + Jrd::ContextPoolHolder contextLooper(tdbb, exeState->oldPool); + tdbb->setRequest(exeState->oldRequest); + fb_assert(request->req_caller == exeState->oldRequest); + request->req_caller = NULL; + + // Save the previous state of req_error_handler + // bit. We need to restore it later. This is + // necessary if the error handler is deeply nested. + + const ULONG prev_req_error_handler = + request->req_flags & req_error_handler; + request->req_flags |= req_error_handler; + temp = EXE_looper(tdbb, request, temp); + request->req_flags &= ~req_error_handler; + request->req_flags |= prev_req_error_handler; + + // Note: Previously the above call "temp = looper (tdbb, request, temp);" + // never returned back till the tree was executed completely. Now that + // the looper has changed its behaviour such that it returns back after + // handling error. This makes it necessary that the jmpbuf be reset + // so that looper can proceede with the processing of execution tree. + // If this is not done then anymore errors will take the engine out of + // looper there by abruptly terminating the processing. + + exeState->catchDisabled = false; + tdbb->setRequest(request); + fb_assert(request->req_caller == NULL); + request->req_caller = exeState->oldRequest; + } + + // The error is dealt with by the application, cleanup + // this block's savepoint. + + if (transaction != sysTransaction) + { + for (const Savepoint* save_point = transaction->tra_save_point; + save_point && count <= save_point->sav_number; + save_point = transaction->tra_save_point) + { + EXE_verb_cleanup(tdbb, transaction); + } + } + } + } + } + else + temp = node->nod_parent; + + // If the application didn't have an error handler, then + // the error will still be pending. Undo the block by + // using its savepoint. + + if (exeState->errorPending && transaction != sysTransaction) + { + for (const Savepoint* save_point = transaction->tra_save_point; + save_point && count <= save_point->sav_number; + save_point = transaction->tra_save_point) + { + ++transaction->tra_save_point->sav_verb_count; + EXE_verb_cleanup(tdbb, transaction); + } + } + + return temp; + } + + case jrd_req::req_return: + if (transaction != sysTransaction) + { + count = *request->getImpure<SLONG>(node->nod_impure); + + for (const Savepoint* save_point = transaction->tra_save_point; + save_point && count <= save_point->sav_number; + save_point = transaction->tra_save_point) + { + EXE_verb_cleanup(tdbb, transaction); + } + } + + default: + return node->nod_parent; + } + + fb_assert(false); + return NULL; +} + +// Test for match of current state with list of error conditions. Fix type and code of the exception. +bool BlockNode::testAndFixupError(thread_db* tdbb, jrd_req* request, const PsqlException* conditions) +{ + if (tdbb->tdbb_flags & TDBB_sys_error) + return false; + + ISC_STATUS* statusVector = tdbb->tdbb_status_vector; + const SSHORT sqlcode = gds__sqlcode(statusVector); + + bool found = false; + + for (USHORT i = 0; i < conditions->xcp_count; i++) + { + switch (conditions->xcp_rpt[i].xcp_type) + { + case xcp_sql_code: + if (sqlcode == conditions->xcp_rpt[i].xcp_code) + found = true; + break; + + case xcp_gds_code: + if (statusVector[1] == conditions->xcp_rpt[i].xcp_code) + found = true; + break; + + case xcp_xcp_code: + // Look at set_error() routine to understand how the + // exception ID info is encoded inside the status vector. + if ((statusVector[1] == isc_except) && + (statusVector[3] == conditions->xcp_rpt[i].xcp_code)) + { + found = true; + } + + break; + + case xcp_default: + found = true; + break; + + default: + fb_assert(false); + } + + if (found) + { + request->req_last_xcp.init(statusVector); + fb_utils::init_status(statusVector); + break; + } + } + + return found; +} + + +//-------------------- + + +static RegisterNode<ContinueLeaveNode> regContinueLeaveNodeContinue(blr_continue_loop); +static RegisterNode<ContinueLeaveNode> regContinueLeaveNodeLeave(blr_leave); + +DmlNode* ContinueLeaveNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp) +{ + ContinueLeaveNode* node = FB_NEW(pool) ContinueLeaveNode(pool, blrOp); + node->labelNumber = csb->csb_blr_reader.getByte(); + return node; +} + +ContinueLeaveNode* ContinueLeaveNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void ContinueLeaveNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "ContinueLeaveNode"; +} + +void ContinueLeaveNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +const jrd_nod* ContinueLeaveNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +{ + request->req_operation = jrd_req::req_unwind; + request->req_label = labelNumber; + request->req_flags |= (blrOp == blr_continue_loop ? req_continue_loop : req_leave); + return node->nod_parent; +} + + +//-------------------- + + +static RegisterNode<CursorStmtNode> regCursorStmtNode(blr_cursor_stmt); + +DmlNode* CursorStmtNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/) +{ + CursorStmtNode* node = FB_NEW(pool) CursorStmtNode(pool); + + node->cursorOp = csb->csb_blr_reader.getByte(); + node->cursorNumber = csb->csb_blr_reader.getWord(); + + switch (node->cursorOp) + { + case blr_cursor_open: + case blr_cursor_close: + break; + + case blr_cursor_fetch_scroll: + node->scrollOp = csb->csb_blr_reader.getByte(); + node->scrollExpr = PAR_parse_value(tdbb, csb); + // fall into + + case blr_cursor_fetch: + csb->csb_g_flags |= csb_reuse_context; + node->intoStmt = PAR_parse_node(tdbb, csb, STATEMENT); + csb->csb_g_flags &= ~csb_reuse_context; + break; + + default: + PAR_syntax_error(csb, "cursor operation clause"); + } + + return node; +} + +CursorStmtNode* CursorStmtNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void CursorStmtNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "CursorStmtNode"; +} + +void CursorStmtNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +CursorStmtNode* CursorStmtNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + scrollExpr = CMP_pass1(tdbb, csb, scrollExpr); + intoStmt = CMP_pass1(tdbb, csb, intoStmt); + return this; +} + +CursorStmtNode* CursorStmtNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + scrollExpr = CMP_pass2(tdbb, csb, scrollExpr); + intoStmt = CMP_pass2(tdbb, csb, intoStmt, node); + return this; +} + +const jrd_nod* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +{ + fb_assert(cursorNumber < request->req_cursors.getCount()); + const Cursor* const cursor = request->req_cursors[cursorNumber]; + bool fetched = false; + + switch (cursorOp) + { + case blr_cursor_open: + if (request->req_operation == jrd_req::req_evaluate) + { + cursor->open(tdbb); + request->req_operation = jrd_req::req_return; + } + return node->nod_parent; + + case blr_cursor_close: + if (request->req_operation == jrd_req::req_evaluate) + { + cursor->close(tdbb); + request->req_operation = jrd_req::req_return; + } + return node->nod_parent; + + case blr_cursor_fetch: + case blr_cursor_fetch_scroll: + switch (request->req_operation) + { + case jrd_req::req_evaluate: + request->req_records_affected.clear(); + + if (cursorOp == blr_cursor_fetch) + fetched = cursor->fetchNext(tdbb); + else + { + fb_assert(cursorOp == blr_cursor_fetch_scroll); + + const dsc* desc = EVL_expr(tdbb, request, scrollExpr); + const bool unknown = !desc || (request->req_flags & req_null); + const SINT64 offset = unknown ? 0 : MOV_get_int64(desc, 0); + + switch (scrollOp) + { + case blr_scroll_forward: + fetched = cursor->fetchNext(tdbb); + break; + case blr_scroll_backward: + fetched = cursor->fetchPrior(tdbb); + break; + case blr_scroll_bof: + fetched = cursor->fetchFirst(tdbb); + break; + case blr_scroll_eof: + fetched = cursor->fetchLast(tdbb); + break; + case blr_scroll_absolute: + fetched = unknown ? false : cursor->fetchAbsolute(tdbb, offset); + break; + case blr_scroll_relative: + fetched = unknown ? false : cursor->fetchRelative(tdbb, offset); + break; + default: + fb_assert(false); + fetched = false; + } + } + + if (fetched) + { + request->req_operation = jrd_req::req_evaluate; + return intoStmt; + } + + request->req_operation = jrd_req::req_return; + + default: + return node->nod_parent; + } + break; + } + + fb_assert(false); + return NULL; +} + + +//-------------------- + + +static RegisterNode<DeclareCursorNode> regDeclareCursorNode(blr_dcl_cursor); + +DmlNode* DeclareCursorNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/) +{ + DeclareCursorNode* node = FB_NEW(pool) DeclareCursorNode(pool); + + node->cursorNumber = csb->csb_blr_reader.getWord(); + node->rse = PAR_rse(tdbb, csb); + + USHORT count = csb->csb_blr_reader.getWord(); + node->refs = PAR_args(tdbb, csb, count, count); + + return node; +} + +DeclareCursorNode* DeclareCursorNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void DeclareCursorNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "DeclareCursorNode"; +} + +void DeclareCursorNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +DeclareCursorNode* DeclareCursorNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + rse = rse->pass1(tdbb, csb); + refs = refs->pass1(tdbb, csb); + return this; +} + +DeclareCursorNode* DeclareCursorNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + rse = rse->pass2(tdbb, csb); + refs = refs->pass2(tdbb, csb); + return this; +} + +const jrd_nod* DeclareCursorNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +{ + if (request->req_operation == jrd_req::req_evaluate) + { + // Set up the cursors array... + if (cursorNumber >= request->req_cursors.getCount()) + request->req_cursors.grow(cursorNumber + 1); + + // And store cursor there. + request->req_cursors[cursorNumber] = cursor; + request->req_operation = jrd_req::req_return; + } + + return node->nod_parent; +} + + +//-------------------- + + +static RegisterNode<EraseNode> regEraseNode(blr_erase); + +DmlNode* EraseNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/) +{ + USHORT n = csb->csb_blr_reader.getByte(); + + if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used)) + PAR_error(csb, Arg::Gds(isc_ctxnotdef)); + + EraseNode* node = FB_NEW(pool) EraseNode(pool); + node->stream = csb->csb_rpt[n].csb_stream; + + return node; +} + +EraseNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void EraseNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "EraseNode"; +} + +void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +EraseNode* EraseNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + pass1Erase(tdbb, csb, this); + + statement = CMP_pass1(tdbb, csb, statement); + subStatement = CMP_pass1(tdbb, csb, subStatement); + + return this; +} + +// Checkout an erase statement. If it references a view, and is kosher, fix it up. +void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* node) +{ + // If updateable views with triggers are involved, there maybe a recursive call to be ignored. + + if (node->subStatement) + return; + + // To support nested views, loop until we hit a table or a view with user-defined triggers + // (which means no update). + + jrd_rel* parent = NULL; + jrd_rel* view = NULL; + USHORT parentStream = 0; + + for (;;) + { + USHORT newStream = node->stream; + const USHORT stream = newStream; + + CompilerScratch::csb_repeat* tail = &csb->csb_rpt[stream]; + tail->csb_flags |= csb_erase; + + jrd_rel* relation = csb->csb_rpt[stream].csb_relation; + view = relation->rel_view_rse ? relation : view; + + if (!parent) + parent = tail->csb_view; + + CMP_post_trigger_access(csb, relation, ExternalAccess::exa_delete, view); + + // Check out delete. If this is a delete thru a view, verify the view by checking for read + // access on the base table. If field-level select privileges are implemented, this needs + // to be enhanced. + + SecurityClass::flags_t priv = SCL_sql_delete; + + if (parent) + priv |= SCL_read; + + const trig_vec* trigger = relation->rel_pre_erase ? + relation->rel_pre_erase : relation->rel_post_erase; + + // If we have a view with triggers, let's expand it. + + if (relation->rel_view_rse && trigger) + { + newStream = csb->nextStream(); + node->stream = newStream; + CMP_csb_element(csb, newStream)->csb_relation = relation; + + node->statement = CMP_pass1_expand_view(tdbb, csb, stream, newStream, false); + } + + // Get the source relation, either a table or yet another view. + + RelationSourceNode* source = CMP_pass1_update(tdbb, csb, relation, trigger, stream, + newStream, priv, parent, parentStream); + + if (!source) + return; // no source means we're done + + parent = relation; + parentStream = stream; + + // Remap the source stream. + + UCHAR* map = csb->csb_rpt[stream].csb_map; + + if (trigger) + { + // ASF: This code is responsible to make view's WITH CHECK OPTION to work as constraints. + // I don't see how it could run for delete statements under normal conditions. + + // Set up the new target stream. + + EraseNode* viewNode = FB_NEW(*tdbb->getDefaultPool()) EraseNode(*tdbb->getDefaultPool()); + viewNode->stream = node->stream; + + node->subStatement = PAR_make_node(tdbb, 1); + node->subStatement->nod_type = nod_class_stmtnode_jrd; + node->subStatement->nod_count = 0; + node->subStatement->nod_arg[0] = reinterpret_cast<jrd_nod*>(viewNode); + + // Substitute the original delete node with the newly created one. + node = viewNode; + } + else + { + // This relation is not actually being updated as this operation + // goes deeper (we have a naturally updatable view). + csb->csb_rpt[newStream].csb_flags &= ~csb_view_update; + } + + // Let's reset the target stream. + newStream = source->getStream(); + node->stream = map[newStream]; + } +} + +EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + statement = CMP_pass2(tdbb, csb, statement, node); + subStatement = CMP_pass2(tdbb, csb, subStatement, node); + + node->nod_impure = CMP_impure(csb, sizeof(SLONG)); + csb->csb_rpt[stream].csb_flags |= csb_update; + + return this; +} + +const jrd_nod* EraseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +{ + const jrd_nod* retNode; + + if (request->req_operation == jrd_req::req_unwind) + retNode = node->nod_parent; + else if (request->req_operation == jrd_req::req_return && subStatement) + { + if (!exeState->topNode) + { + exeState->topNode = node; + exeState->whichEraseTrig = PRE_TRIG; + } + + exeState->prevNode = node; + retNode = erase(tdbb, request, exeState->whichEraseTrig); + + if (exeState->whichEraseTrig == PRE_TRIG) + { + retNode = subStatement; + fb_assert(retNode->nod_parent == node); + ///retNode->nod_parent = exeState->prevNode; + } + + if (exeState->topNode == node && exeState->whichEraseTrig == POST_TRIG) + { + exeState->topNode = NULL; + exeState->whichEraseTrig = ALL_TRIGS; + } + else + request->req_operation = jrd_req::req_evaluate; + } + else + { + exeState->prevNode = node; + retNode = erase(tdbb, request, ALL_TRIGS); + + if (!subStatement && exeState->whichEraseTrig == PRE_TRIG) + exeState->whichEraseTrig = POST_TRIG; + } + + return retNode; +} + +// Perform erase operation. +const jrd_nod* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const +{ + Jrd::Attachment* attachment = tdbb->getAttachment(); + jrd_tra* transaction = request->req_transaction; + record_param* rpb = &request->req_rpb[stream]; + jrd_rel* relation = rpb->rpb_relation; + + if (rpb->rpb_number.isBof() || (!relation->rel_view_rse && !rpb->rpb_number.isValid())) + ERR_post(Arg::Gds(isc_no_cur_rec)); + + switch (request->req_operation) + { + case jrd_req::req_evaluate: + { + request->req_records_affected.bumpModified(false); + + if (!statement) + break; + + const Format* format = MET_current(tdbb, rpb->rpb_relation); + Record* record = VIO_record(tdbb, rpb, format, tdbb->getDefaultPool()); + + rpb->rpb_address = record->rec_data; + rpb->rpb_length = format->fmt_length; + rpb->rpb_format_number = format->fmt_version; + + return statement; + } + + case jrd_req::req_return: + break; + + default: + return node->nod_parent; + } + + request->req_operation = jrd_req::req_return; + RLCK_reserve_relation(tdbb, transaction, relation, true); + + // If the stream was sorted, the various fields in the rpb are probably junk. + // Just to make sure that everything is cool, refetch and release the record. + + if (rpb->rpb_stream_flags & RPB_s_refetch) + { + VIO_refetch_record(tdbb, rpb, transaction); + rpb->rpb_stream_flags &= ~RPB_s_refetch; + } + + if (transaction != attachment->getSysTransaction()) + ++transaction->tra_save_point->sav_verb_count; + + // Handle pre-operation trigger. + EXE_PreModifyEraseTriggers(tdbb, &relation->rel_pre_erase, whichTrig, rpb, NULL, + jrd_req::req_trigger_delete); + + if (relation->rel_file) + EXT_erase(rpb, transaction); + else if (relation->isVirtual()) + VirtualTable::erase(tdbb, rpb); + else if (!relation->rel_view_rse) + VIO_erase(tdbb, rpb, transaction); + + // Handle post operation trigger. + if (relation->rel_post_erase && whichTrig != PRE_TRIG) + { + EXE_execute_triggers(tdbb, &relation->rel_post_erase, rpb, NULL, + jrd_req::req_trigger_delete, POST_TRIG); + } + + // Call IDX_erase (which checks constraints) after all post erase triggers have fired. + // This is required for cascading referential integrity, which can be implemented as + // post_erase triggers. + + if (!relation->rel_file && !relation->rel_view_rse && !relation->isVirtual()) + { + jrd_rel* badRelation = NULL; + USHORT badIndex; + + const idx_e errorCode = IDX_erase(tdbb, rpb, transaction, &badRelation, &badIndex); + + if (errorCode) + ERR_duplicate_error(errorCode, badRelation, badIndex); + } + + // CVC: Increment the counter only if we called VIO/EXT_erase() and we were successful. + if (!(request->req_view_flags & req_first_erase_return)) + { + request->req_view_flags |= req_first_erase_return; + if (relation->rel_view_rse) + request->req_top_view_erase = relation; + } + + if (relation == request->req_top_view_erase) + { + if (whichTrig == ALL_TRIGS || whichTrig == POST_TRIG) + { + request->req_records_deleted++; + request->req_records_affected.bumpModified(true); + } + } + else if (relation->rel_file || !relation->rel_view_rse) + { + request->req_records_deleted++; + request->req_records_affected.bumpModified(true); + } + + if (transaction != attachment->getSysTransaction()) + --transaction->tra_save_point->sav_verb_count; + + rpb->rpb_number.setValid(false); + + return node->nod_parent; +} + + +//-------------------- + + +static RegisterNode<ErrorHandlerNode> regErrorHandlerNode(blr_error_handler); + +DmlNode* ErrorHandlerNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/) +{ + ErrorHandlerNode* node = FB_NEW(pool) ErrorHandlerNode(pool); + + const USHORT n = csb->csb_blr_reader.getWord(); + node->conditions = FB_NEW_RPT(*tdbb->getDefaultPool(), n) PsqlException(); + node->conditions->xcp_count = n; + + for (unsigned i = 0; i < n; i++) + { + const USHORT codeType = csb->csb_blr_reader.getByte(); + xcp_repeat& item = node->conditions->xcp_rpt[i]; + + switch (codeType) + { + case blr_sql_code: + item.xcp_type = xcp_sql_code; + item.xcp_code = (SSHORT) csb->csb_blr_reader.getWord(); + break; + + case blr_gds_code: + { + string name; + item.xcp_type = xcp_gds_code; + PAR_name(csb, name); + name.lower(); + SLONG codeNumber = PAR_symbol_to_gdscode(name); + if (codeNumber) + item.xcp_code = codeNumber; + else + PAR_error(csb, Arg::Gds(isc_codnotdef) << Arg::Str(name)); + break; + } + + case blr_exception: + { + MetaName name; + item.xcp_type = xcp_xcp_code; + PAR_name(csb, name); + if (!(item.xcp_code = MET_lookup_exception_number(tdbb, name))) + PAR_error(csb, Arg::Gds(isc_xcpnotdef) << Arg::Str(name)); + + CompilerScratch::Dependency dependency(obj_exception); + dependency.number = item.xcp_code; + csb->csb_dependencies.push(dependency); + break; + } + + case blr_default_code: + item.xcp_type = xcp_default; + item.xcp_code = 0; + break; + + default: + fb_assert(FALSE); + break; + } + } + + node->action = PAR_parse_node(tdbb, csb, STATEMENT); + + return node; +} + +ErrorHandlerNode* ErrorHandlerNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void ErrorHandlerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "ErrorHandlerNode"; +} + +void ErrorHandlerNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +ErrorHandlerNode* ErrorHandlerNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + action = CMP_pass1(tdbb, csb, action); + return this; +} + +ErrorHandlerNode* ErrorHandlerNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + action = CMP_pass2(tdbb, csb, action, node); + return this; +} + +const jrd_nod* ErrorHandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* exeState) const +{ + if ((request->req_flags & req_error_handler) && !exeState->errorPending) + { + fb_assert(request->req_caller == exeState->oldRequest); + request->req_caller = NULL; + return node; + } + + const jrd_nod* retNode = node->nod_parent; + retNode = retNode->nod_parent; + + if (request->req_operation == jrd_req::req_unwind) + retNode = retNode->nod_parent; + + request->req_last_xcp.clear(); + + return retNode; +} + + +//-------------------- + + +static RegisterNode<ExecProcedureNode> regExecProcedureNodeProc(blr_exec_proc); +static RegisterNode<ExecProcedureNode> regExecProcedureNodeProc2(blr_exec_proc2); +static RegisterNode<ExecProcedureNode> regExecProcedureNodePid(blr_exec_pid); + +// Parse an execute procedure reference. +DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp) +{ + SET_TDBB(tdbb); + + jrd_prc* procedure = NULL; + QualifiedName name; + + if (blrOp == blr_exec_pid) + { + const USHORT pid = csb->csb_blr_reader.getWord(); + if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0))) + name.identifier.printf("id %d", pid); + } + else + { + if (blrOp == blr_exec_proc2) + PAR_name(csb, name.package); + PAR_name(csb, name.identifier); + procedure = MET_lookup_procedure(tdbb, name, false); + } + + if (!procedure) + PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); + + ExecProcedureNode* node = FB_NEW(pool) ExecProcedureNode(pool); + node->procedure = procedure; + + PAR_procedure_parms(tdbb, csb, procedure, node->inputMessage.getAddress(), + node->inputSources.getAddress(), node->inputTargets.getAddress(), true); + PAR_procedure_parms(tdbb, csb, procedure, node->outputMessage.getAddress(), + node->outputSources.getAddress(), node->outputTargets.getAddress(), false); + + CompilerScratch::Dependency dependency(obj_procedure); + dependency.procedure = procedure; + csb->csb_dependencies.push(dependency); + + return node; +} + +ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void ExecProcedureNode::print(string& text, Array<dsql_nod*>& nodes) const +{ + text = "ExecProcedureNode"; +} + +void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +ExecProcedureNode* ExecProcedureNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + // Post access to procedure + CMP_post_procedure_access(tdbb, csb, procedure); + CMP_post_resource(&csb->csb_resources, procedure, Resource::rsc_procedure, procedure->getId()); + + if (inputSources) + inputSources = inputSources->pass1(tdbb, csb); + + if (inputTargets) + inputTargets = inputTargets->pass1(tdbb, csb); + + inputMessage = CMP_pass1(tdbb, csb, inputMessage); + + if (outputSources) + outputSources = outputSources->pass1(tdbb, csb); + + if (outputTargets) + outputTargets = outputTargets->pass1(tdbb, csb); + + outputMessage = CMP_pass1(tdbb, csb, outputMessage); + + return this; +} + +ExecProcedureNode* ExecProcedureNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + if (inputSources) + inputSources = inputSources->pass2(tdbb, csb); + + if (inputTargets) + inputTargets = inputTargets->pass2(tdbb, csb); + + inputMessage = CMP_pass2(tdbb, csb, inputMessage, node); + + if (outputSources) + outputSources = outputSources->pass2(tdbb, csb); + + if (outputTargets) + outputTargets = outputTargets->pass2(tdbb, csb); + + outputMessage = CMP_pass2(tdbb, csb, outputMessage, node); + + return this; +} + +const jrd_nod* ExecProcedureNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +{ + if (request->req_operation == jrd_req::req_unwind) + return node->nod_parent; + + executeProcedure(tdbb, request); + + request->req_operation = jrd_req::req_return; + return node->nod_parent; +} + +// Execute a stored procedure. Begin by assigning the input parameters. +// End by assigning the output parameters. +void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) const +{ + Jrd::Attachment* attachment = tdbb->getAttachment(); + + if (inputSources) + { + const NestConst<ValueExprNode>* const sourceEnd = inputSources->args.end(); + const NestConst<ValueExprNode>* sourcePtr = inputSources->args.begin(); + const NestConst<ValueExprNode>* targetPtr = inputTargets->args.begin(); + + for (; sourcePtr != sourceEnd; ++sourcePtr, ++targetPtr) + EXE_assignment(tdbb, *sourcePtr, *targetPtr); + } + + ULONG inMsgLength = 0; + UCHAR* inMsg = NULL; + + if (inputMessage) + { + const Format* format = (Format*) inputMessage->nod_arg[e_msg_format]; + inMsgLength = format->fmt_length; + inMsg = request->getImpure<UCHAR>(inputMessage->nod_impure); + } + + const Format* format = NULL; + ULONG outMsgLength = 0; + UCHAR* outMsg = NULL; + + if (outputMessage) + { + format = (Format*) outputMessage->nod_arg[e_msg_format]; + outMsgLength = format->fmt_length; + outMsg = request->getImpure<UCHAR>(outputMessage->nod_impure); + } + + jrd_req* procRequest = procedure->getStatement()->findRequest(tdbb); + + // trace procedure execution start + TraceProcExecute trace(tdbb, procRequest, request, inputTargets); + + Array<UCHAR> temp_buffer; + + if (!outputMessage) + { + format = (Format*) procedure->prc_output_msg->nod_arg[e_msg_format]; + outMsgLength = format->fmt_length; + outMsg = temp_buffer.getBuffer(outMsgLength + FB_DOUBLE_ALIGN - 1); + outMsg = (UCHAR*) FB_ALIGN((U_IPTR) outMsg, FB_DOUBLE_ALIGN); + } + + // Catch errors so we can unwind cleanly. + + try + { + Jrd::ContextPoolHolder context(tdbb, procRequest->req_pool); // Save the old pool. + + jrd_tra* transaction = request->req_transaction; + const SLONG savePointNumber = transaction->tra_save_point ? + transaction->tra_save_point->sav_number : 0; + + procRequest->req_timestamp = request->req_timestamp; + + EXE_start(tdbb, procRequest, transaction); + + if (inputMessage) + EXE_send(tdbb, procRequest, 0, inMsgLength, inMsg); + + EXE_receive(tdbb, procRequest, 1, outMsgLength, outMsg); + + // Clean up all savepoints started during execution of the procedure. + + if (transaction != attachment->getSysTransaction()) + { + for (const Savepoint* savePoint = transaction->tra_save_point; + savePoint && savePointNumber < savePoint->sav_number; + savePoint = transaction->tra_save_point) + { + VIO_verb_cleanup(tdbb, transaction); + } + } + } + catch (const Exception& ex) + { + const bool noPriv = (ex.stuff_exception(tdbb->tdbb_status_vector) == isc_no_priv); + trace.finish(false, noPriv ? res_unauthorized : res_failed); + + tdbb->setRequest(request); + EXE_unwind(tdbb, procRequest); + procRequest->req_attachment = NULL; + procRequest->req_flags &= ~(req_in_use | req_proc_fetch); + procRequest->req_timestamp.invalidate(); + throw; + } + + // trace procedure execution finish + trace.finish(false, res_successful); + + EXE_unwind(tdbb, procRequest); + tdbb->setRequest(request); + + if (outputSources) + { + const NestConst<ValueExprNode>* const sourceEnd = outputSources->args.end(); + const NestConst<ValueExprNode>* sourcePtr = outputSources->args.begin(); + const NestConst<ValueExprNode>* targetPtr = outputTargets->args.begin(); + + for (; sourcePtr != sourceEnd; ++sourcePtr, ++targetPtr) + EXE_assignment(tdbb, *sourcePtr, *targetPtr); + } + + procRequest->req_attachment = NULL; + procRequest->req_flags &= ~(req_in_use | req_proc_fetch); + procRequest->req_timestamp.invalidate(); +} + + +//-------------------- + + +static RegisterNode<ExecStatementNode> regExecStatementSql(blr_exec_sql); +static RegisterNode<ExecStatementNode> regExecStatementInto(blr_exec_into); +static RegisterNode<ExecStatementNode> regExecStatementStmt(blr_exec_stmt); + +DmlNode* ExecStatementNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp) +{ + ExecStatementNode* node = FB_NEW(pool) ExecStatementNode(pool); + + switch (blrOp) + { + case blr_exec_sql: + node->sql = PAR_parse_value(tdbb, csb); + break; + + case blr_exec_into: + { + const unsigned outputs = csb->csb_blr_reader.getWord(); + + node->sql = PAR_parse_value(tdbb, csb); + + if (csb->csb_blr_reader.getByte() == 0) // not singleton flag + node->innerStmt = PAR_parse_node(tdbb, csb, STATEMENT); + + node->outputs = PAR_args(tdbb, csb, outputs, outputs); + break; + } + + case blr_exec_stmt: + { + unsigned inputs = 0; + unsigned outputs = 0; + + while (true) + { + const UCHAR code = csb->csb_blr_reader.getByte(); + + switch (code) + { + case blr_exec_stmt_inputs: + inputs = csb->csb_blr_reader.getWord(); + break; + + case blr_exec_stmt_outputs: + outputs = csb->csb_blr_reader.getWord(); + break; + + case blr_exec_stmt_sql: + node->sql = PAR_parse_value(tdbb, csb); + break; + + case blr_exec_stmt_proc_block: + node->innerStmt = PAR_parse_node(tdbb, csb, STATEMENT); + break; + + case blr_exec_stmt_data_src: + node->dataSource = PAR_parse_value(tdbb, csb); + break; + + case blr_exec_stmt_user: + node->userName = PAR_parse_value(tdbb, csb); + break; + + case blr_exec_stmt_pwd: + node->password = PAR_parse_value(tdbb, csb); + break; + + case blr_exec_stmt_role: + node->role = PAR_parse_value(tdbb, csb); + break; + + case blr_exec_stmt_tran: + PAR_syntax_error(csb, "external transaction parameters"); + break; + + case blr_exec_stmt_tran_clone: + node->traScope = static_cast<EDS::TraScope>(csb->csb_blr_reader.getByte()); + break; + + case blr_exec_stmt_privs: + node->useCallerPrivs = true; + break; + + case blr_exec_stmt_in_params: + case blr_exec_stmt_in_params2: + { + node->inputs = FB_NEW(pool) ValueListNode(pool, inputs); + NestConst<ValueExprNode>* const end = node->inputs->args.end(); + + for (NestConst<ValueExprNode>* ptr = node->inputs->args.begin(); + ptr != end; + ++ptr) + { + if (code == blr_exec_stmt_in_params2) + { + string name; + + if (PAR_name(csb, name)) + { + MemoryPool& pool = csb->csb_pool; + + if (!node->inputNames) + node->inputNames = FB_NEW (pool) EDS::ParamNames(pool); + + string* newName = FB_NEW (pool) string(pool, name); + node->inputNames->add(newName); + } + } + + *ptr = PAR_parse_value(tdbb, csb); + } + + break; + } + + case blr_exec_stmt_out_params: + node->outputs = PAR_args(tdbb, csb, outputs, outputs); + break; + + case blr_end: + break; + + default: + PAR_syntax_error(csb, "unknown EXECUTE STATEMENT option"); + } + + if (code == blr_end) + break; + } + + break; + } + + default: + fb_assert(false); + } + + return node; +} + +ExecStatementNode* ExecStatementNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void ExecStatementNode::print(string& text, Array<dsql_nod*>& nodes) const +{ + text = "ExecStatementNode"; +} + +void ExecStatementNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +ExecStatementNode* ExecStatementNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + sql = CMP_pass1(tdbb, csb, sql); + dataSource = CMP_pass1(tdbb, csb, dataSource); + userName = CMP_pass1(tdbb, csb, userName); + password = CMP_pass1(tdbb, csb, password); + role = CMP_pass1(tdbb, csb, role); + + innerStmt = CMP_pass1(tdbb, csb, innerStmt); + + if (inputs) + inputs = inputs->pass1(tdbb, csb); + + if (outputs) + outputs = outputs->pass1(tdbb, csb); + + return this; +} + +ExecStatementNode* ExecStatementNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + sql = CMP_pass2(tdbb, csb, sql); + dataSource = CMP_pass2(tdbb, csb, dataSource); + userName = CMP_pass2(tdbb, csb, userName); + password = CMP_pass2(tdbb, csb, password); + role = CMP_pass2(tdbb, csb, role); + + innerStmt = CMP_pass2(tdbb, csb, innerStmt, node); + + if (inputs) + inputs = inputs->pass2(tdbb, csb); + + if (outputs) + outputs = outputs->pass2(tdbb, csb); + + node->nod_impure = CMP_impure(csb, sizeof(void**)); + + return this; +} + +const jrd_nod* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +{ + EDS::Statement** stmtPtr = request->getImpure<EDS::Statement*>(node->nod_impure); + EDS::Statement* stmt = *stmtPtr; + + if (request->req_operation == jrd_req::req_evaluate) + { + fb_assert(!*stmtPtr); + + string sSql; + getString(tdbb, request, sql, sSql); + + string sDataSrc; + getString(tdbb, request, dataSource, sDataSrc); + + string sUser; + getString(tdbb, request, userName, sUser); + + string sPwd; + getString(tdbb, request, password, sPwd); + + string sRole; + getString(tdbb, request, role, sRole); + + EDS::Connection* conn = EDS::Manager::getConnection(tdbb, sDataSrc, sUser, sPwd, sRole, traScope); + + stmt = conn->createStatement(sSql); + + EDS::Transaction* tran = EDS::Transaction::getTransaction(tdbb, stmt->getConnection(), traScope); + + stmt->bindToRequest(request, stmtPtr); + stmt->setCallerPrivileges(useCallerPrivs); + + const string* const* inpNames = inputNames ? inputNames->begin() : NULL; + stmt->prepare(tdbb, tran, sSql, inputNames != NULL); + + if (stmt->isSelectable()) + stmt->open(tdbb, tran, inpNames, inputs, !innerStmt); + else + stmt->execute(tdbb, tran, inpNames, inputs, outputs); + + request->req_operation = jrd_req::req_return; + } // jrd_req::req_evaluate + + if (request->req_operation == jrd_req::req_return || request->req_operation == jrd_req::req_sync) + { + fb_assert(stmt); + + if (stmt->isSelectable()) + { + if (stmt->fetch(tdbb, outputs)) + { + request->req_operation = jrd_req::req_evaluate; + return innerStmt; + } + + request->req_operation = jrd_req::req_return; + } + } + + if (request->req_operation == jrd_req::req_unwind) + { + const LabelNode* label = StmtNode::as<LabelNode>(node->nod_parent.getObject()); + + if (label && request->req_label == label->labelNumber && + (request->req_flags & req_continue_loop)) + { + request->req_flags &= ~req_continue_loop; + request->req_operation = jrd_req::req_sync; + return node; + } + } + + if (stmt) + stmt->close(tdbb); + + return node->nod_parent; +} + +void ExecStatementNode::getString(thread_db* tdbb, jrd_req* request, const ValueExprNode* node, + string& str) const +{ + MoveBuffer buffer; + UCHAR* p = NULL; + int len = 0; + const dsc* dsc = node ? EVL_expr(tdbb, request, node) : NULL; + + if (dsc && !(request->req_flags & req_null)) + len = MOV_make_string2(tdbb, dsc, dsc->getTextType(), &p, buffer); + + str = string((char*) p, len); + str.trim(); +} + + +//-------------------- + + static RegisterNode<IfNode> regIfNode(blr_if); @@ -155,7 +1670,7 @@ } -const jrd_nod* IfNode::execute(thread_db* tdbb, jrd_req* request) const +const jrd_nod* IfNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const { if (request->req_operation == jrd_req::req_evaluate) { @@ -243,7 +1758,7 @@ } -const jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) const +const jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const { Jrd::Attachment* attachment = request->req_attachment; SLONG* savNumber = request->getImpure<SLONG>(savNumberOffset); @@ -715,7 +2230,7 @@ node->parameters = PAR_args(tdbb, csb, count, count); } else if (type == blr_exception_msg) - node->messageExpr = PAR_parse_node(tdbb, csb, VALUE)->asValue(); + node->messageExpr = PAR_parse_value(tdbb, csb); return node; } @@ -802,7 +2317,7 @@ } -const jrd_nod* ExceptionNode::execute(thread_db* tdbb, jrd_req* request) const +const jrd_nod* ExceptionNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const { if (request->req_operation == jrd_req::req_evaluate) { @@ -1141,7 +2656,7 @@ return this; } -const jrd_nod* ForNode::execute(thread_db* tdbb, jrd_req* request) const +const jrd_nod* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const { switch (request->req_operation) { @@ -1166,10 +2681,9 @@ case jrd_req::req_unwind: { - const jrd_nod* parent = node->nod_parent; + const LabelNode* label = StmtNode::as<LabelNode>(node->nod_parent.getObject()); - if (parent && parent->nod_type == nod_label && - request->req_label == (USHORT)(IPTR) parent->nod_arg[e_lbl_label] && + if (label && request->req_label == label->labelNumber && (request->req_flags & req_continue_loop)) { request->req_flags &= ~req_continue_loop; @@ -1193,22 +2707,695 @@ //-------------------- +static RegisterNode<HandlerNode> regHandlerNode(blr_handler); + +DmlNode* HandlerNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/) +{ + HandlerNode* node = FB_NEW(pool) HandlerNode(pool); + node->statement = PAR_parse_node(tdbb, csb, STATEMENT); + return node; +} + +HandlerNode* HandlerNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void HandlerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "HandlerNode"; +} + +void HandlerNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ +} + +HandlerNode* HandlerNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + statement = CMP_pass1(tdbb, csb, statement); + return this; +} + +HandlerNode* HandlerNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + statement = CMP_pass2(tdbb, csb, statement, node); + return this; +} + +const jrd_nod* HandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +{ + switch (request->req_operation) + { + case jrd_req::req_evaluate: + return statement; + + case jrd_req::req_unwind: + if (!request->req_label) + request->req_operation = jrd_req::req_return; + + default: + return node->nod_parent; + } +} + + +//-------------------- + + +static RegisterNode<LabelNode> regLabelNode(blr_label); + +DmlNode* LabelNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/) +{ + LabelNode* node = FB_NEW(pool) LabelNode(pool); + + node->labelNumber = csb->csb_blr_reader.getByte(); + node->statement = PAR_parse_node(tdbb, csb, STATEMENT); + + return node; +} + +LabelNode* LabelNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + return this; +} + +void LabelNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text = "LabelNode"; +} + +void L... [truncated message content] |