8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:43:02 +01:00

Feature #6815 - Support multiple rows for DML RETURNING.

This commit is contained in:
Adriano dos Santos Fernandes 2021-08-25 14:41:25 -03:00
parent 7e88b82a46
commit b32f96f2a9
47 changed files with 1849 additions and 960 deletions

View File

@ -121,6 +121,7 @@
<ClCompile Include="..\..\..\src\jrd\recsrc\FullTableScan.cpp" /> <ClCompile Include="..\..\..\src\jrd\recsrc\FullTableScan.cpp" />
<ClCompile Include="..\..\..\src\jrd\recsrc\HashJoin.cpp" /> <ClCompile Include="..\..\..\src\jrd\recsrc\HashJoin.cpp" />
<ClCompile Include="..\..\..\src\jrd\recsrc\IndexTableScan.cpp" /> <ClCompile Include="..\..\..\src\jrd\recsrc\IndexTableScan.cpp" />
<ClCompile Include="..\..\..\src\jrd\recsrc\LocalTableStream.cpp" />
<ClCompile Include="..\..\..\src\jrd\recsrc\LockedStream.cpp" /> <ClCompile Include="..\..\..\src\jrd\recsrc\LockedStream.cpp" />
<ClCompile Include="..\..\..\src\jrd\recsrc\MergeJoin.cpp" /> <ClCompile Include="..\..\..\src\jrd\recsrc\MergeJoin.cpp" />
<ClCompile Include="..\..\..\src\jrd\recsrc\NestedLoopJoin.cpp" /> <ClCompile Include="..\..\..\src\jrd\recsrc\NestedLoopJoin.cpp" />

View File

@ -81,6 +81,9 @@
<ClCompile Include="..\..\..\src\jrd\recsrc\IndexTableScan.cpp"> <ClCompile Include="..\..\..\src\jrd\recsrc\IndexTableScan.cpp">
<Filter>JRD files\Data Access</Filter> <Filter>JRD files\Data Access</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\jrd\recsrc\LocalTableStream.cpp">
<Filter>JRD files\Data Access</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\jrd\recsrc\LockedStream.cpp"> <ClCompile Include="..\..\..\src\jrd\recsrc\LockedStream.cpp">
<Filter>JRD files\Data Access</Filter> <Filter>JRD files\Data Access</Filter>
</ClCompile> </ClCompile>

View File

@ -7,7 +7,7 @@ MERGE statement
condition. condition.
Author: Author:
Adriano dos Santos Fernandes <adrianosf@uol.com.br> Adriano dos Santos Fernandes <adrianosf@gmail.com>
Format: Format:
<merge statement> ::= <merge statement> ::=
@ -16,6 +16,8 @@ MERGE statement
USING <table or view or derived table> [ [AS] <correlation name> ] USING <table or view or derived table> [ [AS] <correlation name> ]
ON <condition> ON <condition>
<merge when>... <merge when>...
[<plan clause>]
[<order by clause>]
<returning clause> <returning clause>
<merge when> ::= <merge when> ::=

View File

@ -39,11 +39,12 @@ RETURNING clause
Note(s): Note(s):
1. The INTO part (i.e. the variable list) is allowed in PSQL only (to assign local variables) 1. The INTO part (i.e. the variable list) is allowed in PSQL only (to assign local variables)
and rejected in DSQL. and rejected in DSQL.
2. In DSQL, values are being returned within the same protocol roundtrip as the INSERT itself 2. In DSQL, INSERT INTO ... VALUES are returned within the same protocol roundtrip as the
is executed. INSERT is executed - this is not the case for INSERT INTO ... SELECT.
3. If the RETURNING clause is present, then the statement is described as 3. If the RETURNING clause is present, then the statement is described as
isc_info_sql_stmt_exec_procedure by the API (instead of isc_info_sql_stmt_insert), isc_info_sql_stmt_exec_procedure by the API (for INSERT INTO ... VALUES and statements
so the existing connectivity drivers should support this feature automagically. with WHERE CURRENT OF) and isc_info_sql_stmt_select for the others statements, so the
existing connectivity drivers should support this feature automagically.
4. Any explicit record change (update or delete) performed by AFTER-triggers is ignored by 4. Any explicit record change (update or delete) performed by AFTER-triggers is ignored by
the RETURNING clause. the RETURNING clause.
5. OLD and NEW could be used in RETURNING clause of UPDATE, INSERT OR UPDATE and MERGE statements. 5. OLD and NEW could be used in RETURNING clause of UPDATE, INSERT OR UPDATE and MERGE statements.
@ -53,9 +54,13 @@ RETURNING clause
by table name/alias fields are resolved to NEW. In MERGE WHEN MATCHED DELETE they are by table name/alias fields are resolved to NEW. In MERGE WHEN MATCHED DELETE they are
resolved to OLD. resolved to OLD.
8. Since v4 it's possible to use * and alias.*, as well OLD.* and NEW.* where applicable. 8. Since v4 it's possible to use * and alias.*, as well OLD.* and NEW.* where applicable.
9. ORDER BY works with fields from the OLD context (the original record source before changes are applied).
Limitations: Limitations:
1. The modify statement (INSERT, UPDATE, DELETE, MERGE) should operate in no more than one record 1. The modify statement (INSERT, UPDATE, DELETE, UPDATE OR INSERT, MERGE) in PSQL should
(i.e. should be singleton). operate in no more than one record (i.e. should be singleton).
2. The statement always return one row in DSQL, even if no record is inserted/updated/deleted.
This may be changed in the future (i.e. return empty resultset). Changes in v5:
1. Ability to return multiple rows (or no row) in DSQL.
2. Added PLAN and ORDER BY subclauses to MERGE.
3. Added PLAN, ORDER BY and ROWS subclauses to UPDATE OR INSERT.

View File

@ -6,12 +6,15 @@ UPDATE OR INSERT statement
Allow to update or insert a record based on the existence (checked with IS NOT DISTINCT) or not of it. Allow to update or insert a record based on the existence (checked with IS NOT DISTINCT) or not of it.
Author: Author:
Adriano dos Santos Fernandes <adrianosf@uol.com.br> Adriano dos Santos Fernandes <adrianosf@gmail.com>
Syntax rules: Syntax rules:
UPDATE OR INSERT INTO <table or view> [(<column_list>)] UPDATE OR INSERT INTO <table or view> [(<column_list>)]
VALUES (<value_list>) VALUES (<value_list>)
[MATCHING (<column_list>)] [MATCHING (<column_list>)]
[<plan clause>]
[<order by clause>]
[<rows clause>]
[RETURNING <value_list> [INTO <variable_list>]] [RETURNING <value_list> [INTO <variable_list>]]
Scope: Scope:
@ -27,11 +30,9 @@ UPDATE OR INSERT statement
1. When MATCHING is omitted, the existence of a primary key is required. 1. When MATCHING is omitted, the existence of a primary key is required.
2. INSERT and UPDATE permissions are needed on <table or view>. 2. INSERT and UPDATE permissions are needed on <table or view>.
3. If the RETURNING clause is present, then the statement is described as 3. If the RETURNING clause is present, then the statement is described as
isc_info_sql_stmt_exec_procedure by the API. Otherwise it is described isc_info_sql_stmt_select by the API. Otherwise it is described
as isc_info_sql_stmt_insert. as isc_info_sql_stmt_insert.
Limitation: Limitation:
1. A singleton error will be raised if the RETURNING clause is present and more than 1. There is no "UPDATE OR INSERT ... SELECT ..." as "INSERT ... SELECT". Use MERGE for
one record match the condition.
2. There is no "UPDATE OR INSERT ... SELECT ..." as "INSERT ... SELECT". Use MERGE for
this type of functionality. this type of functionality.

View File

@ -1959,6 +1959,8 @@ const
gds_inf_invalid_args = 335545275; gds_inf_invalid_args = 335545275;
isc_sysf_invalid_null_empty = 335545276; isc_sysf_invalid_null_empty = 335545276;
gds_sysf_invalid_null_empty = 335545276; gds_sysf_invalid_null_empty = 335545276;
isc_bad_loctab_num = 335545277;
gds_bad_loctab_num = 335545277;
isc_gfix_db_name = 335740929; isc_gfix_db_name = 335740929;
gds_gfix_db_name = 335740929; gds_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930; isc_gfix_invalid_sw = 335740930;

View File

