2010-08-27 04:18:00 +02:00
|
|
|
/*
|
|
|
|
* 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 "../dsql/DsqlCompilerScratch.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../dsql/DdlNodes.h"
|
|
|
|
#include "../dsql/ExprNodes.h"
|
2011-02-22 01:51:56 +01:00
|
|
|
#include "../dsql/StmtNodes.h"
|
2010-08-27 04:18:00 +02:00
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/blr.h"
|
2011-01-09 22:58:56 +01:00
|
|
|
#include "../jrd/RecordSourceNodes.h"
|
2010-08-27 04:18:00 +02:00
|
|
|
#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 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())
|
|
|
|
{
|
2011-05-27 04:05:27 +02:00
|
|
|
if (field->fld_type_of_table.hasData())
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
if (field->fld_explicit_collation)
|
|
|
|
{
|
|
|
|
appendUChar(blr_column_name2);
|
|
|
|
appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
2011-05-27 04:05:27 +02:00
|
|
|
appendMetaString(field->fld_type_of_table.c_str());
|
2010-08-27 04:18:00 +02:00
|
|
|
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);
|
2011-05-27 04:05:27 +02:00
|
|
|
appendMetaString(field->fld_type_of_table.c_str());
|
2010-08-27 04:18:00 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
void DsqlCompilerScratch::putType(const TypeClause& type, bool useSubType)
|
|
|
|
{
|
|
|
|
#ifdef DEV_BUILD
|
|
|
|
// Check if the field describes a known datatype
|
|
|
|
if (type.type > FB_NELEM(blr_dtypes) || !blr_dtypes[type.type])
|
|
|
|
{
|
|
|
|
SCHAR buffer[100];
|
|
|
|
|
|
|
|
sprintf(buffer, "Invalid dtype %d in put_dtype", type.type);
|
|
|
|
ERRD_bugcheck(buffer);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (type.notNull)
|
|
|
|
appendUChar(blr_not_nullable);
|
|
|
|
|
|
|
|
if (type.typeOfName.hasData())
|
|
|
|
{
|
|
|
|
if (type.typeOfTable.hasData())
|
|
|
|
{
|
|
|
|
if (type.collateSpecified)
|
|
|
|
{
|
|
|
|
appendUChar(blr_column_name2);
|
|
|
|
appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
appendMetaString(type.typeOfTable.c_str());
|
|
|
|
appendMetaString(type.typeOfName.c_str());
|
|
|
|
appendUShort(type.textType);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendUChar(blr_column_name);
|
|
|
|
appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
appendMetaString(type.typeOfTable.c_str());
|
|
|
|
appendMetaString(type.typeOfName.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (type.collateSpecified)
|
|
|
|
{
|
|
|
|
appendUChar(blr_domain_name2);
|
|
|
|
appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
appendMetaString(type.typeOfName.c_str());
|
|
|
|
appendUShort(type.textType);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendUChar(blr_domain_name);
|
|
|
|
appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
appendMetaString(type.typeOfName.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type.type)
|
|
|
|
{
|
|
|
|
case dtype_cstring:
|
|
|
|
case dtype_text:
|
|
|
|
case dtype_varying:
|
|
|
|
case dtype_blob:
|
|
|
|
if (!useSubType)
|
|
|
|
appendUChar(blr_dtypes[type.type]);
|
|
|
|
else if (type.type == dtype_varying)
|
|
|
|
{
|
|
|
|
appendUChar(blr_varying2);
|
|
|
|
appendUShort(type.textType);
|
|
|
|
}
|
|
|
|
else if (type.type == dtype_cstring)
|
|
|
|
{
|
|
|
|
appendUChar(blr_cstring2);
|
|
|
|
appendUShort(type.textType);
|
|
|
|
}
|
|
|
|
else if (type.type == dtype_blob)
|
|
|
|
{
|
|
|
|
appendUChar(blr_blob2);
|
|
|
|
appendUShort(type.subType);
|
|
|
|
appendUShort(type.textType);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendUChar(blr_text2);
|
|
|
|
appendUShort(type.textType);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type.type == dtype_varying)
|
|
|
|
appendUShort(type.length - sizeof(USHORT));
|
|
|
|
else if (type.type != dtype_blob)
|
|
|
|
appendUShort(type.length);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
appendUChar(blr_dtypes[type.type]);
|
|
|
|
if (DTYPE_IS_EXACT(type.type) || dtype_quad == type.type)
|
|
|
|
appendUChar(type.scale);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-27 04:18:00 +02:00
|
|
|
// Emit dyn for the local variables declared in a procedure or trigger.
|
2011-03-06 03:48:34 +01:00
|
|
|
void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT locals)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
if (!parameters)
|
|
|
|
return;
|
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
NestConst<StmtNode>* ptr = parameters->statements.begin();
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
for (const NestConst<StmtNode>* const end = parameters->statements.end(); ptr != end; ++ptr)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-03-06 03:48:34 +01:00
|
|
|
StmtNode* parameter = *ptr;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
putDebugSrcInfo(parameter->line, parameter->column);
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
const DeclareVariableNode* varNode;
|
|
|
|
|
|
|
|
if ((varNode = parameter->as<DeclareVariableNode>()))
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-03-06 03:48:34 +01:00
|
|
|
dsql_fld* field = varNode->dsqlDef->legacyField;
|
|
|
|
const NestConst<StmtNode>* rest = ptr;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
while (++rest != end)
|
|
|
|
{
|
2011-03-06 03:48:34 +01:00
|
|
|
const DeclareVariableNode* varNode2;
|
|
|
|
|
|
|
|
if ((varNode2 = (*rest)->as<DeclareVariableNode>()))
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-03-06 03:48:34 +01:00
|
|
|
dsql_fld* rest_field = varNode2->dsqlDef->legacyField;
|
|
|
|
|
2010-08-27 04:18:00 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
dsql_var* variable = makeVariable(field, field->fld_name.c_str(), dsql_var::TYPE_LOCAL,
|
|
|
|
0, 0, locals);
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
putLocalVariable(variable, varNode, varNode->dsqlDef->collate);
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2010-11-02 18:05:01 +01:00
|
|
|
// Some field attributes are calculated inside putLocalVariable(), so we reinitialize
|
|
|
|
// the descriptor.
|
2011-01-31 15:47:41 +01:00
|
|
|
MAKE_desc_from_field(&variable->desc, field);
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
++locals;
|
|
|
|
}
|
2011-10-16 22:36:07 +02:00
|
|
|
else if (StmtNode::is<DeclareCursorNode>(parameter) ||
|
|
|
|
StmtNode::is<DeclareSubProcNode>(parameter) ||
|
|
|
|
StmtNode::is<DeclareSubFuncNode>(parameter))
|
2011-10-03 00:11:41 +02:00
|
|
|
{
|
|
|
|
parameter->dsqlPass(this);
|
|
|
|
parameter->genBlr(this);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fb_assert(false);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write out local variable field data type.
|
2011-03-06 03:48:34 +01:00
|
|
|
void DsqlCompilerScratch::putLocalVariable(dsql_var* variable, const DeclareVariableNode* hostParam,
|
|
|
|
const MetaName& collationName)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-01-31 15:47:41 +01:00
|
|
|
dsql_fld* field = variable->field;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
appendUChar(blr_dcl_variable);
|
2011-01-31 15:47:41 +01:00
|
|
|
appendUShort(variable->number);
|
2010-08-27 04:18:00 +02:00
|
|
|
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
|
2012-03-18 22:37:13 +01:00
|
|
|
ValueSourceClause* node = hostParam ? hostParam->dsqlDef->defaultClause : NULL;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-01-19 18:30:58 +01:00
|
|
|
if (variable->type == dsql_var::TYPE_INPUT)
|
|
|
|
{
|
2012-01-20 02:42:03 +01:00
|
|
|
// Assign EXECUTE BLOCK's input parameter to its corresponding internal variable.
|
2012-01-19 18:30:58 +01:00
|
|
|
|
|
|
|
appendUChar(blr_assignment);
|
|
|
|
|
|
|
|
appendUChar(blr_parameter2);
|
|
|
|
appendUChar(variable->msgNumber);
|
|
|
|
appendUShort(variable->msgItem);
|
|
|
|
appendUShort(variable->msgItem + 1);
|
|
|
|
|
|
|
|
appendUChar(blr_variable);
|
|
|
|
appendUShort(variable->number);
|
|
|
|
}
|
|
|
|
else if (node || (!field->fld_full_domain && !field->fld_not_nullable))
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
appendUChar(blr_assignment);
|
|
|
|
|
|
|
|
if (node)
|
2012-04-25 03:42:47 +02:00
|
|
|
GEN_expr(this, Node::doDsqlPass(this, node->value, false));
|
2010-08-27 04:18:00 +02:00
|
|
|
else
|
|
|
|
appendUChar(blr_null); // Initialize variable to NULL
|
|
|
|
|
|
|
|
appendUChar(blr_variable);
|
2011-01-31 15:47:41 +01:00
|
|
|
appendUShort(variable->number);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendUChar(blr_init_variable);
|
2011-01-31 15:47:41 +01:00
|
|
|
appendUShort(variable->number);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
2011-01-31 15:47:41 +01:00
|
|
|
if (variable->name.hasData()) // Not a function return value
|
|
|
|
putDebugVariable(variable->number, variable->name);
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
++hiddenVarsNumber;
|
|
|
|
}
|
|
|
|
|
2011-01-31 15:47:41 +01:00
|
|
|
// Make a variable.
|
|
|
|
dsql_var* DsqlCompilerScratch::makeVariable(dsql_fld* field, const char* name,
|
|
|
|
const dsql_var::Type type, USHORT msgNumber, USHORT itemNumber, USHORT localNumber)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
|
|
|
dsql_var* dsqlVar = FB_NEW(*tdbb->getDefaultPool()) dsql_var(*tdbb->getDefaultPool());
|
|
|
|
dsqlVar->type = type;
|
|
|
|
dsqlVar->msgNumber = msgNumber;
|
|
|
|
dsqlVar->msgItem = itemNumber;
|
|
|
|
dsqlVar->number = localNumber;
|
|
|
|
dsqlVar->field = field;
|
|
|
|
dsqlVar->name = name;
|
|
|
|
|
|
|
|
if (field)
|
|
|
|
MAKE_desc_from_field(&dsqlVar->desc, field);
|
|
|
|
|
|
|
|
if (type == dsql_var::TYPE_HIDDEN)
|
|
|
|
hiddenVariables.push(dsqlVar);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
variables.push(dsqlVar);
|
|
|
|
|
|
|
|
if (type == dsql_var::TYPE_OUTPUT)
|
|
|
|
outputVariables.push(dsqlVar);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dsqlVar;
|
|
|
|
}
|
|
|
|
|
2010-08-27 04:18:00 +02:00
|
|
|
// Try to resolve variable name against parameters and local variables.
|
2012-03-25 03:08:55 +02:00
|
|
|
dsql_var* DsqlCompilerScratch::resolveVariable(const MetaName& varName)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-01-31 01:13:15 +01:00
|
|
|
for (dsql_var* const* i = variables.begin(); i != variables.end(); ++i)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-01-31 01:13:15 +01:00
|
|
|
const dsql_var* variable = *i;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if (variable->name == varName)
|
2010-11-02 18:05:01 +01:00
|
|
|
return *i;
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2011-01-31 01:13:15 +01:00
|
|
|
for (Array<dsql_var*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-01-31 01:13:15 +01:00
|
|
|
const dsql_var* variable = *i;
|
2010-08-27 04:18:00 +02:00
|
|
|
appendUChar(blr_assignment);
|
|
|
|
appendUChar(blr_variable);
|
2011-01-31 15:47:41 +01:00
|
|
|
appendUShort(variable->number);
|
2010-08-27 04:18:00 +02:00
|
|
|
appendUChar(blr_parameter2);
|
2011-01-31 15:47:41 +01:00
|
|
|
appendUChar(variable->msgNumber);
|
|
|
|
appendUShort(variable->msgItem);
|
|
|
|
appendUShort(variable->msgItem + 1);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-26 01:28:09 +02:00
|
|
|
void DsqlCompilerScratch::genParameters(Array<ParameterClause>& parameters,
|
|
|
|
Array<ParameterClause>& returns)
|
|
|
|
{
|
|
|
|
if (parameters.hasData())
|
|
|
|
{
|
|
|
|
fb_assert(parameters.getCount() < MAX_USHORT / 2);
|
|
|
|
appendUChar(blr_message);
|
|
|
|
appendUChar(0);
|
|
|
|
appendUShort(2 * parameters.getCount());
|
|
|
|
|
|
|
|
for (size_t i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
putDebugArgument(fb_dbg_arg_input, i, parameter.name.c_str());
|
|
|
|
putType(parameter, true);
|
|
|
|
|
|
|
|
// Add slot for null flag (parameter2).
|
|
|
|
appendUChar(blr_short);
|
|
|
|
appendUChar(0);
|
|
|
|
|
|
|
|
makeVariable(parameter.legacyField, parameter.name.c_str(),
|
|
|
|
dsql_var::TYPE_INPUT, 0, (USHORT) (2 * i), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(returns.getCount() < MAX_USHORT / 2);
|
|
|
|
appendUChar(blr_message);
|
|
|
|
appendUChar(1);
|
|
|
|
appendUShort(2 * returns.getCount() + 1);
|
|
|
|
|
|
|
|
if (returns.hasData())
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < returns.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = returns[i];
|
|
|
|
putDebugArgument(fb_dbg_arg_output, i, parameter.name.c_str());
|
|
|
|
putType(parameter, true);
|
|
|
|
|
|
|
|
// Add slot for null flag (parameter2).
|
|
|
|
appendUChar(blr_short);
|
|
|
|
appendUChar(0);
|
|
|
|
|
|
|
|
makeVariable(parameter.legacyField, parameter.name.c_str(),
|
|
|
|
dsql_var::TYPE_OUTPUT, 1, (USHORT) (2 * i), i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add slot for EOS.
|
|
|
|
appendUChar(blr_short);
|
|
|
|
appendUChar(0);
|
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
void DsqlCompilerScratch::addCTEs(WithClause* withClause)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (withClause->recursive)
|
2010-08-27 04:18:00 +02:00
|
|
|
flags |= DsqlCompilerScratch::FLAG_RECURSIVE_CTE;
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
const SelectExprNode* const* end = withClause->end();
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
for (SelectExprNode* const* cte = withClause->begin(); cte != end; ++cte)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
if (withClause->recursive)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
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.
|
2012-04-07 05:03:28 +02:00
|
|
|
addCTEAlias((*cte)->alias);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
ctes.add(*cte);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* DsqlCompilerScratch::findCTE(const MetaName& name)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < ctes.getCount(); ++i)
|
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* cte = ctes[i];
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (cte->alias == name.c_str())
|
2010-08-27 04:18:00 +02:00
|
|
|
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)
|
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
const SelectExprNode* cte = ctes[i];
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (!(cte->dsqlFlags & RecordSourceNode::DFLAG_DT_CTE_USED))
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_not_used) << cte->alias);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2012-04-07 05:03:28 +02:00
|
|
|
// For example, if we have 4 CTE's where first two is non-recursive and last two is recursive:
|
2010-08-27 04:18:00 +02:00
|
|
|
//
|
|
|
|
// 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.
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* DsqlCompilerScratch::pass1RecursiveCte(SelectExprNode* input)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RecordSourceNode* query = input->querySpec;
|
|
|
|
UnionSourceNode* unionQuery = query->as<UnionSourceNode>();
|
2012-04-07 05:03:28 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (!unionQuery && pass1RseIsRecursive(query->as<RseNode>()))
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Recursive CTE (%s) must be an UNION
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_not_a_union) << input->alias);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// split queries list on two parts: anchor and recursive
|
2012-04-25 03:42:47 +02:00
|
|
|
RecordSourceNode* anchorRse = NULL;
|
|
|
|
RecordSourceNode* recursiveRse = NULL;
|
|
|
|
RecordSourceNode* qry = query;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
UnionSourceNode* newQry = FB_NEW(pool) UnionSourceNode(pool);
|
2012-04-25 03:42:47 +02:00
|
|
|
newQry->dsqlClauses = FB_NEW(pool) RecSourceListNode(pool, 2);
|
2012-04-07 05:03:28 +02:00
|
|
|
|
|
|
|
if (unionQuery)
|
|
|
|
{
|
|
|
|
newQry->dsqlAll = unionQuery->dsqlAll;
|
|
|
|
newQry->recursive = unionQuery->recursive;
|
|
|
|
}
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
RecordSourceNode* rse = NULL;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (unionQuery)
|
2012-04-25 03:42:47 +02:00
|
|
|
rse = unionQuery->dsqlClauses->dsqlArgs[1];
|
2010-08-27 04:18:00 +02:00
|
|
|
else
|
|
|
|
rse = qry;
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* newRse = pass1RseIsRecursive(rse->as<RseNode>());
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (newRse) // rse is recursive
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
if (anchorRse)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// CTE '%s' defined non-recursive member after recursive
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_nonrecurs_after_recurs) << input->alias);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (newRse->dsqlDistinct)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Recursive member of CTE '%s' has %s clause
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_wrong_clause) << input->alias <<
|
2010-08-27 04:18:00 +02:00
|
|
|
Arg::Str("DISTINCT"));
|
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (newRse->dsqlGroup)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Recursive member of CTE '%s' has %s clause
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_wrong_clause) << input->alias <<
|
2010-08-27 04:18:00 +02:00
|
|
|
Arg::Str("GROUP BY"));
|
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (newRse->dsqlHaving)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Recursive member of CTE '%s' has %s clause
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_wrong_clause) << input->alias <<
|
2010-08-27 04:18:00 +02:00
|
|
|
Arg::Str("HAVING"));
|
|
|
|
}
|
|
|
|
// hvlad: we need also forbid any aggregate function here
|
|
|
|
// but for now i have no idea how to do it simple
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (!newQry->dsqlAll)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Recursive members of CTE (%s) must be linked with another members via UNION ALL
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_union_all) << input->alias);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!recursiveRse)
|
2012-04-25 03:42:47 +02:00
|
|
|
recursiveRse = newQry;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
newRse->dsqlFlags |= RecordSourceNode::DFLAG_RECURSIVE;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (unionQuery)
|
2012-04-25 03:42:47 +02:00
|
|
|
newQry->dsqlClauses->dsqlArgs[1] = newRse;
|
2010-08-27 04:18:00 +02:00
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
newQry->dsqlClauses->dsqlArgs[0] = newRse;
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
if (unionQuery)
|
2012-04-25 03:42:47 +02:00
|
|
|
newQry->dsqlClauses->dsqlArgs[1] = rse;
|
2010-08-27 04:18:00 +02:00
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
newQry->dsqlClauses->dsqlArgs[0] = rse;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
if (!anchorRse)
|
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
if (unionQuery)
|
2012-04-25 03:42:47 +02:00
|
|
|
anchorRse = newQry;
|
2010-08-27 04:18:00 +02:00
|
|
|
else
|
|
|
|
anchorRse = rse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (!unionQuery)
|
2010-08-27 04:18:00 +02:00
|
|
|
break;
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
qry = unionQuery->dsqlClauses->dsqlArgs[0];
|
|
|
|
unionQuery = qry->as<UnionSourceNode>();
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (unionQuery)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
UnionSourceNode* newUnion = FB_NEW(pool) UnionSourceNode(pool);
|
2012-04-25 03:42:47 +02:00
|
|
|
newUnion->dsqlClauses = FB_NEW(pool) RecSourceListNode(pool, 2);
|
2012-04-07 05:03:28 +02:00
|
|
|
newUnion->dsqlAll = unionQuery->dsqlAll;
|
|
|
|
newUnion->recursive = unionQuery->recursive;
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
newQry->dsqlClauses->dsqlArgs[0] = newUnion;
|
2012-04-07 05:03:28 +02:00
|
|
|
newQry = newUnion;
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!recursiveRse)
|
|
|
|
return input;
|
|
|
|
|
|
|
|
if (!anchorRse)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Non-recursive member is missing in CTE '%s'
|
2012-04-07 05:03:28 +02:00
|
|
|
Arg::Gds(isc_dsql_cte_miss_nonrecursive) << input->alias);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
UnionSourceNode* qry2 = recursiveRse->as<UnionSourceNode>();
|
2012-04-07 05:03:28 +02:00
|
|
|
UnionSourceNode* list = NULL;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
while (qry2->dsqlClauses->dsqlArgs[0] != anchorRse)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
list = qry2;
|
2012-04-25 03:42:47 +02:00
|
|
|
qry2 = qry2->dsqlClauses->dsqlArgs[0]->as<UnionSourceNode>();
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
qry2->dsqlClauses->dsqlArgs[0] = NULL;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
if (list)
|
2012-04-25 03:42:47 +02:00
|
|
|
list->dsqlClauses->dsqlArgs[0] = qry2->dsqlClauses->dsqlArgs[1];
|
2010-08-27 04:18:00 +02:00
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
recursiveRse = qry2->dsqlClauses->dsqlArgs[1];
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
UnionSourceNode* unionNode = FB_NEW(pool) UnionSourceNode(pool);
|
|
|
|
unionNode->dsqlAll = true;
|
|
|
|
unionNode->recursive = true;
|
2012-04-25 03:42:47 +02:00
|
|
|
unionNode->dsqlClauses = FB_NEW(pool) RecSourceListNode(pool, 2);
|
|
|
|
unionNode->dsqlClauses->dsqlArgs[0] = anchorRse;
|
|
|
|
unionNode->dsqlClauses->dsqlArgs[1] = recursiveRse;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* select = FB_NEW(getPool()) SelectExprNode(getPool());
|
2012-04-25 03:42:47 +02:00
|
|
|
select->querySpec = unionNode;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
select->alias = input->alias;
|
|
|
|
select->columns = input->columns;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
return select;
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* DsqlCompilerScratch::pass1RseIsRecursive(RseNode* input)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
RseNode* result = FB_NEW(getPool()) RseNode(getPool());
|
|
|
|
result->dsqlFirst = input->dsqlFirst;
|
|
|
|
result->dsqlSkip = input->dsqlSkip;
|
|
|
|
result->dsqlDistinct = input->dsqlDistinct;
|
|
|
|
result->dsqlSelectList = input->dsqlSelectList;
|
|
|
|
result->dsqlWhere = input->dsqlWhere;
|
|
|
|
result->dsqlGroup = input->dsqlGroup;
|
|
|
|
result->dsqlHaving = input->dsqlHaving;
|
2012-04-25 03:42:47 +02:00
|
|
|
result->rse_plan = input->rse_plan;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RecSourceListNode* srcTables = input->dsqlFrom;
|
|
|
|
RecSourceListNode* dstTables = FB_NEW(pool) RecSourceListNode(pool, srcTables->dsqlArgs.getCount());
|
2011-01-09 22:58:56 +01:00
|
|
|
result->dsqlFrom = dstTables;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RecordSourceNode** pDstTable = dstTables->dsqlArgs.begin();
|
|
|
|
RecordSourceNode** pSrcTable = srcTables->dsqlArgs.begin();
|
|
|
|
RecordSourceNode** end = srcTables->dsqlArgs.end();
|
2010-08-27 04:18:00 +02:00
|
|
|
bool found = false;
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
for (RecordSourceNode** prev = pDstTable; pSrcTable < end; ++pSrcTable, ++pDstTable)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
*prev++ = *pDstTable = *pSrcTable;
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* rseNode = (*pDstTable)->as<RseNode>();
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (rseNode)
|
|
|
|
{
|
|
|
|
fb_assert(rseNode->dsqlExplicitJoin);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* dstRse = rseNode->clone();
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
*pDstTable = dstRse;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
BoolExprNode* joinBool = pass1JoinIsRecursive(*pDstTable);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
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));
|
|
|
|
}
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
found = true;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
result->dsqlWhere = PASS1_compose(result->dsqlWhere, joinBool, blr_and);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((*pDstTable)->is<ProcedureSourceNode>() || (*pDstTable)->is<RelationSourceNode>())
|
|
|
|
{
|
|
|
|
if (pass1RelProcIsRecursive(*pDstTable))
|
|
|
|
{
|
|
|
|
if (found)
|
2011-01-30 01:25:46 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
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));
|
2011-01-30 01:25:46 +01:00
|
|
|
}
|
2012-04-25 03:42:47 +02:00
|
|
|
found = true;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
--prev;
|
|
|
|
dstTables->dsqlArgs.pop();
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
2012-04-25 03:42:47 +02:00
|
|
|
else if (!(*pDstTable)->is<SelectExprNode>())
|
|
|
|
fb_assert(false);
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (found)
|
2012-04-25 03:42:47 +02:00
|
|
|
return result;
|
2011-04-02 06:24:20 +02:00
|
|
|
|
|
|
|
return NULL;
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if table reference is recursive i.e. its name is equal to the name of current processing CTE.
|
2012-04-25 03:42:47 +02:00
|
|
|
bool DsqlCompilerScratch::pass1RelProcIsRecursive(RecordSourceNode* input)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
MetaName relName;
|
|
|
|
string relAlias;
|
|
|
|
ProcedureSourceNode* procNode;
|
|
|
|
RelationSourceNode* relNode;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((procNode = input->as<ProcedureSourceNode>()))
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
relName = procNode->dsqlName.identifier;
|
|
|
|
relAlias = procNode->alias;
|
2010-08-27 04:18:00 +02:00
|
|
|
}
|
2012-04-25 03:42:47 +02:00
|
|
|
else if ((relNode = input->as<RelationSourceNode>()))
|
2011-01-30 01:25:46 +01:00
|
|
|
{
|
|
|
|
relName = relNode->dsqlName;
|
|
|
|
relAlias = relNode->alias;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
fb_assert(currCtes.hasData());
|
2012-04-07 05:03:28 +02:00
|
|
|
const SelectExprNode* currCte = currCtes.object();
|
|
|
|
const bool recursive = currCte->alias == relName.c_str();
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
if (recursive)
|
2011-01-30 01:25:46 +01:00
|
|
|
addCTEAlias(relAlias.hasData() ? relAlias.c_str() : relName.c_str());
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
BoolExprNode* DsqlCompilerScratch::pass1JoinIsRecursive(RecordSourceNode*& input)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* inputRse = input->as<RseNode>();
|
|
|
|
fb_assert(inputRse);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
const UCHAR joinType = inputRse->rse_jointype;
|
2010-08-27 04:18:00 +02:00
|
|
|
bool remove = false;
|
|
|
|
|
|
|
|
bool leftRecursive = false;
|
2012-04-25 03:42:47 +02:00
|
|
|
BoolExprNode* leftBool = NULL;
|
|
|
|
RecordSourceNode** joinTable = &inputRse->dsqlFrom->dsqlArgs[0];
|
2011-01-22 21:40:04 +01:00
|
|
|
RseNode* joinRse;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((joinRse = ExprNode::as<RseNode>(*joinTable)) && joinRse->dsqlExplicitJoin)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
leftBool = pass1JoinIsRecursive(*joinTable);
|
2010-08-27 04:18:00 +02:00
|
|
|
leftRecursive = (leftBool != NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
leftBool = inputRse->dsqlWhere;
|
|
|
|
leftRecursive = pass1RelProcIsRecursive(*joinTable);
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
if (leftRecursive)
|
|
|
|
remove = true;
|
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (leftRecursive && joinType != blr_inner)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
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;
|
2012-04-25 03:42:47 +02:00
|
|
|
BoolExprNode* rightBool = NULL;
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
joinTable = &inputRse->dsqlFrom->dsqlArgs[1];
|
2010-08-27 04:18:00 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((joinRse = ExprNode::as<RseNode>(*joinTable)) && joinRse->dsqlExplicitJoin)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
rightBool = pass1JoinIsRecursive(*joinTable);
|
2010-08-27 04:18:00 +02:00
|
|
|
rightRecursive = (rightBool != NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
rightBool = inputRse->dsqlWhere;
|
|
|
|
rightRecursive = pass1RelProcIsRecursive(*joinTable);
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
if (rightRecursive)
|
|
|
|
remove = true;
|
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (rightRecursive && joinType != blr_inner)
|
2010-08-27 04:18:00 +02:00
|
|
|
{
|
|
|
|
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)
|
2012-04-25 03:42:47 +02:00
|
|
|
input = inputRse->dsqlFrom->dsqlArgs[1];
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
return leftBool;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rightRecursive)
|
|
|
|
{
|
|
|
|
if (remove)
|
2012-04-25 03:42:47 +02:00
|
|
|
input = inputRse->dsqlFrom->dsqlArgs[0];
|
2010-08-27 04:18:00 +02:00
|
|
|
|
|
|
|
return rightBool;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|