mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 16:43:03 +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 \
|
||||
ddl.cpp dsql.cpp errd.cpp gen.cpp hsh.cpp make.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)
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../dsql/DSqlDataTypeUtil.h"
|
||||
#include "../dsql/dsql.h"
|
||||
#include "../dsql/DsqlCompilerScratch.h"
|
||||
#include "../dsql/metd_proto.h"
|
||||
|
||||
UCHAR Jrd::DSqlDataTypeUtil::maxBytesPerChar(UCHAR charSet)
|
||||
|
@ -1157,8 +1157,6 @@ void CreateAlterFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) c
|
||||
|
||||
DdlNode* CreateAlterFunctionNode::internalDsqlPass()
|
||||
{
|
||||
DsqlCompiledStatement* const statement = dsqlScratch->getStatement();
|
||||
statement->setBlockNode(this);
|
||||
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_FUNCTION);
|
||||
|
||||
const dsql_nod* variables = localDeclList;
|
||||
@ -1488,7 +1486,6 @@ void CreateAlterFunctionNode::storeArgument(thread_db* tdbb, jrd_tra* transactio
|
||||
unsigned pos, const ParameterClause& parameter)
|
||||
{
|
||||
Attachment* attachment = transaction->getAttachment();
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
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();
|
||||
|
||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
||||
if (dsqlScratch->isVersion4())
|
||||
dsqlScratch->appendUChar(blr_version4);
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_version5);
|
||||
@ -1618,14 +1615,12 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
||||
compiled = true;
|
||||
invalid = true;
|
||||
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
if (body)
|
||||
{
|
||||
dsqlScratch->beginDebug();
|
||||
dsqlScratch->getBlrData().clear();
|
||||
|
||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
||||
if (dsqlScratch->isVersion4())
|
||||
dsqlScratch->appendUChar(blr_version4);
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_version5);
|
||||
@ -1650,7 +1645,7 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
||||
dsqlScratch->appendUChar(blr_short);
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -1667,8 +1662,8 @@ void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
||||
dsqlScratch->appendUChar(0);
|
||||
|
||||
dsql_nod* const var = MAKE_variable(returnType.legacyField, "", VAR_output, 1, 0, 0);
|
||||
variables.add(var);
|
||||
outputVariables.add(var);
|
||||
dsqlScratch->variables.add(var);
|
||||
dsqlScratch->outputVariables.add(var);
|
||||
|
||||
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];
|
||||
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
||||
dsql_var* const variable =
|
||||
(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)
|
||||
// of previous calls to PASS1_node and PASS1_statement.
|
||||
dsqlScratch->setPsql(true);
|
||||
|
||||
putLocalVariables(dsqlScratch, localDeclList, 1);
|
||||
dsqlScratch->putLocalVariables(localDeclList, 1);
|
||||
|
||||
dsqlScratch->appendUChar(blr_stall);
|
||||
// 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));
|
||||
|
||||
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
genReturn(dsqlScratch, false);
|
||||
|
||||
dsqlScratch->genReturn(false);
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
dsqlScratch->appendUChar(blr_eoc);
|
||||
|
||||
@ -1902,8 +1897,6 @@ void CreateAlterProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/)
|
||||
|
||||
DdlNode* CreateAlterProcedureNode::internalDsqlPass()
|
||||
{
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
statement->setBlockNode(this);
|
||||
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_PROCEDURE);
|
||||
|
||||
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);
|
||||
attachment->storeMetaDataBlob(tdbb, transaction, &PRM.RDB$DEFAULT_SOURCE, defaultSource);
|
||||
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
dsqlScratch->getBlrData().clear();
|
||||
|
||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
||||
if (dsqlScratch->isVersion4())
|
||||
dsqlScratch->appendUChar(blr_version4);
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_version5);
|
||||
@ -2375,12 +2366,10 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
||||
|
||||
invalid = true;
|
||||
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
dsqlScratch->beginDebug();
|
||||
dsqlScratch->getBlrData().clear();
|
||||
|
||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
||||
if (dsqlScratch->isVersion4())
|
||||
dsqlScratch->appendUChar(blr_version4);
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_version5);
|
||||
@ -2404,7 +2393,7 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
||||
dsqlScratch->appendUChar(blr_short);
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -2429,8 +2418,8 @@ void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/
|
||||
dsql_nod* const var = MAKE_variable(parameter.legacyField,
|
||||
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
||||
|
||||
variables.add(var);
|
||||
outputVariables.add(var);
|
||||
dsqlScratch->variables.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_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)
|
||||
// of previous calls to PASS1_node and PASS1_statement.
|
||||
dsqlScratch->setPsql(true);
|
||||
|
||||
putLocalVariables(dsqlScratch, localDeclList, returns.getCount());
|
||||
dsqlScratch->putLocalVariables(localDeclList, returns.getCount());
|
||||
|
||||
dsqlScratch->appendUChar(blr_stall);
|
||||
// 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));
|
||||
|
||||
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
genReturn(dsqlScratch, true);
|
||||
|
||||
dsqlScratch->genReturn(true);
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
dsqlScratch->appendUChar(blr_eoc);
|
||||
|
||||
@ -2779,8 +2769,6 @@ void CreateAlterTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) co
|
||||
|
||||
DdlNode* CreateAlterTriggerNode::internalDsqlPass()
|
||||
{
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
statement->setBlockNode(this);
|
||||
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_TRIGGER);
|
||||
|
||||
if (type.specified)
|
||||
@ -2876,8 +2864,6 @@ void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
||||
|
||||
if (body)
|
||||
{
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
dsqlScratch->beginDebug();
|
||||
dsqlScratch->getBlrData().clear();
|
||||
|
||||
@ -2921,7 +2907,7 @@ void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
||||
|
||||
// generate the trigger blr
|
||||
|
||||
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
||||
if (dsqlScratch->isVersion4())
|
||||
dsqlScratch->appendUChar(blr_version4);
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_version5);
|
||||
@ -2929,8 +2915,7 @@ void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
||||
dsqlScratch->appendUChar(blr_begin);
|
||||
|
||||
dsqlScratch->setPsql(true);
|
||||
|
||||
putLocalVariables(dsqlScratch, localDeclList, 0);
|
||||
dsqlScratch->putLocalVariables(localDeclList, 0);
|
||||
|
||||
dsqlScratch->scopeLevel++;
|
||||
// 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 trigger actions, so reset it to reflect the fact that this
|
||||
// is a data definition statement; also reset the ddl node.
|
||||
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL);
|
||||
}
|
||||
|
||||
invalid = false;
|
||||
|
@ -229,12 +229,11 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class CreateAlterFunctionNode : public DdlNode, public BlockNode
|
||||
class CreateAlterFunctionNode : public DdlNode
|
||||
{
|
||||
public:
|
||||
CreateAlterFunctionNode(MemoryPool& pool, const Firebird::MetaName& aName)
|
||||
: DdlNode(pool),
|
||||
BlockNode(pool, false),
|
||||
name(pool, aName),
|
||||
create(true),
|
||||
alter(false),
|
||||
@ -334,12 +333,11 @@ typedef RecreateNode<CreateAlterFunctionNode, DropFunctionNode, isc_dsql_recreat
|
||||
RecreateFunctionNode;
|
||||
|
||||
|
||||
class CreateAlterProcedureNode : public DdlNode, public BlockNode
|
||||
class CreateAlterProcedureNode : public DdlNode
|
||||
{
|
||||
public:
|
||||
CreateAlterProcedureNode(MemoryPool& pool, const Firebird::MetaName& aName)
|
||||
: DdlNode(pool),
|
||||
BlockNode(pool, true),
|
||||
name(pool, aName),
|
||||
create(true),
|
||||
alter(false),
|
||||
@ -476,12 +474,11 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class CreateAlterTriggerNode : public DdlNode, public BlockNode, public TriggerDefinition
|
||||
class CreateAlterTriggerNode : public DdlNode, public TriggerDefinition
|
||||
{
|
||||
public:
|
||||
CreateAlterTriggerNode(MemoryPool& p, const Firebird::MetaName& aName)
|
||||
: DdlNode(p),
|
||||
BlockNode(p, false),
|
||||
TriggerDefinition(p),
|
||||
create(true),
|
||||
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
|
||||
|
||||
#include "../jrd/common.h"
|
||||
#include "../dsql/dsql.h"
|
||||
#include "../dsql/DsqlCompilerScratch.h"
|
||||
#include "../dsql/node.h"
|
||||
#include "../dsql/Visitors.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
|
||||
|
||||
#endif // DSQL_NODES_H
|
||||
|
@ -52,278 +52,6 @@ using 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)
|
||||
{
|
||||
node = aNode;
|
||||
@ -678,8 +406,6 @@ ExecBlockNode* ExecBlockNode::internalDsqlPass()
|
||||
{
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
statement->setBlockNode(this);
|
||||
|
||||
if (returns.hasData())
|
||||
statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK);
|
||||
else
|
||||
@ -793,11 +519,6 @@ void ExecBlockNode::print(string& text, Array<dsql_nod*>& nodes) const
|
||||
|
||||
void ExecBlockNode::genBlr()
|
||||
{
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
// Update blockNode, because we have a reference to the original unprocessed node.
|
||||
statement->setBlockNode(this);
|
||||
|
||||
dsqlScratch->beginDebug();
|
||||
|
||||
// now do the input parameters
|
||||
@ -808,10 +529,10 @@ void ExecBlockNode::genBlr()
|
||||
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
||||
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
|
||||
for (size_t i = 0; i < returns.getCount(); ++i)
|
||||
@ -821,10 +542,12 @@ void ExecBlockNode::genBlr()
|
||||
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
||||
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
||||
|
||||
variables.add(var);
|
||||
outputVariables.add(var);
|
||||
dsqlScratch->variables.add(var);
|
||||
dsqlScratch->outputVariables.add(var);
|
||||
}
|
||||
|
||||
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
||||
|
||||
dsqlScratch->appendUChar(blr_begin);
|
||||
|
||||
if (parameters.hasData())
|
||||
@ -835,10 +558,12 @@ void ExecBlockNode::genBlr()
|
||||
else
|
||||
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,
|
||||
(i - outputVariables.begin()) + 1, *i);
|
||||
(i - dsqlScratch->outputVariables.begin()) + 1, *i);
|
||||
param->par_node = *i;
|
||||
MAKE_desc(dsqlScratch, ¶m->par_desc, *i, NULL);
|
||||
param->par_desc.dsc_flags |= DSC_nullable;
|
||||
@ -864,7 +589,7 @@ void ExecBlockNode::genBlr()
|
||||
|
||||
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_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.
|
||||
dsqlScratch->appendUChar(blr_assignment);
|
||||
dsqlScratch->appendUChar(blr_cast);
|
||||
BlockNode::putDtype(dsqlScratch, field, true);
|
||||
dsqlScratch->putDtype(field, true);
|
||||
dsqlScratch->appendUChar(blr_parameter2);
|
||||
dsqlScratch->appendUChar(0);
|
||||
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_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
||||
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
||||
dsqlScratch->putLocalVariable(variable, 0, NULL);
|
||||
}
|
||||
|
||||
dsqlScratch->setPsql(true);
|
||||
|
||||
putLocalVariables(dsqlScratch, localDeclList, USHORT(returns.getCount()));
|
||||
dsqlScratch->putLocalVariables(localDeclList, USHORT(returns.getCount()));
|
||||
|
||||
dsqlScratch->loopLevel = 0;
|
||||
|
||||
@ -912,7 +639,7 @@ void ExecBlockNode::genBlr()
|
||||
statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK);
|
||||
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
genReturn(dsqlScratch, true);
|
||||
dsqlScratch->genReturn(true);
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
|
||||
dsqlScratch->endDebug();
|
||||
@ -1799,8 +1526,6 @@ SuspendNode* SuspendNode::internalDsqlPass()
|
||||
|
||||
statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE);
|
||||
|
||||
blockNode = statement->getBlockNode();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1813,8 +1538,7 @@ void SuspendNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
||||
|
||||
void SuspendNode::genBlr()
|
||||
{
|
||||
if (blockNode)
|
||||
blockNode->genReturn(dsqlScratch);
|
||||
dsqlScratch->genReturn();
|
||||
}
|
||||
|
||||
|
||||
@ -1879,7 +1603,6 @@ ReturnNode* ReturnNode::internalDsqlPass()
|
||||
|
||||
ReturnNode* node = FB_NEW(getPool()) ReturnNode(getPool());
|
||||
node->dsqlScratch = dsqlScratch;
|
||||
node->blockNode = statement->getBlockNode();
|
||||
node->value = PASS1_node(dsqlScratch, value);
|
||||
|
||||
return node;
|
||||
@ -1899,9 +1622,7 @@ void ReturnNode::genBlr()
|
||||
GEN_expr(dsqlScratch, value);
|
||||
dsqlScratch->appendUChar(blr_variable);
|
||||
dsqlScratch->appendUShort(0);
|
||||
|
||||
blockNode->genReturn(dsqlScratch);
|
||||
|
||||
dsqlScratch->genReturn();
|
||||
dsqlScratch->appendUChar(blr_leave);
|
||||
dsqlScratch->appendUChar(0);
|
||||
}
|
||||
|
@ -103,12 +103,11 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class ExecBlockNode : public DsqlOnlyStmtNode, public BlockNode
|
||||
class ExecBlockNode : public DsqlOnlyStmtNode
|
||||
{
|
||||
public:
|
||||
explicit ExecBlockNode(MemoryPool& pool)
|
||||
: DsqlOnlyStmtNode(pool),
|
||||
BlockNode(pool, true),
|
||||
parameters(pool),
|
||||
returns(pool),
|
||||
localDeclList(NULL),
|
||||
@ -319,7 +318,6 @@ class SuspendNode : public StmtNode
|
||||
public:
|
||||
explicit SuspendNode(MemoryPool& pool)
|
||||
: StmtNode(pool),
|
||||
blockNode(NULL),
|
||||
message(NULL),
|
||||
statement(NULL)
|
||||
{
|
||||
@ -339,7 +337,6 @@ public:
|
||||
virtual const jrd_nod* execute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
public:
|
||||
BlockNode* blockNode;
|
||||
NestConst<jrd_nod> message;
|
||||
NestConst<jrd_nod> statement;
|
||||
};
|
||||
@ -361,7 +358,6 @@ public:
|
||||
virtual void genBlr();
|
||||
|
||||
public:
|
||||
BlockNode* blockNode;
|
||||
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
|
||||
{
|
||||
class Database;
|
||||
class Attachment;
|
||||
class Database;
|
||||
class DsqlCompilerScratch;
|
||||
class jrd_tra;
|
||||
class jrd_req;
|
||||
class blb;
|
||||
struct bid;
|
||||
|
||||
class BlockNode;
|
||||
class dsql_blb;
|
||||
class dsql_ctx;
|
||||
class dsql_msg;
|
||||
@ -103,8 +103,6 @@ namespace Firebird
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
class DsqlCompilerScratch;
|
||||
|
||||
//! generic data type used to store strings
|
||||
class dsql_str : public pool_alloc_rpt<char, dsql_type_str>
|
||||
{
|
||||
@ -416,7 +414,6 @@ public:
|
||||
type(TYPE_SELECT),
|
||||
flags(0),
|
||||
ddlData(p),
|
||||
blockNode(NULL),
|
||||
ddlNode(NULL),
|
||||
blob(NULL),
|
||||
sendMsg(NULL),
|
||||
@ -448,10 +445,6 @@ public:
|
||||
const Firebird::HalfStaticArray<UCHAR, 1024>& getDdlData() const { return ddlData; }
|
||||
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; }
|
||||
const dsql_nod* getDdlNode() const { return ddlNode; }
|
||||
void setDdlNode(dsql_nod* value) { ddlNode = value; }
|
||||
@ -496,7 +489,6 @@ private:
|
||||
ULONG flags; // generic flag
|
||||
Firebird::RefStrPtr sqlText;
|
||||
Firebird::HalfStaticArray<UCHAR, 1024> ddlData;
|
||||
BlockNode* blockNode; // Defining block
|
||||
dsql_nod* ddlNode; // Store metadata statement
|
||||
dsql_blb* blob; // Blob info for blob statements
|
||||
dsql_msg* sendMsg; // Message to be sent to start request
|
||||
@ -580,236 +572,6 @@ protected:
|
||||
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
|
||||
class dsql_blb : public pool_alloc<dsql_type_blb>
|
||||
{
|
||||
@ -904,7 +666,7 @@ public:
|
||||
|
||||
bool getImplicitJoinField(const Firebird::MetaName& name, dsql_nod*& node);
|
||||
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch, dsql_nod* partitionNode,
|
||||
dsql_nod* orderNode);
|
||||
dsql_nod* orderNode);
|
||||
};
|
||||
|
||||
// Flag values for ctx_flags
|
||||
|
@ -1316,7 +1316,7 @@ static void gen_cast( DsqlCompilerScratch* dsqlScratch, const dsql_nod* node)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_cast);
|
||||
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]);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../dsql/dsql.h"
|
||||
#include "../dsql/DsqlCompilerScratch.h"
|
||||
#include "../dsql/misc_func.h"
|
||||
|
||||
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*,
|
||||
const DsqlContextStack&);
|
||||
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_outputs(DsqlCompilerScratch*, const dsql_prc*);
|
||||
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_parameter_name(dsql_nod*, const dsql_nod*, const dsql_rel*);
|
||||
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*);
|
||||
|
||||
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);
|
||||
temp->nod_arg[0] = input->nod_arg[0];
|
||||
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;
|
||||
@ -2623,19 +2617,8 @@ void PASS1_check_unique_fields_names(StrArray& names, const dsql_nod* fields)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
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)
|
||||
// Compose two booleans.
|
||||
dsql_nod* PASS1_compose( dsql_nod* expr1, dsql_nod* expr2, NOD_TYPE dsql_operator)
|
||||
{
|
||||
DEV_BLKCHK(expr1, 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;
|
||||
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;
|
||||
@ -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
|
||||
@ -9011,13 +8585,9 @@ static dsql_nod* pass1_variable( DsqlCompilerScratch* dsqlScratch, dsql_nod* inp
|
||||
|
||||
DEV_BLKCHK(var_name, dsql_type_str);
|
||||
|
||||
BlockNode* block = dsqlScratch->getStatement()->getBlockNode();
|
||||
if (block)
|
||||
{
|
||||
dsql_nod* varNode = block->resolveVariable(var_name);
|
||||
if (varNode)
|
||||
return varNode;
|
||||
}
|
||||
dsql_nod* varNode = dsqlScratch->resolveVariable(var_name);
|
||||
if (varNode)
|
||||
return varNode;
|
||||
|
||||
// field unresolved
|
||||
// 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.
|
||||
// For non-hidden, change "node" if the field is part of an
|
||||
// implicit join.
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define DSQL_PASS1_PROTO_H
|
||||
|
||||
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_label(Jrd::DsqlCompilerScratch*, Jrd::dsql_nod*);
|
||||
Jrd::dsql_nod* PASS1_label2(Jrd::DsqlCompilerScratch*, Jrd::dsql_nod*, Jrd::dsql_nod*);
|
||||
|
Loading…
Reference in New Issue
Block a user