|
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] |