@ -999,6 +999,7 @@ string CountAggNode::internalPrint(NodePrinter& printer) const
return "CountAggNode"; return "CountAggNode";
} }
//// TODO: Improve count(*) in local tables.
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{ {
AggNode::aggInit(tdbb, request); AggNode::aggInit(tdbb, request);

View File

@ -960,6 +960,7 @@ RseNode* DsqlCompilerScratch::pass1RseIsRecursive(RseNode* input)
} }
} }
else if (nodeIs<ProcedureSourceNode>(*pDstTable) || nodeIs<RelationSourceNode>(*pDstTable)) else if (nodeIs<ProcedureSourceNode>(*pDstTable) || nodeIs<RelationSourceNode>(*pDstTable))
//// TODO: LocalTableSourceNode
{ {
if (pass1RelProcIsRecursive(*pDstTable)) if (pass1RelProcIsRecursive(*pDstTable))
{ {
@ -990,19 +991,18 @@ bool DsqlCompilerScratch::pass1RelProcIsRecursive(RecordSourceNode* input)
{ {
MetaName relName; MetaName relName;
string relAlias; string relAlias;
ProcedureSourceNode* procNode;
RelationSourceNode* relNode;
if ((procNode = nodeAs<ProcedureSourceNode>(input))) if (auto procNode = nodeAs<ProcedureSourceNode>(input))
{ {
relName = procNode->dsqlName.identifier; relName = procNode->dsqlName.identifier;
relAlias = procNode->alias; relAlias = procNode->alias;
} }
else if ((relNode = nodeAs<RelationSourceNode>(input))) else if (auto relNode = nodeAs<RelationSourceNode>(input))
{ {
relName = relNode->dsqlName; relName = relNode->dsqlName;
relAlias = relNode->alias; relAlias = relNode->alias;
} }
//// TODO: LocalTableSourceNode
else else
return false; return false;

View File

@ -36,6 +36,7 @@ namespace Jrd
class BinaryBoolNode; class BinaryBoolNode;
class CompoundStmtNode; class CompoundStmtNode;
class DeclareCursorNode; class DeclareCursorNode;
class DeclareLocalTableNode;
class DeclareVariableNode; class DeclareVariableNode;
class ParameterClause; class ParameterClause;
class RseNode; class RseNode;
@ -44,6 +45,9 @@ class TypeClause;
class VariableNode; class VariableNode;
class WithClause; class WithClause;
typedef Firebird::Pair<
Firebird::NonPooled<NestConst<ValueListNode>, NestConst<ValueListNode>>> ReturningClause;
// DSQL Compiler scratch block - may be discarded after compilation in the future. // DSQL Compiler scratch block - may be discarded after compilation in the future.
class DsqlCompilerScratch : public BlrDebugWriter class DsqlCompilerScratch : public BlrDebugWriter
@ -57,7 +61,6 @@ public:
static const unsigned FLAG_BLOCK = 0x0020; static const unsigned FLAG_BLOCK = 0x0020;
static const unsigned FLAG_RECURSIVE_CTE = 0x0040; static const unsigned FLAG_RECURSIVE_CTE = 0x0040;
static const unsigned FLAG_UPDATE_OR_INSERT = 0x0080; static const unsigned FLAG_UPDATE_OR_INSERT = 0x0080;
static const unsigned FLAG_MERGE = 0x0100;
static const unsigned FLAG_FUNCTION = 0x0200; static const unsigned FLAG_FUNCTION = 0x0200;
static const unsigned FLAG_SUB_ROUTINE = 0x0400; static const unsigned FLAG_SUB_ROUTINE = 0x0400;
static const unsigned FLAG_INTERNAL_REQUEST = 0x0800; static const unsigned FLAG_INTERNAL_REQUEST = 0x0800;
@ -91,6 +94,8 @@ public:
labels(p), labels(p),
cursorNumber(0), cursorNumber(0),
cursors(p), cursors(p),
localTableNumber(0),
localTables(p),
inSelectList(0), inSelectList(0),
inWhereClause(0), inWhereClause(0),
inGroupByClause(0), inGroupByClause(0),
@ -110,6 +115,7 @@ public:
hiddenVariables(p), hiddenVariables(p),
variables(p), variables(p),
outputVariables(p), outputVariables(p),
returningClause(nullptr),
currCteAlias(NULL), currCteAlias(NULL),
ctes(p), ctes(p),
cteAliases(p), cteAliases(p),
@ -283,6 +289,8 @@ public:
Firebird::Stack<MetaName*> labels; // Loop labels Firebird::Stack<MetaName*> labels; // Loop labels
USHORT cursorNumber; // Cursor number USHORT cursorNumber; // Cursor number
Firebird::Array<DeclareCursorNode*> cursors; // Cursors Firebird::Array<DeclareCursorNode*> cursors; // Cursors
USHORT localTableNumber; // Local table number
Firebird::Array<DeclareLocalTableNode*> localTables; // Local tables
USHORT inSelectList; // now processing "select list" USHORT inSelectList; // now processing "select list"
USHORT inWhereClause; // processing "where clause" USHORT inWhereClause; // processing "where clause"
USHORT inGroupByClause; // processing "group by clause" USHORT inGroupByClause; // processing "group by clause"
@ -303,6 +311,7 @@ public:
Firebird::Array<dsql_var*> hiddenVariables; // hidden variables Firebird::Array<dsql_var*> hiddenVariables; // hidden variables
Firebird::Array<dsql_var*> variables; Firebird::Array<dsql_var*> variables;
Firebird::Array<dsql_var*> outputVariables; Firebird::Array<dsql_var*> outputVariables;
ReturningClause* returningClause;
const Firebird::string* const* currCteAlias; const Firebird::string* const* currCteAlias;
private: private:

View File

@ -6085,6 +6085,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec
procNode->dsqlContext = stackContext; procNode->dsqlContext = stackContext;
*list = procNode; *list = procNode;
} }
//// TODO: LocalTableSourceNode
fb_assert(*list); fb_assert(*list);
return NULL; return NULL;
@ -9457,14 +9458,7 @@ ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
static RegisterNode<ParameterNode> regParameterNode({blr_parameter, blr_parameter2, blr_parameter3}); static RegisterNode<ParameterNode> regParameterNode({blr_parameter, blr_parameter2, blr_parameter3});
ParameterNode::ParameterNode(MemoryPool& pool) ParameterNode::ParameterNode(MemoryPool& pool)
: TypedNode<ValueExprNode, ExprNode::TYPE_PARAMETER>(pool), : TypedNode<ValueExprNode, ExprNode::TYPE_PARAMETER>(pool)
dsqlParameter(NULL),
message(NULL),
argFlag(NULL),
argIndicator(NULL),
argInfo(NULL),
dsqlParameterIndex(0),
argNumber(0)
{ {
} }
@ -9536,11 +9530,12 @@ ValueExprNode* ParameterNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
Arg::Gds(isc_dsql_command_err)); Arg::Gds(isc_dsql_command_err));
} }
dsql_msg* tempMsg = dsqlParameter ? auto msg = dsqlMessage ? dsqlMessage :
dsqlParameter->par_message : dsqlScratch->getStatement()->getSendMsg(); dsqlParameter ? dsqlParameter->par_message :
dsqlScratch->getStatement()->getSendMsg();
ParameterNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool()); auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool());
node->dsqlParameter = MAKE_parameter(tempMsg, true, true, dsqlParameterIndex, NULL); node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr);
node->dsqlParameterIndex = dsqlParameterIndex; node->dsqlParameterIndex = dsqlParameterIndex;
return node; return node;
@ -9923,11 +9918,12 @@ ValueExprNode* RecordKeyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
PASS1_ambiguity_check(dsqlScratch, getAlias(true), contexts); PASS1_ambiguity_check(dsqlScratch, getAlias(true), contexts);
RelationSourceNode* relNode = FB_NEW_POOL(dsqlScratch->getPool()) RelationSourceNode( //// TODO: LocalTableSourceNode
auto relNode = FB_NEW_POOL(dsqlScratch->getPool()) RelationSourceNode(
dsqlScratch->getPool()); dsqlScratch->getPool());
relNode->dsqlContext = context; relNode->dsqlContext = context;
RecordKeyNode* node = FB_NEW_POOL(dsqlScratch->getPool()) RecordKeyNode(dsqlScratch->getPool(), blrOp); auto node = FB_NEW_POOL(dsqlScratch->getPool()) RecordKeyNode(dsqlScratch->getPool(), blrOp);
node->dsqlRelation = relNode; node->dsqlRelation = relNode;
return node; return node;
@ -9959,11 +9955,12 @@ ValueExprNode* RecordKeyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
if (context->ctx_flags & CTX_null) if (context->ctx_flags & CTX_null)
return NullNode::instance(); return NullNode::instance();
RelationSourceNode* relNode = FB_NEW_POOL(dsqlScratch->getPool()) RelationSourceNode( //// TODO: LocalTableSourceNode
auto relNode = FB_NEW_POOL(dsqlScratch->getPool()) RelationSourceNode(
dsqlScratch->getPool()); dsqlScratch->getPool());
relNode->dsqlContext = context; relNode->dsqlContext = context;
RecordKeyNode* node = FB_NEW_POOL(dsqlScratch->getPool()) RecordKeyNode(dsqlScratch->getPool(), blrOp); auto node = FB_NEW_POOL(dsqlScratch->getPool()) RecordKeyNode(dsqlScratch->getPool(), blrOp);
node->dsqlRelation = relNode; node->dsqlRelation = relNode;
return node; return node;

View File

@ -1585,13 +1585,14 @@ public:
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
public: public:
dsql_par* dsqlParameter; dsql_msg* dsqlMessage = nullptr;
dsql_par* dsqlParameter = nullptr;
NestConst<MessageNode> message; NestConst<MessageNode> message;
NestConst<ValueExprNode> argFlag; NestConst<ValueExprNode> argFlag;
NestConst<ValueExprNode> argIndicator; NestConst<ValueExprNode> argIndicator;
NestConst<ItemInfo> argInfo; NestConst<ItemInfo> argInfo;
USHORT dsqlParameterIndex; USHORT dsqlParameterIndex = 0;
USHORT argNumber; USHORT argNumber = 0;
}; };

View File

@ -508,6 +508,7 @@ public:
// RecordSource types // RecordSource types
TYPE_AGGREGATE_SOURCE, TYPE_AGGREGATE_SOURCE,
TYPE_LOCAL_TABLE,
TYPE_PROCEDURE, TYPE_PROCEDURE,
TYPE_RELATION, TYPE_RELATION,
TYPE_RSE, TYPE_RSE,
@ -1380,6 +1381,7 @@ public:
TYPE_CONTINUE_LEAVE, TYPE_CONTINUE_LEAVE,
TYPE_CURSOR_STMT, TYPE_CURSOR_STMT,
TYPE_DECLARE_CURSOR, TYPE_DECLARE_CURSOR,
TYPE_DECLARE_LOCAL_TABLE,
TYPE_DECLARE_SUBFUNC, TYPE_DECLARE_SUBFUNC,
TYPE_DECLARE_SUBPROC, TYPE_DECLARE_SUBPROC,
TYPE_DECLARE_VARIABLE, TYPE_DECLARE_VARIABLE,
@ -1412,6 +1414,7 @@ public:
TYPE_STALL, TYPE_STALL,
TYPE_STORE, TYPE_STORE,
TYPE_SUSPEND, TYPE_SUSPEND,
TYPE_TRUNCATE_LOCAL_TABLE,
TYPE_UPDATE_OR_INSERT, TYPE_UPDATE_OR_INSERT,
TYPE_USER_SAVEPOINT, TYPE_USER_SAVEPOINT,

