mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 14:43:03 +01:00
Feature #6815 - Support multiple rows for DML RETURNING.
This commit is contained in:
parent
7e88b82a46
commit
b32f96f2a9
@ -121,6 +121,7 @@
|
||||
<ClCompile Include="..\..\..\src\jrd\recsrc\FullTableScan.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\recsrc\HashJoin.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\MergeJoin.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\recsrc\NestedLoopJoin.cpp" />
|
||||
|
@ -81,6 +81,9 @@
|
||||
<ClCompile Include="..\..\..\src\jrd\recsrc\IndexTableScan.cpp">
|
||||
<Filter>JRD files\Data Access</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\jrd\recsrc\LocalTableStream.cpp">
|
||||
<Filter>JRD files\Data Access</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\jrd\recsrc\LockedStream.cpp">
|
||||
<Filter>JRD files\Data Access</Filter>
|
||||
</ClCompile>
|
||||
|
@ -7,7 +7,7 @@ MERGE statement
|
||||
condition.
|
||||
|
||||
Author:
|
||||
Adriano dos Santos Fernandes <adrianosf@uol.com.br>
|
||||
Adriano dos Santos Fernandes <adrianosf@gmail.com>
|
||||
|
||||
Format:
|
||||
<merge statement> ::=
|
||||
@ -16,6 +16,8 @@ MERGE statement
|
||||
USING <table or view or derived table> [ [AS] <correlation name> ]
|
||||
ON <condition>
|
||||
<merge when>...
|
||||
[<plan clause>]
|
||||
[<order by clause>]
|
||||
<returning clause>
|
||||
|
||||
<merge when> ::=
|
||||
|
@ -39,11 +39,12 @@ RETURNING clause
|
||||
Note(s):
|
||||
1. The INTO part (i.e. the variable list) is allowed in PSQL only (to assign local variables)
|
||||
and rejected in DSQL.
|
||||
2. In DSQL, values are being returned within the same protocol roundtrip as the INSERT itself
|
||||
is executed.
|
||||
2. In DSQL, INSERT INTO ... VALUES are returned within the same protocol roundtrip as the
|
||||
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
|
||||
isc_info_sql_stmt_exec_procedure by the API (instead of isc_info_sql_stmt_insert),
|
||||
so the existing connectivity drivers should support this feature automagically.
|
||||
isc_info_sql_stmt_exec_procedure by the API (for INSERT INTO ... VALUES and statements
|
||||
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
|
||||
the RETURNING clause.
|
||||
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
|
||||
resolved to OLD.
|
||||
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:
|
||||
1. The modify statement (INSERT, UPDATE, DELETE, MERGE) should 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).
|
||||
1. The modify statement (INSERT, UPDATE, DELETE, UPDATE OR INSERT, MERGE) in PSQL should
|
||||
operate in no more than one record (i.e. should be singleton).
|
||||
|
||||
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.
|
||||
|
@ -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.
|
||||
|
||||
Author:
|
||||
Adriano dos Santos Fernandes <adrianosf@uol.com.br>
|
||||
Adriano dos Santos Fernandes <adrianosf@gmail.com>
|
||||
|
||||
Syntax rules:
|
||||
UPDATE OR INSERT INTO <table or view> [(<column_list>)]
|
||||
VALUES (<value_list>)
|
||||
[MATCHING (<column_list>)]
|
||||
[<plan clause>]
|
||||
[<order by clause>]
|
||||
[<rows clause>]
|
||||
[RETURNING <value_list> [INTO <variable_list>]]
|
||||
|
||||
Scope:
|
||||
@ -27,11 +30,9 @@ UPDATE OR INSERT statement
|
||||
1. When MATCHING is omitted, the existence of a primary key is required.
|
||||
2. INSERT and UPDATE permissions are needed on <table or view>.
|
||||
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.
|
||||
|
||||
Limitation:
|
||||
1. A singleton error will be raised if the RETURNING clause is present and more than
|
||||
one record match the condition.
|
||||
2. There is no "UPDATE OR INSERT ... SELECT ..." as "INSERT ... SELECT". Use MERGE for
|
||||
1. There is no "UPDATE OR INSERT ... SELECT ..." as "INSERT ... SELECT". Use MERGE for
|
||||
this type of functionality.
|
||||
|
@ -1959,6 +1959,8 @@ const
|
||||
gds_inf_invalid_args = 335545275;
|
||||
isc_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;
|
||||
gds_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
|
@ -999,6 +999,7 @@ string CountAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "CountAggNode";
|
||||
}
|
||||
|
||||
//// TODO: Improve count(*) in local tables.
|
||||
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
@ -960,6 +960,7 @@ RseNode* DsqlCompilerScratch::pass1RseIsRecursive(RseNode* input)
|
||||
}
|
||||
}
|
||||
else if (nodeIs<ProcedureSourceNode>(*pDstTable) || nodeIs<RelationSourceNode>(*pDstTable))
|
||||
//// TODO: LocalTableSourceNode
|
||||
{
|
||||
if (pass1RelProcIsRecursive(*pDstTable))
|
||||
{
|
||||
@ -990,19 +991,18 @@ bool DsqlCompilerScratch::pass1RelProcIsRecursive(RecordSourceNode* input)
|
||||
{
|
||||
MetaName relName;
|
||||
string relAlias;
|
||||
ProcedureSourceNode* procNode;
|
||||
RelationSourceNode* relNode;
|
||||
|
||||
if ((procNode = nodeAs<ProcedureSourceNode>(input)))
|
||||
if (auto procNode = nodeAs<ProcedureSourceNode>(input))
|
||||
{
|
||||
relName = procNode->dsqlName.identifier;
|
||||
relAlias = procNode->alias;
|
||||
}
|
||||
else if ((relNode = nodeAs<RelationSourceNode>(input)))
|
||||
else if (auto relNode = nodeAs<RelationSourceNode>(input))
|
||||
{
|
||||
relName = relNode->dsqlName;
|
||||
relAlias = relNode->alias;
|
||||
}
|
||||
//// TODO: LocalTableSourceNode
|
||||
else
|
||||
return false;
|
||||
|
||||
|
@ -36,6 +36,7 @@ namespace Jrd
|
||||
class BinaryBoolNode;
|
||||
class CompoundStmtNode;
|
||||
class DeclareCursorNode;
|
||||
class DeclareLocalTableNode;
|
||||
class DeclareVariableNode;
|
||||
class ParameterClause;
|
||||
class RseNode;
|
||||
@ -44,6 +45,9 @@ class TypeClause;
|
||||
class VariableNode;
|
||||
class WithClause;
|
||||
|
||||
typedef Firebird::Pair<
|
||||
Firebird::NonPooled<NestConst<ValueListNode>, NestConst<ValueListNode>>> ReturningClause;
|
||||
|
||||
|
||||
// DSQL Compiler scratch block - may be discarded after compilation in the future.
|
||||
class DsqlCompilerScratch : public BlrDebugWriter
|
||||
@ -57,7 +61,6 @@ public:
|
||||
static const unsigned FLAG_BLOCK = 0x0020;
|
||||
static const unsigned FLAG_RECURSIVE_CTE = 0x0040;
|
||||
static const unsigned FLAG_UPDATE_OR_INSERT = 0x0080;
|
||||
static const unsigned FLAG_MERGE = 0x0100;
|
||||
static const unsigned FLAG_FUNCTION = 0x0200;
|
||||
static const unsigned FLAG_SUB_ROUTINE = 0x0400;
|
||||
static const unsigned FLAG_INTERNAL_REQUEST = 0x0800;
|
||||
@ -91,6 +94,8 @@ public:
|
||||
labels(p),
|
||||
cursorNumber(0),
|
||||
cursors(p),
|
||||
localTableNumber(0),
|
||||
localTables(p),
|
||||
inSelectList(0),
|
||||
inWhereClause(0),
|
||||
inGroupByClause(0),
|
||||
@ -110,6 +115,7 @@ public:
|
||||
hiddenVariables(p),
|
||||
variables(p),
|
||||
outputVariables(p),
|
||||
returningClause(nullptr),
|
||||
currCteAlias(NULL),
|
||||
ctes(p),
|
||||
cteAliases(p),
|
||||
@ -283,6 +289,8 @@ public:
|
||||
Firebird::Stack<MetaName*> labels; // Loop labels
|
||||
USHORT cursorNumber; // Cursor number
|
||||
Firebird::Array<DeclareCursorNode*> cursors; // Cursors
|
||||
USHORT localTableNumber; // Local table number
|
||||
Firebird::Array<DeclareLocalTableNode*> localTables; // Local tables
|
||||
USHORT inSelectList; // now processing "select list"
|
||||
USHORT inWhereClause; // processing "where clause"
|
||||
USHORT inGroupByClause; // processing "group by clause"
|
||||
@ -303,6 +311,7 @@ public:
|
||||
Firebird::Array<dsql_var*> hiddenVariables; // hidden variables
|
||||
Firebird::Array<dsql_var*> variables;
|
||||
Firebird::Array<dsql_var*> outputVariables;
|
||||
ReturningClause* returningClause;
|
||||
const Firebird::string* const* currCteAlias;
|
||||
|
||||
private:
|
||||
|
@ -6085,6 +6085,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec
|
||||
procNode->dsqlContext = stackContext;
|
||||
*list = procNode;
|
||||
}
|
||||
//// TODO: LocalTableSourceNode
|
||||
|
||||
fb_assert(*list);
|
||||
return NULL;
|
||||
@ -9457,14 +9458,7 @@ ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
static RegisterNode<ParameterNode> regParameterNode({blr_parameter, blr_parameter2, blr_parameter3});
|
||||
|
||||
ParameterNode::ParameterNode(MemoryPool& pool)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_PARAMETER>(pool),
|
||||
dsqlParameter(NULL),
|
||||
message(NULL),
|
||||
argFlag(NULL),
|
||||
argIndicator(NULL),
|
||||
argInfo(NULL),
|
||||
dsqlParameterIndex(0),
|
||||
argNumber(0)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_PARAMETER>(pool)
|
||||
{
|
||||
}
|
||||
|
||||
@ -9536,11 +9530,12 @@ ValueExprNode* ParameterNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
Arg::Gds(isc_dsql_command_err));
|
||||
}
|
||||
|
||||
dsql_msg* tempMsg = dsqlParameter ?
|
||||
dsqlParameter->par_message : dsqlScratch->getStatement()->getSendMsg();
|
||||
auto msg = dsqlMessage ? dsqlMessage :
|
||||
dsqlParameter ? dsqlParameter->par_message :
|
||||
dsqlScratch->getStatement()->getSendMsg();
|
||||
|
||||
ParameterNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool());
|
||||
node->dsqlParameter = MAKE_parameter(tempMsg, true, true, dsqlParameterIndex, NULL);
|
||||
auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool());
|
||||
node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr);
|
||||
node->dsqlParameterIndex = dsqlParameterIndex;
|
||||
|
||||
return node;
|
||||
@ -9923,11 +9918,12 @@ ValueExprNode* RecordKeyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
|
||||
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());
|
||||
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;
|
||||
|
||||
return node;
|
||||
@ -9959,11 +9955,12 @@ ValueExprNode* RecordKeyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
if (context->ctx_flags & CTX_null)
|
||||
return NullNode::instance();
|
||||
|
||||
RelationSourceNode* relNode = FB_NEW_POOL(dsqlScratch->getPool()) RelationSourceNode(
|
||||
//// TODO: LocalTableSourceNode
|
||||
auto relNode = FB_NEW_POOL(dsqlScratch->getPool()) RelationSourceNode(
|
||||
dsqlScratch->getPool());
|
||||
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;
|
||||
|
||||
return node;
|
||||
|
@ -1585,13 +1585,14 @@ public:
|
||||
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
public:
|
||||
dsql_par* dsqlParameter;
|
||||
dsql_msg* dsqlMessage = nullptr;
|
||||
dsql_par* dsqlParameter = nullptr;
|
||||
NestConst<MessageNode> message;
|
||||
NestConst<ValueExprNode> argFlag;
|
||||
NestConst<ValueExprNode> argIndicator;
|
||||
NestConst<ItemInfo> argInfo;
|
||||
USHORT dsqlParameterIndex;
|
||||
USHORT argNumber;
|
||||
USHORT dsqlParameterIndex = 0;
|
||||
USHORT argNumber = 0;
|
||||
};
|
||||
|
||||
|
||||
|
@ -508,6 +508,7 @@ public:
|
||||
|
||||
// RecordSource types
|
||||
TYPE_AGGREGATE_SOURCE,
|
||||
TYPE_LOCAL_TABLE,
|
||||
TYPE_PROCEDURE,
|
||||
TYPE_RELATION,
|
||||
TYPE_RSE,
|
||||
@ -1380,6 +1381,7 @@ public:
|
||||
TYPE_CONTINUE_LEAVE,
|
||||
TYPE_CURSOR_STMT,
|
||||
TYPE_DECLARE_CURSOR,
|
||||
TYPE_DECLARE_LOCAL_TABLE,
|
||||
TYPE_DECLARE_SUBFUNC,
|
||||
TYPE_DECLARE_SUBPROC,
|
||||
TYPE_DECLARE_VARIABLE,
|
||||
@ -1412,6 +1414,7 @@ public:
|
||||
TYPE_STALL,
|
||||
TYPE_STORE,
|
||||
TYPE_SUSPEND,
|
||||
TYPE_TRUNCATE_LOCAL_TABLE,
|
||||
TYPE_UPDATE_OR_INSERT,
|
||||
TYPE_USER_SAVEPOINT,
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,13 +38,11 @@ class CompoundStmtNode;
|
||||
class ExecBlockNode;
|
||||
class ForNode;
|
||||
class PlanNode;
|
||||
class RecordBuffer;
|
||||
class RelationSourceNode;
|
||||
class SelectNode;
|
||||
class GeneratorItem;
|
||||
|
||||
typedef Firebird::Pair<
|
||||
Firebird::NonPooled<NestConst<ValueListNode>, NestConst<ValueListNode> > > ReturningClause;
|
||||
|
||||
|
||||
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>
|
||||
{
|
||||
public:
|
||||
@ -1075,38 +1112,42 @@ public:
|
||||
struct Matched
|
||||
{
|
||||
explicit Matched(MemoryPool& pool)
|
||||
: assignments(NULL),
|
||||
condition(NULL)
|
||||
: processedFields(pool),
|
||||
processedValues(pool)
|
||||
{
|
||||
}
|
||||
|
||||
NestConst<CompoundStmtNode> assignments;
|
||||
NestConst<BoolExprNode> condition;
|
||||
|
||||
NestConst<Jrd::RecordSourceNode> modifyRelation;
|
||||
NestValueArray processedFields;
|
||||
NestValueArray processedValues;
|
||||
NestConst<ReturningClause> processedReturning;
|
||||
};
|
||||
|
||||
struct NotMatched
|
||||
{
|
||||
explicit NotMatched(MemoryPool& pool)
|
||||
: fields(pool),
|
||||
values(NULL),
|
||||
condition(NULL)
|
||||
processedFields(pool)
|
||||
{
|
||||
}
|
||||
|
||||
Firebird::Array<NestConst<FieldNode> > fields;
|
||||
Firebird::Array<NestConst<FieldNode>> fields;
|
||||
NestConst<ValueListNode> values;
|
||||
NestConst<BoolExprNode> condition;
|
||||
Nullable<OverrideClause> overrideClause;
|
||||
|
||||
NestConst<Jrd::RecordSourceNode> storeRelation;
|
||||
NestValueArray processedFields;
|
||||
NestConst<ReturningClause> processedReturning;
|
||||
};
|
||||
|
||||
explicit MergeNode(MemoryPool& pool)
|
||||
: TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_MERGE>(pool),
|
||||
relation(NULL),
|
||||
usingClause(NULL),
|
||||
condition(NULL),
|
||||
whenMatched(pool),
|
||||
whenNotMatched(pool),
|
||||
returning(NULL)
|
||||
whenNotMatched(pool)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1120,7 +1161,12 @@ public:
|
||||
NestConst<BoolExprNode> condition;
|
||||
Firebird::ObjectsArray<Matched> whenMatched;
|
||||
Firebird::ObjectsArray<NotMatched> whenNotMatched;
|
||||
NestConst<PlanNode> plan;
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<ReturningClause> returning;
|
||||
|
||||
NestConst<RseNode> rse;
|
||||
dsql_ctx* targetContext = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@ -1163,24 +1209,8 @@ class ModifyNode : public TypedNode<StmtNode, StmtNode::TYPE_MODIFY>
|
||||
public:
|
||||
explicit ModifyNode(MemoryPool& pool)
|
||||
: TypedNode<StmtNode, StmtNode::TYPE_MODIFY>(pool),
|
||||
dsqlRelation(NULL),
|
||||
dsqlBoolean(NULL),
|
||||
dsqlPlan(NULL),
|
||||
dsqlOrder(NULL),
|
||||
dsqlRows(NULL),
|
||||
dsqlCursorName(pool),
|
||||
dsqlReturning(NULL),
|
||||
dsqlRse(NULL),
|
||||
dsqlContext(NULL),
|
||||
statement(NULL),
|
||||
statement2(NULL),
|
||||
subMod(NULL),
|
||||
validations(pool),
|
||||
mapView(NULL),
|
||||
orgStream(0),
|
||||
newStream(0),
|
||||
marks(0),
|
||||
dsqlRseFlags(0)
|
||||
validations(pool)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1208,17 +1238,18 @@ public:
|
||||
MetaName dsqlCursorName;
|
||||
NestConst<ReturningClause> dsqlReturning;
|
||||
NestConst<RecordSourceNode> dsqlRse;
|
||||
dsql_ctx* dsqlContext;
|
||||
dsql_ctx* dsqlContext = nullptr;
|
||||
NestConst<StmtNode> statement;
|
||||
NestConst<StmtNode> statement2;
|
||||
NestConst<StmtNode> subMod;
|
||||
Firebird::Array<ValidateInfo> validations;
|
||||
NestConst<StmtNode> mapView;
|
||||
NestConst<ForNode> forNode; // parent implicit cursor, if present
|
||||
StreamType orgStream;
|
||||
StreamType newStream;
|
||||
unsigned marks; // see StmtNode::IUD_MARK_xxx
|
||||
USHORT dsqlRseFlags;
|
||||
StreamType orgStream = 0;
|
||||
StreamType newStream = 0;
|
||||
unsigned marks = 0; // see StmtNode::IUD_MARK_xxx
|
||||
USHORT dsqlRseFlags = 0;
|
||||
Nullable<USHORT> dsqlReturningLocalTableNumber;
|
||||
};
|
||||
|
||||
|
||||
@ -1281,16 +1312,8 @@ class StoreNode : public TypedNode<StmtNode, StmtNode::TYPE_STORE>
|
||||
public:
|
||||
explicit StoreNode(MemoryPool& pool)
|
||||
: TypedNode<StmtNode, StmtNode::TYPE_STORE>(pool),
|
||||
dsqlRelation(NULL),
|
||||
dsqlFields(pool),
|
||||
dsqlValues(NULL),
|
||||
dsqlReturning(NULL),
|
||||
dsqlRse(NULL),
|
||||
statement(NULL),
|
||||
statement2(NULL),
|
||||
subStore(NULL),
|
||||
validations(pool),
|
||||
relationSource(NULL)
|
||||
validations(pool)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1311,8 +1334,8 @@ private:
|
||||
const StmtNode* store(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const;
|
||||
|
||||
public:
|
||||
NestConst<RecordSourceNode> dsqlRelation;
|
||||
Firebird::Array<NestConst<FieldNode> > dsqlFields;
|
||||
NestConst<RecordSourceNode> target;
|
||||
Firebird::Array<NestConst<FieldNode>> dsqlFields;
|
||||
NestConst<ValueListNode> dsqlValues;
|
||||
NestConst<ReturningClause> dsqlReturning;
|
||||
NestConst<RecordSourceNode> dsqlRse;
|
||||
@ -1320,7 +1343,7 @@ public:
|
||||
NestConst<StmtNode> statement2;
|
||||
NestConst<StmtNode> subStore;
|
||||
Firebird::Array<ValidateInfo> validations;
|
||||
NestConst<RelationSourceNode> relationSource;
|
||||
Nullable<USHORT> dsqlReturningLocalTableNumber;
|
||||
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>
|
||||
{
|
||||
public:
|
||||
explicit UpdateOrInsertNode(MemoryPool& pool)
|
||||
: TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_UPDATE_OR_INSERT>(pool),
|
||||
relation(NULL),
|
||||
fields(pool),
|
||||
values(NULL),
|
||||
matching(pool),
|
||||
returning(NULL)
|
||||
varAssignments(pool)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1884,11 +1943,17 @@ public:
|
||||
|
||||
public:
|
||||
NestConst<RelationSourceNode> relation;
|
||||
Firebird::Array<NestConst<FieldNode> > fields;
|
||||
Firebird::Array<NestConst<FieldNode>> fields;
|
||||
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;
|
||||
Nullable<OverrideClause> overrideClause;
|
||||
NestConst<StoreNode> storeNode;
|
||||
NestConst<ModifyNode> modifyNode;
|
||||
Firebird::Array<NestConst<AssignmentNode>> varAssignments;
|
||||
};
|
||||
|
||||
|
||||
|
@ -98,6 +98,7 @@ static inline bool reqTypeWithCursor(DsqlCompiledStatement::Type type)
|
||||
case DsqlCompiledStatement::TYPE_SELECT:
|
||||
case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
|
||||
case DsqlCompiledStatement::TYPE_SELECT_UPD:
|
||||
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2062,6 +2063,7 @@ static void sql_info(thread_db* tdbb,
|
||||
case DsqlCompiledStatement::TYPE_SELECT:
|
||||
case DsqlCompiledStatement::TYPE_SELECT_UPD:
|
||||
case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
|
||||
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
|
||||
value |= IStatement::FLAG_HAS_CURSOR;
|
||||
break;
|
||||
}
|
||||
@ -2075,6 +2077,7 @@ static void sql_info(thread_db* tdbb,
|
||||
switch (statement->getType())
|
||||
{
|
||||
case DsqlCompiledStatement::TYPE_SELECT:
|
||||
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
|
||||
number = isc_info_sql_stmt_select;
|
||||
break;
|
||||
case DsqlCompiledStatement::TYPE_SELECT_UPD:
|
||||
|
@ -461,7 +461,8 @@ public:
|
||||
TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR,
|
||||
TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS,
|
||||
TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR,
|
||||
TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT
|
||||
TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT,
|
||||
TYPE_RETURNING_CURSOR
|
||||
};
|
||||
|
||||
// Statement flags.
|
||||
|
@ -267,6 +267,8 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node)
|
||||
case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
|
||||
node->genBlr(scratch);
|
||||
break;
|
||||
|
||||
///case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
|
||||
default:
|
||||
{
|
||||
dsql_msg* message = statement->getSendMsg();
|
||||
@ -660,3 +662,13 @@ void GEN_stuff_context(DsqlCompilerScratch* dsqlScratch, const dsql_ctx* context
|
||||
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);
|
||||
}
|
||||
|
@ -39,5 +39,6 @@ void GEN_request(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*);
|
||||
void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*);
|
||||
void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*);
|
||||
void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*);
|
||||
void GEN_stuff_context_number(Jrd::DsqlCompilerScratch*, USHORT);
|
||||
|
||||
#endif // DSQL_GEN_PROTO_H
|
||||
|
@ -6524,7 +6524,7 @@ insert_start
|
||||
: INSERT INTO simple_table_name
|
||||
{
|
||||
StoreNode* node = newNode<StoreNode>();
|
||||
node->dsqlRelation = $3;
|
||||
node->target = $3;
|
||||
$$ = node;
|
||||
}
|
||||
;
|
||||
@ -6559,10 +6559,13 @@ merge
|
||||
node->usingClause = $5;
|
||||
node->condition = $7;
|
||||
}
|
||||
merge_when_clause($8) returning_clause
|
||||
merge_when_clause($8)
|
||||
plan_clause order_clause_opt returning_clause
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
node->overrideClause = $8;
|
||||
node->values = $11;
|
||||
node->returning = $14;
|
||||
node->plan = $14;
|
||||
node->order = $15;
|
||||
node->rows = $16;
|
||||
node->returning = $17;
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -362,6 +362,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
|
||||
relation_name = procNode->dsqlName.identifier;
|
||||
else if ((relNode = nodeAs<RelationSourceNode>(relationNode)))
|
||||
relation_name = relNode->dsqlName;
|
||||
//// TODO: LocalTableSourceNode
|
||||
else if ((selNode = nodeAs<SelectExprNode>(relationNode)))
|
||||
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 DeclareVariableNode* varNode;
|
||||
const DeclareCursorNode* cursorNode;
|
||||
|
||||
if ((varNode = nodeAs<DeclareVariableNode>(*ptr)))
|
||||
if (auto varNode = nodeAs<DeclareVariableNode>(*ptr))
|
||||
name = varNode->dsqlDef->name.c_str();
|
||||
else if ((cursorNode = nodeAs<DeclareCursorNode>(*ptr)))
|
||||
else if (auto cursorNode = nodeAs<DeclareCursorNode>(*ptr))
|
||||
name = cursorNode->dsqlName.c_str();
|
||||
else if (nodeAs<DeclareSubProcNode>(*ptr) || nodeAs<DeclareSubFuncNode>(*ptr))
|
||||
continue;
|
||||
@ -1326,12 +1324,10 @@ ValueListNode* PASS1_expand_select_list(DsqlCompilerScratch* dsqlScratch, ValueL
|
||||
void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, ValueListNode* list,
|
||||
bool hide_using)
|
||||
{
|
||||
RseNode* rseNode;
|
||||
ProcedureSourceNode* procNode;
|
||||
RelationSourceNode* relNode;
|
||||
FieldNode* fieldNode;
|
||||
|
||||
if ((rseNode = nodeAs<RseNode>(node)))
|
||||
//// TODO: LocalTableSourceNode
|
||||
if (auto rseNode = nodeAs<RseNode>(node))
|
||||
{
|
||||
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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1730,6 +1726,7 @@ RecordSourceNode* PASS1_relation(DsqlCompilerScratch* dsqlScratch, RecordSourceN
|
||||
procNode->dsqlContext = context;
|
||||
return procNode;
|
||||
}
|
||||
//// TODO: LocalTableSourceNode
|
||||
|
||||
fb_assert(false);
|
||||
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);
|
||||
|
||||
RecSourceListNode* listNode;
|
||||
ProcedureSourceNode* procNode;
|
||||
RelationSourceNode* relNode;
|
||||
RseNode* rseNode;
|
||||
UnionSourceNode* unionNode;
|
||||
|
||||
if ((listNode = nodeAs<RecSourceListNode>(input)))
|
||||
if (auto listNode = nodeAs<RecSourceListNode>(input))
|
||||
{
|
||||
NestConst<RecordSourceNode>* ptr = listNode->items.begin();
|
||||
for (const NestConst<RecordSourceNode>* const end = listNode->items.end(); ptr != end; ++ptr)
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
else if ((unionNode = nodeAs<UnionSourceNode>(input)))
|
||||
else if (auto unionNode = nodeAs<UnionSourceNode>(input))
|
||||
remap_streams_to_parent_context(unionNode->dsqlClauses, parent_context);
|
||||
else
|
||||
fb_assert(nodeAs<AggregateSourceNode>(input));
|
||||
|
@ -451,4 +451,12 @@
|
||||
|
||||
#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
|
||||
|
@ -769,6 +769,7 @@
|
||||
#define fb_dbg_subproc 5
|
||||
#define fb_dbg_subfunc 6
|
||||
#define fb_dbg_map_curname 7
|
||||
//// TODO: LocalTable name.
|
||||
|
||||
// sub code for fb_dbg_map_argument
|
||||
#define fb_dbg_arg_input 0
|
||||
|
@ -5135,6 +5135,7 @@ const
|
||||
isc_tom_key_length = 335545274;
|
||||
isc_inf_invalid_args = 335545275;
|
||||
isc_sysf_invalid_null_empty = 335545276;
|
||||
isc_bad_loctab_num = 335545276;
|
||||
isc_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
isc_gfix_incmp_sw = 335740932;
|
||||
|
@ -978,6 +978,7 @@ static const struct {
|
||||
{"tom_key_length", 335545274},
|
||||
{"inf_invalid_args", 335545275},
|
||||
{"sysf_invalid_null_empty", 335545276},
|
||||
{"bad_loctab_num", 335545277},
|
||||
{"gfix_db_name", 335740929},
|
||||
{"gfix_invalid_sw", 335740930},
|
||||
{"gfix_incmp_sw", 335740932},
|
||||
|
@ -1012,6 +1012,7 @@ const ISC_STATUS isc_block_size = 335545273L;
|
||||
const ISC_STATUS isc_tom_key_length = 335545274L;
|
||||
const ISC_STATUS isc_inf_invalid_args = 335545275L;
|
||||
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_invalid_sw = 335740930L;
|
||||
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_param_act_notcompat = 337182759L;
|
||||
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 */
|
||||
|
||||
@ -2486,6 +2487,7 @@ const ISC_STATUS isc_err_max = 1448;
|
||||
#define isc_tom_key_length 335545274L
|
||||
#define isc_inf_invalid_args 335545275L
|
||||
#define isc_sysf_invalid_null_empty 335545276L
|
||||
#define isc_bad_loctab_num 335545277L
|
||||
#define isc_gfix_db_name 335740929L
|
||||
#define isc_gfix_invalid_sw 335740930L
|
||||
#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_param_act_notcompat 337182759L
|
||||
#define isc_trace_mandatory_switch_miss 337182760L
|
||||
#define isc_err_max 1448
|
||||
#define isc_err_max 1449
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -981,6 +981,7 @@ Data source : @4"}, /* eds_statement */
|
||||
{335545274, "Invalid key length @1, need >@2"}, /* tom_key_length */
|
||||
{335545275, "Invalid information arguments"}, /* inf_invalid_args */
|
||||
{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 */
|
||||
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
|
||||
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */
|
||||
|
@ -977,6 +977,7 @@ static const struct {
|
||||
{335545274, -901}, /* 954 tom_key_length */
|
||||
{335545275, -901}, /* 955 inf_invalid_args */
|
||||
{335545276, -901}, /* 956 sysf_invalid_null_empty */
|
||||
{335545277, -901}, /* 957 bad_loctab_num */
|
||||
{335740929, -901}, /* 1 gfix_db_name */
|
||||
{335740930, -901}, /* 2 gfix_invalid_sw */
|
||||
{335740932, -901}, /* 4 gfix_incmp_sw */
|
||||
|
@ -977,6 +977,7 @@ static const struct {
|
||||
{335545274, "22023"}, // 954 tom_key_length
|
||||
{335545275, "HY000"}, // 955 inf_invalid_args
|
||||
{335545276, "22023"}, // 956 sysf_invalid_null_empty
|
||||
{335545277, "HY000"}, // 957 bad_loctab_num
|
||||
{335740929, "00000"}, // 1 gfix_db_name
|
||||
{335740930, "00000"}, // 2 gfix_invalid_sw
|
||||
{335740932, "00000"}, // 4 gfix_incmp_sw
|
||||
|
@ -71,6 +71,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
parentStatement(NULL),
|
||||
subStatements(*p),
|
||||
fors(*p),
|
||||
localTables(*p),
|
||||
invariants(*p),
|
||||
blr(*p),
|
||||
mapFieldInfo(*p),
|
||||
@ -155,6 +156,8 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
// make a vector of all used RSEs
|
||||
fors = csb->csb_fors;
|
||||
|
||||
localTables = csb->csb_localTables;
|
||||
|
||||
// make a vector of all invariant-type nodes, so that we will
|
||||
// be able to easily reinitialize them when we restart the request
|
||||
invariants.join(csb->csb_invariants);
|
||||
|
@ -81,6 +81,7 @@ public:
|
||||
Firebird::Array<JrdStatement*> subStatements; // Array of subroutines' statements
|
||||
const StmtNode* topNode; // top of execution tree
|
||||
Firebird::Array<const RecordSource*> fors; // record sources
|
||||
Firebird::Array<const DeclareLocalTableNode*> localTables; // local tables
|
||||
Firebird::Array<ULONG*> invariants; // pointer to nodes invariant offsets
|
||||
Firebird::RefStrPtr sqlText; // SQL text (encoded in the metadata charset)
|
||||
Firebird::Array<UCHAR> blr; // BLR for non-SQL query
|
||||
|
@ -187,6 +187,7 @@ namespace Jrd
|
||||
alias = csb_tail->csb_relation->rel_name.c_str();
|
||||
else if (csb_tail->csb_procedure)
|
||||
alias = csb_tail->csb_procedure->getName().toString();
|
||||
//// TODO: LocalTableSourceNode
|
||||
else
|
||||
fb_assert(false);
|
||||
|
||||
|
@ -256,6 +256,7 @@ PlanNode* PlanNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
procNode->dsqlContext = context;
|
||||
node->dsqlRecordSourceNode = procNode;
|
||||
}
|
||||
//// TODO: LocalTableSourceNode
|
||||
|
||||
// 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.
|
||||
@ -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.
|
||||
RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* csb,
|
||||
const SSHORT blrOp, bool parseContext)
|
||||
@ -3345,26 +3484,24 @@ RseNode* SelectExprNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
|
||||
static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* source)
|
||||
{
|
||||
ProcedureSourceNode* procNode = nodeAs<ProcedureSourceNode>(source);
|
||||
RelationSourceNode* relNode = nodeAs<RelationSourceNode>(source);
|
||||
|
||||
fb_assert(procNode || relNode);
|
||||
|
||||
bool couldBeCte = true;
|
||||
MetaName relName;
|
||||
string relAlias;
|
||||
|
||||
if (procNode)
|
||||
if (auto procNode = nodeAs<ProcedureSourceNode>(source))
|
||||
{
|
||||
relName = procNode->dsqlName.identifier;
|
||||
relAlias = procNode->alias;
|
||||
couldBeCte = !procNode->sourceList && procNode->dsqlName.package.isEmpty();
|
||||
}
|
||||
else if (relNode)
|
||||
else if (auto relNode = nodeAs<RelationSourceNode>(source))
|
||||
{
|
||||
relName = relNode->dsqlName;
|
||||
relAlias = relNode->alias;
|
||||
}
|
||||
//// TODO: LocalTableSourceNode
|
||||
else
|
||||
fb_assert(false);
|
||||
|
||||
if (relAlias.isEmpty())
|
||||
relAlias = relName.c_str();
|
||||
|
@ -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>
|
||||
{
|
||||
public:
|
||||
|
@ -51,7 +51,7 @@ static const struct
|
||||
{"field", field},
|
||||
{"fid", parm},
|
||||
{"parameter", parm},
|
||||
{"variable", variable},
|
||||
{"variable", one_word},
|
||||
{"average", two},
|
||||
{"count", one},
|
||||
{"maximum", two},
|
||||
@ -213,7 +213,7 @@ static const struct
|
||||
{"strlen", strlength},
|
||||
{"trim", trim},
|
||||
// New BLR in FB2.1
|
||||
{"init_variable", variable},
|
||||
{"init_variable", one_word},
|
||||
{"recurse", union_ops},
|
||||
{"sys_function", function},
|
||||
// New BLR in FB2.5
|
||||
@ -248,5 +248,9 @@ static const struct
|
||||
{"local_time", byte_line},
|
||||
{"at", verb_byte_verb},
|
||||
{"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}
|
||||
};
|
||||
|
@ -257,17 +257,12 @@ const Format* CMP_format(thread_db* tdbb, CompilerScratch* csb, StreamType strea
|
||||
if (!tail->csb_format)
|
||||
{
|
||||
if (tail->csb_relation)
|
||||
{
|
||||
tail->csb_format = MET_current(tdbb, tail->csb_relation);
|
||||
}
|
||||
else if (tail->csb_procedure)
|
||||
{
|
||||
tail->csb_format = tail->csb_procedure->prc_record_format;
|
||||
}
|
||||
//// TODO: LocalTableSourceNode
|
||||
else
|
||||
{
|
||||
IBERROR(222); // msg 222 bad blr - invalid stream
|
||||
}
|
||||
}
|
||||
|
||||
fb_assert(tail->csb_format);
|
||||
|
@ -982,6 +982,17 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request)
|
||||
tdbb->setRequest(old_request);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -448,7 +448,7 @@ public:
|
||||
csb_resources(p),
|
||||
csb_dependencies(p),
|
||||
csb_fors(p),
|
||||
csb_cursors(p),
|
||||
csb_localTables(p),
|
||||
csb_invariants(p),
|
||||
csb_current_nodes(p),
|
||||
csb_current_for_nodes(p),
|
||||
@ -511,7 +511,7 @@ public:
|
||||
ResourceList csb_resources; // Resources (relations and indexes)
|
||||
Firebird::Array<Dependency> csb_dependencies; // objects that this statement depends upon
|
||||
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<ExprNode*> csb_current_nodes; // RseNode's and other invariant
|
||||
// candidates within whose scope we are
|
||||
|
@ -548,6 +548,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql)
|
||||
switch (statement->getType())
|
||||
{
|
||||
case DsqlCompiledStatement::TYPE_SELECT:
|
||||
case DsqlCompiledStatement::TYPE_RETURNING_CURSOR:
|
||||
case DsqlCompiledStatement::TYPE_SELECT_UPD:
|
||||
case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
|
||||
m_stmt_selectable = true;
|
||||
|
@ -1281,7 +1281,7 @@ static void check_sorts(CompilerScratch* csb, RseNode* rse)
|
||||
{
|
||||
RecordSourceNode* subNode = new_rse->rse_relations[i];
|
||||
|
||||
if (nodeIs<RelationSourceNode>(subNode) &&
|
||||
if ((nodeIs<RelationSourceNode>(subNode) || nodeIs<LocalTableSourceNode>(subNode)) &&
|
||||
subNode->getStream() == sort_stream &&
|
||||
new_rse != rse)
|
||||
{
|
||||
@ -1289,7 +1289,6 @@ static void check_sorts(CompilerScratch* csb, RseNode* rse)
|
||||
sortStreamFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (sortStreamFound)
|
||||
@ -1309,7 +1308,7 @@ static void check_sorts(CompilerScratch* csb, RseNode* rse)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nodeIs<RelationSourceNode>(node) &&
|
||||
if ((nodeIs<RelationSourceNode>(node) || nodeIs<LocalTableSourceNode>(node)) &&
|
||||
node->getStream() == sort_stream &&
|
||||
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];
|
||||
|
||||
if (nodeIs<RelationSourceNode>(node))
|
||||
if (nodeIs<RelationSourceNode>(node) || nodeIs<LocalTableSourceNode>(node))
|
||||
{
|
||||
stream_ptr[i]->stream_rsb = NULL;
|
||||
stream_ptr[i]->stream_num = node->getStream();
|
||||
|
@ -180,7 +180,8 @@ namespace
|
||||
if (relationNode->getKind() != DmlNode::KIND_REC_SOURCE)
|
||||
PAR_syntax_error(csb, "TABLE");
|
||||
|
||||
RelationSourceNode* relationSource = nodeAs<RelationSourceNode>(
|
||||
//// TODO: LocalTableSourceNode
|
||||
auto relationSource = nodeAs<RelationSourceNode>(
|
||||
static_cast<RecordSourceNode*>(relationNode));
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
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
|
||||
// an existing context
|
||||
|
||||
//// TODO: LocalTableSourceNode
|
||||
plan->relationNode = RelationSourceNode::parse(tdbb, csb, n, false);
|
||||
|
||||
jrd_rel* relation = plan->relationNode->relation;
|
||||
@ -1297,6 +1300,9 @@ RecordSourceNode* PAR_parseRecordSource(thread_db* tdbb, CompilerScratch* csb)
|
||||
case blr_rid2:
|
||||
return RelationSourceNode::parse(tdbb, csb, blrOp, true);
|
||||
|
||||
case blr_local_table_id:
|
||||
return LocalTableSourceNode::parse(tdbb, csb, blrOp, true);
|
||||
|
||||
case blr_union:
|
||||
case blr_recurse:
|
||||
return UnionSourceNode::parse(tdbb, csb, blrOp);
|
||||
@ -1622,6 +1628,7 @@ DmlNode* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb)
|
||||
case blr_rid:
|
||||
case blr_relation2:
|
||||
case blr_rid2:
|
||||
case blr_local_table_id:
|
||||
case blr_union:
|
||||
case blr_recurse:
|
||||
case blr_window:
|
||||
|
@ -59,7 +59,6 @@ namespace Jrd
|
||||
bool irsb_active;
|
||||
State irsb_state;
|
||||
FB_UINT64 irsb_position;
|
||||
RecordBuffer* irsb_buffer;
|
||||
};
|
||||
|
||||
public:
|
||||
|
128
src/jrd/recsrc/LocalTableStream.cpp
Normal file
128
src/jrd/recsrc/LocalTableStream.cpp
Normal 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 += ")";
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ namespace Jrd
|
||||
class jrd_prc;
|
||||
class AggNode;
|
||||
class BoolExprNode;
|
||||
class DeclareLocalTableNode;
|
||||
class Sort;
|
||||
class CompilerScratch;
|
||||
class RecordBuffer;
|
||||
@ -1145,6 +1146,24 @@ namespace Jrd
|
||||
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
|
||||
{
|
||||
struct Impure : public RecordSource::Impure
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* 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 (?, ?, ?, ?);
|
||||
--
|
||||
('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)
|
||||
('2018-03-17 12:00:00', 'GFIX', 3, 136)
|
||||
('1996-11-07 13:39:40', 'GPRE', 4, 1)
|
||||
|
@ -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);
|
||||
('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);
|
||||
('bad_loctab_num', NULL, NULL, NULL, 0, 957, NULL, 'Undefined local table number @1', NULL, NULL);
|
||||
-- GFIX
|
||||
('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);
|
||||
|
@ -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, 'HY', '000', 0, 955, 'inf_invalid_args', NULL, 'WARNING')
|
||||
(-901, '22', '023', 0, 956, 'sysf_invalid_null_empty', NULL, NULL)
|
||||
(-901, 'HY', '000', 0, 957, 'bad_loctab_num', NULL, NULL)
|
||||
-- GFIX
|
||||
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
|
||||
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)
|
||||
|
@ -263,6 +263,7 @@ const int op_subproc_decl = 27;
|
||||
const int op_subfunc_decl = 28;
|
||||
const int op_window_win = 29;
|
||||
const int op_erase = 30; // special due to optional blr_marks after blr_erase
|
||||
const int op_dcl_local_table = 31;
|
||||
|
||||
static const UCHAR
|
||||
// generic print formats
|
||||
@ -302,7 +303,7 @@ static const UCHAR
|
||||
gen_id[] = { op_byte, op_literal, op_line, op_verb, 0},
|
||||
gen_id2[] = { op_byte, op_literal, 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},
|
||||
seek[] = { op_line, op_verb, op_verb, 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},
|
||||
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_local_table[] = { op_dcl_local_table, 0 },
|
||||
cursor_stmt[] = { op_cursor_stmt, 0 },
|
||||
strlength[] = { op_byte, op_line, 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},
|
||||
store3[] = { op_line, op_byte, op_line, op_verb, op_verb, 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"
|
||||
@ -3779,6 +3782,56 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
|
||||
offset = blr_print_line(control, offset);
|
||||
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:
|
||||
fb_assert(false);
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user