mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 18:43:02 +01:00
1) Separate DsqlCompilerScratch in its own files.
2) Move BlockNode functionality to it. 3) Move some related CTE functions to it.
This commit is contained in:
parent
8047048dc4
commit
52a4c39f41
@ -81,7 +81,8 @@ DSQL_ClientFiles = array.epp blob.epp \
|
|||||||
DSQL_ServerFiles= metd.epp DSqlDataTypeUtil.cpp \
|
DSQL_ServerFiles= metd.epp DSqlDataTypeUtil.cpp \
|
||||||
ddl.cpp dsql.cpp errd.cpp gen.cpp hsh.cpp make.cpp \
|
ddl.cpp dsql.cpp errd.cpp gen.cpp hsh.cpp make.cpp \
|
||||||
movd.cpp parse.cpp Parser.cpp pass1.cpp misc_func.cpp \
|
movd.cpp parse.cpp Parser.cpp pass1.cpp misc_func.cpp \
|
||||||
DdlNodes.epp PackageNodes.epp AggNodes.cpp BlrWriter.cpp ExprNodes.cpp StmtNodes.cpp WinNodes.cpp
|
DdlNodes.epp PackageNodes.epp AggNodes.cpp BlrWriter.cpp DsqlCompilerScratch.cpp \
|
||||||
|
ExprNodes.cpp StmtNodes.cpp WinNodes.cpp
|
||||||
|
|
||||||
|
|
||||||
DSQL_Files = $(DSQL_ClientFiles) $(DSQL_ServerFiles)
|
DSQL_Files = $(DSQL_ClientFiles) $(DSQL_ServerFiles)
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include "firebird.h"
|
#include "firebird.h"
|
||||||
#include "../dsql/DSqlDataTypeUtil.h"
|
#include "../dsql/DSqlDataTypeUtil.h"
|
||||||
#include "../dsql/dsql.h"
|
#include "../dsql/DsqlCompilerScratch.h"
|
||||||
#include "../dsql/metd_proto.h"
|
#include "../dsql/metd_proto.h"
|
||||||
|
|
||||||
UCHAR Jrd::DSqlDataTypeUtil::maxBytesPerChar(UCHAR charSet)
|
UCHAR Jrd::DSqlDataTypeUtil::maxBytesPerChar(UCHAR charSet)
|
||||||
|
@ -1157,8 +1157,6 @@ void CreateAlterFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) c
|
|||||||
|
|
||||||
DdlNode* CreateAlterFunctionNode::internalDsqlPass()
|
DdlNode* CreateAlterFunctionNode::internalDsqlPass()
|
||||||
{
|
{
|
||||||
DsqlCompiledStatement* const statement = dsqlScratch->getStatement();
|
|
||||||
statement->setBlockNode(this);
|
|
||||||
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_FUNCTION);
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_FUNCTION);
|
||||||
|
|
||||||
const dsql_nod* variables = localDeclList;
|
const dsql_nod* variables = localDeclList;
|
||||||
@ -1488,7 +1486,6 @@ void CreateAlterFunctionNode::storeArgument(thread_db* tdbb, jrd_tra* transactio
|
|||||||
unsigned pos, const ParameterClause& parameter)
|
unsigned pos, const ParameterClause& parameter)
|
||||||
{
|
{
|
||||||
Attachment* attachment = transaction->getAttachment();
|
Attachment* attachment = transaction->getAttachment();
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
|
|
||||||
AutoCacheRequest requestHandle(tdbb, drq_s_func_args2, DYN_REQUESTS);
|
AutoCacheRequest requestHandle(tdbb, drq_s_func_args2, DYN_REQUESTS);
|
||||||
|
|
||||||
@ -1591,7 +1588,7 @@ void CreateAlterFunctionNode::storeArgument(thread_db* tdbb, jrd_tra* transactio
|
|||||||
|
|
||||||
dsqlScratch->getBlrData().clear();
|
dsqlScratch->getBlrData().clear();
|
||||||
|
|
||||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
if (dsqlScratch->isVersion4())
|
||||||
dsqlScratch->appendUChar(blr_version4);
|
dsqlScratch->appendUChar(blr_version4);
|
||||||
else
|
else
|
||||||
dsqlScratch->appendUChar(blr_version5);
|
dsqlScratch->appendUChar(blr_version5);
|
||||||
@ -1618,14 +1615,12 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
compiled = true;
|
compiled = true;
|
||||||
invalid = true;
|
invalid = true;
|
||||||
|
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
|
|
||||||
if (body)
|
if (body)
|
||||||
{
|
{
|
||||||
dsqlScratch->beginDebug();
|
dsqlScratch->beginDebug();
|
||||||
dsqlScratch->getBlrData().clear();
|
dsqlScratch->getBlrData().clear();
|
||||||
|
|
||||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
if (dsqlScratch->isVersion4())
|
||||||
dsqlScratch->appendUChar(blr_version4);
|
dsqlScratch->appendUChar(blr_version4);
|
||||||
else
|
else
|
||||||
dsqlScratch->appendUChar(blr_version5);
|
dsqlScratch->appendUChar(blr_version5);
|
||||||
@ -1650,7 +1645,7 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
dsqlScratch->appendUChar(blr_short);
|
dsqlScratch->appendUChar(blr_short);
|
||||||
dsqlScratch->appendUChar(0);
|
dsqlScratch->appendUChar(0);
|
||||||
|
|
||||||
variables.add(MAKE_variable(parameter.legacyField,
|
dsqlScratch->variables.add(MAKE_variable(parameter.legacyField,
|
||||||
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
|
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1667,8 +1662,8 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
dsqlScratch->appendUChar(0);
|
dsqlScratch->appendUChar(0);
|
||||||
|
|
||||||
dsql_nod* const var = MAKE_variable(returnType.legacyField, "", VAR_output, 1, 0, 0);
|
dsql_nod* const var = MAKE_variable(returnType.legacyField, "", VAR_output, 1, 0, 0);
|
||||||
variables.add(var);
|
dsqlScratch->variables.add(var);
|
||||||
outputVariables.add(var);
|
dsqlScratch->outputVariables.add(var);
|
||||||
|
|
||||||
if (parameters.getCount() != 0)
|
if (parameters.getCount() != 0)
|
||||||
{
|
{
|
||||||
@ -1695,14 +1690,15 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dsql_var* const variable = (dsql_var*) outputVariables[0]->nod_arg[Dsql::e_var_variable];
|
dsql_var* const variable =
|
||||||
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
(dsql_var*) dsqlScratch->outputVariables[0]->nod_arg[Dsql::e_var_variable];
|
||||||
|
dsqlScratch->putLocalVariable(variable, 0, NULL);
|
||||||
|
|
||||||
// ASF: This is here to not change the old logic (proc_flag)
|
// ASF: This is here to not change the old logic (proc_flag)
|
||||||
// of previous calls to PASS1_node and PASS1_statement.
|
// of previous calls to PASS1_node and PASS1_statement.
|
||||||
dsqlScratch->setPsql(true);
|
dsqlScratch->setPsql(true);
|
||||||
|
|
||||||
putLocalVariables(dsqlScratch, localDeclList, 1);
|
dsqlScratch->putLocalVariables(localDeclList, 1);
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_stall);
|
dsqlScratch->appendUChar(blr_stall);
|
||||||
// put a label before body of procedure,
|
// put a label before body of procedure,
|
||||||
@ -1714,10 +1710,9 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
|
|
||||||
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
|
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
|
||||||
|
|
||||||
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||||
dsqlScratch->appendUChar(blr_end);
|
dsqlScratch->appendUChar(blr_end);
|
||||||
genReturn(dsqlScratch, false);
|
dsqlScratch->genReturn(false);
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_end);
|
dsqlScratch->appendUChar(blr_end);
|
||||||
dsqlScratch->appendUChar(blr_eoc);
|
dsqlScratch->appendUChar(blr_eoc);
|
||||||
|
|
||||||
@ -1902,8 +1897,6 @@ void CreateAlterProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/)
|
|||||||
|
|
||||||
DdlNode* CreateAlterProcedureNode::internalDsqlPass()
|
DdlNode* CreateAlterProcedureNode::internalDsqlPass()
|
||||||
{
|
{
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
statement->setBlockNode(this);
|
|
||||||
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_PROCEDURE);
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_PROCEDURE);
|
||||||
|
|
||||||
const dsql_nod* variables = localDeclList;
|
const dsql_nod* variables = localDeclList;
|
||||||
@ -2340,11 +2333,9 @@ void CreateAlterProcedureNode::storeParameter(thread_db* tdbb, jrd_tra* transact
|
|||||||
string defaultSource = string(defaultString->str_data, defaultString->str_length);
|
string defaultSource = string(defaultString->str_data, defaultString->str_length);
|
||||||
attachment->storeMetaDataBlob(tdbb, transaction, &PRM.RDB$DEFAULT_SOURCE, defaultSource);
|
attachment->storeMetaDataBlob(tdbb, transaction, &PRM.RDB$DEFAULT_SOURCE, defaultSource);
|
||||||
|
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
|
|
||||||
dsqlScratch->getBlrData().clear();
|
dsqlScratch->getBlrData().clear();
|
||||||
|
|
||||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
if (dsqlScratch->isVersion4())
|
||||||
dsqlScratch->appendUChar(blr_version4);
|
dsqlScratch->appendUChar(blr_version4);
|
||||||
else
|
else
|
||||||
dsqlScratch->appendUChar(blr_version5);
|
dsqlScratch->appendUChar(blr_version5);
|
||||||
@ -2375,12 +2366,10 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
|||||||
|
|
||||||
invalid = true;
|
invalid = true;
|
||||||
|
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
|
|
||||||
dsqlScratch->beginDebug();
|
dsqlScratch->beginDebug();
|
||||||
dsqlScratch->getBlrData().clear();
|
dsqlScratch->getBlrData().clear();
|
||||||
|
|
||||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
if (dsqlScratch->isVersion4())
|
||||||
dsqlScratch->appendUChar(blr_version4);
|
dsqlScratch->appendUChar(blr_version4);
|
||||||
else
|
else
|
||||||
dsqlScratch->appendUChar(blr_version5);
|
dsqlScratch->appendUChar(blr_version5);
|
||||||
@ -2404,7 +2393,7 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
|||||||
dsqlScratch->appendUChar(blr_short);
|
dsqlScratch->appendUChar(blr_short);
|
||||||
dsqlScratch->appendUChar(0);
|
dsqlScratch->appendUChar(0);
|
||||||
|
|
||||||
variables.add(MAKE_variable(parameter.legacyField,
|
dsqlScratch->variables.add(MAKE_variable(parameter.legacyField,
|
||||||
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
|
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2429,8 +2418,8 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
|||||||
dsql_nod* const var = MAKE_variable(parameter.legacyField,
|
dsql_nod* const var = MAKE_variable(parameter.legacyField,
|
||||||
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
||||||
|
|
||||||
variables.add(var);
|
dsqlScratch->variables.add(var);
|
||||||
outputVariables.add(var);
|
dsqlScratch->outputVariables.add(var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2463,18 +2452,20 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
for (Array<dsql_nod*>::const_iterator i = dsqlScratch->outputVariables.begin();
|
||||||
|
i != dsqlScratch->outputVariables.end();
|
||||||
|
++i)
|
||||||
{
|
{
|
||||||
dsql_nod* parameter = *i;
|
dsql_nod* parameter = *i;
|
||||||
dsql_var* const variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
dsql_var* const variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
||||||
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
dsqlScratch->putLocalVariable(variable, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ASF: This is here to not change the old logic (proc_flag)
|
// ASF: This is here to not change the old logic (proc_flag)
|
||||||
// of previous calls to PASS1_node and PASS1_statement.
|
// of previous calls to PASS1_node and PASS1_statement.
|
||||||
dsqlScratch->setPsql(true);
|
dsqlScratch->setPsql(true);
|
||||||
|
|
||||||
putLocalVariables(dsqlScratch, localDeclList, returns.getCount());
|
dsqlScratch->putLocalVariables(localDeclList, returns.getCount());
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_stall);
|
dsqlScratch->appendUChar(blr_stall);
|
||||||
// put a label before body of procedure,
|
// put a label before body of procedure,
|
||||||
@ -2486,10 +2477,9 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
|||||||
|
|
||||||
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
|
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
|
||||||
|
|
||||||
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||||
dsqlScratch->appendUChar(blr_end);
|
dsqlScratch->appendUChar(blr_end);
|
||||||
genReturn(dsqlScratch, true);
|
dsqlScratch->genReturn(true);
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_end);
|
dsqlScratch->appendUChar(blr_end);
|
||||||
dsqlScratch->appendUChar(blr_eoc);
|
dsqlScratch->appendUChar(blr_eoc);
|
||||||
|
|
||||||
@ -2779,8 +2769,6 @@ void CreateAlterTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) co
|
|||||||
|
|
||||||
DdlNode* CreateAlterTriggerNode::internalDsqlPass()
|
DdlNode* CreateAlterTriggerNode::internalDsqlPass()
|
||||||
{
|
{
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
statement->setBlockNode(this);
|
|
||||||
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_TRIGGER);
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_TRIGGER);
|
||||||
|
|
||||||
if (type.specified)
|
if (type.specified)
|
||||||
@ -2876,8 +2864,6 @@ void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
|
|
||||||
if (body)
|
if (body)
|
||||||
{
|
{
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
|
|
||||||
dsqlScratch->beginDebug();
|
dsqlScratch->beginDebug();
|
||||||
dsqlScratch->getBlrData().clear();
|
dsqlScratch->getBlrData().clear();
|
||||||
|
|
||||||
@ -2921,7 +2907,7 @@ void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
|
|
||||||
// generate the trigger blr
|
// generate the trigger blr
|
||||||
|
|
||||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
if (dsqlScratch->isVersion4())
|
||||||
dsqlScratch->appendUChar(blr_version4);
|
dsqlScratch->appendUChar(blr_version4);
|
||||||
else
|
else
|
||||||
dsqlScratch->appendUChar(blr_version5);
|
dsqlScratch->appendUChar(blr_version5);
|
||||||
@ -2929,8 +2915,7 @@ void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
dsqlScratch->appendUChar(blr_begin);
|
dsqlScratch->appendUChar(blr_begin);
|
||||||
|
|
||||||
dsqlScratch->setPsql(true);
|
dsqlScratch->setPsql(true);
|
||||||
|
dsqlScratch->putLocalVariables(localDeclList, 0);
|
||||||
putLocalVariables(dsqlScratch, localDeclList, 0);
|
|
||||||
|
|
||||||
dsqlScratch->scopeLevel++;
|
dsqlScratch->scopeLevel++;
|
||||||
// dimitr: I see no reason to deny EXIT command in triggers,
|
// dimitr: I see no reason to deny EXIT command in triggers,
|
||||||
@ -2954,7 +2939,7 @@ void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
|||||||
// The statement type may have been set incorrectly when parsing
|
// The statement type may have been set incorrectly when parsing
|
||||||
// the trigger actions, so reset it to reflect the fact that this
|
// the trigger actions, so reset it to reflect the fact that this
|
||||||
// is a data definition statement; also reset the ddl node.
|
// is a data definition statement; also reset the ddl node.
|
||||||
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid = false;
|
invalid = false;
|
||||||
|
@ -229,12 +229,11 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class CreateAlterFunctionNode : public DdlNode, public BlockNode
|
class CreateAlterFunctionNode : public DdlNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CreateAlterFunctionNode(MemoryPool& pool, const Firebird::MetaName& aName)
|
CreateAlterFunctionNode(MemoryPool& pool, const Firebird::MetaName& aName)
|
||||||
: DdlNode(pool),
|
: DdlNode(pool),
|
||||||
BlockNode(pool, false),
|
|
||||||
name(pool, aName),
|
name(pool, aName),
|
||||||
create(true),
|
create(true),
|
||||||
alter(false),
|
alter(false),
|
||||||
@ -334,12 +333,11 @@ typedef RecreateNode<CreateAlterFunctionNode, DropFunctionNode, isc_dsql_recreat
|
|||||||
RecreateFunctionNode;
|
RecreateFunctionNode;
|
||||||
|
|
||||||
|
|
||||||
class CreateAlterProcedureNode : public DdlNode, public BlockNode
|
class CreateAlterProcedureNode : public DdlNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CreateAlterProcedureNode(MemoryPool& pool, const Firebird::MetaName& aName)
|
CreateAlterProcedureNode(MemoryPool& pool, const Firebird::MetaName& aName)
|
||||||
: DdlNode(pool),
|
: DdlNode(pool),
|
||||||
BlockNode(pool, true),
|
|
||||||
name(pool, aName),
|
name(pool, aName),
|
||||||
create(true),
|
create(true),
|
||||||
alter(false),
|
alter(false),
|
||||||
@ -476,12 +474,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class CreateAlterTriggerNode : public DdlNode, public BlockNode, public TriggerDefinition
|
class CreateAlterTriggerNode : public DdlNode, public TriggerDefinition
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CreateAlterTriggerNode(MemoryPool& p, const Firebird::MetaName& aName)
|
CreateAlterTriggerNode(MemoryPool& p, const Firebird::MetaName& aName)
|
||||||
: DdlNode(p),
|
: DdlNode(p),
|
||||||
BlockNode(p, false),
|
|
||||||
TriggerDefinition(p),
|
TriggerDefinition(p),
|
||||||
create(true),
|
create(true),
|
||||||
alter(false),
|
alter(false),
|
||||||
|
761
src/dsql/DsqlCompilerScratch.cpp
Normal file
761
src/dsql/DsqlCompilerScratch.cpp
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
/*
|
||||||
|
* The contents of this file are subject to the Interbase Public
|
||||||
|
* License Version 1.0 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.Inprise.com/IPL.html
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an
|
||||||
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing
|
||||||
|
* rights and limitations under the License.
|
||||||
|
*
|
||||||
|
* The Original Code was created by Inprise Corporation
|
||||||
|
* and its predecessors. Portions created by Inprise Corporation are
|
||||||
|
* Copyright (C) Inprise Corporation.
|
||||||
|
*
|
||||||
|
* All Rights Reserved.
|
||||||
|
* Contributor(s): ______________________________________.
|
||||||
|
* Adriano dos Santos Fernandes
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "firebird.h"
|
||||||
|
#include "../jrd/common.h"
|
||||||
|
#include "../dsql/DsqlCompilerScratch.h"
|
||||||
|
#include "../jrd/jrd.h"
|
||||||
|
#include "../jrd/blr.h"
|
||||||
|
#include "../dsql/node.h"
|
||||||
|
#include "../dsql/ddl_proto.h"
|
||||||
|
#include "../dsql/errd_proto.h"
|
||||||
|
#include "../dsql/gen_proto.h"
|
||||||
|
#include "../dsql/make_proto.h"
|
||||||
|
#include "../dsql/pass1_proto.h"
|
||||||
|
|
||||||
|
using namespace Firebird;
|
||||||
|
using namespace Dsql;
|
||||||
|
using namespace Jrd;
|
||||||
|
|
||||||
|
|
||||||
|
// Write out field data type.
|
||||||
|
// Taking special care to declare international text.
|
||||||
|
void DsqlCompilerScratch::putDtype(const dsql_fld* field, bool useSubType)
|
||||||
|
{
|
||||||
|
#ifdef DEV_BUILD
|
||||||
|
// Check if the field describes a known datatype
|
||||||
|
|
||||||
|
if (field->fld_dtype > FB_NELEM(blr_dtypes) || !blr_dtypes[field->fld_dtype])
|
||||||
|
{
|
||||||
|
SCHAR buffer[100];
|
||||||
|
|
||||||
|
sprintf(buffer, "Invalid dtype %d in BlockNode::putDtype", field->fld_dtype);
|
||||||
|
ERRD_bugcheck(buffer);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (field->fld_not_nullable)
|
||||||
|
appendUChar(blr_not_nullable);
|
||||||
|
|
||||||
|
if (field->fld_type_of_name.hasData())
|
||||||
|
{
|
||||||
|
if (field->fld_type_of_table)
|
||||||
|
{
|
||||||
|
if (field->fld_explicit_collation)
|
||||||
|
{
|
||||||
|
appendUChar(blr_column_name2);
|
||||||
|
appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
||||||
|
appendMetaString(field->fld_type_of_table->str_data);
|
||||||
|
appendMetaString(field->fld_type_of_name.c_str());
|
||||||
|
appendUShort(field->fld_ttype);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendUChar(blr_column_name);
|
||||||
|
appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
||||||
|
appendMetaString(field->fld_type_of_table->str_data);
|
||||||
|
appendMetaString(field->fld_type_of_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (field->fld_explicit_collation)
|
||||||
|
{
|
||||||
|
appendUChar(blr_domain_name2);
|
||||||
|
appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
||||||
|
appendMetaString(field->fld_type_of_name.c_str());
|
||||||
|
appendUShort(field->fld_ttype);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendUChar(blr_domain_name);
|
||||||
|
appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
||||||
|
appendMetaString(field->fld_type_of_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field->fld_dtype)
|
||||||
|
{
|
||||||
|
case dtype_cstring:
|
||||||
|
case dtype_text:
|
||||||
|
case dtype_varying:
|
||||||
|
case dtype_blob:
|
||||||
|
if (!useSubType)
|
||||||
|
appendUChar(blr_dtypes[field->fld_dtype]);
|
||||||
|
else if (field->fld_dtype == dtype_varying)
|
||||||
|
{
|
||||||
|
appendUChar(blr_varying2);
|
||||||
|
appendUShort(field->fld_ttype);
|
||||||
|
}
|
||||||
|
else if (field->fld_dtype == dtype_cstring)
|
||||||
|
{
|
||||||
|
appendUChar(blr_cstring2);
|
||||||
|
appendUShort(field->fld_ttype);
|
||||||
|
}
|
||||||
|
else if (field->fld_dtype == dtype_blob)
|
||||||
|
{
|
||||||
|
appendUChar(blr_blob2);
|
||||||
|
appendUShort(field->fld_sub_type);
|
||||||
|
appendUShort(field->fld_ttype);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendUChar(blr_text2);
|
||||||
|
appendUShort(field->fld_ttype);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field->fld_dtype == dtype_varying)
|
||||||
|
appendUShort(field->fld_length - sizeof(USHORT));
|
||||||
|
else if (field->fld_dtype != dtype_blob)
|
||||||
|
appendUShort(field->fld_length);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
appendUChar(blr_dtypes[field->fld_dtype]);
|
||||||
|
if (DTYPE_IS_EXACT(field->fld_dtype) || (dtype_quad == field->fld_dtype))
|
||||||
|
appendUChar(field->fld_scale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit dyn for the local variables declared in a procedure or trigger.
|
||||||
|
void DsqlCompilerScratch::putLocalVariables(const dsql_nod* parameters, SSHORT locals)
|
||||||
|
{
|
||||||
|
if (!parameters)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dsql_nod* const* ptr = parameters->nod_arg;
|
||||||
|
|
||||||
|
for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++)
|
||||||
|
{
|
||||||
|
dsql_nod* parameter = *ptr;
|
||||||
|
|
||||||
|
putDebugSrcInfo(parameter->nod_line, parameter->nod_column);
|
||||||
|
|
||||||
|
if (parameter->nod_type == Dsql::nod_def_field)
|
||||||
|
{
|
||||||
|
dsql_fld* field = (dsql_fld*) parameter->nod_arg[Dsql::e_dfl_field];
|
||||||
|
const dsql_nod* const* rest = ptr;
|
||||||
|
|
||||||
|
while (++rest != end)
|
||||||
|
{
|
||||||
|
if ((*rest)->nod_type == Dsql::nod_def_field)
|
||||||
|
{
|
||||||
|
const dsql_fld* rest_field = (dsql_fld*) (*rest)->nod_arg[Dsql::e_dfl_field];
|
||||||
|
if (field->fld_name == rest_field->fld_name)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
|
||||||
|
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(field->fld_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsql_nod* varNode = MAKE_variable(field, field->fld_name.c_str(), VAR_local, 0, 0, locals);
|
||||||
|
variables.add(varNode);
|
||||||
|
|
||||||
|
dsql_var* variable = (dsql_var*) varNode->nod_arg[Dsql::e_var_variable];
|
||||||
|
putLocalVariable(variable, parameter,
|
||||||
|
reinterpret_cast<const dsql_str*>(parameter->nod_arg[Dsql::e_dfl_collate]));
|
||||||
|
|
||||||
|
// Some field attributes are calculated inside
|
||||||
|
// putLocalVariable(), so we reinitialize the
|
||||||
|
// descriptor
|
||||||
|
MAKE_desc_from_field(&varNode->nod_desc, field);
|
||||||
|
|
||||||
|
++locals;
|
||||||
|
}
|
||||||
|
else if (parameter->nod_type == Dsql::nod_cursor)
|
||||||
|
{
|
||||||
|
PASS1_statement(this, parameter);
|
||||||
|
GEN_statement(this, parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out local variable field data type.
|
||||||
|
void DsqlCompilerScratch::putLocalVariable(dsql_var* variable, dsql_nod* hostParam,
|
||||||
|
const dsql_str* collationName)
|
||||||
|
{
|
||||||
|
dsql_fld* field = variable->var_field;
|
||||||
|
|
||||||
|
appendUChar(blr_dcl_variable);
|
||||||
|
appendUShort(variable->var_variable_number);
|
||||||
|
DDL_resolve_intl_type(this, field, collationName);
|
||||||
|
|
||||||
|
//const USHORT dtype = field->fld_dtype;
|
||||||
|
|
||||||
|
putDtype(field, true);
|
||||||
|
//field->fld_dtype = dtype;
|
||||||
|
|
||||||
|
// Check for a default value, borrowed from define_domain
|
||||||
|
dsql_nod* node = hostParam ? hostParam->nod_arg[Dsql::e_dfl_default] : NULL;
|
||||||
|
|
||||||
|
if (node || (!field->fld_full_domain && !field->fld_not_nullable))
|
||||||
|
{
|
||||||
|
appendUChar(blr_assignment);
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
fb_assert(node->nod_type == Dsql::nod_def_default);
|
||||||
|
PsqlChanger psqlChanger(this, false);
|
||||||
|
node = PASS1_node(this, node->nod_arg[Dsql::e_dft_default]);
|
||||||
|
GEN_expr(this, node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendUChar(blr_null); // Initialize variable to NULL
|
||||||
|
|
||||||
|
appendUChar(blr_variable);
|
||||||
|
appendUShort(variable->var_variable_number);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendUChar(blr_init_variable);
|
||||||
|
appendUShort(variable->var_variable_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variable->var_name[0]) // Not a function return value
|
||||||
|
putDebugVariable(variable->var_variable_number, variable->var_name);
|
||||||
|
|
||||||
|
++hiddenVarsNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to resolve variable name against parameters and local variables.
|
||||||
|
dsql_nod* DsqlCompilerScratch::resolveVariable(const dsql_str* varName)
|
||||||
|
{
|
||||||
|
for (dsql_nod* const* i = variables.begin(); i != variables.end(); ++i)
|
||||||
|
{
|
||||||
|
dsql_nod* varNode = *i;
|
||||||
|
fb_assert(varNode->nod_type == Dsql::nod_variable);
|
||||||
|
|
||||||
|
if (varNode->nod_type == Dsql::nod_variable)
|
||||||
|
{
|
||||||
|
const dsql_var* variable = (dsql_var*) varNode->nod_arg[Dsql::e_var_variable];
|
||||||
|
DEV_BLKCHK(variable, dsql_type_var);
|
||||||
|
|
||||||
|
if (!strcmp(varName->str_data, variable->var_name))
|
||||||
|
return varNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate BLR for a return.
|
||||||
|
void DsqlCompilerScratch::genReturn(bool eosFlag)
|
||||||
|
{
|
||||||
|
const bool hasEos = !(flags & (FLAG_TRIGGER | FLAG_FUNCTION));
|
||||||
|
|
||||||
|
if (hasEos && !eosFlag)
|
||||||
|
appendUChar(blr_begin);
|
||||||
|
|
||||||
|
appendUChar(blr_send);
|
||||||
|
appendUChar(1);
|
||||||
|
appendUChar(blr_begin);
|
||||||
|
|
||||||
|
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
||||||
|
{
|
||||||
|
const dsql_nod* parameter = *i;
|
||||||
|
const dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
||||||
|
appendUChar(blr_assignment);
|
||||||
|
appendUChar(blr_variable);
|
||||||
|
appendUShort(variable->var_variable_number);
|
||||||
|
appendUChar(blr_parameter2);
|
||||||
|
appendUChar(variable->var_msg_number);
|
||||||
|
appendUShort(variable->var_msg_item);
|
||||||
|
appendUShort(variable->var_msg_item + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasEos)
|
||||||
|
{
|
||||||
|
appendUChar(blr_assignment);
|
||||||
|
appendUChar(blr_literal);
|
||||||
|
appendUChar(blr_short);
|
||||||
|
appendUChar(0);
|
||||||
|
appendUShort((eosFlag ? 0 : 1));
|
||||||
|
appendUChar(blr_parameter);
|
||||||
|
appendUChar(1);
|
||||||
|
appendUShort(USHORT(2 * outputVariables.getCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendUChar(blr_end);
|
||||||
|
|
||||||
|
if (hasEos && !eosFlag)
|
||||||
|
{
|
||||||
|
appendUChar(blr_stall);
|
||||||
|
appendUChar(blr_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DsqlCompilerScratch::addCTEs(dsql_nod* with)
|
||||||
|
{
|
||||||
|
DEV_BLKCHK(with, dsql_type_nod);
|
||||||
|
fb_assert(with->nod_type == Dsql::nod_with);
|
||||||
|
|
||||||
|
if (ctes.getCount())
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// WITH clause can't be nested
|
||||||
|
Arg::Gds(isc_dsql_cte_nested_with));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (with->nod_flags & NOD_UNION_RECURSIVE)
|
||||||
|
flags |= DsqlCompilerScratch::FLAG_RECURSIVE_CTE;
|
||||||
|
|
||||||
|
const dsql_nod* list = with->nod_arg[0];
|
||||||
|
const dsql_nod* const* end = list->nod_arg + list->nod_count;
|
||||||
|
|
||||||
|
for (dsql_nod* const* cte = list->nod_arg; cte < end; cte++)
|
||||||
|
{
|
||||||
|
fb_assert((*cte)->nod_type == Dsql::nod_derived_table);
|
||||||
|
|
||||||
|
if (with->nod_flags & NOD_UNION_RECURSIVE)
|
||||||
|
{
|
||||||
|
currCtes.push(*cte);
|
||||||
|
PsqlChanger changer(this, false);
|
||||||
|
ctes.add(pass1RecursiveCte(*cte));
|
||||||
|
currCtes.pop();
|
||||||
|
|
||||||
|
// Add CTE name into CTE aliases stack. It allows later to search for
|
||||||
|
// aliases of given CTE.
|
||||||
|
const dsql_str* cteName = (dsql_str*) (*cte)->nod_arg[Dsql::e_derived_table_alias];
|
||||||
|
addCTEAlias(cteName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ctes.add(*cte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsql_nod* DsqlCompilerScratch::findCTE(const dsql_str* name)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < ctes.getCount(); ++i)
|
||||||
|
{
|
||||||
|
dsql_nod* cte = ctes[i];
|
||||||
|
const dsql_str* cteName = (dsql_str*) cte->nod_arg[Dsql::e_derived_table_alias];
|
||||||
|
|
||||||
|
if (name->str_length == cteName->str_length &&
|
||||||
|
strncmp(name->str_data, cteName->str_data, cteName->str_length) == 0)
|
||||||
|
{
|
||||||
|
return cte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DsqlCompilerScratch::clearCTEs()
|
||||||
|
{
|
||||||
|
flags &= ~DsqlCompilerScratch::FLAG_RECURSIVE_CTE;
|
||||||
|
ctes.clear();
|
||||||
|
cteAliases.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DsqlCompilerScratch::checkUnusedCTEs() const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < ctes.getCount(); ++i)
|
||||||
|
{
|
||||||
|
const dsql_nod* cte = ctes[i];
|
||||||
|
|
||||||
|
if (!(cte->nod_flags & NOD_DT_CTE_USED))
|
||||||
|
{
|
||||||
|
const dsql_str* cteName = (dsql_str*) cte->nod_arg[Dsql::e_derived_table_alias];
|
||||||
|
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
Arg::Gds(isc_dsql_cte_not_used) << Arg::Str(cteName->str_data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process derived table which can be recursive CTE.
|
||||||
|
// If it is non-recursive return input node unchanged.
|
||||||
|
// If it is recursive return new derived table which is an union of union of anchor (non-recursive)
|
||||||
|
// queries and union of recursive queries. Check recursive queries to satisfy various criterias.
|
||||||
|
// Note that our parser is right-to-left therefore nested list linked as first node in parent list
|
||||||
|
// and second node is always query spec.
|
||||||
|
// For example, if we have 4 CTE's where first two is non-recursive and last two is recursive:
|
||||||
|
//
|
||||||
|
// list union
|
||||||
|
// [0] [1] [0] [1]
|
||||||
|
// list cte3 ===> anchor recursive
|
||||||
|
// [0] [1] [0] [1] [0] [1]
|
||||||
|
// list cte3 cte1 cte2 cte3 cte4
|
||||||
|
// [0] [1]
|
||||||
|
// cte1 cte2
|
||||||
|
//
|
||||||
|
// Also, we should not change layout of original parse tree to allow it to be parsed again if
|
||||||
|
// needed. Therefore recursive part is built using newly allocated list nodes.
|
||||||
|
dsql_nod* DsqlCompilerScratch::pass1RecursiveCte(dsql_nod* input)
|
||||||
|
{
|
||||||
|
dsql_str* const cte_alias = (dsql_str*) input->nod_arg[Dsql::e_derived_table_alias];
|
||||||
|
dsql_nod* const select_expr = input->nod_arg[Dsql::e_derived_table_rse];
|
||||||
|
dsql_nod* query = select_expr->nod_arg[Dsql::e_sel_query_spec];
|
||||||
|
|
||||||
|
if (query->nod_type != Dsql::nod_list && pass1RseIsRecursive(query))
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive CTE (%s) must be an UNION
|
||||||
|
Arg::Gds(isc_dsql_cte_not_a_union) << Arg::Str(cte_alias->str_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// split queries list on two parts: anchor and recursive
|
||||||
|
dsql_nod* anchorRse = NULL, *recursiveRse = NULL;
|
||||||
|
dsql_nod* qry = query;
|
||||||
|
|
||||||
|
dsql_nod* newQry = MAKE_node(Dsql::nod_list, 2);
|
||||||
|
newQry->nod_flags = query->nod_flags;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
dsql_nod* rse = NULL;
|
||||||
|
|
||||||
|
if (qry->nod_type == Dsql::nod_list)
|
||||||
|
rse = qry->nod_arg[1];
|
||||||
|
else
|
||||||
|
rse = qry;
|
||||||
|
|
||||||
|
dsql_nod* newRse = pass1RseIsRecursive(rse);
|
||||||
|
|
||||||
|
if (newRse) // rse is recursive
|
||||||
|
{
|
||||||
|
if (anchorRse)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// CTE '%s' defined non-recursive member after recursive
|
||||||
|
Arg::Gds(isc_dsql_cte_nonrecurs_after_recurs) << Arg::Str(cte_alias->str_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newRse->nod_arg[Dsql::e_qry_distinct])
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE '%s' has %s clause
|
||||||
|
Arg::Gds(isc_dsql_cte_wrong_clause) << Arg::Str(cte_alias->str_data) <<
|
||||||
|
Arg::Str("DISTINCT"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newRse->nod_arg[Dsql::e_qry_group])
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE '%s' has %s clause
|
||||||
|
Arg::Gds(isc_dsql_cte_wrong_clause) << Arg::Str(cte_alias->str_data) <<
|
||||||
|
Arg::Str("GROUP BY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newRse->nod_arg[Dsql::e_qry_having])
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE '%s' has %s clause
|
||||||
|
Arg::Gds(isc_dsql_cte_wrong_clause) << Arg::Str(cte_alias->str_data) <<
|
||||||
|
Arg::Str("HAVING"));
|
||||||
|
}
|
||||||
|
// hvlad: we need also forbid any aggregate function here
|
||||||
|
// but for now i have no idea how to do it simple
|
||||||
|
|
||||||
|
if ((newQry->nod_type == Dsql::nod_list) && !(newQry->nod_flags & NOD_UNION_ALL))
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive members of CTE (%s) must be linked with another members via UNION ALL
|
||||||
|
Arg::Gds(isc_dsql_cte_union_all) << Arg::Str(cte_alias->str_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recursiveRse)
|
||||||
|
recursiveRse = newQry;
|
||||||
|
|
||||||
|
newRse->nod_flags |= NOD_SELECT_EXPR_RECURSIVE;
|
||||||
|
|
||||||
|
if (qry->nod_type == Dsql::nod_list)
|
||||||
|
newQry->nod_arg[1] = newRse;
|
||||||
|
else
|
||||||
|
newQry->nod_arg[0] = newRse;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (qry->nod_type == Dsql::nod_list)
|
||||||
|
newQry->nod_arg[1] = rse;
|
||||||
|
else
|
||||||
|
newQry->nod_arg[0] = rse;
|
||||||
|
|
||||||
|
if (!anchorRse)
|
||||||
|
{
|
||||||
|
if (qry->nod_type == Dsql::nod_list)
|
||||||
|
anchorRse = newQry;
|
||||||
|
else
|
||||||
|
anchorRse = rse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qry->nod_type != Dsql::nod_list)
|
||||||
|
break;
|
||||||
|
|
||||||
|
qry = qry->nod_arg[0];
|
||||||
|
|
||||||
|
if (qry->nod_type == Dsql::nod_list)
|
||||||
|
{
|
||||||
|
newQry->nod_arg[0] = MAKE_node(Dsql::nod_list, 2);
|
||||||
|
newQry = newQry->nod_arg[0];
|
||||||
|
newQry->nod_flags = qry->nod_flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recursiveRse)
|
||||||
|
return input;
|
||||||
|
|
||||||
|
if (!anchorRse)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Non-recursive member is missing in CTE '%s'
|
||||||
|
Arg::Gds(isc_dsql_cte_miss_nonrecursive) << Arg::Str(cte_alias->str_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
qry = recursiveRse;
|
||||||
|
dsql_nod* list = NULL;
|
||||||
|
|
||||||
|
while (qry->nod_arg[0] != anchorRse)
|
||||||
|
{
|
||||||
|
list = qry;
|
||||||
|
qry = qry->nod_arg[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
qry->nod_arg[0] = 0;
|
||||||
|
|
||||||
|
if (list)
|
||||||
|
list->nod_arg[0] = qry->nod_arg[1];
|
||||||
|
else
|
||||||
|
recursiveRse = qry->nod_arg[1];
|
||||||
|
|
||||||
|
dsql_nod* unionNode = MAKE_node(Dsql::nod_list, 2);
|
||||||
|
unionNode->nod_flags = NOD_UNION_ALL | NOD_UNION_RECURSIVE;
|
||||||
|
unionNode->nod_arg[0] = anchorRse;
|
||||||
|
unionNode->nod_arg[1] = recursiveRse;
|
||||||
|
|
||||||
|
dsql_nod* select = MAKE_node(Dsql::nod_select_expr, Dsql::e_sel_count);
|
||||||
|
select->nod_arg[Dsql::e_sel_query_spec] = unionNode;
|
||||||
|
select->nod_arg[Dsql::e_sel_order] = select->nod_arg[Dsql::e_sel_rows] =
|
||||||
|
select->nod_arg[Dsql::e_sel_with_list] = NULL;
|
||||||
|
|
||||||
|
dsql_nod* node = MAKE_node(Dsql::nod_derived_table, Dsql::e_derived_table_count);
|
||||||
|
dsql_str* alias = (dsql_str*) input->nod_arg[Dsql::e_derived_table_alias];
|
||||||
|
node->nod_arg[Dsql::e_derived_table_alias] = (dsql_nod*) alias;
|
||||||
|
node->nod_arg[Dsql::e_derived_table_column_alias] =
|
||||||
|
input->nod_arg[Dsql::e_derived_table_column_alias];
|
||||||
|
node->nod_arg[Dsql::e_derived_table_rse] = select;
|
||||||
|
node->nod_arg[Dsql::e_derived_table_context] = input->nod_arg[Dsql::e_derived_table_context];
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if rse is recursive. If recursive reference is a table in the FROM list remove it.
|
||||||
|
// If recursive reference is a part of join add join boolean (returned by pass1JoinIsRecursive)
|
||||||
|
// to the WHERE clause. Punt if more than one recursive reference is found.
|
||||||
|
dsql_nod* DsqlCompilerScratch::pass1RseIsRecursive(dsql_nod* input)
|
||||||
|
{
|
||||||
|
fb_assert(input->nod_type == Dsql::nod_query_spec);
|
||||||
|
|
||||||
|
dsql_nod* result = MAKE_node(Dsql::nod_query_spec, Dsql::e_qry_count);
|
||||||
|
memcpy(result->nod_arg, input->nod_arg, Dsql::e_qry_count * sizeof(dsql_nod*));
|
||||||
|
|
||||||
|
dsql_nod* srcTables = input->nod_arg[Dsql::e_qry_from];
|
||||||
|
dsql_nod* dstTables = MAKE_node(Dsql::nod_list, srcTables->nod_count);
|
||||||
|
result->nod_arg[Dsql::e_qry_from] = dstTables;
|
||||||
|
|
||||||
|
dsql_nod** pDstTable = dstTables->nod_arg;
|
||||||
|
dsql_nod** pSrcTable = srcTables->nod_arg;
|
||||||
|
dsql_nod** end = srcTables->nod_arg + srcTables->nod_count;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (dsql_nod** prev = pDstTable; pSrcTable < end; ++pSrcTable, ++pDstTable)
|
||||||
|
{
|
||||||
|
*prev++ = *pDstTable = *pSrcTable;
|
||||||
|
|
||||||
|
switch ((*pDstTable)->nod_type)
|
||||||
|
{
|
||||||
|
case Dsql::nod_rel_proc_name:
|
||||||
|
case Dsql::nod_relation_name:
|
||||||
|
if (pass1RelProcIsRecursive(*pDstTable))
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE can't reference itself more than once
|
||||||
|
Arg::Gds(isc_dsql_cte_mult_references));
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
prev--;
|
||||||
|
dstTables->nod_count--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Dsql::nod_join:
|
||||||
|
{
|
||||||
|
*pDstTable = MAKE_node(Dsql::nod_join, Dsql::e_join_count);
|
||||||
|
memcpy((*pDstTable)->nod_arg, (*pSrcTable)->nod_arg,
|
||||||
|
Dsql::e_join_count * sizeof(dsql_nod*));
|
||||||
|
|
||||||
|
dsql_nod* joinBool = pass1JoinIsRecursive(*pDstTable);
|
||||||
|
if (joinBool)
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE can't reference itself more than once
|
||||||
|
Arg::Gds(isc_dsql_cte_mult_references));
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
result->nod_arg[Dsql::e_qry_where] =
|
||||||
|
PASS1_compose(result->nod_arg[Dsql::e_qry_where], joinBool, Dsql::nod_and);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Dsql::nod_derived_table:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fb_assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found ? result : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if table reference is recursive i.e. its name is equal to the name of current processing CTE.
|
||||||
|
bool DsqlCompilerScratch::pass1RelProcIsRecursive(dsql_nod* input)
|
||||||
|
{
|
||||||
|
const dsql_str* relName = NULL;
|
||||||
|
const dsql_str* relAlias = NULL;
|
||||||
|
|
||||||
|
switch (input->nod_type)
|
||||||
|
{
|
||||||
|
case Dsql::nod_rel_proc_name:
|
||||||
|
relName = (dsql_str*) input->nod_arg[Dsql::e_rpn_name];
|
||||||
|
relAlias = (dsql_str*) input->nod_arg[Dsql::e_rpn_alias];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Dsql::nod_relation_name:
|
||||||
|
relName = (dsql_str*) input->nod_arg[Dsql::e_rln_name];
|
||||||
|
relAlias = (dsql_str*) input->nod_arg[Dsql::e_rln_alias];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fb_assert(currCtes.hasData());
|
||||||
|
const dsql_nod* curr_cte = currCtes.object();
|
||||||
|
const dsql_str* cte_name = (dsql_str*) curr_cte->nod_arg[Dsql::e_derived_table_alias];
|
||||||
|
|
||||||
|
const bool recursive = (cte_name->str_length == relName->str_length) &&
|
||||||
|
(strncmp(relName->str_data, cte_name->str_data, cte_name->str_length) == 0);
|
||||||
|
|
||||||
|
if (recursive)
|
||||||
|
addCTEAlias(relAlias ? relAlias : relName);
|
||||||
|
|
||||||
|
return recursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if join have recursive members. If found remove this member from join and return its
|
||||||
|
// boolean (to be added into WHERE clause).
|
||||||
|
// We must remove member only if it is a table reference. Punt if recursive reference is found in
|
||||||
|
// outer join or more than one recursive reference is found
|
||||||
|
dsql_nod* DsqlCompilerScratch::pass1JoinIsRecursive(dsql_nod*& input)
|
||||||
|
{
|
||||||
|
const NOD_TYPE join_type = input->nod_arg[Dsql::e_join_type]->nod_type;
|
||||||
|
bool remove = false;
|
||||||
|
|
||||||
|
bool leftRecursive = false;
|
||||||
|
dsql_nod* leftBool = NULL;
|
||||||
|
dsql_nod** join_table = &input->nod_arg[Dsql::e_join_left_rel];
|
||||||
|
|
||||||
|
if ((*join_table)->nod_type == Dsql::nod_join)
|
||||||
|
{
|
||||||
|
leftBool = pass1JoinIsRecursive(*join_table);
|
||||||
|
leftRecursive = (leftBool != NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
leftBool = input->nod_arg[Dsql::e_join_boolean];
|
||||||
|
leftRecursive = pass1RelProcIsRecursive(*join_table);
|
||||||
|
|
||||||
|
if (leftRecursive)
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftRecursive && join_type != Dsql::nod_join_inner)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE can't be member of an outer join
|
||||||
|
Arg::Gds(isc_dsql_cte_outer_join));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rightRecursive = false;
|
||||||
|
dsql_nod* rightBool = NULL;
|
||||||
|
|
||||||
|
join_table = &input->nod_arg[Dsql::e_join_rght_rel];
|
||||||
|
|
||||||
|
if ((*join_table)->nod_type == Dsql::nod_join)
|
||||||
|
{
|
||||||
|
rightBool = pass1JoinIsRecursive(*join_table);
|
||||||
|
rightRecursive = (rightBool != NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rightBool = input->nod_arg[Dsql::e_join_boolean];
|
||||||
|
rightRecursive = pass1RelProcIsRecursive(*join_table);
|
||||||
|
|
||||||
|
if (rightRecursive)
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightRecursive && join_type != Dsql::nod_join_inner)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE can't be member of an outer join
|
||||||
|
Arg::Gds(isc_dsql_cte_outer_join));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftRecursive && rightRecursive)
|
||||||
|
{
|
||||||
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||||
|
// Recursive member of CTE can't reference itself more than once
|
||||||
|
Arg::Gds(isc_dsql_cte_mult_references));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftRecursive)
|
||||||
|
{
|
||||||
|
if (remove)
|
||||||
|
input = input->nod_arg[Dsql::e_join_rght_rel];
|
||||||
|
|
||||||
|
return leftBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightRecursive)
|
||||||
|
{
|
||||||
|
if (remove)
|
||||||
|
input = input->nod_arg[Dsql::e_join_left_rel];
|
||||||
|
|
||||||
|
return rightBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
281
src/dsql/DsqlCompilerScratch.h
Normal file
281
src/dsql/DsqlCompilerScratch.h
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Interbase Public
|
||||||
|
* License Version 1.0 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.Inprise.com/IPL.html
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an
|
||||||
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing
|
||||||
|
* rights and limitations under the License.
|
||||||
|
*
|
||||||
|
* The Original Code was created by Inprise Corporation
|
||||||
|
* and its predecessors. Portions created by Inprise Corporation are
|
||||||
|
* Copyright (C) Inprise Corporation.
|
||||||
|
*
|
||||||
|
* All Rights Reserved.
|
||||||
|
* Contributor(s): ______________________________________.
|
||||||
|
* Adriano dos Santos Fernandes
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DSQL_COMPILER_SCRATCH_H
|
||||||
|
#define DSQL_COMPILER_SCRATCH_H
|
||||||
|
|
||||||
|
#include "../jrd/common.h"
|
||||||
|
#include "../dsql/dsql.h"
|
||||||
|
#include "../dsql/BlrWriter.h"
|
||||||
|
#include "../common/classes/array.h"
|
||||||
|
#include "../common/classes/MetaName.h"
|
||||||
|
#include "../common/classes/stack.h"
|
||||||
|
#include "../common/classes/alloc.h"
|
||||||
|
|
||||||
|
namespace Jrd
|
||||||
|
{
|
||||||
|
|
||||||
|
// DSQL Compiler scratch block - may be discarded after compilation in the future.
|
||||||
|
class DsqlCompilerScratch : public BlrWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const unsigned FLAG_IN_AUTO_TRANS_BLOCK = 0x001;
|
||||||
|
static const unsigned FLAG_RETURNING_INTO = 0x002;
|
||||||
|
static const unsigned FLAG_METADATA_SAVED = 0x004;
|
||||||
|
static const unsigned FLAG_PROCEDURE = 0x008;
|
||||||
|
static const unsigned FLAG_TRIGGER = 0x010;
|
||||||
|
static const unsigned FLAG_BLOCK = 0x020;
|
||||||
|
static const unsigned FLAG_RECURSIVE_CTE = 0x040;
|
||||||
|
static const unsigned FLAG_UPDATE_OR_INSERT = 0x080;
|
||||||
|
static const unsigned FLAG_MERGE = 0x100;
|
||||||
|
static const unsigned FLAG_FUNCTION = 0x200;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DsqlCompilerScratch(MemoryPool& p, dsql_dbb* aDbb, jrd_tra* aTransaction,
|
||||||
|
DsqlCompiledStatement* aStatement)
|
||||||
|
: BlrWriter(p),
|
||||||
|
dbb(aDbb),
|
||||||
|
transaction(aTransaction),
|
||||||
|
statement(aStatement),
|
||||||
|
flags(0),
|
||||||
|
ports(p),
|
||||||
|
relation(NULL),
|
||||||
|
procedure(NULL),
|
||||||
|
mainContext(p),
|
||||||
|
context(&mainContext),
|
||||||
|
unionContext(p),
|
||||||
|
derivedContext(p),
|
||||||
|
outerAggContext(NULL),
|
||||||
|
contextNumber(0),
|
||||||
|
derivedContextNumber(0),
|
||||||
|
scopeLevel(0),
|
||||||
|
loopLevel(0),
|
||||||
|
labels(p),
|
||||||
|
cursorNumber(0),
|
||||||
|
cursors(p),
|
||||||
|
inSelectList(0),
|
||||||
|
inWhereClause(0),
|
||||||
|
inGroupByClause(0),
|
||||||
|
inHavingClause(0),
|
||||||
|
inOrderByClause(0),
|
||||||
|
errorHandlers(0),
|
||||||
|
clientDialect(0),
|
||||||
|
inOuterJoin(0),
|
||||||
|
aliasRelationPrefix(NULL),
|
||||||
|
hiddenVars(p),
|
||||||
|
hiddenVarsNumber(0),
|
||||||
|
package(p),
|
||||||
|
currCtes(p),
|
||||||
|
recursiveCtx(0),
|
||||||
|
recursiveCtxId(0),
|
||||||
|
processingWindow(false),
|
||||||
|
checkConstraintTrigger(false),
|
||||||
|
variables(p),
|
||||||
|
outputVariables(p),
|
||||||
|
ctes(p),
|
||||||
|
cteAliases(p),
|
||||||
|
currCteAlias(NULL),
|
||||||
|
psql(false)
|
||||||
|
{
|
||||||
|
domainValue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// DsqlCompilerScratch should never be destroyed using delete.
|
||||||
|
// It dies together with it's pool in release_request().
|
||||||
|
~DsqlCompilerScratch()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isDdlDyn()
|
||||||
|
{
|
||||||
|
return (statement->getType() == DsqlCompiledStatement::TYPE_DDL || statement->getDdlNode()) &&
|
||||||
|
!(flags & FLAG_BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual bool isVersion4()
|
||||||
|
{
|
||||||
|
return statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryPool& getPool()
|
||||||
|
{
|
||||||
|
return PermanentStorage::getPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
dsql_dbb* getAttachment()
|
||||||
|
{
|
||||||
|
return dbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
jrd_tra* getTransaction()
|
||||||
|
{
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTransaction(jrd_tra* value)
|
||||||
|
{
|
||||||
|
transaction = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
DsqlCompiledStatement* getStatement()
|
||||||
|
{
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
DsqlCompiledStatement* getStatement() const
|
||||||
|
{
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putDtype(const dsql_fld* field, bool useSubType);
|
||||||
|
void putLocalVariables(const dsql_nod* parameters, SSHORT locals);
|
||||||
|
void putLocalVariable(dsql_var* variable, dsql_nod* hostParam, const dsql_str* collationName);
|
||||||
|
dsql_nod* resolveVariable(const dsql_str* varName);
|
||||||
|
void genReturn(bool eosFlag = false);
|
||||||
|
|
||||||
|
void addCTEs(dsql_nod* list);
|
||||||
|
dsql_nod* findCTE(const dsql_str* name);
|
||||||
|
void clearCTEs();
|
||||||
|
void checkUnusedCTEs() const;
|
||||||
|
|
||||||
|
// hvlad: each member of recursive CTE can refer to CTE itself (only once) via
|
||||||
|
// CTE name or via alias. We need to substitute this aliases when processing CTE
|
||||||
|
// member to resolve field names. Therefore we store all aliases in order of
|
||||||
|
// occurrence and later use it in backward order (since our parser is right-to-left).
|
||||||
|
// Also we put CTE name after all such aliases to distinguish aliases for
|
||||||
|
// different CTE's.
|
||||||
|
// We also need to repeat this process if main select expression contains union with
|
||||||
|
// recursive CTE
|
||||||
|
void addCTEAlias(const dsql_str* alias)
|
||||||
|
{
|
||||||
|
cteAliases.add(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dsql_str* getNextCTEAlias()
|
||||||
|
{
|
||||||
|
return *(--currCteAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetCTEAlias(const dsql_str* alias)
|
||||||
|
{
|
||||||
|
const dsql_str* const* begin = cteAliases.begin();
|
||||||
|
|
||||||
|
currCteAlias = cteAliases.end() - 1;
|
||||||
|
fb_assert(currCteAlias >= begin);
|
||||||
|
|
||||||
|
const dsql_str* curr = *(currCteAlias);
|
||||||
|
while (strcmp(curr->str_data, alias->str_data))
|
||||||
|
{
|
||||||
|
currCteAlias--;
|
||||||
|
fb_assert(currCteAlias >= begin);
|
||||||
|
|
||||||
|
curr = *(currCteAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPsql() const { return psql; }
|
||||||
|
void setPsql(bool value) { psql = value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
dsql_nod* pass1RecursiveCte(dsql_nod* input);
|
||||||
|
dsql_nod* pass1RseIsRecursive(dsql_nod* input);
|
||||||
|
bool pass1RelProcIsRecursive(dsql_nod* input);
|
||||||
|
dsql_nod* pass1JoinIsRecursive(dsql_nod*& input);
|
||||||
|
|
||||||
|
private:
|
||||||
|
dsql_dbb* dbb; // DSQL attachment
|
||||||
|
jrd_tra* transaction; // Transaction
|
||||||
|
DsqlCompiledStatement* statement; // Compiled statement
|
||||||
|
|
||||||
|
public:
|
||||||
|
unsigned flags; // flags
|
||||||
|
Firebird::Array<dsql_msg*> ports; // Port messages
|
||||||
|
dsql_rel* relation; // relation created by this request (for DDL)
|
||||||
|
dsql_prc* procedure; // procedure created by this request (for DDL)
|
||||||
|
DsqlContextStack mainContext;
|
||||||
|
DsqlContextStack* context;
|
||||||
|
DsqlContextStack unionContext; // Save contexts for views of unions
|
||||||
|
DsqlContextStack derivedContext; // Save contexts for views of derived tables
|
||||||
|
dsql_ctx* outerAggContext; // agg context for outer ref
|
||||||
|
USHORT contextNumber; // Next available context number
|
||||||
|
USHORT derivedContextNumber; // Next available context number for derived tables
|
||||||
|
USHORT scopeLevel; // Scope level for parsing aliases in subqueries
|
||||||
|
USHORT loopLevel; // Loop level
|
||||||
|
DsqlStrStack labels; // Loop labels
|
||||||
|
USHORT cursorNumber; // Cursor number
|
||||||
|
DsqlNodStack cursors; // Cursors
|
||||||
|
USHORT inSelectList; // now processing "select list"
|
||||||
|
USHORT inWhereClause; // processing "where clause"
|
||||||
|
USHORT inGroupByClause; // processing "group by clause"
|
||||||
|
USHORT inHavingClause; // processing "having clause"
|
||||||
|
USHORT inOrderByClause; // processing "order by clause"
|
||||||
|
USHORT errorHandlers; // count of active error handlers
|
||||||
|
USHORT clientDialect; // dialect passed into the API call
|
||||||
|
USHORT inOuterJoin; // processing inside outer-join part
|
||||||
|
dsql_str* aliasRelationPrefix; // prefix for every relation-alias.
|
||||||
|
DsqlNodStack hiddenVars; // hidden variables
|
||||||
|
USHORT hiddenVarsNumber; // next hidden variable number
|
||||||
|
Firebird::MetaName package; // package being defined
|
||||||
|
DsqlNodStack currCtes; // current processing CTE's
|
||||||
|
class dsql_ctx* recursiveCtx; // context of recursive CTE
|
||||||
|
USHORT recursiveCtxId; // id of recursive union stream context
|
||||||
|
bool processingWindow; // processing window functions
|
||||||
|
bool checkConstraintTrigger; // compiling a check constraint trigger
|
||||||
|
dsc domainValue; // VALUE in the context of domain's check constraint
|
||||||
|
Firebird::Array<dsql_nod*> variables;
|
||||||
|
Firebird::Array<dsql_nod*> outputVariables;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Firebird::HalfStaticArray<dsql_nod*, 4> ctes; // common table expressions
|
||||||
|
Firebird::HalfStaticArray<const dsql_str*, 4> cteAliases; // CTE aliases in recursive members
|
||||||
|
const dsql_str* const* currCteAlias;
|
||||||
|
bool psql;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PsqlChanger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PsqlChanger(DsqlCompilerScratch* aDsqlScratch, bool value)
|
||||||
|
: dsqlScratch(aDsqlScratch),
|
||||||
|
oldValue(dsqlScratch->isPsql())
|
||||||
|
{
|
||||||
|
dsqlScratch->setPsql(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
~PsqlChanger()
|
||||||
|
{
|
||||||
|
dsqlScratch->setPsql(oldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// copying is prohibited
|
||||||
|
PsqlChanger(const PsqlChanger&);
|
||||||
|
PsqlChanger& operator =(const PsqlChanger&);
|
||||||
|
|
||||||
|
DsqlCompilerScratch* dsqlScratch;
|
||||||
|
const bool oldValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Jrd
|
||||||
|
|
||||||
|
#endif // DSQL_COMPILER_SCRATCH_H
|
@ -24,7 +24,7 @@
|
|||||||
#define DSQL_NODES_H
|
#define DSQL_NODES_H
|
||||||
|
|
||||||
#include "../jrd/common.h"
|
#include "../jrd/common.h"
|
||||||
#include "../dsql/dsql.h"
|
#include "../dsql/DsqlCompilerScratch.h"
|
||||||
#include "../dsql/node.h"
|
#include "../dsql/node.h"
|
||||||
#include "../dsql/Visitors.h"
|
#include "../dsql/Visitors.h"
|
||||||
#include "../common/classes/array.h"
|
#include "../common/classes/array.h"
|
||||||
@ -611,39 +611,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Common node for all "code blocks" (i.e.: procedures, triggers and execute block)
|
|
||||||
class BlockNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit BlockNode(MemoryPool& pool, bool aHasEos)
|
|
||||||
: hasEos(aHasEos),
|
|
||||||
variables(pool),
|
|
||||||
outputVariables(pool)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~BlockNode()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void putDtype(DsqlCompilerScratch* dsqlScratch, const dsql_fld* field, bool useSubType);
|
|
||||||
|
|
||||||
void putLocalVariables(DsqlCompilerScratch* dsqlScratch, const dsql_nod* parameters,
|
|
||||||
SSHORT locals);
|
|
||||||
void putLocalVariable(DsqlCompilerScratch* dsqlScratch, dsql_var* variable,
|
|
||||||
dsql_nod* hostParam, const dsql_str* collationName);
|
|
||||||
dsql_nod* resolveVariable(const dsql_str* varName);
|
|
||||||
void genReturn(DsqlCompilerScratch* dsqlScratch, bool eosFlag = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool hasEos;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Firebird::Array<dsql_nod*> variables;
|
|
||||||
Firebird::Array<dsql_nod*> outputVariables;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#endif // DSQL_NODES_H
|
#endif // DSQL_NODES_H
|
||||||
|
@ -52,278 +52,6 @@ using namespace Jrd;
|
|||||||
namespace Jrd {
|
namespace Jrd {
|
||||||
|
|
||||||
|
|
||||||
// Write out field data type.
|
|
||||||
// Taking special care to declare international text.
|
|
||||||
void BlockNode::putDtype(DsqlCompilerScratch* dsqlScratch, const dsql_fld* field, bool useSubType)
|
|
||||||
{
|
|
||||||
#ifdef DEV_BUILD
|
|
||||||
// Check if the field describes a known datatype
|
|
||||||
|
|
||||||
if (field->fld_dtype > FB_NELEM(blr_dtypes) || !blr_dtypes[field->fld_dtype])
|
|
||||||
{
|
|
||||||
SCHAR buffer[100];
|
|
||||||
|
|
||||||
sprintf(buffer, "Invalid dtype %d in BlockNode::putDtype", field->fld_dtype);
|
|
||||||
ERRD_bugcheck(buffer);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (field->fld_not_nullable)
|
|
||||||
dsqlScratch->appendUChar(blr_not_nullable);
|
|
||||||
|
|
||||||
if (field->fld_type_of_name.hasData())
|
|
||||||
{
|
|
||||||
if (field->fld_type_of_table)
|
|
||||||
{
|
|
||||||
if (field->fld_explicit_collation)
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_column_name2);
|
|
||||||
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
||||||
dsqlScratch->appendMetaString(field->fld_type_of_table->str_data);
|
|
||||||
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
|
||||||
dsqlScratch->appendUShort(field->fld_ttype);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_column_name);
|
|
||||||
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
||||||
dsqlScratch->appendMetaString(field->fld_type_of_table->str_data);
|
|
||||||
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (field->fld_explicit_collation)
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_domain_name2);
|
|
||||||
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
||||||
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
|
||||||
dsqlScratch->appendUShort(field->fld_ttype);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_domain_name);
|
|
||||||
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
||||||
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (field->fld_dtype)
|
|
||||||
{
|
|
||||||
case dtype_cstring:
|
|
||||||
case dtype_text:
|
|
||||||
case dtype_varying:
|
|
||||||
case dtype_blob:
|
|
||||||
if (!useSubType)
|
|
||||||
dsqlScratch->appendUChar(blr_dtypes[field->fld_dtype]);
|
|
||||||
else if (field->fld_dtype == dtype_varying)
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_varying2);
|
|
||||||
dsqlScratch->appendUShort(field->fld_ttype);
|
|
||||||
}
|
|
||||||
else if (field->fld_dtype == dtype_cstring)
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_cstring2);
|
|
||||||
dsqlScratch->appendUShort(field->fld_ttype);
|
|
||||||
}
|
|
||||||
else if (field->fld_dtype == dtype_blob)
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_blob2);
|
|
||||||
dsqlScratch->appendUShort(field->fld_sub_type);
|
|
||||||
dsqlScratch->appendUShort(field->fld_ttype);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_text2);
|
|
||||||
dsqlScratch->appendUShort(field->fld_ttype);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field->fld_dtype == dtype_varying)
|
|
||||||
dsqlScratch->appendUShort(field->fld_length - sizeof(USHORT));
|
|
||||||
else if (field->fld_dtype != dtype_blob)
|
|
||||||
dsqlScratch->appendUShort(field->fld_length);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
dsqlScratch->appendUChar(blr_dtypes[field->fld_dtype]);
|
|
||||||
if (DTYPE_IS_EXACT(field->fld_dtype) || (dtype_quad == field->fld_dtype))
|
|
||||||
dsqlScratch->appendUChar(field->fld_scale);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit dyn for the local variables declared in a procedure or trigger.
|
|
||||||
void BlockNode::putLocalVariables(DsqlCompilerScratch* dsqlScratch, const dsql_nod* parameters,
|
|
||||||
SSHORT locals)
|
|
||||||
{
|
|
||||||
if (!parameters)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dsql_nod* const* ptr = parameters->nod_arg;
|
|
||||||
for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++)
|
|
||||||
{
|
|
||||||
dsql_nod* parameter = *ptr;
|
|
||||||
|
|
||||||
dsqlScratch->putDebugSrcInfo(parameter->nod_line, parameter->nod_column);
|
|
||||||
|
|
||||||
if (parameter->nod_type == Dsql::nod_def_field)
|
|
||||||
{
|
|
||||||
dsql_fld* field = (dsql_fld*) parameter->nod_arg[Dsql::e_dfl_field];
|
|
||||||
const dsql_nod* const* rest = ptr;
|
|
||||||
while (++rest != end)
|
|
||||||
{
|
|
||||||
if ((*rest)->nod_type == Dsql::nod_def_field)
|
|
||||||
{
|
|
||||||
const dsql_fld* rest_field = (dsql_fld*) (*rest)->nod_arg[Dsql::e_dfl_field];
|
|
||||||
if (field->fld_name == rest_field->fld_name)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
|
|
||||||
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(field->fld_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dsql_nod* var_node = MAKE_variable(field, field->fld_name.c_str(), VAR_local, 0, 0, locals);
|
|
||||||
variables.add(var_node);
|
|
||||||
|
|
||||||
dsql_var* variable = (dsql_var*) var_node->nod_arg[Dsql::e_var_variable];
|
|
||||||
putLocalVariable(dsqlScratch, variable, parameter,
|
|
||||||
reinterpret_cast<const dsql_str*>(parameter->nod_arg[Dsql::e_dfl_collate]));
|
|
||||||
|
|
||||||
// Some field attributes are calculated inside
|
|
||||||
// putLocalVariable(), so we reinitialize the
|
|
||||||
// descriptor
|
|
||||||
MAKE_desc_from_field(&var_node->nod_desc, field);
|
|
||||||
|
|
||||||
locals++;
|
|
||||||
}
|
|
||||||
else if (parameter->nod_type == Dsql::nod_cursor)
|
|
||||||
{
|
|
||||||
PASS1_statement(dsqlScratch, parameter);
|
|
||||||
GEN_statement(dsqlScratch, parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out local variable field data type.
|
|
||||||
void BlockNode::putLocalVariable(DsqlCompilerScratch* dsqlScratch, dsql_var* variable,
|
|
||||||
dsql_nod* hostParam, const dsql_str* collationName)
|
|
||||||
{
|
|
||||||
dsql_fld* field = variable->var_field;
|
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_dcl_variable);
|
|
||||||
dsqlScratch->appendUShort(variable->var_variable_number);
|
|
||||||
DDL_resolve_intl_type(dsqlScratch, field, collationName);
|
|
||||||
|
|
||||||
//const USHORT dtype = field->fld_dtype;
|
|
||||||
|
|
||||||
putDtype(dsqlScratch, field, true);
|
|
||||||
//field->fld_dtype = dtype;
|
|
||||||
|
|
||||||
// Check for a default value, borrowed from define_domain
|
|
||||||
dsql_nod* node = hostParam ? hostParam->nod_arg[Dsql::e_dfl_default] : NULL;
|
|
||||||
|
|
||||||
if (node || (!field->fld_full_domain && !field->fld_not_nullable))
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_assignment);
|
|
||||||
|
|
||||||
if (node)
|
|
||||||
{
|
|
||||||
fb_assert(node->nod_type == Dsql::nod_def_default);
|
|
||||||
PsqlChanger psqlChanger(dsqlScratch, false);
|
|
||||||
node = PASS1_node(dsqlScratch, node->nod_arg[Dsql::e_dft_default]);
|
|
||||||
GEN_expr(dsqlScratch, node);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
dsqlScratch->appendUChar(blr_null); // Initialize variable to NULL
|
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_variable);
|
|
||||||
dsqlScratch->appendUShort(variable->var_variable_number);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_init_variable);
|
|
||||||
dsqlScratch->appendUShort(variable->var_variable_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (variable->var_name[0]) // Not a function return value
|
|
||||||
dsqlScratch->putDebugVariable(variable->var_variable_number, variable->var_name);
|
|
||||||
|
|
||||||
++dsqlScratch->hiddenVarsNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to resolve variable name against parameters and local variables.
|
|
||||||
dsql_nod* BlockNode::resolveVariable(const dsql_str* varName)
|
|
||||||
{
|
|
||||||
for (dsql_nod* const* i = variables.begin(); i != variables.end(); ++i)
|
|
||||||
{
|
|
||||||
dsql_nod* var_node = *i;
|
|
||||||
fb_assert(var_node->nod_type == Dsql::nod_variable);
|
|
||||||
|
|
||||||
if (var_node->nod_type == Dsql::nod_variable)
|
|
||||||
{
|
|
||||||
const dsql_var* variable = (dsql_var*) var_node->nod_arg[Dsql::e_var_variable];
|
|
||||||
DEV_BLKCHK(variable, dsql_type_var);
|
|
||||||
|
|
||||||
if (!strcmp(varName->str_data, variable->var_name))
|
|
||||||
return var_node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate BLR for a return.
|
|
||||||
void BlockNode::genReturn(DsqlCompilerScratch* dsqlScratch, bool eosFlag)
|
|
||||||
{
|
|
||||||
if (hasEos && !eosFlag)
|
|
||||||
dsqlScratch->appendUChar(blr_begin);
|
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_send);
|
|
||||||
dsqlScratch->appendUChar(1);
|
|
||||||
dsqlScratch->appendUChar(blr_begin);
|
|
||||||
|
|
||||||
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
|
||||||
{
|
|
||||||
const dsql_nod* parameter = *i;
|
|
||||||
const dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
|
||||||
dsqlScratch->appendUChar(blr_assignment);
|
|
||||||
dsqlScratch->appendUChar(blr_variable);
|
|
||||||
dsqlScratch->appendUShort(variable->var_variable_number);
|
|
||||||
dsqlScratch->appendUChar(blr_parameter2);
|
|
||||||
dsqlScratch->appendUChar(variable->var_msg_number);
|
|
||||||
dsqlScratch->appendUShort(variable->var_msg_item);
|
|
||||||
dsqlScratch->appendUShort(variable->var_msg_item + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasEos)
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_assignment);
|
|
||||||
dsqlScratch->appendUChar(blr_literal);
|
|
||||||
dsqlScratch->appendUChar(blr_short);
|
|
||||||
dsqlScratch->appendUChar(0);
|
|
||||||
dsqlScratch->appendUShort((eosFlag ? 0 : 1));
|
|
||||||
dsqlScratch->appendUChar(blr_parameter);
|
|
||||||
dsqlScratch->appendUChar(1);
|
|
||||||
dsqlScratch->appendUShort(USHORT(2 * outputVariables.getCount()));
|
|
||||||
}
|
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_end);
|
|
||||||
|
|
||||||
if (hasEos && !eosFlag)
|
|
||||||
{
|
|
||||||
dsqlScratch->appendUChar(blr_stall);
|
|
||||||
dsqlScratch->appendUChar(blr_end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//--------------------
|
|
||||||
|
|
||||||
|
|
||||||
DmlNode* DmlNode::pass1(thread_db* tdbb, CompilerScratch* csb, jrd_nod* aNode)
|
DmlNode* DmlNode::pass1(thread_db* tdbb, CompilerScratch* csb, jrd_nod* aNode)
|
||||||
{
|
{
|
||||||
node = aNode;
|
node = aNode;
|
||||||
@ -678,8 +406,6 @@ ExecBlockNode* ExecBlockNode::internalDsqlPass()
|
|||||||
{
|
{
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||||
|
|
||||||
statement->setBlockNode(this);
|
|
||||||
|
|
||||||
if (returns.hasData())
|
if (returns.hasData())
|
||||||
statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK);
|
statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK);
|
||||||
else
|
else
|
||||||
@ -793,11 +519,6 @@ void ExecBlockNode::print(string& text, Array<dsql_nod*>& nodes) const
|
|||||||
|
|
||||||
void ExecBlockNode::genBlr()
|
void ExecBlockNode::genBlr()
|
||||||
{
|
{
|
||||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
||||||
|
|
||||||
// Update blockNode, because we have a reference to the original unprocessed node.
|
|
||||||
statement->setBlockNode(this);
|
|
||||||
|
|
||||||
dsqlScratch->beginDebug();
|
dsqlScratch->beginDebug();
|
||||||
|
|
||||||
// now do the input parameters
|
// now do the input parameters
|
||||||
@ -808,10 +529,10 @@ void ExecBlockNode::genBlr()
|
|||||||
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
||||||
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0);
|
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0);
|
||||||
|
|
||||||
variables.add(var);
|
dsqlScratch->variables.add(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned returnsPos = variables.getCount();
|
const unsigned returnsPos = dsqlScratch->variables.getCount();
|
||||||
|
|
||||||
// now do the output parameters
|
// now do the output parameters
|
||||||
for (size_t i = 0; i < returns.getCount(); ++i)
|
for (size_t i = 0; i < returns.getCount(); ++i)
|
||||||
@ -821,10 +542,12 @@ void ExecBlockNode::genBlr()
|
|||||||
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
||||||
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
||||||
|
|
||||||
variables.add(var);
|
dsqlScratch->variables.add(var);
|
||||||
outputVariables.add(var);
|
dsqlScratch->outputVariables.add(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_begin);
|
dsqlScratch->appendUChar(blr_begin);
|
||||||
|
|
||||||
if (parameters.hasData())
|
if (parameters.hasData())
|
||||||
@ -835,10 +558,12 @@ void ExecBlockNode::genBlr()
|
|||||||
else
|
else
|
||||||
statement->setSendMsg(NULL);
|
statement->setSendMsg(NULL);
|
||||||
|
|
||||||
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
for (Array<dsql_nod*>::const_iterator i = dsqlScratch->outputVariables.begin();
|
||||||
|
i != dsqlScratch->outputVariables.end();
|
||||||
|
++i)
|
||||||
{
|
{
|
||||||
dsql_par* param = MAKE_parameter(statement->getReceiveMsg(), true, true,
|
dsql_par* param = MAKE_parameter(statement->getReceiveMsg(), true, true,
|
||||||
(i - outputVariables.begin()) + 1, *i);
|
(i - dsqlScratch->outputVariables.begin()) + 1, *i);
|
||||||
param->par_node = *i;
|
param->par_node = *i;
|
||||||
MAKE_desc(dsqlScratch, ¶m->par_desc, *i, NULL);
|
MAKE_desc(dsqlScratch, ¶m->par_desc, *i, NULL);
|
||||||
param->par_desc.dsc_flags |= DSC_nullable;
|
param->par_desc.dsc_flags |= DSC_nullable;
|
||||||
@ -864,7 +589,7 @@ void ExecBlockNode::genBlr()
|
|||||||
|
|
||||||
for (unsigned i = 0; i < returnsPos; ++i)
|
for (unsigned i = 0; i < returnsPos; ++i)
|
||||||
{
|
{
|
||||||
const dsql_nod* parameter = variables[i];
|
const dsql_nod* parameter = dsqlScratch->variables[i];
|
||||||
const dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
const dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
||||||
const dsql_fld* field = variable->var_field;
|
const dsql_fld* field = variable->var_field;
|
||||||
|
|
||||||
@ -875,7 +600,7 @@ void ExecBlockNode::genBlr()
|
|||||||
// connection charset influence. So to validate, we cast them and assign to null.
|
// connection charset influence. So to validate, we cast them and assign to null.
|
||||||
dsqlScratch->appendUChar(blr_assignment);
|
dsqlScratch->appendUChar(blr_assignment);
|
||||||
dsqlScratch->appendUChar(blr_cast);
|
dsqlScratch->appendUChar(blr_cast);
|
||||||
BlockNode::putDtype(dsqlScratch, field, true);
|
dsqlScratch->putDtype(field, true);
|
||||||
dsqlScratch->appendUChar(blr_parameter2);
|
dsqlScratch->appendUChar(blr_parameter2);
|
||||||
dsqlScratch->appendUChar(0);
|
dsqlScratch->appendUChar(0);
|
||||||
dsqlScratch->appendUShort(variable->var_msg_item);
|
dsqlScratch->appendUShort(variable->var_msg_item);
|
||||||
@ -884,16 +609,18 @@ void ExecBlockNode::genBlr()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
for (Array<dsql_nod*>::const_iterator i = dsqlScratch->outputVariables.begin();
|
||||||
|
i != dsqlScratch->outputVariables.end();
|
||||||
|
++i)
|
||||||
{
|
{
|
||||||
dsql_nod* parameter = *i;
|
dsql_nod* parameter = *i;
|
||||||
dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
||||||
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
dsqlScratch->putLocalVariable(variable, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
dsqlScratch->setPsql(true);
|
dsqlScratch->setPsql(true);
|
||||||
|
|
||||||
putLocalVariables(dsqlScratch, localDeclList, USHORT(returns.getCount()));
|
dsqlScratch->putLocalVariables(localDeclList, USHORT(returns.getCount()));
|
||||||
|
|
||||||
dsqlScratch->loopLevel = 0;
|
dsqlScratch->loopLevel = 0;
|
||||||
|
|
||||||
@ -912,7 +639,7 @@ void ExecBlockNode::genBlr()
|
|||||||
statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK);
|
statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK);
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_end);
|
dsqlScratch->appendUChar(blr_end);
|
||||||
genReturn(dsqlScratch, true);
|
dsqlScratch->genReturn(true);
|
||||||
dsqlScratch->appendUChar(blr_end);
|
dsqlScratch->appendUChar(blr_end);
|
||||||
|
|
||||||
dsqlScratch->endDebug();
|
dsqlScratch->endDebug();
|
||||||
@ -1799,8 +1526,6 @@ SuspendNode* SuspendNode::internalDsqlPass()
|
|||||||
|
|
||||||
statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE);
|
statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE);
|
||||||
|
|
||||||
blockNode = statement->getBlockNode();
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1813,8 +1538,7 @@ void SuspendNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|||||||
|
|
||||||
void SuspendNode::genBlr()
|
void SuspendNode::genBlr()
|
||||||
{
|
{
|
||||||
if (blockNode)
|
dsqlScratch->genReturn();
|
||||||
blockNode->genReturn(dsqlScratch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1879,7 +1603,6 @@ ReturnNode* ReturnNode::internalDsqlPass()
|
|||||||
|
|
||||||
ReturnNode* node = FB_NEW(getPool()) ReturnNode(getPool());
|
ReturnNode* node = FB_NEW(getPool()) ReturnNode(getPool());
|
||||||
node->dsqlScratch = dsqlScratch;
|
node->dsqlScratch = dsqlScratch;
|
||||||
node->blockNode = statement->getBlockNode();
|
|
||||||
node->value = PASS1_node(dsqlScratch, value);
|
node->value = PASS1_node(dsqlScratch, value);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@ -1899,9 +1622,7 @@ void ReturnNode::genBlr()
|
|||||||
GEN_expr(dsqlScratch, value);
|
GEN_expr(dsqlScratch, value);
|
||||||
dsqlScratch->appendUChar(blr_variable);
|
dsqlScratch->appendUChar(blr_variable);
|
||||||
dsqlScratch->appendUShort(0);
|
dsqlScratch->appendUShort(0);
|
||||||
|
dsqlScratch->genReturn();
|
||||||
blockNode->genReturn(dsqlScratch);
|
|
||||||
|
|
||||||
dsqlScratch->appendUChar(blr_leave);
|
dsqlScratch->appendUChar(blr_leave);
|
||||||
dsqlScratch->appendUChar(0);
|
dsqlScratch->appendUChar(0);
|
||||||
}
|
}
|
||||||
|
@ -103,12 +103,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ExecBlockNode : public DsqlOnlyStmtNode, public BlockNode
|
class ExecBlockNode : public DsqlOnlyStmtNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ExecBlockNode(MemoryPool& pool)
|
explicit ExecBlockNode(MemoryPool& pool)
|
||||||
: DsqlOnlyStmtNode(pool),
|
: DsqlOnlyStmtNode(pool),
|
||||||
BlockNode(pool, true),
|
|
||||||
parameters(pool),
|
parameters(pool),
|
||||||
returns(pool),
|
returns(pool),
|
||||||
localDeclList(NULL),
|
localDeclList(NULL),
|
||||||
@ -319,7 +318,6 @@ class SuspendNode : public StmtNode
|
|||||||
public:
|
public:
|
||||||
explicit SuspendNode(MemoryPool& pool)
|
explicit SuspendNode(MemoryPool& pool)
|
||||||
: StmtNode(pool),
|
: StmtNode(pool),
|
||||||
blockNode(NULL),
|
|
||||||
message(NULL),
|
message(NULL),
|
||||||
statement(NULL)
|
statement(NULL)
|
||||||
{
|
{
|
||||||
@ -339,7 +337,6 @@ public:
|
|||||||
virtual const jrd_nod* execute(thread_db* tdbb, jrd_req* request) const;
|
virtual const jrd_nod* execute(thread_db* tdbb, jrd_req* request) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BlockNode* blockNode;
|
|
||||||
NestConst<jrd_nod> message;
|
NestConst<jrd_nod> message;
|
||||||
NestConst<jrd_nod> statement;
|
NestConst<jrd_nod> statement;
|
||||||
};
|
};
|
||||||
@ -361,7 +358,6 @@ public:
|
|||||||
virtual void genBlr();
|
virtual void genBlr();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BlockNode* blockNode;
|
|
||||||
dsql_nod* value;
|
dsql_nod* value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
244
src/dsql/dsql.h
244
src/dsql/dsql.h
@ -66,14 +66,14 @@ const char* const TEMP_CONTEXT = "TEMP";
|
|||||||
|
|
||||||
namespace Jrd
|
namespace Jrd
|
||||||
{
|
{
|
||||||
class Database;
|
|
||||||
class Attachment;
|
class Attachment;
|
||||||
|
class Database;
|
||||||
|
class DsqlCompilerScratch;
|
||||||
class jrd_tra;
|
class jrd_tra;
|
||||||
class jrd_req;
|
class jrd_req;
|
||||||
class blb;
|
class blb;
|
||||||
struct bid;
|
struct bid;
|
||||||
|
|
||||||
class BlockNode;
|
|
||||||
class dsql_blb;
|
class dsql_blb;
|
||||||
class dsql_ctx;
|
class dsql_ctx;
|
||||||
class dsql_msg;
|
class dsql_msg;
|
||||||
@ -103,8 +103,6 @@ namespace Firebird
|
|||||||
|
|
||||||
namespace Jrd {
|
namespace Jrd {
|
||||||
|
|
||||||
class DsqlCompilerScratch;
|
|
||||||
|
|
||||||
//! generic data type used to store strings
|
//! generic data type used to store strings
|
||||||
class dsql_str : public pool_alloc_rpt<char, dsql_type_str>
|
class dsql_str : public pool_alloc_rpt<char, dsql_type_str>
|
||||||
{
|
{
|
||||||
@ -416,7 +414,6 @@ public:
|
|||||||
type(TYPE_SELECT),
|
type(TYPE_SELECT),
|
||||||
flags(0),
|
flags(0),
|
||||||
ddlData(p),
|
ddlData(p),
|
||||||
blockNode(NULL),
|
|
||||||
ddlNode(NULL),
|
ddlNode(NULL),
|
||||||
blob(NULL),
|
blob(NULL),
|
||||||
sendMsg(NULL),
|
sendMsg(NULL),
|
||||||
@ -448,10 +445,6 @@ public:
|
|||||||
const Firebird::HalfStaticArray<UCHAR, 1024>& getDdlData() const { return ddlData; }
|
const Firebird::HalfStaticArray<UCHAR, 1024>& getDdlData() const { return ddlData; }
|
||||||
void setDdlData(Firebird::HalfStaticArray<UCHAR, 1024>& value) { ddlData = value; }
|
void setDdlData(Firebird::HalfStaticArray<UCHAR, 1024>& value) { ddlData = value; }
|
||||||
|
|
||||||
BlockNode* getBlockNode() { return blockNode; }
|
|
||||||
const BlockNode* getBlockNode() const { return blockNode; }
|
|
||||||
void setBlockNode(BlockNode* value) { blockNode = value; }
|
|
||||||
|
|
||||||
dsql_nod* getDdlNode() { return ddlNode; }
|
dsql_nod* getDdlNode() { return ddlNode; }
|
||||||
const dsql_nod* getDdlNode() const { return ddlNode; }
|
const dsql_nod* getDdlNode() const { return ddlNode; }
|
||||||
void setDdlNode(dsql_nod* value) { ddlNode = value; }
|
void setDdlNode(dsql_nod* value) { ddlNode = value; }
|
||||||
@ -496,7 +489,6 @@ private:
|
|||||||
ULONG flags; // generic flag
|
ULONG flags; // generic flag
|
||||||
Firebird::RefStrPtr sqlText;
|
Firebird::RefStrPtr sqlText;
|
||||||
Firebird::HalfStaticArray<UCHAR, 1024> ddlData;
|
Firebird::HalfStaticArray<UCHAR, 1024> ddlData;
|
||||||
BlockNode* blockNode; // Defining block
|
|
||||||
dsql_nod* ddlNode; // Store metadata statement
|
dsql_nod* ddlNode; // Store metadata statement
|
||||||
dsql_blb* blob; // Blob info for blob statements
|
dsql_blb* blob; // Blob info for blob statements
|
||||||
dsql_msg* sendMsg; // Message to be sent to start request
|
dsql_msg* sendMsg; // Message to be sent to start request
|
||||||
@ -580,236 +572,6 @@ protected:
|
|||||||
friend class Firebird::MemoryPool;
|
friend class Firebird::MemoryPool;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// DSQL Compiler scratch block - may be discarded after compilation in the future.
|
|
||||||
class DsqlCompilerScratch : public BlrWriter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static const unsigned FLAG_IN_AUTO_TRANS_BLOCK = 0x001;
|
|
||||||
static const unsigned FLAG_RETURNING_INTO = 0x002;
|
|
||||||
static const unsigned FLAG_METADATA_SAVED = 0x004;
|
|
||||||
static const unsigned FLAG_PROCEDURE = 0x008;
|
|
||||||
static const unsigned FLAG_TRIGGER = 0x010;
|
|
||||||
static const unsigned FLAG_BLOCK = 0x020;
|
|
||||||
static const unsigned FLAG_RECURSIVE_CTE = 0x040;
|
|
||||||
static const unsigned FLAG_UPDATE_OR_INSERT = 0x080;
|
|
||||||
static const unsigned FLAG_MERGE = 0x100;
|
|
||||||
static const unsigned FLAG_FUNCTION = 0x200;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit DsqlCompilerScratch(MemoryPool& p, dsql_dbb* aDbb, jrd_tra* aTransaction,
|
|
||||||
DsqlCompiledStatement* aStatement)
|
|
||||||
: BlrWriter(p),
|
|
||||||
dbb(aDbb),
|
|
||||||
transaction(aTransaction),
|
|
||||||
statement(aStatement),
|
|
||||||
flags(0),
|
|
||||||
ports(p),
|
|
||||||
relation(NULL),
|
|
||||||
procedure(NULL),
|
|
||||||
mainContext(p),
|
|
||||||
context(&mainContext),
|
|
||||||
unionContext(p),
|
|
||||||
derivedContext(p),
|
|
||||||
outerAggContext(NULL),
|
|
||||||
contextNumber(0),
|
|
||||||
derivedContextNumber(0),
|
|
||||||
scopeLevel(0),
|
|
||||||
loopLevel(0),
|
|
||||||
labels(p),
|
|
||||||
cursorNumber(0),
|
|
||||||
cursors(p),
|
|
||||||
inSelectList(0),
|
|
||||||
inWhereClause(0),
|
|
||||||
inGroupByClause(0),
|
|
||||||
inHavingClause(0),
|
|
||||||
inOrderByClause(0),
|
|
||||||
errorHandlers(0),
|
|
||||||
clientDialect(0),
|
|
||||||
inOuterJoin(0),
|
|
||||||
aliasRelationPrefix(NULL),
|
|
||||||
hiddenVars(p),
|
|
||||||
hiddenVarsNumber(0),
|
|
||||||
package(p),
|
|
||||||
currCtes(p),
|
|
||||||
recursiveCtx(0),
|
|
||||||
recursiveCtxId(0),
|
|
||||||
processingWindow(false),
|
|
||||||
checkConstraintTrigger(false),
|
|
||||||
ctes(p),
|
|
||||||
cteAliases(p),
|
|
||||||
currCteAlias(NULL),
|
|
||||||
psql(false)
|
|
||||||
{
|
|
||||||
domainValue.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// DsqlCompilerScratch should never be destroyed using delete.
|
|
||||||
// It dies together with it's pool in release_request().
|
|
||||||
~DsqlCompilerScratch()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool isDdlDyn()
|
|
||||||
{
|
|
||||||
return (statement->getType() == DsqlCompiledStatement::TYPE_DDL || statement->getDdlNode()) &&
|
|
||||||
!statement->getBlockNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual bool isVersion4()
|
|
||||||
{
|
|
||||||
return statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryPool& getPool()
|
|
||||||
{
|
|
||||||
return PermanentStorage::getPool();
|
|
||||||
}
|
|
||||||
|
|
||||||
dsql_dbb* getAttachment()
|
|
||||||
{
|
|
||||||
return dbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
jrd_tra* getTransaction()
|
|
||||||
{
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTransaction(jrd_tra* value)
|
|
||||||
{
|
|
||||||
transaction = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
DsqlCompiledStatement* getStatement()
|
|
||||||
{
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
DsqlCompiledStatement* getStatement() const
|
|
||||||
{
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addCTEs(dsql_nod* list);
|
|
||||||
dsql_nod* findCTE(const dsql_str* name);
|
|
||||||
void clearCTEs();
|
|
||||||
void checkUnusedCTEs() const;
|
|
||||||
|
|
||||||
// hvlad: each member of recursive CTE can refer to CTE itself (only once) via
|
|
||||||
// CTE name or via alias. We need to substitute this aliases when processing CTE
|
|
||||||
// member to resolve field names. Therefore we store all aliases in order of
|
|
||||||
// occurrence and later use it in backward order (since our parser is right-to-left).
|
|
||||||
// Also we put CTE name after all such aliases to distinguish aliases for
|
|
||||||
// different CTE's.
|
|
||||||
// We also need to repeat this process if main select expression contains union with
|
|
||||||
// recursive CTE
|
|
||||||
void addCTEAlias(const dsql_str* alias)
|
|
||||||
{
|
|
||||||
cteAliases.add(alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dsql_str* getNextCTEAlias()
|
|
||||||
{
|
|
||||||
return *(--currCteAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetCTEAlias(const dsql_str* alias)
|
|
||||||
{
|
|
||||||
const dsql_str* const* begin = cteAliases.begin();
|
|
||||||
|
|
||||||
currCteAlias = cteAliases.end() - 1;
|
|
||||||
fb_assert(currCteAlias >= begin);
|
|
||||||
|
|
||||||
const dsql_str* curr = *(currCteAlias);
|
|
||||||
while (strcmp(curr->str_data, alias->str_data))
|
|
||||||
{
|
|
||||||
currCteAlias--;
|
|
||||||
fb_assert(currCteAlias >= begin);
|
|
||||||
|
|
||||||
curr = *(currCteAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPsql() const { return psql; }
|
|
||||||
void setPsql(bool value) { psql = value; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
dsql_dbb* dbb; // DSQL attachment
|
|
||||||
jrd_tra* transaction; // Transaction
|
|
||||||
DsqlCompiledStatement* statement; // Compiled statement
|
|
||||||
|
|
||||||
public:
|
|
||||||
unsigned flags; // flags
|
|
||||||
Firebird::Array<dsql_msg*> ports; // Port messages
|
|
||||||
dsql_rel* relation; // relation created by this request (for DDL)
|
|
||||||
dsql_prc* procedure; // procedure created by this request (for DDL)
|
|
||||||
DsqlContextStack mainContext;
|
|
||||||
DsqlContextStack* context;
|
|
||||||
DsqlContextStack unionContext; // Save contexts for views of unions
|
|
||||||
DsqlContextStack derivedContext; // Save contexts for views of derived tables
|
|
||||||
dsql_ctx* outerAggContext; // agg context for outer ref
|
|
||||||
USHORT contextNumber; // Next available context number
|
|
||||||
USHORT derivedContextNumber; // Next available context number for derived tables
|
|
||||||
USHORT scopeLevel; // Scope level for parsing aliases in subqueries
|
|
||||||
USHORT loopLevel; // Loop level
|
|
||||||
DsqlStrStack labels; // Loop labels
|
|
||||||
USHORT cursorNumber; // Cursor number
|
|
||||||
DsqlNodStack cursors; // Cursors
|
|
||||||
USHORT inSelectList; // now processing "select list"
|
|
||||||
USHORT inWhereClause; // processing "where clause"
|
|
||||||
USHORT inGroupByClause; // processing "group by clause"
|
|
||||||
USHORT inHavingClause; // processing "having clause"
|
|
||||||
USHORT inOrderByClause; // processing "order by clause"
|
|
||||||
USHORT errorHandlers; // count of active error handlers
|
|
||||||
USHORT clientDialect; // dialect passed into the API call
|
|
||||||
USHORT inOuterJoin; // processing inside outer-join part
|
|
||||||
dsql_str* aliasRelationPrefix; // prefix for every relation-alias.
|
|
||||||
DsqlNodStack hiddenVars; // hidden variables
|
|
||||||
USHORT hiddenVarsNumber; // next hidden variable number
|
|
||||||
Firebird::MetaName package; // package being defined
|
|
||||||
DsqlNodStack currCtes; // current processing CTE's
|
|
||||||
class dsql_ctx* recursiveCtx; // context of recursive CTE
|
|
||||||
USHORT recursiveCtxId; // id of recursive union stream context
|
|
||||||
bool processingWindow; // processing window functions
|
|
||||||
bool checkConstraintTrigger; // compiling a check constraint trigger
|
|
||||||
dsc domainValue; // VALUE in the context of domain's check constraint
|
|
||||||
|
|
||||||
private:
|
|
||||||
Firebird::HalfStaticArray<dsql_nod*, 4> ctes; // common table expressions
|
|
||||||
Firebird::HalfStaticArray<const dsql_str*, 4> cteAliases; // CTE aliases in recursive members
|
|
||||||
const dsql_str* const* currCteAlias;
|
|
||||||
bool psql;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class PsqlChanger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PsqlChanger(DsqlCompilerScratch* aStatement, bool value)
|
|
||||||
: statement(aStatement),
|
|
||||||
oldValue(statement->isPsql())
|
|
||||||
{
|
|
||||||
statement->setPsql(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
~PsqlChanger()
|
|
||||||
{
|
|
||||||
statement->setPsql(oldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// copying is prohibited
|
|
||||||
PsqlChanger(const PsqlChanger&);
|
|
||||||
PsqlChanger& operator =(const PsqlChanger&);
|
|
||||||
|
|
||||||
DsqlCompilerScratch* statement;
|
|
||||||
const bool oldValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Blob
|
// Blob
|
||||||
class dsql_blb : public pool_alloc<dsql_type_blb>
|
class dsql_blb : public pool_alloc<dsql_type_blb>
|
||||||
{
|
{
|
||||||
@ -904,7 +666,7 @@ public:
|
|||||||
|
|
||||||
bool getImplicitJoinField(const Firebird::MetaName& name, dsql_nod*& node);
|
bool getImplicitJoinField(const Firebird::MetaName& name, dsql_nod*& node);
|
||||||
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch, dsql_nod* partitionNode,
|
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch, dsql_nod* partitionNode,
|
||||||
dsql_nod* orderNode);
|
dsql_nod* orderNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flag values for ctx_flags
|
// Flag values for ctx_flags
|
||||||
|
@ -1316,7 +1316,7 @@ static void gen_cast( DsqlCompilerScratch* dsqlScratch, const dsql_nod* node)
|
|||||||
{
|
{
|
||||||
dsqlScratch->appendUChar(blr_cast);
|
dsqlScratch->appendUChar(blr_cast);
|
||||||
const dsql_fld* field = (dsql_fld*) node->nod_arg[e_cast_target];
|
const dsql_fld* field = (dsql_fld*) node->nod_arg[e_cast_target];
|
||||||
BlockNode::putDtype(dsqlScratch, field, true);
|
dsqlScratch->putDtype(field, true);
|
||||||
GEN_expr(dsqlScratch, node->nod_arg[e_cast_source]);
|
GEN_expr(dsqlScratch, node->nod_arg[e_cast_source]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "firebird.h"
|
#include "firebird.h"
|
||||||
#include "../dsql/dsql.h"
|
#include "../dsql/DsqlCompilerScratch.h"
|
||||||
#include "../dsql/misc_func.h"
|
#include "../dsql/misc_func.h"
|
||||||
|
|
||||||
using namespace Jrd;
|
using namespace Jrd;
|
||||||
|
@ -186,7 +186,6 @@ static void DSQL_pretty(const dsql_nod*, int);
|
|||||||
static dsql_nod* ambiguity_check(DsqlCompilerScratch*, dsql_nod*, const dsql_str*,
|
static dsql_nod* ambiguity_check(DsqlCompilerScratch*, dsql_nod*, const dsql_str*,
|
||||||
const DsqlContextStack&);
|
const DsqlContextStack&);
|
||||||
static void assign_fld_dtype_from_dsc(dsql_fld*, const dsc*);
|
static void assign_fld_dtype_from_dsc(dsql_fld*, const dsc*);
|
||||||
static dsql_nod* compose(dsql_nod*, dsql_nod*, NOD_TYPE);
|
|
||||||
static dsql_nod* explode_fields(dsql_rel*);
|
static dsql_nod* explode_fields(dsql_rel*);
|
||||||
static dsql_nod* explode_outputs(DsqlCompilerScratch*, const dsql_prc*);
|
static dsql_nod* explode_outputs(DsqlCompilerScratch*, const dsql_prc*);
|
||||||
static void field_appears_once(const dsql_nod*, const dsql_nod*, const bool, const char*);
|
static void field_appears_once(const dsql_nod*, const dsql_nod*, const bool, const char*);
|
||||||
@ -243,11 +242,6 @@ static dsql_nod* resolve_using_field(DsqlCompilerScratch* dsqlScratch, dsql_str*
|
|||||||
static void set_parameters_name(dsql_nod*, const dsql_nod*);
|
static void set_parameters_name(dsql_nod*, const dsql_nod*);
|
||||||
static void set_parameter_name(dsql_nod*, const dsql_nod*, const dsql_rel*);
|
static void set_parameter_name(dsql_nod*, const dsql_nod*, const dsql_rel*);
|
||||||
static dsql_nod* pass1_savepoint(const DsqlCompilerScratch*, dsql_nod*);
|
static dsql_nod* pass1_savepoint(const DsqlCompilerScratch*, dsql_nod*);
|
||||||
|
|
||||||
static bool pass1_relproc_is_recursive(DsqlCompilerScratch*, dsql_nod*);
|
|
||||||
static dsql_nod* pass1_join_is_recursive(DsqlCompilerScratch*, dsql_nod*&);
|
|
||||||
static dsql_nod* pass1_rse_is_recursive(DsqlCompilerScratch*, dsql_nod*);
|
|
||||||
static dsql_nod* pass1_recursive_cte(DsqlCompilerScratch*, dsql_nod*);
|
|
||||||
static dsql_nod* process_returning(DsqlCompilerScratch*, dsql_nod*);
|
static dsql_nod* process_returning(DsqlCompilerScratch*, dsql_nod*);
|
||||||
|
|
||||||
const char* const DB_KEY_STRING = "DB_KEY"; // NTX: pseudo field name
|
const char* const DB_KEY_STRING = "DB_KEY"; // NTX: pseudo field name
|
||||||
@ -1607,7 +1601,7 @@ dsql_nod* PASS1_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|||||||
dsql_nod* temp = MAKE_node(input->nod_type, 2);
|
dsql_nod* temp = MAKE_node(input->nod_type, 2);
|
||||||
temp->nod_arg[0] = input->nod_arg[0];
|
temp->nod_arg[0] = input->nod_arg[0];
|
||||||
temp->nod_arg[1] = *ptr;
|
temp->nod_arg[1] = *ptr;
|
||||||
node = compose(node, PASS1_node(dsqlScratch, temp), nod_or);
|
node = PASS1_compose(node, PASS1_node(dsqlScratch, temp), nod_or);
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@ -2623,19 +2617,8 @@ void PASS1_check_unique_fields_names(StrArray& names, const dsql_nod* fields)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
// Compose two booleans.
|
||||||
|
dsql_nod* PASS1_compose( dsql_nod* expr1, dsql_nod* expr2, NOD_TYPE dsql_operator)
|
||||||
compose
|
|
||||||
|
|
||||||
@brief Compose two booleans.
|
|
||||||
|
|
||||||
|
|
||||||
@param expr1
|
|
||||||
@param expr2
|
|
||||||
@param dsql_operator
|
|
||||||
|
|
||||||
**/
|
|
||||||
static dsql_nod* compose( dsql_nod* expr1, dsql_nod* expr2, NOD_TYPE dsql_operator)
|
|
||||||
{
|
{
|
||||||
DEV_BLKCHK(expr1, dsql_type_nod);
|
DEV_BLKCHK(expr1, dsql_type_nod);
|
||||||
DEV_BLKCHK(expr2, dsql_type_nod);
|
DEV_BLKCHK(expr2, dsql_type_nod);
|
||||||
@ -3897,7 +3880,7 @@ static dsql_nod* pass1_cursor_reference( DsqlCompilerScratch* dsqlScratch, const
|
|||||||
temp->nod_arg[e_par_parameter] = (dsql_nod*) parameter;
|
temp->nod_arg[e_par_parameter] = (dsql_nod*) parameter;
|
||||||
parameter->par_desc = rv_source->par_desc;
|
parameter->par_desc = rv_source->par_desc;
|
||||||
|
|
||||||
rse->nod_arg[e_rse_boolean] = compose(rse->nod_arg[e_rse_boolean], node, nod_and);
|
rse->nod_arg[e_rse_boolean] = PASS1_compose(rse->nod_arg[e_rse_boolean], node, nod_and);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rse;
|
return rse;
|
||||||
@ -4084,415 +4067,6 @@ static dsql_nod* pass1_delete( DsqlCompilerScratch* dsqlScratch, dsql_nod* input
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
pass1_relproc_is_recursive
|
|
||||||
|
|
||||||
@brief check if table reference is recursive i.e. its name is equal
|
|
||||||
to the name of current processing CTE
|
|
||||||
|
|
||||||
@param dsqlScratch
|
|
||||||
@param input
|
|
||||||
|
|
||||||
**/
|
|
||||||
static bool pass1_relproc_is_recursive(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|
||||||
{
|
|
||||||
const dsql_str* rel_name = NULL;
|
|
||||||
const dsql_str* rel_alias = NULL;
|
|
||||||
|
|
||||||
switch (input->nod_type)
|
|
||||||
{
|
|
||||||
case nod_rel_proc_name:
|
|
||||||
rel_name = (dsql_str*) input->nod_arg[e_rpn_name];
|
|
||||||
rel_alias = (dsql_str*) input->nod_arg[e_rpn_alias];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nod_relation_name:
|
|
||||||
rel_name = (dsql_str*) input->nod_arg[e_rln_name];
|
|
||||||
rel_alias = (dsql_str*) input->nod_arg[e_rln_alias];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fb_assert(dsqlScratch->currCtes.hasData());
|
|
||||||
const dsql_nod* curr_cte = dsqlScratch->currCtes.object();
|
|
||||||
const dsql_str* cte_name = (dsql_str*) curr_cte->nod_arg[e_derived_table_alias];
|
|
||||||
|
|
||||||
const bool recursive = (cte_name->str_length == rel_name->str_length) &&
|
|
||||||
(strncmp(rel_name->str_data, cte_name->str_data, cte_name->str_length) == 0);
|
|
||||||
|
|
||||||
if (recursive) {
|
|
||||||
dsqlScratch->addCTEAlias(rel_alias ? rel_alias : rel_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return recursive;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
pass1_join_is_recursive
|
|
||||||
|
|
||||||
@brief check if join have recursive members. If found remove this member
|
|
||||||
from join and return its boolean (to be added into WHERE clause).
|
|
||||||
We must remove member only if it is a table reference.
|
|
||||||
Punt if recursive reference is found in outer join or more than one
|
|
||||||
recursive reference is found
|
|
||||||
|
|
||||||
@param dsqlScratch
|
|
||||||
@param input
|
|
||||||
|
|
||||||
**/
|
|
||||||
static dsql_nod* pass1_join_is_recursive(DsqlCompilerScratch* dsqlScratch, dsql_nod*& input)
|
|
||||||
{
|
|
||||||
const NOD_TYPE join_type = input->nod_arg[e_join_type]->nod_type;
|
|
||||||
bool remove = false;
|
|
||||||
|
|
||||||
bool leftRecursive = false;
|
|
||||||
dsql_nod* leftBool = NULL;
|
|
||||||
dsql_nod** join_table = &input->nod_arg[e_join_left_rel];
|
|
||||||
if ((*join_table)->nod_type == nod_join)
|
|
||||||
{
|
|
||||||
leftBool = pass1_join_is_recursive(dsqlScratch, *join_table);
|
|
||||||
leftRecursive = (leftBool != NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
leftBool = input->nod_arg[e_join_boolean];
|
|
||||||
leftRecursive = pass1_relproc_is_recursive(dsqlScratch, *join_table);
|
|
||||||
if (leftRecursive)
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leftRecursive && join_type != nod_join_inner)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE can't be member of an outer join
|
|
||||||
Arg::Gds(isc_dsql_cte_outer_join));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rightRecursive = false;
|
|
||||||
dsql_nod* rightBool = NULL;
|
|
||||||
join_table = &input->nod_arg[e_join_rght_rel];
|
|
||||||
if ((*join_table)->nod_type == nod_join)
|
|
||||||
{
|
|
||||||
rightBool = pass1_join_is_recursive(dsqlScratch, *join_table);
|
|
||||||
rightRecursive = (rightBool != NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rightBool = input->nod_arg[e_join_boolean];
|
|
||||||
rightRecursive = pass1_relproc_is_recursive(dsqlScratch, *join_table);
|
|
||||||
if (rightRecursive)
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rightRecursive && join_type != nod_join_inner)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE can't be member of an outer join
|
|
||||||
Arg::Gds(isc_dsql_cte_outer_join));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leftRecursive && rightRecursive)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE can't reference itself more than once
|
|
||||||
Arg::Gds(isc_dsql_cte_mult_references));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leftRecursive)
|
|
||||||
{
|
|
||||||
if (remove)
|
|
||||||
input = input->nod_arg[e_join_rght_rel];
|
|
||||||
|
|
||||||
return leftBool;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rightRecursive)
|
|
||||||
{
|
|
||||||
if (remove)
|
|
||||||
input = input->nod_arg[e_join_left_rel];
|
|
||||||
|
|
||||||
return rightBool;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
pass1_rse_is_recursive
|
|
||||||
|
|
||||||
@brief check if rse is recursive. If recursive reference is a table
|
|
||||||
in the FROM list remove it. If recursive reference is a part of
|
|
||||||
join add join boolean (returned by pass1_join_is_recursive) to the
|
|
||||||
WHERE clause. Punt if more than one recursive reference is found
|
|
||||||
|
|
||||||
@param dsqlScratch
|
|
||||||
@param input
|
|
||||||
|
|
||||||
**/
|
|
||||||
static dsql_nod* pass1_rse_is_recursive(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|
||||||
{
|
|
||||||
fb_assert(input->nod_type == nod_query_spec);
|
|
||||||
|
|
||||||
dsql_nod* result = MAKE_node(nod_query_spec, e_qry_count);
|
|
||||||
memcpy(result->nod_arg, input->nod_arg, e_qry_count * sizeof(dsql_nod*));
|
|
||||||
|
|
||||||
dsql_nod* src_tables = input->nod_arg[e_qry_from];
|
|
||||||
dsql_nod* dst_tables = MAKE_node(nod_list, src_tables->nod_count);
|
|
||||||
result->nod_arg[e_qry_from] = dst_tables;
|
|
||||||
|
|
||||||
dsql_nod** p_dst_table = dst_tables->nod_arg;
|
|
||||||
dsql_nod** p_src_table = src_tables->nod_arg;
|
|
||||||
dsql_nod** end = src_tables->nod_arg + src_tables->nod_count;
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
for (dsql_nod** prev = p_dst_table; p_src_table < end; p_src_table++, p_dst_table++)
|
|
||||||
{
|
|
||||||
*prev++ = *p_dst_table = *p_src_table;
|
|
||||||
|
|
||||||
switch ((*p_dst_table)->nod_type)
|
|
||||||
{
|
|
||||||
case nod_rel_proc_name:
|
|
||||||
case nod_relation_name:
|
|
||||||
if (pass1_relproc_is_recursive(dsqlScratch, *p_dst_table))
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE can't reference itself more than once
|
|
||||||
Arg::Gds(isc_dsql_cte_mult_references));
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
|
|
||||||
prev--;
|
|
||||||
dst_tables->nod_count--;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nod_join:
|
|
||||||
{
|
|
||||||
*p_dst_table = MAKE_node(nod_join, e_join_count);
|
|
||||||
memcpy((*p_dst_table)->nod_arg, (*p_src_table)->nod_arg,
|
|
||||||
e_join_count * sizeof(dsql_nod*));
|
|
||||||
|
|
||||||
dsql_nod* joinBool = pass1_join_is_recursive(dsqlScratch, *p_dst_table);
|
|
||||||
if (joinBool)
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE can't reference itself more than once
|
|
||||||
Arg::Gds(isc_dsql_cte_mult_references));
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
|
|
||||||
result->nod_arg[e_qry_where] =
|
|
||||||
compose(result->nod_arg[e_qry_where], joinBool, nod_and);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nod_derived_table:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fb_assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found ? result : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
pass1_recursive_cte
|
|
||||||
|
|
||||||
@brief Process derived table which can be recursive CTE
|
|
||||||
If it is non-recursive return input node unchanged
|
|
||||||
If it is recursive return new derived table which is an union of
|
|
||||||
union of anchor (non-recursive) queries and union of recursive
|
|
||||||
queries. Check recursive queries to satisfy various criterias.
|
|
||||||
Note that our parser is right-to-left therefore nested list linked
|
|
||||||
as first node in parent list and second node is always query spec.
|
|
||||||
|
|
||||||
For example, if we have 4 CTE's where first two is non-recursive
|
|
||||||
and last two is recursive :
|
|
||||||
|
|
||||||
list union
|
|
||||||
[0] [1] [0] [1]
|
|
||||||
list cte3 ===> anchor recursive
|
|
||||||
[0] [1] [0] [1] [0] [1]
|
|
||||||
list cte3 cte1 cte2 cte3 cte4
|
|
||||||
[0] [1]
|
|
||||||
cte1 cte2
|
|
||||||
|
|
||||||
Also, we should not change layout of original parse tree to allow it to
|
|
||||||
be parsed again if needed. Therefore recursive part is built using newly
|
|
||||||
allocated list nodes.
|
|
||||||
|
|
||||||
@param dsqlScratch
|
|
||||||
@param input
|
|
||||||
|
|
||||||
**/
|
|
||||||
static dsql_nod* pass1_recursive_cte(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|
||||||
{
|
|
||||||
dsql_str* const cte_alias = (dsql_str*) input->nod_arg[e_derived_table_alias];
|
|
||||||
dsql_nod* const select_expr = input->nod_arg[e_derived_table_rse];
|
|
||||||
dsql_nod* query = select_expr->nod_arg[e_sel_query_spec];
|
|
||||||
|
|
||||||
if (query->nod_type != nod_list && pass1_rse_is_recursive(dsqlScratch, query))
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive CTE (%s) must be an UNION
|
|
||||||
Arg::Gds(isc_dsql_cte_not_a_union) << Arg::Str(cte_alias->str_data));
|
|
||||||
}
|
|
||||||
|
|
||||||
// split queries list on two parts: anchor and recursive
|
|
||||||
dsql_nod* anchor_rse = 0, *recursive_rse = 0;
|
|
||||||
dsql_nod* qry = query;
|
|
||||||
|
|
||||||
dsql_nod* new_qry = MAKE_node(nod_list, 2);
|
|
||||||
new_qry->nod_flags = query->nod_flags;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
dsql_nod* rse = 0;
|
|
||||||
if (qry->nod_type == nod_list)
|
|
||||||
rse = qry->nod_arg[1];
|
|
||||||
else
|
|
||||||
rse = qry;
|
|
||||||
|
|
||||||
dsql_nod* new_rse = pass1_rse_is_recursive(dsqlScratch, rse);
|
|
||||||
if (new_rse) // rse is recursive
|
|
||||||
{
|
|
||||||
if (anchor_rse)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// CTE '%s' defined non-recursive member after recursive
|
|
||||||
Arg::Gds(isc_dsql_cte_nonrecurs_after_recurs) << Arg::Str(cte_alias->str_data));
|
|
||||||
}
|
|
||||||
if (new_rse->nod_arg[e_qry_distinct])
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE '%s' has %s clause
|
|
||||||
Arg::Gds(isc_dsql_cte_wrong_clause) << Arg::Str(cte_alias->str_data) <<
|
|
||||||
Arg::Str("DISTINCT"));
|
|
||||||
}
|
|
||||||
if (new_rse->nod_arg[e_qry_group])
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE '%s' has %s clause
|
|
||||||
Arg::Gds(isc_dsql_cte_wrong_clause) << Arg::Str(cte_alias->str_data) <<
|
|
||||||
Arg::Str("GROUP BY"));
|
|
||||||
}
|
|
||||||
if (new_rse->nod_arg[e_qry_having])
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive member of CTE '%s' has %s clause
|
|
||||||
Arg::Gds(isc_dsql_cte_wrong_clause) << Arg::Str(cte_alias->str_data) <<
|
|
||||||
Arg::Str("HAVING"));
|
|
||||||
}
|
|
||||||
// hvlad: we need also forbid any aggregate function here
|
|
||||||
// but for now i have no idea how to do it simple
|
|
||||||
|
|
||||||
if ((new_qry->nod_type == nod_list) && !(new_qry->nod_flags & NOD_UNION_ALL))
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Recursive members of CTE (%s) must be linked with another members via UNION ALL
|
|
||||||
Arg::Gds(isc_dsql_cte_union_all) << Arg::Str(cte_alias->str_data));
|
|
||||||
}
|
|
||||||
if (!recursive_rse)
|
|
||||||
{
|
|
||||||
recursive_rse = new_qry;
|
|
||||||
}
|
|
||||||
new_rse->nod_flags |= NOD_SELECT_EXPR_RECURSIVE;
|
|
||||||
|
|
||||||
if (qry->nod_type == nod_list)
|
|
||||||
new_qry->nod_arg[1] = new_rse;
|
|
||||||
else
|
|
||||||
new_qry->nod_arg[0] = new_rse;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (qry->nod_type == nod_list)
|
|
||||||
new_qry->nod_arg[1] = rse;
|
|
||||||
else
|
|
||||||
new_qry->nod_arg[0] = rse;
|
|
||||||
|
|
||||||
if (!anchor_rse)
|
|
||||||
{
|
|
||||||
if (qry->nod_type == nod_list)
|
|
||||||
anchor_rse = new_qry;
|
|
||||||
else
|
|
||||||
anchor_rse = rse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qry->nod_type != nod_list)
|
|
||||||
break;
|
|
||||||
|
|
||||||
qry = qry->nod_arg[0];
|
|
||||||
|
|
||||||
if (qry->nod_type == nod_list)
|
|
||||||
{
|
|
||||||
new_qry->nod_arg[0] = MAKE_node(nod_list, 2);
|
|
||||||
new_qry = new_qry->nod_arg[0];
|
|
||||||
new_qry->nod_flags = qry->nod_flags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!recursive_rse) {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
if (!anchor_rse)
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// Non-recursive member is missing in CTE '%s'
|
|
||||||
Arg::Gds(isc_dsql_cte_miss_nonrecursive) << Arg::Str(cte_alias->str_data));
|
|
||||||
}
|
|
||||||
|
|
||||||
qry = recursive_rse;
|
|
||||||
dsql_nod* list = 0;
|
|
||||||
while (qry->nod_arg[0] != anchor_rse)
|
|
||||||
{
|
|
||||||
list = qry;
|
|
||||||
qry = qry->nod_arg[0];
|
|
||||||
}
|
|
||||||
qry->nod_arg[0] = 0;
|
|
||||||
if (list) {
|
|
||||||
list->nod_arg[0] = qry->nod_arg[1];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
recursive_rse = qry->nod_arg[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
dsql_nod* union_node = MAKE_node(nod_list, 2);
|
|
||||||
union_node->nod_flags = NOD_UNION_ALL | NOD_UNION_RECURSIVE;
|
|
||||||
union_node->nod_arg[0] = anchor_rse;
|
|
||||||
union_node->nod_arg[1] = recursive_rse;
|
|
||||||
|
|
||||||
dsql_nod* select = MAKE_node(nod_select_expr, e_sel_count);
|
|
||||||
select->nod_arg[e_sel_query_spec] = union_node;
|
|
||||||
select->nod_arg[e_sel_order] = select->nod_arg[e_sel_rows] =
|
|
||||||
select->nod_arg[e_sel_with_list] = NULL;
|
|
||||||
|
|
||||||
dsql_nod* node = MAKE_node(nod_derived_table, e_derived_table_count);
|
|
||||||
dsql_str* alias = (dsql_str*) input->nod_arg[e_derived_table_alias];
|
|
||||||
node->nod_arg[e_derived_table_alias] = (dsql_nod*) alias;
|
|
||||||
node->nod_arg[e_derived_table_column_alias] = input->nod_arg[e_derived_table_column_alias];
|
|
||||||
node->nod_arg[e_derived_table_rse] = select;
|
|
||||||
node->nod_arg[e_derived_table_context] = input->nod_arg[e_derived_table_context];
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
process_returning
|
process_returning
|
||||||
@ -9011,13 +8585,9 @@ static dsql_nod* pass1_variable( DsqlCompilerScratch* dsqlScratch, dsql_nod* inp
|
|||||||
|
|
||||||
DEV_BLKCHK(var_name, dsql_type_str);
|
DEV_BLKCHK(var_name, dsql_type_str);
|
||||||
|
|
||||||
BlockNode* block = dsqlScratch->getStatement()->getBlockNode();
|
dsql_nod* varNode = dsqlScratch->resolveVariable(var_name);
|
||||||
if (block)
|
if (varNode)
|
||||||
{
|
return varNode;
|
||||||
dsql_nod* varNode = block->resolveVariable(var_name);
|
|
||||||
if (varNode)
|
|
||||||
return varNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// field unresolved
|
// field unresolved
|
||||||
// CVC: That's all [the fix], folks!
|
// CVC: That's all [the fix], folks!
|
||||||
@ -9676,88 +9246,6 @@ static dsql_nod* pass1_savepoint(const DsqlCompilerScratch* dsqlScratch, dsql_no
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DsqlCompilerScratch::addCTEs(dsql_nod* with)
|
|
||||||
{
|
|
||||||
DEV_BLKCHK(with, dsql_type_nod);
|
|
||||||
fb_assert(with->nod_type == nod_with);
|
|
||||||
|
|
||||||
if (ctes.getCount())
|
|
||||||
{
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
// WITH clause can't be nested
|
|
||||||
Arg::Gds(isc_dsql_cte_nested_with));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (with->nod_flags & NOD_UNION_RECURSIVE)
|
|
||||||
flags |= DsqlCompilerScratch::FLAG_RECURSIVE_CTE;
|
|
||||||
|
|
||||||
const dsql_nod* list = with->nod_arg[0];
|
|
||||||
const dsql_nod* const* end = list->nod_arg + list->nod_count;
|
|
||||||
for (dsql_nod* const* cte = list->nod_arg; cte < end; cte++)
|
|
||||||
{
|
|
||||||
fb_assert((*cte)->nod_type == nod_derived_table);
|
|
||||||
|
|
||||||
if (with->nod_flags & NOD_UNION_RECURSIVE)
|
|
||||||
{
|
|
||||||
currCtes.push(*cte);
|
|
||||||
PsqlChanger changer(this, false);
|
|
||||||
ctes.add(pass1_recursive_cte(this, *cte));
|
|
||||||
currCtes.pop();
|
|
||||||
|
|
||||||
// Add CTE name into CTE aliases stack. It allows later to search for
|
|
||||||
// aliases of given CTE.
|
|
||||||
const dsql_str* cte_name = (dsql_str*) (*cte)->nod_arg[e_derived_table_alias];
|
|
||||||
addCTEAlias(cte_name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ctes.add(*cte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dsql_nod* DsqlCompilerScratch::findCTE(const dsql_str* name)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < ctes.getCount(); i++)
|
|
||||||
{
|
|
||||||
dsql_nod* cte = ctes[i];
|
|
||||||
const dsql_str* cte_name = (dsql_str*) cte->nod_arg[e_derived_table_alias];
|
|
||||||
|
|
||||||
if (name->str_length == cte_name->str_length &&
|
|
||||||
strncmp(name->str_data, cte_name->str_data, cte_name->str_length) == 0)
|
|
||||||
{
|
|
||||||
return cte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DsqlCompilerScratch::clearCTEs()
|
|
||||||
{
|
|
||||||
flags &= ~DsqlCompilerScratch::FLAG_RECURSIVE_CTE;
|
|
||||||
ctes.clear();
|
|
||||||
cteAliases.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DsqlCompilerScratch::checkUnusedCTEs() const
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < ctes.getCount(); i++)
|
|
||||||
{
|
|
||||||
const dsql_nod* cte = ctes[i];
|
|
||||||
|
|
||||||
if (!(cte->nod_flags & NOD_DT_CTE_USED))
|
|
||||||
{
|
|
||||||
const dsql_str* cte_name = (dsql_str*) cte->nod_arg[e_derived_table_alias];
|
|
||||||
|
|
||||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
||||||
Arg::Gds(isc_dsql_cte_not_used) << Arg::Str(cte_name->str_data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns false for hidden fields and true for non-hidden.
|
// Returns false for hidden fields and true for non-hidden.
|
||||||
// For non-hidden, change "node" if the field is part of an
|
// For non-hidden, change "node" if the field is part of an
|
||||||
// implicit join.
|
// implicit join.
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#define DSQL_PASS1_PROTO_H
|
#define DSQL_PASS1_PROTO_H
|
||||||
|
|
||||||
void PASS1_check_unique_fields_names(Jrd::StrArray& names, const Jrd::dsql_nod* fields);
|
void PASS1_check_unique_fields_names(Jrd::StrArray& names, const Jrd::dsql_nod* fields);
|
||||||
|
Jrd::dsql_nod* PASS1_compose(Jrd::dsql_nod*, Jrd::dsql_nod*, Jrd::NOD_TYPE);
|
||||||
Jrd::dsql_nod* PASS1_cursor_name(Jrd::DsqlCompilerScratch*, const Jrd::dsql_str*, USHORT, bool);
|
Jrd::dsql_nod* PASS1_cursor_name(Jrd::DsqlCompilerScratch*, const Jrd::dsql_str*, USHORT, bool);
|
||||||
Jrd::dsql_nod* PASS1_label(Jrd::DsqlCompilerScratch*, Jrd::dsql_nod*);
|
Jrd::dsql_nod* PASS1_label(Jrd::DsqlCompilerScratch*, Jrd::dsql_nod*);
|
||||||
Jrd::dsql_nod* PASS1_label2(Jrd::DsqlCompilerScratch*, Jrd::dsql_nod*, Jrd::dsql_nod*);
|
Jrd::dsql_nod* PASS1_label2(Jrd::DsqlCompilerScratch*, Jrd::dsql_nod*, Jrd::dsql_nod*);
|
||||||
|
Loading…
Reference in New Issue
Block a user