File diff suppressed because it is too large Load Diff

View File

@ -38,13 +38,11 @@ class CompoundStmtNode;
class ExecBlockNode; class ExecBlockNode;
class ForNode; class ForNode;
class PlanNode; class PlanNode;
class RecordBuffer;
class RelationSourceNode; class RelationSourceNode;
class SelectNode; class SelectNode;
class GeneratorItem; class GeneratorItem;
typedef Firebird::Pair<
Firebird::NonPooled<NestConst<ValueListNode>, NestConst<ValueListNode> > > ReturningClause;
class ExceptionItem : public Firebird::PermanentStorage, public Printable class ExceptionItem : public Firebird::PermanentStorage, public Printable
{ {
@ -414,6 +412,45 @@ public:
}; };
class DeclareLocalTableNode : public TypedNode<StmtNode, StmtNode::TYPE_DECLARE_LOCAL_TABLE>
{
public:
struct Impure
{
RecordBuffer* recordBuffer;
};
public:
explicit DeclareLocalTableNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_DECLARE_LOCAL_TABLE>(pool)
{
}
public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
Firebird::string internalPrint(NodePrinter& printer) const override;
DeclareLocalTableNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override;
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
DeclareLocalTableNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
DeclareLocalTableNode* pass1(thread_db* tdbb, CompilerScratch* csb) override
{
return this;
}
DeclareLocalTableNode* pass2(thread_db* tdbb, CompilerScratch* csb) override;
const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override;
public:
Impure* getImpure(thread_db* tdbb, jrd_req* request, bool createWhenDead = true) const;
public:
NestConst<Format> format;
USHORT tableNumber = 0;
};
class DeclareSubFuncNode : public TypedNode<StmtNode, StmtNode::TYPE_DECLARE_SUBFUNC> class DeclareSubFuncNode : public TypedNode<StmtNode, StmtNode::TYPE_DECLARE_SUBFUNC>
{ {
public: public:
@ -1075,38 +1112,42 @@ public:
struct Matched struct Matched
{ {
explicit Matched(MemoryPool& pool) explicit Matched(MemoryPool& pool)
: assignments(NULL), : processedFields(pool),
condition(NULL) processedValues(pool)
{ {
} }
NestConst<CompoundStmtNode> assignments; NestConst<CompoundStmtNode> assignments;
NestConst<BoolExprNode> condition; NestConst<BoolExprNode> condition;
NestConst<Jrd::RecordSourceNode> modifyRelation;
NestValueArray processedFields;
NestValueArray processedValues;
NestConst<ReturningClause> processedReturning;
}; };
struct NotMatched struct NotMatched
{ {
explicit NotMatched(MemoryPool& pool) explicit NotMatched(MemoryPool& pool)
: fields(pool), : fields(pool),
values(NULL), processedFields(pool)
condition(NULL)
{ {
} }
Firebird::Array<NestConst<FieldNode> > fields; Firebird::Array<NestConst<FieldNode>> fields;
NestConst<ValueListNode> values; NestConst<ValueListNode> values;
NestConst<BoolExprNode> condition; NestConst<BoolExprNode> condition;
Nullable<OverrideClause> overrideClause; Nullable<OverrideClause> overrideClause;
NestConst<Jrd::RecordSourceNode> storeRelation;
NestValueArray processedFields;
NestConst<ReturningClause> processedReturning;
}; };
explicit MergeNode(MemoryPool& pool) explicit MergeNode(MemoryPool& pool)
: TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_MERGE>(pool), : TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_MERGE>(pool),
relation(NULL),
usingClause(NULL),
condition(NULL),
whenMatched(pool), whenMatched(pool),
whenNotMatched(pool), whenNotMatched(pool)
returning(NULL)
{ {
} }
@ -1120,7 +1161,12 @@ public:
NestConst<BoolExprNode> condition; NestConst<BoolExprNode> condition;
Firebird::ObjectsArray<Matched> whenMatched; Firebird::ObjectsArray<Matched> whenMatched;
Firebird::ObjectsArray<NotMatched> whenNotMatched; Firebird::ObjectsArray<NotMatched> whenNotMatched;
NestConst<PlanNode> plan;
NestConst<ValueListNode> order;
NestConst<ReturningClause> returning; NestConst<ReturningClause> returning;
NestConst<RseNode> rse;
dsql_ctx* targetContext = nullptr;
}; };
@ -1163,24 +1209,8 @@ class ModifyNode : public TypedNode<StmtNode, StmtNode::TYPE_MODIFY>
public: public:
explicit ModifyNode(MemoryPool& pool) explicit ModifyNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_MODIFY>(pool), : TypedNode<StmtNode, StmtNode::TYPE_MODIFY>(pool),
dsqlRelation(NULL),
dsqlBoolean(NULL),
dsqlPlan(NULL),
dsqlOrder(NULL),
dsqlRows(NULL),
dsqlCursorName(pool), dsqlCursorName(pool),
dsqlReturning(NULL), validations(pool)
dsqlRse(NULL),
dsqlContext(NULL),
statement(NULL),
statement2(NULL),
subMod(NULL),
validations(pool),
mapView(NULL),
orgStream(0),
newStream(0),
marks(0),
dsqlRseFlags(0)
{ {
} }
@ -1208,17 +1238,18 @@ public:
MetaName dsqlCursorName; MetaName dsqlCursorName;
NestConst<ReturningClause> dsqlReturning; NestConst<ReturningClause> dsqlReturning;
NestConst<RecordSourceNode> dsqlRse; NestConst<RecordSourceNode> dsqlRse;
dsql_ctx* dsqlContext; dsql_ctx* dsqlContext = nullptr;
NestConst<StmtNode> statement; NestConst<StmtNode> statement;
NestConst<StmtNode> statement2; NestConst<StmtNode> statement2;
NestConst<StmtNode> subMod; NestConst<StmtNode> subMod;
Firebird::Array<ValidateInfo> validations; Firebird::Array<ValidateInfo> validations;
NestConst<StmtNode> mapView; NestConst<StmtNode> mapView;
NestConst<ForNode> forNode; // parent implicit cursor, if present NestConst<ForNode> forNode; // parent implicit cursor, if present
StreamType orgStream; StreamType orgStream = 0;
StreamType newStream; StreamType newStream = 0;
unsigned marks; // see StmtNode::IUD_MARK_xxx unsigned marks = 0; // see StmtNode::IUD_MARK_xxx
USHORT dsqlRseFlags; USHORT dsqlRseFlags = 0;
Nullable<USHORT> dsqlReturningLocalTableNumber;
}; };
@ -1281,16 +1312,8 @@ class StoreNode : public TypedNode<StmtNode, StmtNode::TYPE_STORE>
public: public:
explicit StoreNode(MemoryPool& pool) explicit StoreNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_STORE>(pool), : TypedNode<StmtNode, StmtNode::TYPE_STORE>(pool),
dsqlRelation(NULL),
dsqlFields(pool), dsqlFields(pool),
dsqlValues(NULL), validations(pool)
dsqlReturning(NULL),
dsqlRse(NULL),
statement(NULL),
statement2(NULL),
subStore(NULL),
validations(pool),
relationSource(NULL)
{ {
} }
@ -1311,8 +1334,8 @@ private:
const StmtNode* store(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const; const StmtNode* store(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const;
public: public:
NestConst<RecordSourceNode> dsqlRelation; NestConst<RecordSourceNode> target;
Firebird::Array<NestConst<FieldNode> > dsqlFields; Firebird::Array<NestConst<FieldNode>> dsqlFields;
NestConst<ValueListNode> dsqlValues; NestConst<ValueListNode> dsqlValues;
NestConst<ReturningClause> dsqlReturning; NestConst<ReturningClause> dsqlReturning;
NestConst<RecordSourceNode> dsqlRse; NestConst<RecordSourceNode> dsqlRse;
@ -1320,7 +1343,7 @@ public:
NestConst<StmtNode> statement2; NestConst<StmtNode> statement2;
NestConst<StmtNode> subStore; NestConst<StmtNode> subStore;
Firebird::Array<ValidateInfo> validations; Firebird::Array<ValidateInfo> validations;
NestConst<RelationSourceNode> relationSource; Nullable<USHORT> dsqlReturningLocalTableNumber;
Nullable<OverrideClause> overrideClause; Nullable<OverrideClause> overrideClause;
}; };
@ -1865,16 +1888,52 @@ public:
}; };
class TruncateLocalTableNode : public TypedNode<StmtNode, StmtNode::TYPE_TRUNCATE_LOCAL_TABLE>
{
public:
explicit TruncateLocalTableNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_TRUNCATE_LOCAL_TABLE>(pool)
{
}
public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
Firebird::string internalPrint(NodePrinter& printer) const override;
TruncateLocalTableNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override
{
return this;
}
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
TruncateLocalTableNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
TruncateLocalTableNode* pass1(thread_db* tdbb, CompilerScratch* csb) override
{
return this;
}
TruncateLocalTableNode* pass2(thread_db* tdbb, CompilerScratch* csb) override
{
return this;
}
const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override;
public:
USHORT tableNumber = 0;
};
class UpdateOrInsertNode : public TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_UPDATE_OR_INSERT> class UpdateOrInsertNode : public TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_UPDATE_OR_INSERT>
{ {
public: public:
explicit UpdateOrInsertNode(MemoryPool& pool) explicit UpdateOrInsertNode(MemoryPool& pool)
: TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_UPDATE_OR_INSERT>(pool), : TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_UPDATE_OR_INSERT>(pool),
relation(NULL),
fields(pool), fields(pool),
values(NULL),
matching(pool), matching(pool),
returning(NULL) varAssignments(pool)
{ {
} }
@ -1884,11 +1943,17 @@ public:
public: public:
NestConst<RelationSourceNode> relation; NestConst<RelationSourceNode> relation;
Firebird::Array<NestConst<FieldNode> > fields; Firebird::Array<NestConst<FieldNode>> fields;
NestConst<ValueListNode> values; NestConst<ValueListNode> values;
Firebird::Array<NestConst<FieldNode> > matching; Firebird::Array<NestConst<FieldNode>> matching;
NestConst<PlanNode> plan;
NestConst<ValueListNode> order;
NestConst<RowsClause> rows;
NestConst<ReturningClause> returning; NestConst<ReturningClause> returning;
Nullable<OverrideClause> overrideClause; Nullable<OverrideClause> overrideClause;
NestConst<StoreNode> storeNode;
NestConst<ModifyNode> modifyNode;
Firebird::Array<NestConst<AssignmentNode>> varAssignments;
}; };

View File

@ -98,6 +98,7 @@ static inline bool reqTypeWithCursor(DsqlCompiledStatement::Type type)
case DsqlCompiledStatement::TYPE_SELECT: case DsqlCompiledStatement::TYPE_SELECT:
case DsqlCompiledStatement::TYPE_SELECT_BLOCK: case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
case DsqlCompiledStatement::TYPE_SELECT_UPD: case DsqlCompiledStatement::TYPE_SELECT_UPD:
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
return true; return true;
} }
@ -2062,6 +2063,7 @@ static void sql_info(thread_db* tdbb,
case DsqlCompiledStatement::TYPE_SELECT: case DsqlCompiledStatement::TYPE_SELECT:
case DsqlCompiledStatement::TYPE_SELECT_UPD: case DsqlCompiledStatement::TYPE_SELECT_UPD:
case DsqlCompiledStatement::TYPE_SELECT_BLOCK: case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
value |= IStatement::FLAG_HAS_CURSOR; value |= IStatement::FLAG_HAS_CURSOR;
break; break;
} }
@ -2075,6 +2077,7 @@ static void sql_info(thread_db* tdbb,
switch (statement->getType()) switch (statement->getType())
{ {
case DsqlCompiledStatement::TYPE_SELECT: case DsqlCompiledStatement::TYPE_SELECT:
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
number = isc_info_sql_stmt_select; number = isc_info_sql_stmt_select;
break; break;
case DsqlCompiledStatement::TYPE_SELECT_UPD: case DsqlCompiledStatement::TYPE_SELECT_UPD:

View File

@ -461,7 +461,8 @@ public:
TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR,
TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS,
TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR,
TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT,
TYPE_RETURNING_CURSOR
}; };
// Statement flags. // Statement flags.

View File

@ -267,6 +267,8 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node)
case DsqlCompiledStatement::TYPE_SELECT_BLOCK: case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
node->genBlr(scratch); node->genBlr(scratch);
break; break;
///case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
default: default:
{ {
dsql_msg* message = statement->getSendMsg(); dsql_msg* message = statement->getSendMsg();
@ -660,3 +662,13 @@ void GEN_stuff_context(DsqlCompilerScratch* dsqlScratch, const dsql_ctx* context
dsqlScratch->appendUChar(context->ctx_recursive); dsqlScratch->appendUChar(context->ctx_recursive);
} }
} }
// Write a context number into the BLR buffer. Check for possible overflow.
void GEN_stuff_context_number(DsqlCompilerScratch* dsqlScratch, USHORT contextNumber)
{
if (contextNumber > MAX_UCHAR)
ERRD_post(Arg::Gds(isc_too_many_contexts));
dsqlScratch->appendUChar(contextNumber);
}

View File

@ -39,5 +39,6 @@ void GEN_request(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*);
void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*); void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*);
void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*); void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*);
void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*); void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*);
void GEN_stuff_context_number(Jrd::DsqlCompilerScratch*, USHORT);
#endif // DSQL_GEN_PROTO_H #endif // DSQL_GEN_PROTO_H

View File

@ -6524,7 +6524,7 @@ insert_start
: INSERT INTO simple_table_name : INSERT INTO simple_table_name
{ {
StoreNode* node = newNode<StoreNode>(); StoreNode* node = newNode<StoreNode>();
node->dsqlRelation = $3; node->target = $3;
$$ = node; $$ = node;
} }
; ;
@ -6559,10 +6559,13 @@ merge
node->usingClause = $5; node->usingClause = $5;
node->condition = $7; node->condition = $7;
} }
merge_when_clause($8) returning_clause merge_when_clause($8)
plan_clause order_clause_opt returning_clause
{ {
MergeNode* node = $$ = $8; MergeNode* node = $$ = $8;
node->returning = $10; node->plan = $10;
node->order = $11;
node->returning = $12;
} }
; ;
@ -6705,12 +6708,16 @@ update_or_insert
node->relation = $5; node->relation = $5;
} }
ins_column_parens_opt(NOTRIAL(&$6->fields)) override_opt VALUES '(' value_or_default_list ')' ins_column_parens_opt(NOTRIAL(&$6->fields)) override_opt VALUES '(' value_or_default_list ')'
update_or_insert_matching_opt(NOTRIAL(&$6->matching)) returning_clause update_or_insert_matching_opt(NOTRIAL(&$6->matching))
plan_clause order_clause_opt rows_clause_optional returning_clause
{ {
UpdateOrInsertNode* node = $$ = $6; UpdateOrInsertNode* node = $$ = $6;
node->overrideClause = $8; node->overrideClause = $8;
node->values = $11; node->values = $11;
node->returning = $14; node->plan = $14;
node->order = $15;
node->rows = $16;
node->returning = $17;
} }
; ;

View File

@ -362,6 +362,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
relation_name = procNode->dsqlName.identifier; relation_name = procNode->dsqlName.identifier;
else if ((relNode = nodeAs<RelationSourceNode>(relationNode))) else if ((relNode = nodeAs<RelationSourceNode>(relationNode)))
relation_name = relNode->dsqlName; relation_name = relNode->dsqlName;
//// TODO: LocalTableSourceNode
else if ((selNode = nodeAs<SelectExprNode>(relationNode))) else if ((selNode = nodeAs<SelectExprNode>(relationNode)))
relation_name = selNode->alias.c_str(); relation_name = selNode->alias.c_str();
@ -683,12 +684,9 @@ void PASS1_check_unique_fields_names(StrArray& names, const CompoundStmtNode* fi
{ {
const char* name = NULL; const char* name = NULL;
const DeclareVariableNode* varNode; if (auto varNode = nodeAs<DeclareVariableNode>(*ptr))
const DeclareCursorNode* cursorNode;
if ((varNode = nodeAs<DeclareVariableNode>(*ptr)))
name = varNode->dsqlDef->name.c_str(); name = varNode->dsqlDef->name.c_str();
else if ((cursorNode = nodeAs<DeclareCursorNode>(*ptr))) else if (auto cursorNode = nodeAs<DeclareCursorNode>(*ptr))
name = cursorNode->dsqlName.c_str(); name = cursorNode->dsqlName.c_str();
else if (nodeAs<DeclareSubProcNode>(*ptr) || nodeAs<DeclareSubFuncNode>(*ptr)) else if (nodeAs<DeclareSubProcNode>(*ptr) || nodeAs<DeclareSubFuncNode>(*ptr))
continue; continue;
@ -1326,12 +1324,10 @@ ValueListNode* PASS1_expand_select_list(DsqlCompilerScratch* dsqlScratch, ValueL
void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, ValueListNode* list, void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, ValueListNode* list,
bool hide_using) bool hide_using)
{ {
RseNode* rseNode;
ProcedureSourceNode* procNode;
RelationSourceNode* relNode;
FieldNode* fieldNode; FieldNode* fieldNode;
if ((rseNode = nodeAs<RseNode>(node))) //// TODO: LocalTableSourceNode
if (auto rseNode = nodeAs<RseNode>(node))
{ {
ValueListNode* sub_items = rseNode->dsqlSelectList; ValueListNode* sub_items = rseNode->dsqlSelectList;
@ -1375,7 +1371,7 @@ void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node,
} }
} }
} }
else if ((procNode = nodeAs<ProcedureSourceNode>(node))) else if (auto procNode = nodeAs<ProcedureSourceNode>(node))
{ {
dsql_ctx* context = procNode->dsqlContext; dsql_ctx* context = procNode->dsqlContext;
@ -1395,7 +1391,7 @@ void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node,
} }
} }
} }
else if ((relNode = nodeAs<RelationSourceNode>(node))) else if (auto relNode = nodeAs<RelationSourceNode>(node))
{ {
dsql_ctx* context = relNode->dsqlContext; dsql_ctx* context = relNode->dsqlContext;
@ -1730,6 +1726,7 @@ RecordSourceNode* PASS1_relation(DsqlCompilerScratch* dsqlScratch, RecordSourceN
procNode->dsqlContext = context; procNode->dsqlContext = context;
return procNode; return procNode;
} }
//// TODO: LocalTableSourceNode
fb_assert(false); fb_assert(false);
return NULL; return NULL;
@ -2928,31 +2925,26 @@ static void remap_streams_to_parent_context(ExprNode* input, dsql_ctx* parent_co
{ {
DEV_BLKCHK(parent_context, dsql_type_ctx); DEV_BLKCHK(parent_context, dsql_type_ctx);
RecSourceListNode* listNode; if (auto listNode = nodeAs<RecSourceListNode>(input))
ProcedureSourceNode* procNode;
RelationSourceNode* relNode;
RseNode* rseNode;
UnionSourceNode* unionNode;
if ((listNode = nodeAs<RecSourceListNode>(input)))
{ {
NestConst<RecordSourceNode>* ptr = listNode->items.begin(); NestConst<RecordSourceNode>* ptr = listNode->items.begin();
for (const NestConst<RecordSourceNode>* const end = listNode->items.end(); ptr != end; ++ptr) for (const NestConst<RecordSourceNode>* const end = listNode->items.end(); ptr != end; ++ptr)
remap_streams_to_parent_context(*ptr, parent_context); remap_streams_to_parent_context(*ptr, parent_context);
} }
else if ((procNode = nodeAs<ProcedureSourceNode>(input))) else if (auto procNode = nodeAs<ProcedureSourceNode>(input))
{ {
DEV_BLKCHK(procNode->dsqlContext, dsql_type_ctx); DEV_BLKCHK(procNode->dsqlContext, dsql_type_ctx);
procNode->dsqlContext->ctx_parent = parent_context; procNode->dsqlContext->ctx_parent = parent_context;
} }
else if ((relNode = nodeAs<RelationSourceNode>(input))) else if (auto relNode = nodeAs<RelationSourceNode>(input))
{ {
DEV_BLKCHK(relNode->dsqlContext, dsql_type_ctx); DEV_BLKCHK(relNode->dsqlContext, dsql_type_ctx);
relNode->dsqlContext->ctx_parent = parent_context; relNode->dsqlContext->ctx_parent = parent_context;
} }
else if ((rseNode = nodeAs<RseNode>(input))) //// TODO: LocalTableSourceNode
else if (auto rseNode = nodeAs<RseNode>(input))
remap_streams_to_parent_context(rseNode->dsqlStreams, parent_context); remap_streams_to_parent_context(rseNode->dsqlStreams, parent_context);
else if ((unionNode = nodeAs<UnionSourceNode>(input))) else if (auto unionNode = nodeAs<UnionSourceNode>(input))
remap_streams_to_parent_context(unionNode->dsqlClauses, parent_context); remap_streams_to_parent_context(unionNode->dsqlClauses, parent_context);
else else
fb_assert(nodeAs<AggregateSourceNode>(input)); fb_assert(nodeAs<AggregateSourceNode>(input));

View File

@ -451,4 +451,12 @@
#define blr_marks (unsigned char) 217 // mark some blr code with specific flags #define blr_marks (unsigned char) 217 // mark some blr code with specific flags
#define blr_dcl_local_table (unsigned char) 218
// subcodes of blr_dcl_local_table
#define blr_dcl_local_table_format (unsigned char) 1
#define blr_local_table_truncate (unsigned char) 219
#define blr_local_table_id (unsigned char) 220
#endif // FIREBIRD_IMPL_BLR_H #endif // FIREBIRD_IMPL_BLR_H

View File

@ -769,6 +769,7 @@
#define fb_dbg_subproc 5 #define fb_dbg_subproc 5
#define fb_dbg_subfunc 6 #define fb_dbg_subfunc 6
#define fb_dbg_map_curname 7 #define fb_dbg_map_curname 7
//// TODO: LocalTable name.
// sub code for fb_dbg_map_argument // sub code for fb_dbg_map_argument
#define fb_dbg_arg_input 0 #define fb_dbg_arg_input 0

View File

@ -5135,6 +5135,7 @@ const
isc_tom_key_length = 335545274; isc_tom_key_length = 335545274;
isc_inf_invalid_args = 335545275; isc_inf_invalid_args = 335545275;
isc_sysf_invalid_null_empty = 335545276; isc_sysf_invalid_null_empty = 335545276;
isc_bad_loctab_num = 335545276;
isc_gfix_db_name = 335740929; isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930; isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932; isc_gfix_incmp_sw = 335740932;

View File

@ -978,6 +978,7 @@ static const struct {
{"tom_key_length", 335545274}, {"tom_key_length", 335545274},
{"inf_invalid_args", 335545275}, {"inf_invalid_args", 335545275},
{"sysf_invalid_null_empty", 335545276}, {"sysf_invalid_null_empty", 335545276},
{"bad_loctab_num", 335545277},
{"gfix_db_name", 335740929}, {"gfix_db_name", 335740929},
{"gfix_invalid_sw", 335740930}, {"gfix_invalid_sw", 335740930},
{"gfix_incmp_sw", 335740932}, {"gfix_incmp_sw", 335740932},

View File

@ -1012,6 +1012,7 @@ const ISC_STATUS isc_block_size = 335545273L;
const ISC_STATUS isc_tom_key_length = 335545274L; const ISC_STATUS isc_tom_key_length = 335545274L;
const ISC_STATUS isc_inf_invalid_args = 335545275L; const ISC_STATUS isc_inf_invalid_args = 335545275L;
const ISC_STATUS isc_sysf_invalid_null_empty = 335545276L; const ISC_STATUS isc_sysf_invalid_null_empty = 335545276L;
const ISC_STATUS isc_bad_loctab_num = 335545277L;
const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_db_name = 335740929L;
const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
const ISC_STATUS isc_gfix_incmp_sw = 335740932L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
@ -1504,7 +1505,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
const ISC_STATUS isc_trace_switch_param_miss = 337182758L; const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L; const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L; const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
const ISC_STATUS isc_err_max = 1448; const ISC_STATUS isc_err_max = 1449;
#else /* c definitions */ #else /* c definitions */
@ -2486,6 +2487,7 @@ const ISC_STATUS isc_err_max = 1448;
#define isc_tom_key_length 335545274L #define isc_tom_key_length 335545274L
#define isc_inf_invalid_args 335545275L #define isc_inf_invalid_args 335545275L
#define isc_sysf_invalid_null_empty 335545276L #define isc_sysf_invalid_null_empty 335545276L
#define isc_bad_loctab_num 335545277L
#define isc_gfix_db_name 335740929L #define isc_gfix_db_name 335740929L
#define isc_gfix_invalid_sw 335740930L #define isc_gfix_invalid_sw 335740930L
#define isc_gfix_incmp_sw 335740932L #define isc_gfix_incmp_sw 335740932L
@ -2978,7 +2980,7 @@ const ISC_STATUS isc_err_max = 1448;
#define isc_trace_switch_param_miss 337182758L #define isc_trace_switch_param_miss 337182758L
#define isc_trace_param_act_notcompat 337182759L #define isc_trace_param_act_notcompat 337182759L
#define isc_trace_mandatory_switch_miss 337182760L #define isc_trace_mandatory_switch_miss 337182760L
#define isc_err_max 1448 #define isc_err_max 1449
#endif #endif

View File

@ -981,6 +981,7 @@ Data source : @4"}, /* eds_statement */
{335545274, "Invalid key length @1, need >@2"}, /* tom_key_length */ {335545274, "Invalid key length @1, need >@2"}, /* tom_key_length */
{335545275, "Invalid information arguments"}, /* inf_invalid_args */ {335545275, "Invalid information arguments"}, /* inf_invalid_args */
{335545276, "Empty or NULL parameter @1 is not accepted"}, /* sysf_invalid_null_empty */ {335545276, "Empty or NULL parameter @1 is not accepted"}, /* sysf_invalid_null_empty */
{335545277, "Undefined local table number @1"}, /* bad_loctab_num */
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */ {335740929, "data base file name (@1) already given"}, /* gfix_db_name */
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */ {335740930, "invalid switch @1"}, /* gfix_invalid_sw */
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */ {335740932, "incompatible switch combination"}, /* gfix_incmp_sw */

View File

@ -977,6 +977,7 @@ static const struct {
{335545274, -901}, /* 954 tom_key_length */ {335545274, -901}, /* 954 tom_key_length */
{335545275, -901}, /* 955 inf_invalid_args */ {335545275, -901}, /* 955 inf_invalid_args */
{335545276, -901}, /* 956 sysf_invalid_null_empty */ {335545276, -901}, /* 956 sysf_invalid_null_empty */
{335545277, -901}, /* 957 bad_loctab_num */
{335740929, -901}, /* 1 gfix_db_name */ {335740929, -901}, /* 1 gfix_db_name */
{335740930, -901}, /* 2 gfix_invalid_sw */ {335740930, -901}, /* 2 gfix_invalid_sw */
{335740932, -901}, /* 4 gfix_incmp_sw */ {335740932, -901}, /* 4 gfix_incmp_sw */

View File

@ -977,6 +977,7 @@ static const struct {
{335545274, "22023"}, // 954 tom_key_length {335545274, "22023"}, // 954 tom_key_length
{335545275, "HY000"}, // 955 inf_invalid_args {335545275, "HY000"}, // 955 inf_invalid_args
{335545276, "22023"}, // 956 sysf_invalid_null_empty {335545276, "22023"}, // 956 sysf_invalid_null_empty
{335545277, "HY000"}, // 957 bad_loctab_num
{335740929, "00000"}, // 1 gfix_db_name {335740929, "00000"}, // 1 gfix_db_name
{335740930, "00000"}, // 2 gfix_invalid_sw {335740930, "00000"}, // 2 gfix_invalid_sw
{335740932, "00000"}, // 4 gfix_incmp_sw {335740932, "00000"}, // 4 gfix_incmp_sw

View File

@ -71,6 +71,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
parentStatement(NULL), parentStatement(NULL),
subStatements(*p), subStatements(*p),
fors(*p), fors(*p),
localTables(*p),
invariants(*p), invariants(*p),
blr(*p), blr(*p),
mapFieldInfo(*p), mapFieldInfo(*p),
@ -155,6 +156,8 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
// make a vector of all used RSEs // make a vector of all used RSEs
fors = csb->csb_fors; fors = csb->csb_fors;
localTables = csb->csb_localTables;
// make a vector of all invariant-type nodes, so that we will // make a vector of all invariant-type nodes, so that we will
// be able to easily reinitialize them when we restart the request // be able to easily reinitialize them when we restart the request
invariants.join(csb->csb_invariants); invariants.join(csb->csb_invariants);

View File

@ -81,6 +81,7 @@ public:
Firebird::Array<JrdStatement*> subStatements; // Array of subroutines' statements Firebird::Array<JrdStatement*> subStatements; // Array of subroutines' statements
const StmtNode* topNode; // top of execution tree const StmtNode* topNode; // top of execution tree
Firebird::Array<const RecordSource*> fors; // record sources Firebird::Array<const RecordSource*> fors; // record sources
Firebird::Array<const DeclareLocalTableNode*> localTables; // local tables
Firebird::Array<ULONG*> invariants; // pointer to nodes invariant offsets Firebird::Array<ULONG*> invariants; // pointer to nodes invariant offsets
Firebird::RefStrPtr sqlText; // SQL text (encoded in the metadata charset) Firebird::RefStrPtr sqlText; // SQL text (encoded in the metadata charset)
Firebird::Array<UCHAR> blr; // BLR for non-SQL query Firebird::Array<UCHAR> blr; // BLR for non-SQL query

View File

@ -187,6 +187,7 @@ namespace Jrd
alias = csb_tail->csb_relation->rel_name.c_str(); alias = csb_tail->csb_relation->rel_name.c_str();
else if (csb_tail->csb_procedure) else if (csb_tail->csb_procedure)
alias = csb_tail->csb_procedure->getName().toString(); alias = csb_tail->csb_procedure->getName().toString();
//// TODO: LocalTableSourceNode
else else
fb_assert(false); fb_assert(false);

View File

@ -256,6 +256,7 @@ PlanNode* PlanNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
procNode->dsqlContext = context; procNode->dsqlContext = context;
node->dsqlRecordSourceNode = procNode; node->dsqlRecordSourceNode = procNode;
} }
//// TODO: LocalTableSourceNode
// ASF: I think it's a error to let node->dsqlRecordSourceNode be NULL here, but it happens // ASF: I think it's a error to let node->dsqlRecordSourceNode be NULL here, but it happens
// at least since v2.5. See gen.cpp/gen_plan for more information. // at least since v2.5. See gen.cpp/gen_plan for more information.
@ -453,6 +454,144 @@ RecSourceListNode* RecSourceListNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
//-------------------- //--------------------
// Parse a local table reference.
LocalTableSourceNode* LocalTableSourceNode::parse(thread_db* tdbb, CompilerScratch* csb,
const SSHORT blrOp, bool parseContext)
{
const USHORT tableNumber = csb->csb_blr_reader.getWord();
if (tableNumber >= csb->csb_localTables.getCount())
PAR_error(csb, Arg::Gds(isc_bad_loctab_num) << Arg::Num(tableNumber));
// Make a relation reference node
const auto node = FB_NEW_POOL(*tdbb->getDefaultPool()) LocalTableSourceNode(
*tdbb->getDefaultPool());
node->tableNumber = tableNumber;
AutoPtr<string> aliasString(FB_NEW_POOL(csb->csb_pool) string(csb->csb_pool));
csb->csb_blr_reader.getString(*aliasString);
if (aliasString->hasData())
node->alias = *aliasString;
else
aliasString.reset();
// generate a stream for the relation reference, assuming it is a real reference
if (parseContext)
{
node->stream = PAR_context(csb, &node->context);
if (tableNumber >= csb->csb_localTables.getCount() || !csb->csb_localTables[tableNumber])
PAR_error(csb, Arg::Gds(isc_bad_loctab_num) << Arg::Num(tableNumber));
csb->csb_rpt[node->stream].csb_format = csb->csb_localTables[tableNumber]->format;
csb->csb_rpt[node->stream].csb_alias = aliasString.release();
}
return node;
}
string LocalTableSourceNode::internalPrint(NodePrinter& printer) const
{
RecordSourceNode::internalPrint(printer);
NODE_PRINT(printer, alias);
NODE_PRINT(printer, tableNumber);
NODE_PRINT(printer, context);
return "LocalTableSourceNode";
}
RecordSourceNode* LocalTableSourceNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
{
return dsqlPassRelProc(dsqlScratch, this);
}
bool LocalTableSourceNode::dsqlMatch(DsqlCompilerScratch* /*dsqlScratch*/, const ExprNode* other,
bool /*ignoreMapCast*/) const
{
const auto o = nodeAs<LocalTableSourceNode>(other);
return o && dsqlContext == o->dsqlContext;
}
void LocalTableSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{
dsqlScratch->appendUChar(blr_local_table_id);
dsqlScratch->appendUShort(tableNumber);
dsqlScratch->appendMetaString(alias.c_str()); // dsqlContext->ctx_alias?
GEN_stuff_context(dsqlScratch, dsqlContext);
}
LocalTableSourceNode* LocalTableSourceNode::copy(thread_db* tdbb, NodeCopier& copier) const
{
if (!copier.remap)
BUGCHECK(221); // msg 221 (CMP) copy: cannot remap
const auto newSource = FB_NEW_POOL(*tdbb->getDefaultPool()) LocalTableSourceNode(
*tdbb->getDefaultPool());
newSource->stream = copier.csb->nextStream();
copier.remap[stream] = newSource->stream;
newSource->context = context;
if (tableNumber >= copier.csb->csb_localTables.getCount() || !copier.csb->csb_localTables[tableNumber])
ERR_post(Arg::Gds(isc_bad_loctab_num) << Arg::Num(tableNumber));
const auto element = CMP_csb_element(copier.csb, newSource->stream);
element->csb_format = copier.csb->csb_localTables[tableNumber]->format;
element->csb_view_stream = copier.remap[0];
if (alias.hasData())
{
element->csb_alias = FB_NEW_POOL(*tdbb->getDefaultPool())
string(*tdbb->getDefaultPool(), alias);
}
return newSource;
}
void LocalTableSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* /*rse*/,
BoolExprNode** /*boolean*/, RecordSourceNodeStack& stack)
{
fb_assert(!csb->csb_view); // local tables cannot be inside a view
stack.push(this); // Assume that the source will be used. Push it on the final stream stack.
pass1(tdbb, csb);
}
void LocalTableSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb)
{
csb->csb_rpt[stream].activate();
pass2(tdbb, csb);
}
RecordSource* LocalTableSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/)
{
opt->beds.add(stream);
opt->localStreams.add(stream);
const auto csb = opt->opt_csb;
if (tableNumber >= opt->opt_csb->csb_localTables.getCount() || !opt->opt_csb->csb_localTables[tableNumber])
ERR_post(Arg::Gds(isc_bad_loctab_num) << Arg::Num(tableNumber));
auto localTable = csb->csb_localTables[tableNumber];
return FB_NEW_POOL(*tdbb->getDefaultPool()) LocalTableStream(csb, stream, localTable);
}
//--------------------
// Parse a relation reference. // Parse a relation reference.
RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* csb, RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* csb,
const SSHORT blrOp, bool parseContext) const SSHORT blrOp, bool parseContext)
@ -3345,26 +3484,24 @@ RseNode* SelectExprNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* source) static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* source)
{ {
ProcedureSourceNode* procNode = nodeAs<ProcedureSourceNode>(source);
RelationSourceNode* relNode = nodeAs<RelationSourceNode>(source);
fb_assert(procNode || relNode);
bool couldBeCte = true; bool couldBeCte = true;
MetaName relName; MetaName relName;
string relAlias; string relAlias;
if (procNode) if (auto procNode = nodeAs<ProcedureSourceNode>(source))
{ {
relName = procNode->dsqlName.identifier; relName = procNode->dsqlName.identifier;
relAlias = procNode->alias; relAlias = procNode->alias;
couldBeCte = !procNode->sourceList && procNode->dsqlName.package.isEmpty(); couldBeCte = !procNode->sourceList && procNode->dsqlName.package.isEmpty();
} }
else if (relNode) else if (auto relNode = nodeAs<RelationSourceNode>(source))
{ {
relName = relNode->dsqlName; relName = relNode->dsqlName;
relAlias = relNode->alias; relAlias = relNode->alias;
} }
//// TODO: LocalTableSourceNode
else
fb_assert(false);
if (relAlias.isEmpty()) if (relAlias.isEmpty())
relAlias = relName.c_str(); relAlias = relName.c_str();

View File

@ -283,6 +283,75 @@ public:
}; };
class LocalTableSourceNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_LOCAL_TABLE>
{
public:
explicit LocalTableSourceNode(MemoryPool& pool, const MetaName& aDsqlName = NULL)
: TypedNode<RecordSourceNode, RecordSourceNode::TYPE_LOCAL_TABLE>(pool),
alias(pool)
{
}
static LocalTableSourceNode* parse(thread_db* tdbb, CompilerScratch* csb, const SSHORT blrOp,
bool parseContext);
Firebird::string internalPrint(NodePrinter& printer) const override;
RecordSourceNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override;
bool dsqlSubSelectFinder(SubSelectFinder& /*visitor*/) override
{
return false;
}
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
LocalTableSourceNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
RecordSourceNode* pass1(thread_db* tdbb, CompilerScratch* csb) override
{
return this;
}
void pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse,
BoolExprNode** boolean, RecordSourceNodeStack& stack) override;
RecordSourceNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override
{
return this;
}
void pass2Rse(thread_db* tdbb, CompilerScratch* csb) override;
bool containsStream(StreamType checkStream) const override
{
return checkStream == stream;
}
void computeDbKeyStreams(StreamList& streamList) const override
{
streamList.add(getStream());
}
bool computable(CompilerScratch* /*csb*/, StreamType /*stream*/,
bool /*allowOnlyCurrentStream*/, ValueExprNode* /*value*/) override
{
return true;
}
void findDependentFromStreams(const OptimizerRetrieval* /*optRet*/,
SortedStreamList* /*streamList*/) override
{
}
RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream) override;
public:
Firebird::string alias;
USHORT tableNumber = 0;
SSHORT context = 0; // user-specified context number for the local table reference
};
class RelationSourceNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_RELATION> class RelationSourceNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_RELATION>
{ {
public: public:

View File

@ -51,7 +51,7 @@ static const struct
{"field", field}, {"field", field},
{"fid", parm}, {"fid", parm},
{"parameter", parm}, {"parameter", parm},
{"variable", variable}, {"variable", one_word},
{"average", two}, {"average", two},
{"count", one}, {"count", one},
{"maximum", two}, {"maximum", two},
@ -213,7 +213,7 @@ static const struct
{"strlen", strlength}, {"strlen", strlength},
{"trim", trim}, {"trim", trim},
// New BLR in FB2.1 // New BLR in FB2.1
{"init_variable", variable}, {"init_variable", one_word},
{"recurse", union_ops}, {"recurse", union_ops},
{"sys_function", function}, {"sys_function", function},
// New BLR in FB2.5 // New BLR in FB2.5
@ -248,5 +248,9 @@ static const struct
{"local_time", byte_line}, {"local_time", byte_line},
{"at", verb_byte_verb}, {"at", verb_byte_verb},
{"marks", marks}, {"marks", marks},
// New BLR in FB5.0
{"dcl_local_table", dcl_local_table},
{"local_table_truncate", one_word},
{"local_table_id", local_table},
{0, 0} {0, 0}
}; };

View File

@ -257,18 +257,13 @@ const Format* CMP_format(thread_db* tdbb, CompilerScratch* csb, StreamType strea
if (!tail->csb_format) if (!tail->csb_format)
{ {
if (tail->csb_relation) if (tail->csb_relation)
{
tail->csb_format = MET_current(tdbb, tail->csb_relation); tail->csb_format = MET_current(tdbb, tail->csb_relation);
}
else if (tail->csb_procedure) else if (tail->csb_procedure)
{
tail->csb_format = tail->csb_procedure->prc_record_format; tail->csb_format = tail->csb_procedure->prc_record_format;
} //// TODO: LocalTableSourceNode
else else
{
IBERROR(222); // msg 222 bad blr - invalid stream IBERROR(222); // msg 222 bad blr - invalid stream
} }
}
fb_assert(tail->csb_format); fb_assert(tail->csb_format);
return tail->csb_format; return tail->csb_format;

View File

@ -982,6 +982,17 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request)
tdbb->setRequest(old_request); tdbb->setRequest(old_request);
tdbb->setTransaction(old_transaction); tdbb->setTransaction(old_transaction);
} }
for (auto localTable : statement->localTables)
{
if (!localTable)
continue;
auto impure = localTable->getImpure(tdbb, request, false);
delete impure->recordBuffer;
impure->recordBuffer = nullptr;
}
release_blobs(tdbb, request); release_blobs(tdbb, request);
} }

View File

@ -448,7 +448,7 @@ public:
csb_resources(p), csb_resources(p),
csb_dependencies(p), csb_dependencies(p),
csb_fors(p), csb_fors(p),
csb_cursors(p), csb_localTables(p),
csb_invariants(p), csb_invariants(p),
csb_current_nodes(p), csb_current_nodes(p),
csb_current_for_nodes(p), csb_current_for_nodes(p),
@ -511,7 +511,7 @@ public:
ResourceList csb_resources; // Resources (relations and indexes) ResourceList csb_resources; // Resources (relations and indexes)
Firebird::Array<Dependency> csb_dependencies; // objects that this statement depends upon Firebird::Array<Dependency> csb_dependencies; // objects that this statement depends upon
Firebird::Array<const RecordSource*> csb_fors; // record sources Firebird::Array<const RecordSource*> csb_fors; // record sources
Firebird::Array<const Cursor*> csb_cursors; // named cursors Firebird::Array<const DeclareLocalTableNode*> csb_localTables; // local tables
Firebird::Array<ULONG*> csb_invariants; // stack of pointer to nodes invariant offsets Firebird::Array<ULONG*> csb_invariants; // stack of pointer to nodes invariant offsets
Firebird::Array<ExprNode*> csb_current_nodes; // RseNode's and other invariant Firebird::Array<ExprNode*> csb_current_nodes; // RseNode's and other invariant
// candidates within whose scope we are // candidates within whose scope we are

View File

@ -548,6 +548,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql)
switch (statement->getType()) switch (statement->getType())
{ {
case DsqlCompiledStatement::TYPE_SELECT: case DsqlCompiledStatement::TYPE_SELECT:
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
case DsqlCompiledStatement::TYPE_SELECT_UPD: case DsqlCompiledStatement::TYPE_SELECT_UPD:
case DsqlCompiledStatement::TYPE_SELECT_BLOCK: case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
m_stmt_selectable = true; m_stmt_selectable = true;

View File

@ -1281,7 +1281,7 @@ static void check_sorts(CompilerScratch* csb, RseNode* rse)
{ {
RecordSourceNode* subNode = new_rse->rse_relations[i]; RecordSourceNode* subNode = new_rse->rse_relations[i];
if (nodeIs<RelationSourceNode>(subNode) && if ((nodeIs<RelationSourceNode>(subNode) || nodeIs<LocalTableSourceNode>(subNode)) &&
subNode->getStream() == sort_stream && subNode->getStream() == sort_stream &&
new_rse != rse) new_rse != rse)
{ {
@ -1289,7 +1289,6 @@ static void check_sorts(CompilerScratch* csb, RseNode* rse)
sortStreamFound = true; sortStreamFound = true;
break; break;
} }
} }
if (sortStreamFound) if (sortStreamFound)
@ -1309,7 +1308,7 @@ static void check_sorts(CompilerScratch* csb, RseNode* rse)
} }
else else
{ {
if (nodeIs<RelationSourceNode>(node) && if ((nodeIs<RelationSourceNode>(node) || nodeIs<LocalTableSourceNode>(node)) &&
node->getStream() == sort_stream && node->getStream() == sort_stream &&
new_rse && new_rse != rse) new_rse && new_rse != rse)
{ {
@ -2090,7 +2089,7 @@ static RecordSource* gen_outer(thread_db* tdbb, OptimizerBlk* opt, RseNode* rse,
{ {
const RecordSourceNode* node = rse->rse_relations[i]; const RecordSourceNode* node = rse->rse_relations[i];
if (nodeIs<RelationSourceNode>(node)) if (nodeIs<RelationSourceNode>(node) || nodeIs<LocalTableSourceNode>(node))
{ {
stream_ptr[i]->stream_rsb = NULL; stream_ptr[i]->stream_rsb = NULL;
stream_ptr[i]->stream_num = node->getStream(); stream_ptr[i]->stream_num = node->getStream();

View File

@ -180,7 +180,8 @@ namespace
if (relationNode->getKind() != DmlNode::KIND_REC_SOURCE) if (relationNode->getKind() != DmlNode::KIND_REC_SOURCE)
PAR_syntax_error(csb, "TABLE"); PAR_syntax_error(csb, "TABLE");
RelationSourceNode* relationSource = nodeAs<RelationSourceNode>( //// TODO: LocalTableSourceNode
auto relationSource = nodeAs<RelationSourceNode>(
static_cast<RecordSourceNode*>(relationNode)); static_cast<RecordSourceNode*>(relationNode));
if (!relationSource) if (!relationSource)
@ -1011,6 +1012,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
// in which case the base relation (and alias) must be specified // in which case the base relation (and alias) must be specified
USHORT n = (unsigned int) csb->csb_blr_reader.getByte(); USHORT n = (unsigned int) csb->csb_blr_reader.getByte();
//// TODO: LocalTableSourceNode (blr_local_table_id)
if (n != blr_relation && n != blr_relation2 && n != blr_rid && n != blr_rid2) if (n != blr_relation && n != blr_relation2 && n != blr_rid && n != blr_rid2)
PAR_syntax_error(csb, "TABLE"); PAR_syntax_error(csb, "TABLE");
@ -1018,6 +1020,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
// this would add a new context; while this is a reference to // this would add a new context; while this is a reference to
// an existing context // an existing context
//// TODO: LocalTableSourceNode
plan->relationNode = RelationSourceNode::parse(tdbb, csb, n, false); plan->relationNode = RelationSourceNode::parse(tdbb, csb, n, false);
jrd_rel* relation = plan->relationNode->relation; jrd_rel* relation = plan->relationNode->relation;
@ -1297,6 +1300,9 @@ RecordSourceNode* PAR_parseRecordSource(thread_db* tdbb, CompilerScratch* csb)
case blr_rid2: case blr_rid2:
return RelationSourceNode::parse(tdbb, csb, blrOp, true); return RelationSourceNode::parse(tdbb, csb, blrOp, true);
case blr_local_table_id:
return LocalTableSourceNode::parse(tdbb, csb, blrOp, true);
case blr_union: case blr_union:
case blr_recurse: case blr_recurse:
return UnionSourceNode::parse(tdbb, csb, blrOp); return UnionSourceNode::parse(tdbb, csb, blrOp);
@ -1622,6 +1628,7 @@ DmlNode* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb)
case blr_rid: case blr_rid:
case blr_relation2: case blr_relation2:
case blr_rid2: case blr_rid2:
case blr_local_table_id:
case blr_union: case blr_union:
case blr_recurse: case blr_recurse:
case blr_window: case blr_window:

View File

@ -59,7 +59,6 @@ namespace Jrd
bool irsb_active; bool irsb_active;
State irsb_state; State irsb_state;
FB_UINT64 irsb_position; FB_UINT64 irsb_position;
RecordBuffer* irsb_buffer;
}; };
public: public:

View File

@ -0,0 +1,128 @@
/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Adriano dos Santos Fernandes
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2021 Adriano dos Santos Fernandes <adrianosf@gmail.com>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "../jrd/align.h"
#include "../jrd/jrd.h"
#include "../jrd/req.h"
#include "../dsql/StmtNodes.h"
#include "RecordSource.h"
using namespace Firebird;
using namespace Jrd;
// ------------------------
// Data access: local table
// ------------------------
LocalTableStream::LocalTableStream(CompilerScratch* csb, StreamType stream, const DeclareLocalTableNode* table)
: RecordStream(csb, stream),
m_table(table)
{
fb_assert(m_table);
m_impure = csb->allocImpure<Impure>();
}
void LocalTableStream::open(thread_db* tdbb) const
{
const auto request = tdbb->getRequest();
const auto impure = request->getImpure<Impure>(m_impure);
impure->irsb_flags = irsb_open;
const auto rpb = &request->req_rpb[m_stream];
rpb->getWindow(tdbb).win_flags = 0;
rpb->rpb_number.setValue(BOF_NUMBER);
}
void LocalTableStream::close(thread_db* tdbb) const
{
const auto request = tdbb->getRequest();
invalidateRecords(request);
const auto impure = request->getImpure<Impure>(m_impure);
if (impure->irsb_flags & irsb_open)
impure->irsb_flags &= ~irsb_open;
}
bool LocalTableStream::getRecord(thread_db* tdbb) const
{
JRD_reschedule(tdbb);
const auto request = tdbb->getRequest();
const auto rpb = &request->req_rpb[m_stream];
const auto impure = request->getImpure<Impure>(m_impure);
if (!(impure->irsb_flags & irsb_open))
{
rpb->rpb_number.setValid(false);
return false;
}
if (!rpb->rpb_record)
rpb->rpb_record = FB_NEW_POOL(*tdbb->getDefaultPool()) Record(*tdbb->getDefaultPool(), m_format);
rpb->rpb_number.increment();
if (!m_table->getImpure(tdbb, request)->recordBuffer->fetch(rpb->rpb_number.getValue(), rpb->rpb_record))
{
rpb->rpb_number.setValid(false);
return false;
}
return true;
}
bool LocalTableStream::refetchRecord(thread_db* tdbb) const
{
return true;
}
bool LocalTableStream::lockRecord(thread_db* tdbb) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
return false; // compiler silencer
}
void LocalTableStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const
{
//// TODO: Use Local Table name/alias.
if (detailed)
plan += printIndent(++level) + "Local Table Full Scan";
else
{
if (!level)
plan += "(";
plan += "Local_Table";
plan += " NATURAL";
if (!level)
plan += ")";
}
}

View File

@ -39,6 +39,7 @@ namespace Jrd
class jrd_prc; class jrd_prc;
class AggNode; class AggNode;
class BoolExprNode; class BoolExprNode;
class DeclareLocalTableNode;
class Sort; class Sort;
class CompilerScratch; class CompilerScratch;
class RecordBuffer; class RecordBuffer;
@ -1145,6 +1146,24 @@ namespace Jrd
Firebird::Array<const NestValueArray*> m_keys; Firebird::Array<const NestValueArray*> m_keys;
}; };
class LocalTableStream : public RecordStream
{
public:
LocalTableStream(CompilerScratch* csb, StreamType stream, const DeclareLocalTableNode* table);
void open(thread_db* tdbb) const override;
void close(thread_db* tdbb) const override;
bool getRecord(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
bool lockRecord(thread_db* tdbb) const override;
void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level) const override;
private:
const DeclareLocalTableNode* m_table;
};
class Union : public RecordStream class Union : public RecordStream
{ {
struct Impure : public RecordSource::Impure struct Impure : public RecordSource::Impure

View File

@ -1,7 +1,7 @@
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
-- --
('2021-05-11 14:10:00', 'JRD', 0, 957) ('2021-08-25 07:16:00', 'JRD', 0, 958)
--('2015-03-17 18:33:00', 'QLI', 1, 533) --('2015-03-17 18:33:00', 'QLI', 1, 533)
('2018-03-17 12:00:00', 'GFIX', 3, 136) ('2018-03-17 12:00:00', 'GFIX', 3, 136)
('1996-11-07 13:39:40', 'GPRE', 4, 1) ('1996-11-07 13:39:40', 'GPRE', 4, 1)

View File

@ -1064,6 +1064,7 @@ Data source : @4', NULL, NULL)
('tom_key_length', NULL, 'SysFunction.cpp', NULL, 0, 954, NULL, 'Invalid key length @1, need >@2', NULL, NULL); ('tom_key_length', NULL, 'SysFunction.cpp', NULL, 0, 954, NULL, 'Invalid key length @1, need >@2', NULL, NULL);
('inf_invalid_args', NULL, NULL, NULL, 0, 955, NULL, 'Invalid information arguments', NULL, NULL); ('inf_invalid_args', NULL, NULL, NULL, 0, 955, NULL, 'Invalid information arguments', NULL, NULL);
('sysf_invalid_null_empty', NULL, 'SysFunction.cpp', NULL, 0, 956, NULL, 'Empty or NULL parameter @1 is not accepted', NULL, NULL); ('sysf_invalid_null_empty', NULL, 'SysFunction.cpp', NULL, 0, 956, NULL, 'Empty or NULL parameter @1 is not accepted', NULL, NULL);
('bad_loctab_num', NULL, NULL, NULL, 0, 957, NULL, 'Undefined local table number @1', NULL, NULL);
-- GFIX -- GFIX
('gfix_db_name', 'ALICE_gfix', 'alice.c', NULL, 3, 1, NULL, 'data base file name (@1) already given', NULL, NULL); ('gfix_db_name', 'ALICE_gfix', 'alice.c', NULL, 3, 1, NULL, 'data base file name (@1) already given', NULL, NULL);
('gfix_invalid_sw', 'ALICE_gfix', 'alice.c', NULL, 3, 2, NULL, 'invalid switch @1', NULL, NULL); ('gfix_invalid_sw', 'ALICE_gfix', 'alice.c', NULL, 3, 2, NULL, 'invalid switch @1', NULL, NULL);

View File

@ -963,6 +963,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-901, '22', '023', 0, 954, 'tom_key_length', NULL, NULL) (-901, '22', '023', 0, 954, 'tom_key_length', NULL, NULL)
(-901, 'HY', '000', 0, 955, 'inf_invalid_args', NULL, 'WARNING') (-901, 'HY', '000', 0, 955, 'inf_invalid_args', NULL, 'WARNING')
(-901, '22', '023', 0, 956, 'sysf_invalid_null_empty', NULL, NULL) (-901, '22', '023', 0, 956, 'sysf_invalid_null_empty', NULL, NULL)
(-901, 'HY', '000', 0, 957, 'bad_loctab_num', NULL, NULL)
-- GFIX -- GFIX
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)

View File

@ -263,6 +263,7 @@ const int op_subproc_decl = 27;
const int op_subfunc_decl = 28; const int op_subfunc_decl = 28;
const int op_window_win = 29; const int op_window_win = 29;
const int op_erase = 30; // special due to optional blr_marks after blr_erase const int op_erase = 30; // special due to optional blr_marks after blr_erase
const int op_dcl_local_table = 31;
static const UCHAR static const UCHAR
// generic print formats // generic print formats
@ -302,7 +303,7 @@ static const UCHAR
gen_id[] = { op_byte, op_literal, op_line, op_verb, 0}, gen_id[] = { op_byte, op_literal, op_line, op_verb, 0},
gen_id2[] = { op_byte, op_literal, op_line, 0}, gen_id2[] = { op_byte, op_literal, op_line, 0},
declare[] = { op_word, op_dtype, op_line, 0}, declare[] = { op_word, op_dtype, op_line, 0},
variable[] = { op_word, op_line, 0}, one_word[] = { op_word, op_line, 0},
indx[] = { op_line, op_verb, op_indent, op_byte, op_line, op_args, 0}, indx[] = { op_line, op_verb, op_indent, op_byte, op_line, op_args, 0},
seek[] = { op_line, op_verb, op_verb, 0}, seek[] = { op_line, op_verb, op_verb, 0},
join[] = { op_join, op_line, 0}, join[] = { op_join, op_line, 0},
@ -332,6 +333,7 @@ static const UCHAR
user_savepoint[] = { op_byte, op_byte, op_literal, op_line, 0}, user_savepoint[] = { op_byte, op_byte, op_literal, op_line, 0},
exec_into[] = { op_word, op_line, op_indent, op_exec_into, 0}, exec_into[] = { op_word, op_line, op_indent, op_exec_into, 0},
dcl_cursor[] = { op_word, op_line, op_verb, op_indent, op_word, op_line, op_args, 0}, dcl_cursor[] = { op_word, op_line, op_verb, op_indent, op_word, op_line, op_args, 0},
dcl_local_table[] = { op_dcl_local_table, 0 },
cursor_stmt[] = { op_cursor_stmt, 0 }, cursor_stmt[] = { op_cursor_stmt, 0 },
strlength[] = { op_byte, op_line, op_verb, 0}, strlength[] = { op_byte, op_line, op_verb, 0},
trim[] = { op_byte, op_byte_opt_verb, op_verb, 0}, trim[] = { op_byte, op_byte_opt_verb, op_verb, 0},
@ -350,7 +352,8 @@ static const UCHAR
op_line, op_indent, op_byte, op_literal, op_pad, op_line, 0}, op_line, op_indent, op_byte, op_literal, op_pad, op_line, 0},
store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0}, store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0},
marks[] = { op_byte, op_literal, op_line, op_verb, 0}, marks[] = { op_byte, op_literal, op_line, op_verb, 0},
erase[] = { op_erase, 0}; erase[] = { op_erase, 0},
local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0};
#include "../jrd/blp.h" #include "../jrd/blp.h"
@ -3779,6 +3782,56 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
offset = blr_print_line(control, offset); offset = blr_print_line(control, offset);
break; break;
case op_dcl_local_table:
{
offset = blr_print_line(control, offset);
blr_indent(control, level);
blr_print_word(control);
offset = blr_print_line(control, offset);
static const char* subCodes[] =
{
nullptr,
"format"
};
while ((blr_operator = control->ctl_blr_reader.getByte()) != blr_end)
{
blr_indent(control, level);
if (blr_operator == 0 || blr_operator >= FB_NELEM(subCodes))
blr_error(control, "*** invalid blr_dcl_local_table sub code ***");
blr_format(control, "blr_dcl_local_table_%s, ", subCodes[blr_operator]);
switch (blr_operator)
{
case blr_dcl_local_table_format:
n = blr_print_word(control);
offset = blr_print_line(control, offset);
++level;
while (--n >= 0)
{
blr_indent(control, level);
blr_print_dtype(control);
offset = blr_print_line(control, offset);
}
--level;
break;
default:
fb_assert(false);
}
}
// print blr_end
control->ctl_blr_reader.seekBackward(1);
blr_print_verb(control, level);
break;
}
default: default:
fb_assert(false); fb_assert(false);
break; break;