8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 12:43:02 +01:00
firebird-mirror/src/dsql/pass1.cpp

2925 lines
88 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: Dynamic SQL runtime support
* MODULE: pass1.cpp
* DESCRIPTION: First-pass compiler for statement trees.
2001-05-23 15:26:42 +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): ______________________________________.
2002-06-29 08:56:51 +02:00
*
* 2001.5.26: Claudio Valderrama: field names should be skimmed from trailing
*
* 2001.5.26: Claudio Valderrama: COMPUTED fields will be skipped if a dummy
* "insert into tbl values(...)" sentence is issued.
*
* 2001.5.26: Claudio Valderrama: field names should be skimmed from trailing
* blanks to allow reliable comparisons in pass1_field. Same for table and
* and index names in plans.
*
* 2001.5.29: Claudio Valderrama: handle DROP VIEW case in pass1_statement().
*
* 2001.6.12: Claudio Valderrama: add basic BREAK capability to procedures.
*
* 2001.6.27: Claudio Valderrama: pass1_variable() now gives the name of the
* variable it can't find in the error message.
*
* 2001.6.30: Claudio Valderrama: Enhanced again to provide (line, col), see node.h.
*
* 2001.7.28: John Bellardo: added code to handle nod_limit and associated fields.
*
* 2001.08.14 Claudio Valderrama: fixed crash with trigger and CURRENT OF <cursor> syntax.
*
* 2001.09.10 John Bellardo: fixed gen_rse to attribute skip/first nodes to the parent_rse
* if present instead of the child rse. BUG #451798
*
* 2001.09.26 Claudio Valderrama: ambiguous field names are rejected from now.
*
* 2001.10.01 Claudio Valderrama: check constraints are allowed to have ambiguous field
* names because they use OLD and NEW as aliases of the same table. However, if the
* check constraint has an embedded ambiguous SELECT statement, it won't be detected.
* The code should be revisited if check constraints' before delete triggers are used
* for whatever reason. Currently they are never generated. The code can be improved
* to not report errors for fields between NEW and OLD contexts but complain otherwise.
*
* 2001.10.05 Neil McCalden: validate udf and parameters when comparing select list and
* group by list, to detect invalid SQL statements when grouping by UDFs.
*
* 2001.10.23 Ann Harrison: allow reasonable checking of ambiguous names in unions.
* Remembering, later, that LLS_PUSH expects an object, not an LLS block. Also
* stuck in the code for handling variables in pass1 - it apparently doesn't happen
* because the code returned an uninitialized pointer.
*
* 2001.11.17 Neil McCalden: Add aggregate_in_list procedure to handle cases
* where select statement has aggregate as a parameter to a udf which does
* not have to be in a group by clause.
*
* 2001.11.21 Claudio Valderrama: don't try to detect ambiguity in pass1_field()
* if the field or output procedure parameter has been fully qualified!!!
*
* 2001.11.27 Ann Harrison: Redo the amiguity checking so as to give better
* error messages, return warnings for dialect 1, and simplify.
*
* 2001.11.28 Claudio Valderrama: allow udf arguments to be query parameters.
* Honor the code in the parser that already accepts those parameters.
* This closes SF Bug# 409769.
*
* 2001.11.29 Claudio Valderrama: make the nice new ambiguity checking code do the
* right thing instead of crashing the engine and restore fix from 2001.11.21.
*
* 2001.12.21 Claudio Valderrama: Fix SF Bug #494832 - pass1_variable() should work
* with def_proc, mod_proc, redef_proc, def_trig and mod_trig node types.
*
* 2002.07.30 Arno Brinkman: Added pass1_coalesce, pass1_simple_case, pass1_searched_case
* and PASS1_put_args_on_stack
*
* 2002.08.04 Arno Brinkman: Added ignore_cast as parameter to PASS1_node_match,
* Changed invalid_reference procedure for allow EXTRACT, SUBSTRING, CASE,
* COALESCE and NULLIF functions in GROUP BY and as select_items.
* Removed aggregate_in_list procedure.
*
* 2002.08.07 Dmitry Yemanov: Disabled BREAK statement in triggers
*
* 2002.08.10 Dmitry Yemanov: ALTER VIEW
*
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
* exception handling in SPs/triggers,
* implemented ROWS_AFFECTED system variable
*
* 2002.09.29 Arno Brinkman: Adding more checking for aggregate functions
* and adding support for 'linking' from sub-selects to aggregate functions
2005-05-28 00:45:31 +02:00
* which are in an lower level.
* Modified functions pass1_field, pass1_rse, copy_field, PASS1_sort.
* Functions pass1_found_aggregate and pass1_found_field added.
2002-10-25 10:29:12 +02:00
*
* 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks
*
2002-10-25 10:29:12 +02:00
* 2002.10.25 Dmitry Yemanov: Re-allowed plans in triggers
*
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
*
* 2002.12.03 Dmitry Yemanov: Implemented ORDER BY clause in subqueries
*
* 2002.12.18 Dmitry Yemanov: Fixed bug with BREAK and partially implemented
* SQL-compliant labels and LEAVE statement
*
* 2003.01.11 Arno Brinkman: Reworked a lot of functions for bringing back backwards compatibilty
* with sub-selects and aggregates.
*
* 2003.01.14 Dmitry Yemanov: Fixed bug with cursors in triggers
*
* 2003.01.15 Dmitry Yemanov: Added support for parametrized events
*
* 2003.04.05 Dmitry Yemanov: Changed logic of ORDER BY with collations
* (because of the parser change)
*
* 2003.08.14 Arno Brinkman: Added derived table support.
*
* 2003.08.16 Arno Brinkman: Changed ambiguous column name checking.
*
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL.
*
2005-05-28 00:45:31 +02:00
* 2004.01.16 Vlad Horsun: added support for default parameters and
* EXECUTE BLOCK statement
*
* Adriano dos Santos Fernandes
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
2004-04-29 00:00:03 +02:00
#include <stdio.h>
2003-11-08 00:27:24 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../dsql/dsql.h"
#include "../dsql/Nodes.h"
#include "../dsql/BoolNodes.h"
#include "../dsql/ExprNodes.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/intl.h"
#include "../jrd/blr.h"
2008-02-28 14:48:16 +01:00
#include "../jrd/jrd.h"
2005-05-28 00:45:31 +02:00
#include "../jrd/constants.h"
#include "../jrd/intl_classes.h"
#include "../jrd/RecordSourceNodes.h"
#include "../dsql/DdlNodes.h"
#include "../dsql/StmtNodes.h"
2001-05-23 15:26:42 +02:00
#include "../dsql/ddl_proto.h"
#include "../dsql/errd_proto.h"
#include "../dsql/gen_proto.h"
2001-05-23 15:26:42 +02:00
#include "../dsql/make_proto.h"
#include "../dsql/metd_proto.h"
#include "../dsql/pass1_proto.h"
#include "../dsql/utld_proto.h"
#include "../dsql/DSqlDataTypeUtil.h"
2010-10-12 10:02:57 +02:00
#include "../common/dsc_proto.h"
#include "../jrd/intl_proto.h"
2008-02-28 14:48:16 +01:00
#include "../jrd/jrd_proto.h"
2005-05-28 00:45:31 +02:00
#include "../jrd/thread_proto.h"
2010-10-12 10:02:57 +02:00
#include "../yvalve/why_proto.h"
#include "../jrd/SysFunction.h"
#include "../common/classes/array.h"
#include "../common/classes/auto.h"
2003-12-31 06:36:12 +01:00
#include "../common/utils_proto.h"
#include "../common/config/config.h"
#include "../common/StatusArg.h"
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
using namespace Jrd;
using namespace Firebird;
2008-02-28 14:48:16 +01:00
static string pass1_alias_concat(const string&, const string&);
static void pass1_expand_contexts(DsqlContextStack& contexts, dsql_ctx* context);
2012-04-25 03:42:47 +02:00
static ValueListNode* pass1_expand_select_list(DsqlCompilerScratch*, ValueListNode*, RecSourceListNode*);
static ValueListNode* pass1_group_by_list(DsqlCompilerScratch*, ValueListNode*, ValueListNode*);
static ValueExprNode* pass1_make_derived_field(thread_db*, DsqlCompilerScratch*, ValueExprNode*);
static RseNode* pass1_rse(DsqlCompilerScratch*, RecordSourceNode*, ValueListNode*, RowsClause*, bool, USHORT);
static RseNode* pass1_rse_impl(DsqlCompilerScratch*, RecordSourceNode*, ValueListNode*, RowsClause*, bool, USHORT);
static ValueListNode* pass1_sel_list(DsqlCompilerScratch*, ValueListNode*);
static RseNode* pass1_union(DsqlCompilerScratch*, UnionSourceNode*, ValueListNode*, RowsClause*, bool, USHORT);
2012-05-20 12:00:52 +02:00
static void pass1_union_auto_cast(DsqlCompilerScratch*, ExprNode*, const dsc&, size_t position);
2012-04-25 03:42:47 +02:00
static void remap_streams_to_parent_context(ExprNode*, dsql_ctx*);
AggregateFinder::AggregateFinder(DsqlCompilerScratch* aDsqlScratch, bool aWindow)
: dsqlScratch(aDsqlScratch),
window(aWindow),
currentLevel(dsqlScratch->scopeLevel),
deepestLevel(0),
ignoreSubSelects(false)
{
}
2012-04-25 03:42:47 +02:00
bool AggregateFinder::find(DsqlCompilerScratch* dsqlScratch, bool window, ExprNode* node)
{
2012-04-25 03:42:47 +02:00
AggregateFinder visitor(dsqlScratch, window);
return visitor.visit(node);
}
bool AggregateFinder::visit(ExprNode* node)
{
return node && node->dsqlAggregateFinder(*this);
}
2001-05-23 15:26:42 +02:00
Aggregate2Finder::Aggregate2Finder(USHORT aCheckScopeLevel, FieldMatchType aMatchType, bool aWindowOnly)
2012-04-25 03:42:47 +02:00
: checkScopeLevel(aCheckScopeLevel),
matchType(aMatchType),
windowOnly(aWindowOnly),
currentScopeLevelEqual(true)
{
}
2012-04-25 03:42:47 +02:00
bool Aggregate2Finder::find(USHORT checkScopeLevel, FieldMatchType matchType, bool windowOnly,
ExprNode* node)
{
2012-04-25 03:42:47 +02:00
Aggregate2Finder visitor(checkScopeLevel, matchType, windowOnly);
return visitor.visit(node);
}
bool Aggregate2Finder::visit(ExprNode* node)
{
return node && node->dsqlAggregate2Finder(*this);
}
FieldFinder::FieldFinder(USHORT aCheckScopeLevel, FieldMatchType aMatchType)
2012-04-25 03:42:47 +02:00
: checkScopeLevel(aCheckScopeLevel),
matchType(aMatchType),
field(false)
{
}
2012-04-25 03:42:47 +02:00
bool FieldFinder::find(USHORT checkScopeLevel, FieldMatchType matchType, ExprNode* node)
{
2012-04-25 03:42:47 +02:00
FieldFinder visitor(checkScopeLevel, matchType);
return visitor.visit(node);
}
bool FieldFinder::visit(ExprNode* node)
{
return node && node->dsqlFieldFinder(*this);
}
2012-04-25 03:42:47 +02:00
InvalidReferenceFinder::InvalidReferenceFinder(const dsql_ctx* aContext, const ValueListNode* aList)
: context(aContext),
list(aList),
insideOwnMap(false),
insideHigherMap(false)
{
DEV_BLKCHK(list, dsql_type_nod);
}
2012-04-25 03:42:47 +02:00
bool InvalidReferenceFinder::find(const dsql_ctx* context, const ValueListNode* list, ExprNode* node)
{
InvalidReferenceFinder visitor(context, list);
return visitor.visit(node);
}
bool InvalidReferenceFinder::visit(ExprNode* node)
{
DEV_BLKCHK(node, dsql_type_nod);
if (!node)
return false;
bool invalid = false;
// ASF: What we do in this function is the verification of all fields/dbkeys (or any parent
// expression involving them) are present in the passed node list.
// That makes valid:
// select n + 0 from table group by n => The n item is present in the list
// select n + 0 from table group by n + 0 => The n + 0 item is present in the list
// And makes invalid:
// select n + 1 from table group by n + 0 => The n + 1 item is not present in the list
if (list)
{
2010-01-29 12:07:42 +01:00
// Check if this node (with ignoring of CASTs) appears also
// in the list of group by. If yes then it's allowed
2012-05-03 18:43:29 +02:00
const NestConst<ValueExprNode>* ptr = list->items.begin();
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
2009-11-19 10:37:10 +01:00
{
if (PASS1_node_match(node, *ptr, true))
return false;
2001-05-23 15:26:42 +02:00
}
}
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
return node->dsqlInvalidReferenceFinder(*this);
}
2003-08-15 02:02:18 +02:00
FieldRemapper::FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
2012-04-25 03:42:47 +02:00
ValueListNode* aPartitionNode, ValueListNode* aOrderNode)
: dsqlScratch(aDsqlScratch),
context(aContext),
window(aWindow),
partitionNode(aPartitionNode),
orderNode(aOrderNode),
currentLevel(dsqlScratch->scopeLevel)
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
DEV_BLKCHK(context, dsql_type_ctx);
}
2012-04-25 03:42:47 +02:00
ExprNode* FieldRemapper::visit(ExprNode* node)
{
2012-04-25 03:42:47 +02:00
return node ? node->dsqlFieldRemapper(*this) : NULL;
2001-05-23 15:26:42 +02:00
}
2012-04-25 03:42:47 +02:00
bool SubSelectFinder::visit(ExprNode* node)
{
2012-04-25 03:42:47 +02:00
return node && node->dsqlSubSelectFinder(*this);
2001-05-23 15:26:42 +02:00
}
/**
2005-05-28 00:45:31 +02:00
PASS1_make_context
2005-05-28 00:45:31 +02:00
@brief Generate a context for a dsqlScratch.
2005-05-28 00:45:31 +02:00
@param dsqlScratch
2012-04-25 03:42:47 +02:00
@param relationNode
**/
2012-05-03 18:43:29 +02:00
dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* relationNode)
2001-05-23 15:26:42 +02:00
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2001-05-23 15:26:42 +02:00
thread_db* const tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
dsql_rel* relation = NULL;
dsql_prc* procedure = NULL;
2001-05-23 15:26:42 +02:00
// figure out whether this is a relation or a procedure
// and give an error if it is neither
MetaName relation_name;
2012-05-03 18:43:29 +02:00
ProcedureSourceNode* procNode = NULL;
RelationSourceNode* relNode = NULL;
SelectExprNode* selNode = NULL;
2012-04-25 03:42:47 +02:00
if ((procNode = relationNode->as<ProcedureSourceNode>()))
relation_name = procNode->dsqlName.identifier;
2012-04-25 03:42:47 +02:00
else if ((relNode = relationNode->as<RelationSourceNode>()))
relation_name = relNode->dsqlName;
2012-04-25 03:42:47 +02:00
else if ((selNode = relationNode->as<SelectExprNode>()))
relation_name = selNode->alias.c_str();
2008-02-28 14:48:16 +01:00
SelectExprNode* cte = NULL;
2001-05-23 15:26:42 +02:00
if (selNode)
{
// No processing needed here for derived tables.
}
2012-05-03 18:43:29 +02:00
else if (procNode && (procNode->dsqlName.package.hasData() || procNode->sourceList))
{
if (procNode->dsqlName.package.isEmpty())
procedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
if (!procedure)
{
procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch,
procNode->dsqlName);
}
if (!procedure)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_procedure_err) <<
Arg::Gds(isc_random) <<
Arg::Str(procNode->dsqlName.toString()) <<
2012-04-25 03:42:47 +02:00
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relationNode->line) <<
Arg::Num(relationNode->column));
}
}
else if ((cte = dsqlScratch->findCTE(relation_name)))
2012-04-25 03:42:47 +02:00
relationNode = cte;
else
{
if (procNode && procNode->dsqlName.package.isEmpty())
procedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
if (!procedure)
relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name);
if (!relation && !procedure && procNode)
{
procedure = METD_get_procedure(dsqlScratch->getTransaction(),
dsqlScratch, procNode->dsqlName);
}
2001-05-23 15:26:42 +02:00
if (!relation && !procedure)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_relation_err) <<
Arg::Gds(isc_random) << Arg::Str(relation_name) <<
2012-04-25 03:42:47 +02:00
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relationNode->line) <<
Arg::Num(relationNode->column));
}
}
if (procedure && !procedure->prc_out_count)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-84) <<
Arg::Gds(isc_dsql_procedure_use_err) << Arg::Str(procedure->prc_name.toString()) <<
2012-04-25 03:42:47 +02:00
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relationNode->line) <<
Arg::Num(relationNode->column));
}
2001-05-23 15:26:42 +02:00
// Set up context block.
dsql_ctx* context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
context->ctx_relation = relation;
context->ctx_procedure = procedure;
if (selNode)
context->ctx_context = USHORT(MAX_UCHAR) + 1 + dsqlScratch->derivedContextNumber++;
else
context->ctx_context = dsqlScratch->contextNumber++;
context->ctx_scope_level = dsqlScratch->scopeLevel;
// When we're in a outer-join part mark context for it.
if (dsqlScratch->inOuterJoin)
context->ctx_flags |= CTX_outer_join;
context->ctx_in_outer_join = dsqlScratch->inOuterJoin;
// find the context alias name, if it exists.
string str;
2012-04-25 03:42:47 +02:00
if ((procNode = relationNode->as<ProcedureSourceNode>()))
str = procNode->alias;
2012-04-25 03:42:47 +02:00
else if ((relNode = relationNode->as<RelationSourceNode>()))
str = relNode->alias;
2012-04-25 03:42:47 +02:00
else if ((selNode = relationNode->as<SelectExprNode>()))
{
str = selNode->alias;
2012-04-25 03:42:47 +02:00
// ASF: In the case of a UNION contained in a CTE, selNode->querySpec will be a
// UnionSourceNode instead of a RseNode. Since ctx_rse is a RseNode and is always accessed
// as one, I'll leave this assignment here. It will be set in PASS1_derived_table anyway.
///context->ctx_rse = selNode->querySpec;
}
if (str.hasData())
context->ctx_internal_alias = str;
if (dsqlScratch->aliasRelationPrefix.hasData() && !selNode)
{
if (str.hasData())
str = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, str);
else
str = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, relation_name.c_str());
}
if (str.hasData())
{
context->ctx_alias = str;
// check to make sure the context is not already used at this same
// query level (if there are no subqueries, this checks that the
// alias is not used twice in the dsqlScratch).
for (DsqlContextStack::iterator stack(*dsqlScratch->context); stack.hasData(); ++stack)
2003-11-10 10:16:38 +01:00
{
const dsql_ctx* conflict = stack.object();
if (conflict->ctx_scope_level != context->ctx_scope_level)
continue;
const TEXT* conflict_name;
ISC_STATUS error_code;
if (conflict->ctx_alias.hasData())
{
conflict_name = conflict->ctx_alias.c_str();
error_code = isc_alias_conflict_err;
// alias %s conflicts with an alias in the same dsqlScratch.
2005-05-22 05:11:41 +02:00
}
else if (conflict->ctx_procedure)
{
conflict_name = conflict->ctx_procedure->prc_name.identifier.c_str();
error_code = isc_procedure_conflict_error;
// alias %s conflicts with a procedure in the same dsqlScratch.
}
else if (conflict->ctx_relation)
2005-05-22 05:11:41 +02:00
{
conflict_name = conflict->ctx_relation->rel_name.c_str();
error_code = isc_relation_conflict_err;
// alias %s conflicts with a relation in the same dsqlScratch.
}
else
continue;
if (context->ctx_alias == conflict_name)
2009-01-07 10:30:57 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(error_code) << Arg::Str(conflict_name));
2001-05-23 15:26:42 +02:00
}
}
}
if (procedure)
{
USHORT count = 0;
2012-05-03 18:43:29 +02:00
if (procNode->sourceList)
{
2012-05-03 18:43:29 +02:00
context->ctx_proc_inputs = Node::doDsqlPass(dsqlScratch, procNode->sourceList, false);
count = context->ctx_proc_inputs->items.getCount();
}
if (count > procedure->prc_in_count ||
count < procedure->prc_in_count - procedure->prc_def_count)
{
ERRD_post(Arg::Gds(isc_prcmismat) << Arg::Str(procNode->dsqlName.toString()));
}
if (count)
{
// Initialize this stack variable, and make it look like a node
2012-04-25 03:42:47 +02:00
dsc desc_node;
ValueListNode* inputList = context->ctx_proc_inputs;
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* input = inputList->items.begin();
2012-04-25 03:42:47 +02:00
for (dsql_fld* field = procedure->prc_inputs;
2012-05-03 18:43:29 +02:00
input != inputList->items.end();
2012-04-25 03:42:47 +02:00
++input, field = field->fld_next)
2009-11-19 10:37:10 +01:00
{
DEV_BLKCHK(field, dsql_type_fld);
2012-04-25 03:42:47 +02:00
MAKE_desc_from_field(&desc_node, field);
PASS1_set_parameter_type(dsqlScratch, *input, &desc_node, false);
}
}
}
2001-05-23 15:26:42 +02:00
// push the context onto the dsqlScratch context stack
// for matching fields against
2005-05-22 05:11:41 +02:00
dsqlScratch->context->push(context);
2005-05-22 05:11:41 +02:00
return context;
}
2005-05-22 05:11:41 +02:00
2006-09-14 04:05:32 +02:00
2012-02-10 04:06:57 +01:00
// Compile a record selection expression, bumping up the statement scope level everytime an rse is
// seen. The scope level controls parsing of aliases.
2012-04-25 03:42:47 +02:00
RseNode* PASS1_rse(DsqlCompilerScratch* dsqlScratch, SelectExprNode* input, bool updateLock)
2001-05-23 15:26:42 +02:00
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
DEV_BLKCHK(input, dsql_type_nod);
2001-05-23 15:26:42 +02:00
dsqlScratch->scopeLevel++;
2012-04-25 03:42:47 +02:00
RseNode* node = pass1_rse(dsqlScratch, input, NULL, NULL, updateLock, 0);
dsqlScratch->scopeLevel--;
return node;
2001-05-23 15:26:42 +02:00
}
// Check for ambiguity in a field reference. The list with contexts where the field was found is
// checked and the necessary message is build from it.
void PASS1_ambiguity_check(DsqlCompilerScratch* dsqlScratch,
const MetaName& name, const DsqlContextStack& ambiguous_contexts)
2006-09-14 04:05:32 +02:00
{
// If there are no relations or only 1 there's no ambiguity, thus return.
if (ambiguous_contexts.getCount() < 2)
return;
2006-09-14 04:05:32 +02:00
TEXT buffer[1024];
USHORT loop = 0;
2008-01-16 07:52:43 +01:00
buffer[0] = 0;
TEXT* b = buffer;
TEXT* p = NULL;
for (DsqlContextStack::const_iterator stack(ambiguous_contexts); stack.hasData(); ++stack)
{
const dsql_ctx* context = stack.object();
const dsql_rel* relation = context->ctx_relation;
const dsql_prc* procedure = context->ctx_procedure;
if (strlen(b) > (sizeof(buffer) - 50))
{
// Buffer full
break;
}
// if this is the second loop add "and " before relation.
if (++loop > 2)
strcat(buffer, "and ");
// Process relation when present.
if (relation)
{
if (!(relation->rel_flags & REL_view))
strcat(buffer, "table ");
else
strcat(buffer, "view ");
strcat(buffer, relation->rel_name.c_str());
}
else if (procedure)
{
// Process procedure when present.
strcat(b, "procedure ");
strcat(b, procedure->prc_name.toString().c_str());
}
else
{
// When there's no relation and no procedure it's a derived table.
strcat(b, "derived table ");
if (context->ctx_alias.hasData())
strcat(b, context->ctx_alias.c_str());
}
strcat(buffer, " ");
if (!p)
p = b + strlen(b);
}
if (p)
*--p = 0;
if (dsqlScratch->clientDialect >= SQL_DIALECT_V6)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_ambiguous_field_name) << Arg::Str(buffer) << Arg::Str(++p) <<
Arg::Gds(isc_random) << name);
}
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(204) <<
Arg::Warning(isc_dsql_ambiguous_field_name) << Arg::Str(buffer) <<
Arg::Str(++p) <<
Arg::Warning(isc_random) << name);
}
2003-09-02 01:22:22 +02:00
/**
PASS1_check_unique_fields_names
check fields (params, variables, cursors etc) names against
sorted array
if success, add them into array
**/
void PASS1_check_unique_fields_names(StrArray& names, const CompoundStmtNode* fields)
{
if (!fields)
return;
2003-09-02 01:22:22 +02:00
const NestConst<StmtNode>* ptr = fields->statements.begin();
const NestConst<StmtNode>* const end = fields->statements.end();
2003-09-02 01:22:22 +02:00
for (; ptr != end; ++ptr)
{
const char* name = NULL;
2011-02-26 22:51:45 +01:00
const DeclareVariableNode* varNode;
const DeclareCursorNode* cursorNode;
2011-02-26 22:51:45 +01:00
if ((varNode = (*ptr)->as<DeclareVariableNode>()))
name = varNode->dsqlDef->name.c_str();
else if ((cursorNode = (*ptr)->as<DeclareCursorNode>()))
name = cursorNode->dsqlName.c_str();
else if ((*ptr)->as<DeclareSubProcNode>() || (*ptr)->as<DeclareSubFuncNode>())
continue;
fb_assert(name);
size_t pos;
if (!names.find(name, pos))
names.insert(pos, name);
else
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(name));
}
}
}
// Compose two booleans.
2012-04-25 03:42:47 +02:00
BoolExprNode* PASS1_compose(BoolExprNode* expr1, BoolExprNode* expr2, UCHAR blrOp)
{
thread_db* tdbb = JRD_get_thread_data();
fb_assert(blrOp == blr_and || blrOp == blr_or);
if (!expr1)
return expr2;
2001-05-23 15:26:42 +02:00
if (!expr2)
return expr1;
2012-04-25 03:42:47 +02:00
return FB_NEW(*tdbb->getDefaultPool()) BinaryBoolNode(
*tdbb->getDefaultPool(), blrOp, expr1, expr2);
}
// Report a field parsing recognition error.
void PASS1_field_unknown(const TEXT* qualifier_name, const TEXT* field_name,
const ExprNode* flawed_node)
{
TEXT field_buffer[MAX_SQL_IDENTIFIER_SIZE * 2];
2001-05-23 15:26:42 +02:00
if (qualifier_name)
{
sprintf(field_buffer, "%.*s.%.*s", (int) MAX_SQL_IDENTIFIER_LEN, qualifier_name,
(int) MAX_SQL_IDENTIFIER_LEN, field_name ? field_name : "*");
field_name = field_buffer;
}
if (flawed_node)
{
if (field_name)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-206) <<
Arg::Gds(isc_dsql_field_err) <<
Arg::Gds(isc_random) << Arg::Str(field_name) <<
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(flawed_node->line) <<
Arg::Num(flawed_node->column));
}
else
2003-09-02 01:22:22 +02:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-206) <<
Arg::Gds(isc_dsql_field_err) <<
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(flawed_node->line) <<
Arg::Num(flawed_node->column));
2001-05-23 15:26:42 +02:00
}
}
else
{
if (field_name)
2003-09-02 01:22:22 +02:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-206) <<
Arg::Gds(isc_dsql_field_err) <<
Arg::Gds(isc_random) << Arg::Str(field_name) <<
Arg::Gds(isc_dsql_unknown_pos));
2001-05-23 15:26:42 +02:00
}
else
2009-01-07 10:30:57 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-206) <<
Arg::Gds(isc_dsql_field_err) <<
Arg::Gds(isc_dsql_unknown_pos));
2009-01-07 10:30:57 +01:00
}
}
}
2009-04-13 03:46:26 +02:00
/**
2005-05-28 00:45:31 +02:00
PASS1_node_match
2005-05-28 00:45:31 +02:00
@brief Compare two nodes for equality of value.
2005-05-28 00:45:31 +02:00
[2002-08-04]--- Arno Brinkman
2012-04-25 03:42:47 +02:00
If ignoreMapCast is true and the node1 is
type nod_cast or nod_map then PASS1_node_match is
calling itselfs again with the node1
CASTs source or map->node.
This is for allow CAST to other datatypes
without complaining that it's an unknown
column reference. (Aggregate functions)
@param node1
@param node2
2012-04-25 03:42:47 +02:00
@param ignoreMapCast
**/
2012-04-25 03:42:47 +02:00
bool PASS1_node_match(const ExprNode* node1, const ExprNode* node2, bool ignoreMapCast)
2001-05-23 15:26:42 +02:00
{
DEV_BLKCHK(node1, dsql_type_nod);
DEV_BLKCHK(node2, dsql_type_nod);
2001-05-23 15:26:42 +02:00
if (!node1 && !node2)
return true;
2001-05-23 15:26:42 +02:00
if (!node1 || !node2)
return false;
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
const CastNode* castNode1 = node1->as<CastNode>();
2012-04-25 03:42:47 +02:00
if (ignoreMapCast && castNode1)
{
2012-04-25 03:42:47 +02:00
const CastNode* castNode2 = node2->as<CastNode>();
// If node2 is also cast and same type continue with both sources.
if (castNode2 &&
castNode1->castDesc.dsc_dtype == castNode2->castDesc.dsc_dtype &&
castNode1->castDesc.dsc_scale == castNode2->castDesc.dsc_scale &&
castNode1->castDesc.dsc_length == castNode2->castDesc.dsc_length &&
castNode1->castDesc.dsc_sub_type == castNode2->castDesc.dsc_sub_type)
{
2012-05-03 18:43:29 +02:00
return PASS1_node_match(castNode1->source, castNode2->source, ignoreMapCast);
}
2009-04-19 12:06:07 +02:00
2012-05-03 18:43:29 +02:00
return PASS1_node_match(castNode1->source, node2, ignoreMapCast);
}
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
const DsqlMapNode* mapNode1 = node1->as<DsqlMapNode>();
2012-04-25 03:42:47 +02:00
if (ignoreMapCast && mapNode1)
{
2012-04-25 03:42:47 +02:00
const DsqlMapNode* mapNode2 = node2->as<DsqlMapNode>();
if (mapNode2)
{
if (mapNode1->context != mapNode2->context)
return false;
2012-04-25 03:42:47 +02:00
return PASS1_node_match(mapNode1->map->map_node, mapNode2->map->map_node, ignoreMapCast);
}
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
return PASS1_node_match(mapNode1->map->map_node, node2, ignoreMapCast);
}
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
const DsqlAliasNode* aliasNode1 = node1->as<DsqlAliasNode>();
const DsqlAliasNode* aliasNode2 = node2->as<DsqlAliasNode>();
2011-02-17 15:25:56 +01:00
// We don't care about the alias itself but only about its field.
2011-02-17 15:25:56 +01:00
if (aliasNode1 || aliasNode2)
2009-11-19 10:37:10 +01:00
{
2011-02-17 15:25:56 +01:00
if (aliasNode1 && aliasNode2)
2012-04-25 03:42:47 +02:00
return PASS1_node_match(aliasNode1->value, aliasNode2->value, ignoreMapCast);
2011-02-17 15:25:56 +01:00
if (aliasNode1)
2012-04-25 03:42:47 +02:00
return PASS1_node_match(aliasNode1->value, node2, ignoreMapCast);
2011-02-17 15:25:56 +01:00
if (aliasNode2)
2012-04-25 03:42:47 +02:00
return PASS1_node_match(node1, aliasNode2->value, ignoreMapCast);
2003-09-02 01:22:22 +02:00
}
2001-05-23 15:26:42 +02:00
// Handle derived fields.
2012-04-25 03:42:47 +02:00
const DerivedFieldNode* derivedField1 = node1->as<DerivedFieldNode>();
const DerivedFieldNode* derivedField2 = node2->as<DerivedFieldNode>();
if (derivedField1 || derivedField2)
2003-09-02 01:22:22 +02:00
{
if (derivedField1 && derivedField2)
2003-09-02 01:22:22 +02:00
{
if (derivedField1->context->ctx_context != derivedField2->context->ctx_context ||
derivedField1->name != derivedField2->name)
{
return false;
}
2012-05-03 18:43:29 +02:00
return PASS1_node_match(derivedField1->value, derivedField2->value, ignoreMapCast);
}
if (derivedField1)
2012-05-03 18:43:29 +02:00
return PASS1_node_match(derivedField1->value, node2, ignoreMapCast);
if (derivedField2)
2012-05-03 18:43:29 +02:00
return PASS1_node_match(node1, derivedField2->value, ignoreMapCast);
2001-05-23 15:26:42 +02:00
}
2012-04-25 03:42:47 +02:00
return node1->type == node2->type && node1->dsqlMatch(node2, ignoreMapCast);
2001-05-23 15:26:42 +02:00
}
/**
PASS1_cursor_name
@brief Find a cursor.
@param dsqlScratch
@param name
@param mask
@param existence_flag
**/
DeclareCursorNode* PASS1_cursor_name(DsqlCompilerScratch* dsqlScratch, const MetaName& name,
USHORT mask, bool existence_flag)
{
DeclareCursorNode* cursor = NULL;
if (name.isEmpty())
{
if (existence_flag)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
Arg::Gds(isc_dsql_cursor_err) <<
Arg::Gds(isc_dsql_cursor_invalid));
}
else
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
Arg::Gds(isc_dsql_decl_err) <<
Arg::Gds(isc_dsql_cursor_invalid));
}
}
2011-02-26 22:51:45 +01:00
for (Array<DeclareCursorNode*>::iterator itr = dsqlScratch->cursors.begin();
itr != dsqlScratch->cursors.end();
++itr)
2009-11-19 10:37:10 +01:00
{
2011-02-26 22:51:45 +01:00
cursor = *itr;
if (cursor->dsqlName == name && (cursor->dsqlCursorType & mask))
break;
cursor = NULL;
}
if (!cursor && existence_flag)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
Arg::Gds(isc_dsql_cursor_err) <<
2011-02-26 22:51:45 +01:00
Arg::Gds(isc_dsql_cursor_not_found) << name);
}
else if (cursor && !existence_flag)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
Arg::Gds(isc_dsql_decl_err) <<
2011-02-26 22:51:45 +01:00
Arg::Gds(isc_dsql_cursor_exists) << name);
}
return cursor;
}
// Extract relation and procedure context and expand derived child contexts.
static void pass1_expand_contexts(DsqlContextStack& contexts, dsql_ctx* context)
{
if (context->ctx_relation || context->ctx_procedure || context->ctx_map)
2009-01-07 10:30:57 +01:00
{
if (context->ctx_parent)
context = context->ctx_parent;
contexts.push(context);
}
else
2009-01-07 10:30:57 +01:00
{
for (DsqlContextStack::iterator i(context->ctx_childs_derived_table); i.hasData(); ++i)
pass1_expand_contexts(contexts, i.object());
}
}
2012-04-25 03:42:47 +02:00
// Process derived table which is part of a from clause.
RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* input,
const char* cte_alias)
2001-05-23 15:26:42 +02:00
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2001-05-23 15:26:42 +02:00
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
2001-05-23 15:26:42 +02:00
const string& alias = input->alias;
// Create the context now, because we need to know it for the tables inside.
2012-04-25 03:42:47 +02:00
dsql_ctx* const context = PASS1_make_context(dsqlScratch, input);
// Save some values to restore after rse process.
DsqlContextStack* const req_base = dsqlScratch->context;
2013-05-21 23:46:28 +02:00
const string aliasRelationPrefix = dsqlScratch->aliasRelationPrefix;
// Change context, because when we are processing the derived table rse
// it may not reference to other streams in the same scope_level.
DsqlContextStack temp;
// Put special contexts (NEW/OLD) also on the stack
for (DsqlContextStack::iterator stack(*dsqlScratch->context); stack.hasData(); ++stack)
2009-11-19 10:37:10 +01:00
{
dsql_ctx* local_context = stack.object();
if ((local_context->ctx_scope_level < dsqlScratch->scopeLevel) ||
(local_context->ctx_flags & CTX_system))
{
temp.push(local_context);
}
2001-05-23 15:26:42 +02:00
}
dsql_ctx* baseContext = NULL;
if (temp.hasData())
baseContext = temp.object();
dsqlScratch->context = &temp;
dsqlScratch->aliasRelationPrefix = pass1_alias_concat(aliasRelationPrefix, alias);
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
RecordSourceNode* query = input->querySpec;
UnionSourceNode* unionQuery = query->as<UnionSourceNode>();
RseNode* rse = NULL;
const bool isRecursive = unionQuery && unionQuery->recursive;
USHORT recursive_map_ctx = 0;
if (isRecursive)
2009-11-19 10:37:10 +01:00
{
// Create dummy, non-recursive select dsqlScratch by doing a union of
// one, non-recursive member. The dummy will be replaced at the end
// of this function.
2001-05-23 15:26:42 +02:00
2012-05-03 18:43:29 +02:00
RecordSourceNode* save = unionQuery->dsqlClauses->items.pop();
unionQuery->recursive = false;
2002-06-29 08:56:51 +02:00
dsql_ctx* baseUnionCtx = dsqlScratch->unionContext.hasData() ?
dsqlScratch->unionContext.object() : NULL;
// reserve extra context number for map's secondary context
recursive_map_ctx = dsqlScratch->contextNumber++;
dsqlScratch->recursiveCtxId = dsqlScratch->contextNumber;
2012-04-25 03:42:47 +02:00
rse = pass1_union(dsqlScratch, unionQuery, NULL, NULL, NULL, 0);
dsqlScratch->contextNumber = dsqlScratch->recursiveCtxId + 1;
2002-06-29 08:56:51 +02:00
2012-04-10 04:37:34 +02:00
// recursive union always has exactly 2 members
2012-05-03 18:43:29 +02:00
unionQuery->dsqlClauses->items.push(save);
unionQuery->recursive = true;
while (dsqlScratch->unionContext.hasData() &&
dsqlScratch->unionContext.object() != baseUnionCtx)
{
dsqlScratch->unionContext.pop();
}
}
else
{
// AB: 2005-01-06
// If our derived table contains a single query with a sub-select buried
// inside the select items then we need a special handling, because we don't
// want creating a new sub-select for every reference outside the derived
// table to that sub-select.
// To handle this we simple create a UNION ALL with derived table inside it.
// Due this mappings are created and we simple reference to these mappings.
// Optimizer effects:
// Good thing is that only 1 recordstream is made for the sub-select, but
// the worse thing is that a UNION currently can't be used in
// deciding the JOIN order.
bool foundSubSelect = false;
2012-04-25 03:42:47 +02:00
RseNode* queryNode = query->as<RseNode>();
if (queryNode)
foundSubSelect = SubSelectFinder::find(queryNode->dsqlSelectList);
if (foundSubSelect)
{
UnionSourceNode* unionExpr = FB_NEW(pool) UnionSourceNode(pool);
2012-04-25 03:42:47 +02:00
unionExpr->dsqlClauses = FB_NEW(pool) RecSourceListNode(pool, 1);
2012-05-03 18:43:29 +02:00
unionExpr->dsqlClauses->items[0] = input;
unionExpr->dsqlAll = true;
2012-04-25 03:42:47 +02:00
rse = pass1_union(dsqlScratch, unionExpr, NULL, NULL, NULL, 0);
}
else
2012-04-25 03:42:47 +02:00
rse = PASS1_rse(dsqlScratch, input, false);
// Finish off by cleaning up contexts and put them into derivedContext
// so create view (ddl) can deal with it.
// Also add the used contexts into the childs stack.
while (temp.hasData() && (temp.object() != baseContext))
2009-11-19 10:37:10 +01:00
{
2010-08-07 04:43:29 +02:00
dsql_ctx* childCtx = temp.pop();
dsqlScratch->derivedContext.push(childCtx);
2010-08-07 04:43:29 +02:00
context->ctx_childs_derived_table.push(childCtx);
2002-06-29 08:56:51 +02:00
// Collect contexts that will be used for blr_derived_expr generation.
pass1_expand_contexts(context->ctx_main_derived_contexts, childCtx);
}
2008-06-03 08:19:21 +02:00
while (temp.hasData())
temp.pop();
}
context->ctx_rse = rse;
2003-08-15 02:02:18 +02:00
// CVC: prepare a truncated alias for the derived table here
// because we need it several times.
TEXT aliasbuffer[100] = "";
const TEXT* aliasname = aliasbuffer;
if (alias.hasData())
{
int length = alias.length();
if (length > 99)
{
length = 99;
memcpy(aliasbuffer, alias.c_str(), length);
aliasbuffer[length] = 0;
}
else
aliasname = alias.c_str();
}
else
aliasname = "<unnamed>";
// If an alias-list is specified, process it.
const bool ignoreColumnChecks =
(input->dsqlFlags & RecordSourceNode::DFLAG_DT_IGNORE_COLUMN_CHECK);
2012-04-25 03:42:47 +02:00
if (input->columns && input->columns->hasData())
{
// Do both lists have the same number of items?
2012-05-03 18:43:29 +02:00
if (input->columns->getCount() != rse->dsqlSelectList->items.getCount())
{
// Column list by derived table %s [alias-name] has %s [more/fewer] columns
// than the number of items.
int errcode = isc_dsql_derived_table_less_columns;
2012-05-03 18:43:29 +02:00
if (input->columns->getCount() > rse->dsqlSelectList->items.getCount())
errcode = isc_dsql_derived_table_more_columns;
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(errcode) << Arg::Str(aliasname));
}
// Generate derived fields and assign alias-name to them.
2012-05-20 12:00:52 +02:00
for (size_t count = 0; count < input->columns->getCount(); ++count)
{
2012-05-03 18:43:29 +02:00
ValueExprNode* select_item = rse->dsqlSelectList->items[count];
2012-04-25 03:42:47 +02:00
MAKE_desc(dsqlScratch, &select_item->nodDesc, select_item);
// Make new derived field node.
DerivedFieldNode* derivedField = FB_NEW(pool) DerivedFieldNode(pool,
2012-04-25 03:42:47 +02:00
(*input->columns)[count], dsqlScratch->scopeLevel, select_item);
derivedField->nodDesc = select_item->nodDesc;
2012-05-03 18:43:29 +02:00
rse->dsqlSelectList->items[count] = derivedField;
}
}
else
{
// For those select-items where no alias is specified try
// to generate one from the field_name.
2012-05-20 12:00:52 +02:00
for (size_t count = 0; count < rse->dsqlSelectList->items.getCount(); ++count)
{
2012-04-25 03:42:47 +02:00
ValueExprNode* select_item = pass1_make_derived_field(tdbb, dsqlScratch,
2012-05-03 18:43:29 +02:00
rse->dsqlSelectList->items[count]);
// Auto-create dummy column name for pass1_any()
if (ignoreColumnChecks && !ExprNode::is<DerivedFieldNode>(select_item))
{
2012-04-25 03:42:47 +02:00
MAKE_desc(dsqlScratch, &select_item->nodDesc, select_item);
// Construct dummy fieldname
char fieldname[25];
2012-06-06 16:50:53 +02:00
sprintf(fieldname, "f%"SIZEFORMAT, count);
// Make new derived field node.
DerivedFieldNode* derivedField = FB_NEW(pool) DerivedFieldNode(pool,
fieldname, dsqlScratch->scopeLevel, select_item);
2012-04-25 03:42:47 +02:00
derivedField->nodDesc = select_item->nodDesc;
select_item = derivedField;
}
2012-05-03 18:43:29 +02:00
rse->dsqlSelectList->items[count] = select_item;
2001-05-23 15:26:42 +02:00
}
}
2012-05-20 12:00:52 +02:00
size_t count;
// Check if all root select-items have a derived field else show a message.
2012-05-03 18:43:29 +02:00
for (count = 0; count < rse->dsqlSelectList->items.getCount(); ++count)
{
2012-05-03 18:43:29 +02:00
ValueExprNode* select_item = rse->dsqlSelectList->items[count];
DerivedFieldNode* derivedField;
if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
derivedField->context = context;
else
{
// no column name specified for column number %d in derived table %s
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_derived_field_unnamed) << Arg::Num(count + 1) <<
Arg::Str(aliasname));
}
2001-05-23 15:26:42 +02:00
}
// Check for ambiguous column names inside this derived table.
2012-05-03 18:43:29 +02:00
for (count = 0; count < rse->dsqlSelectList->items.getCount(); ++count)
{
const DerivedFieldNode* selectItem1 =
2012-05-03 18:43:29 +02:00
rse->dsqlSelectList->items[count]->as<DerivedFieldNode>();
2012-05-20 12:00:52 +02:00
for (size_t count2 = (count + 1); count2 < rse->dsqlSelectList->items.getCount(); ++count2)
{
const DerivedFieldNode* selectItem2 =
2012-05-03 18:43:29 +02:00
rse->dsqlSelectList->items[count2]->as<DerivedFieldNode>();
if (selectItem1->name == selectItem2->name)
{
// column %s was specified multiple times for derived table %s
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_derived_field_dup_name) << selectItem1->name <<
aliasname);
}
}
}
// If we used a dummy rse before, replace it with the real one now.
// We cannot do this earlier, because recursive processing needs a fully
// developed context block.
if (isRecursive)
{
dsqlScratch->recursiveCtx = context;
dsqlScratch->context = &temp;
2001-05-23 15:26:42 +02:00
dsqlScratch->resetCTEAlias(alias.c_str());
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
rse = PASS1_rse(dsqlScratch, input, false);
2001-05-23 15:26:42 +02:00
// Finish off by cleaning up contexts and put them into derivedContext
// so create view (ddl) can deal with it.
// Also add the used contexts into the childs stack.
while (temp.hasData() && (temp.object() != baseContext))
{
dsqlScratch->derivedContext.push(temp.object());
context->ctx_childs_derived_table.push(temp.pop());
}
2001-05-23 15:26:42 +02:00
temp.clear();
2005-05-28 00:45:31 +02:00
2012-04-25 03:42:47 +02:00
rse->dsqlSelectList = context->ctx_rse->dsqlSelectList;
2005-05-28 00:45:31 +02:00
context->ctx_rse = rse;
if (cte_alias)
context->ctx_alias = cte_alias;
dsqlScratch->context = req_base;
// Mark union's map context as recursive and assign secondary context number to it.
2012-04-25 03:42:47 +02:00
ValueListNode* items = rse->dsqlSelectList;
2012-05-03 18:43:29 +02:00
ValueExprNode* map_item = items->items[0];
DerivedFieldNode* derivedField;
if ((derivedField = ExprNode::as<DerivedFieldNode>(map_item)))
2012-05-03 18:43:29 +02:00
map_item = derivedField->value;
dsql_ctx* map_context = ExprNode::as<DsqlMapNode>(map_item)->context;
map_context->ctx_flags |= CTX_recursive;
map_context->ctx_recursive = recursive_map_ctx;
}
// Restore our original values.
dsqlScratch->context = req_base;
dsqlScratch->aliasRelationPrefix = aliasRelationPrefix;
2012-04-25 03:42:47 +02:00
rse->dsqlContext = context;
return rse;
}
/**
pass1_expand_select_list
@brief Expand asterisk nodes into fields.
@param dsqlScratch
@param list
@param streams
**/
2012-04-25 03:42:47 +02:00
static ValueListNode* pass1_expand_select_list(DsqlCompilerScratch* dsqlScratch, ValueListNode* list,
RecSourceListNode* streams)
{
2012-04-25 03:42:47 +02:00
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
2012-04-25 03:42:47 +02:00
ValueListNode* retList = FB_NEW(pool) ValueListNode(pool, 0u);
2012-04-25 03:42:47 +02:00
if (list)
{
2012-05-03 18:43:29 +02:00
for (NestConst<ValueExprNode>* ptr = list->items.begin(); ptr != list->items.end(); ++ptr)
2012-04-25 03:42:47 +02:00
PASS1_expand_select_node(dsqlScratch, *ptr, retList, true);
}
else
{
2012-05-03 18:43:29 +02:00
for (NestConst<RecordSourceNode>* ptr = streams->items.begin(); ptr != streams->items.end(); ++ptr)
2012-04-25 03:42:47 +02:00
PASS1_expand_select_node(dsqlScratch, *ptr, retList, true);
}
2012-04-25 03:42:47 +02:00
return retList;
}
// Expand a select item node.
2012-04-25 03:42:47 +02:00
void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, ValueListNode* list,
bool hide_using)
{
RseNode* rseNode;
ProcedureSourceNode* procNode;
RelationSourceNode* relNode;
FieldNode* fieldNode;
if ((rseNode = ExprNode::as<RseNode>(node)))
{
2012-04-25 03:42:47 +02:00
ValueListNode* sub_items = rseNode->dsqlSelectList;
if (sub_items) // AB: Derived table support
{
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = sub_items->items.begin();
2012-05-03 18:43:29 +02:00
for (const NestConst<ValueExprNode>* const end = sub_items->items.end(); ptr != end; ++ptr)
{
// Create a new alias else mappings would be mangled.
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode> select_item = *ptr;
// select-item should always be a derived field!
DerivedFieldNode* derivedField;
2012-05-03 18:43:29 +02:00
if (!(derivedField = select_item->as<DerivedFieldNode>()))
{
// Internal dsql error: alias type expected by PASS1_expand_select_node
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_derived_alias_select));
}
dsql_ctx* context = derivedField->context;
DEV_BLKCHK(context, dsql_type_ctx);
if (!hide_using || context->getImplicitJoinField(derivedField->name, select_item))
2012-04-25 03:42:47 +02:00
list->add(select_item);
}
}
else // joins
{
2012-04-25 03:42:47 +02:00
RecSourceListNode* streamList = rseNode->dsqlStreams;
2012-05-03 18:43:29 +02:00
for (NestConst<RecordSourceNode>* ptr = streamList->items.begin();
ptr != streamList->items.end();
++ptr)
{
2012-04-25 03:42:47 +02:00
PASS1_expand_select_node(dsqlScratch, *ptr, list, true);
2012-05-03 18:43:29 +02:00
}
}
}
else if ((procNode = ExprNode::as<ProcedureSourceNode>(node)))
{
dsql_ctx* context = procNode->dsqlContext;
if (context->ctx_procedure)
{
for (dsql_fld* field = context->ctx_procedure->prc_outputs; field; field = field->fld_next)
{
DEV_BLKCHK(field, dsql_type_fld);
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode> select_item = NULL;
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
{
if (!select_item)
2012-04-25 03:42:47 +02:00
select_item = MAKE_field(context, field, NULL);
list->add(select_item);
}
}
}
}
else if ((relNode = ExprNode::as<RelationSourceNode>(node)))
{
dsql_ctx* context = relNode->dsqlContext;
if (context->ctx_relation)
{
for (dsql_fld* field = context->ctx_relation->rel_fields; field; field = field->fld_next)
{
DEV_BLKCHK(field, dsql_type_fld);
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode> select_item = NULL;
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
{
if (!select_item)
2012-04-25 03:42:47 +02:00
select_item = MAKE_field(context, field, NULL);
list->add(select_item);
}
}
}
}
else if ((fieldNode = ExprNode::as<FieldNode>(node)))
{
2012-04-25 03:42:47 +02:00
RecordSourceNode* recSource = NULL;
ValueExprNode* value = fieldNode->internalDsqlPass(dsqlScratch, &recSource);
2010-10-22 17:00:22 +02:00
2012-04-25 03:42:47 +02:00
if (recSource)
PASS1_expand_select_node(dsqlScratch, recSource, list, false);
else
2012-04-25 03:42:47 +02:00
list->add(value);
2005-05-22 05:11:41 +02:00
}
else
2012-04-25 03:42:47 +02:00
{
fb_assert(node->kind == DmlNode::KIND_VALUE);
list->add(static_cast<ValueExprNode*>(node));
}
}
2003-08-15 02:02:18 +02:00
/**
2005-05-28 00:45:31 +02:00
2003-08-15 02:02:18 +02:00
pass1_group_by_list
2005-05-28 00:45:31 +02:00
@brief Process GROUP BY list, which may contain
an ordinal or alias which references the
2005-05-28 00:45:31 +02:00
select list.
2003-08-15 02:02:18 +02:00
@param dsqlScratch
2003-08-15 02:02:18 +02:00
@param input
@param select_list
**/
2012-04-25 03:42:47 +02:00
static ValueListNode* pass1_group_by_list(DsqlCompilerScratch* dsqlScratch, ValueListNode* input,
ValueListNode* selectList)
2003-08-15 02:02:18 +02:00
{
2012-04-25 03:42:47 +02:00
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2003-08-15 02:02:18 +02:00
2012-05-03 18:43:29 +02:00
if (input->items.getCount() > MAX_SORT_ITEMS) // sort, group and distinct have the same limit for now
{
2008-07-01 03:12:02 +02:00
// cannot group on more than 255 items
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_max_group_items));
}
2012-04-25 03:42:47 +02:00
ValueListNode* retList = FB_NEW(pool) ValueListNode(pool, 0u);
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = input->items.begin();
for (const NestConst<ValueExprNode>* const end = input->items.end(); ptr != end; ++ptr)
{
2012-04-25 03:42:47 +02:00
ValueExprNode* sub = (*ptr);
ValueExprNode* frnode = NULL;
FieldNode* field;
2010-10-24 02:26:00 +02:00
LiteralNode* literal;
if ((field = ExprNode::as<FieldNode>(sub)))
{
// check for alias or field node
if (selectList && field->dsqlQualifier.isEmpty() && field->dsqlName.hasData())
{
// AB: Check first against the select list for matching column.
// When no matches at all are found we go on with our
// normal way of field name lookup.
frnode = PASS1_lookup_alias(dsqlScratch, field->dsqlName, selectList, true);
}
if (!frnode)
frnode = field->internalDsqlPass(dsqlScratch, NULL);
2003-08-15 02:02:18 +02:00
}
2010-10-24 02:26:00 +02:00
else if ((literal = ExprNode::as<LiteralNode>(sub)) && (literal->litDesc.dsc_dtype == dtype_long))
{
2010-10-24 02:26:00 +02:00
const ULONG position = literal->getSlong();
2012-05-03 18:43:29 +02:00
if (position < 1 || !selectList || position > (ULONG) selectList->items.getCount())
{
2008-07-01 03:12:02 +02:00
// Invalid column position used in the GROUP BY clause
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_column_pos_err) << Arg::Str("GROUP BY"));
2003-08-15 02:02:18 +02:00
}
2010-10-24 02:26:00 +02:00
2012-05-03 18:43:29 +02:00
frnode = Node::doDsqlPass(dsqlScratch, selectList->items[position - 1], false);
2003-08-15 02:02:18 +02:00
}
else
2012-04-25 03:42:47 +02:00
frnode = Node::doDsqlPass(dsqlScratch, *ptr, false);
2008-12-05 02:20:14 +01:00
2012-04-25 03:42:47 +02:00
retList->add(frnode);
2003-08-15 02:02:18 +02:00
}
2012-04-25 03:42:47 +02:00
// Finally make the complete list.
return retList;
2003-08-15 02:02:18 +02:00
}
2006-09-14 04:05:32 +02:00
// Process the limit clause (FIRST/SKIP/ROWS)
2012-05-03 18:43:29 +02:00
void PASS1_limit(DsqlCompilerScratch* dsqlScratch, NestConst<ValueExprNode> firstNode,
NestConst<ValueExprNode> skipNode, RseNode* rse)
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
DEV_BLKCHK(firstNode, dsql_type_nod);
DEV_BLKCHK(skipNode, dsql_type_nod);
2012-04-25 03:42:47 +02:00
dsc descNode;
if (dsqlScratch->clientDialect <= SQL_DIALECT_V5)
2012-04-25 03:42:47 +02:00
descNode.makeLong(0);
else
2012-04-25 03:42:47 +02:00
descNode.makeInt64(0);
2012-04-25 03:42:47 +02:00
rse->dsqlFirst = Node::doDsqlPass(dsqlScratch, firstNode, false);
PASS1_set_parameter_type(dsqlScratch, rse->dsqlFirst, &descNode, false);
2012-04-25 03:42:47 +02:00
rse->dsqlSkip = Node::doDsqlPass(dsqlScratch, skipNode, false);
PASS1_set_parameter_type(dsqlScratch, rse->dsqlSkip, &descNode, false);
}
// Lookup a matching item in the select list. Return node if found else return NULL.
// If more matches are found we raise ambiguity error.
2012-04-25 03:42:47 +02:00
ValueExprNode* PASS1_lookup_alias(DsqlCompilerScratch* dsqlScratch, const MetaName& name,
ValueListNode* selectList, bool process)
{
2012-04-25 03:42:47 +02:00
ValueExprNode* returnNode = NULL;
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = selectList->items.begin();
const NestConst<ValueExprNode>* const end = selectList->items.end();
2012-04-25 03:42:47 +02:00
for (; ptr < end; ++ptr)
2009-01-07 10:30:57 +01:00
{
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode> matchingNode = NULL;
2012-04-25 03:42:47 +02:00
ValueExprNode* node = *ptr;
2011-02-17 15:25:56 +01:00
DsqlAliasNode* aliasNode;
2010-12-19 22:42:32 +01:00
FieldNode* fieldNode;
DerivedFieldNode* derivedField;
2012-05-03 18:43:29 +02:00
if ((aliasNode = node->as<DsqlAliasNode>()))
2009-01-07 10:30:57 +01:00
{
if (aliasNode->name == name)
2011-02-17 15:25:56 +01:00
matchingNode = node;
}
2012-05-03 18:43:29 +02:00
else if ((fieldNode = node->as<FieldNode>()))
2011-02-17 15:25:56 +01:00
{
if (fieldNode->dsqlField->fld_name == name.c_str())
2011-02-17 15:25:56 +01:00
matchingNode = node;
}
2012-05-03 18:43:29 +02:00
else if ((derivedField = node->as<DerivedFieldNode>()))
2011-02-17 15:25:56 +01:00
{
if (derivedField->name == name)
2011-02-17 15:25:56 +01:00
matchingNode = node;
}
2009-01-07 10:30:57 +01:00
if (matchingNode)
{
if (process)
2012-04-25 03:42:47 +02:00
matchingNode = Node::doDsqlPass(dsqlScratch, matchingNode, false);
2009-01-07 10:30:57 +01:00
if (returnNode)
{
2005-05-28 00:45:31 +02:00
// There was already a node matched, thus raise ambiguous field name error.
TEXT buffer1[256];
buffer1[0] = 0;
2012-05-03 18:43:29 +02:00
if (returnNode->is<DsqlAliasNode>())
2011-02-17 15:25:56 +01:00
strcat(buffer1, "an alias");
2012-05-03 18:43:29 +02:00
else if (returnNode->is<FieldNode>())
2011-02-17 15:25:56 +01:00
strcat(buffer1, "a field");
2012-05-03 18:43:29 +02:00
else if (returnNode->is<DerivedFieldNode>())
2011-02-17 15:25:56 +01:00
strcat(buffer1, "a derived field");
else
strcat(buffer1, "an item");
TEXT buffer2[256];
buffer2[0] = 0;
2012-05-03 18:43:29 +02:00
if (matchingNode->is<DsqlAliasNode>())
2011-02-17 15:25:56 +01:00
strcat(buffer2, "an alias");
2012-05-03 18:43:29 +02:00
else if (matchingNode->is<FieldNode>())
2011-02-17 15:25:56 +01:00
strcat(buffer2, "a field");
2012-05-03 18:43:29 +02:00
else if (matchingNode->is<DerivedFieldNode>())
2011-02-17 15:25:56 +01:00
strcat(buffer2, "a derived field");
else
strcat(buffer2, "an item");
strcat(buffer2, " in the select list with name");
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
2008-12-05 02:20:14 +01:00
Arg::Gds(isc_dsql_ambiguous_field_name) << Arg::Str(buffer1) <<
Arg::Str(buffer2) <<
Arg::Gds(isc_random) << name);
}
returnNode = matchingNode;
}
}
2008-07-03 09:12:36 +02:00
return returnNode;
}
2003-08-24 04:36:46 +02:00
/**
2005-05-28 00:45:31 +02:00
pass1_make_derived_field
2005-05-28 00:45:31 +02:00
@brief Create a derived field based on underlying expressions
2005-05-28 00:45:31 +02:00
2003-08-24 04:36:46 +02:00
@param dsqlScratch
2008-02-28 14:48:16 +01:00
@param tdbb
@param select_item
2003-08-24 04:36:46 +02:00
**/
2012-04-25 03:42:47 +02:00
static ValueExprNode* pass1_make_derived_field(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
ValueExprNode* select_item)
2003-08-24 04:36:46 +02:00
{
MemoryPool& pool = *tdbb->getDefaultPool();
2011-02-17 15:25:56 +01:00
DsqlAliasNode* aliasNode;
SubQueryNode* subQueryNode;
DsqlMapNode* mapNode;
FieldNode* fieldNode;
DerivedFieldNode* derivedField;
2011-02-17 15:25:56 +01:00
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)))
2005-05-22 05:11:41 +02:00
{
2011-02-17 15:25:56 +01:00
// Create a derived field and ignore alias node.
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
aliasNode->name, dsqlScratch->scopeLevel, aliasNode->value);
2012-04-25 03:42:47 +02:00
newField->nodDesc = aliasNode->value->nodDesc;
return newField;
2011-02-17 15:25:56 +01:00
}
else if ((subQueryNode = ExprNode::as<SubQueryNode>(select_item)))
{
// Try to generate derived field from sub-select
2012-04-25 03:42:47 +02:00
ValueExprNode* derived_field = pass1_make_derived_field(tdbb, dsqlScratch,
2012-05-03 18:43:29 +02:00
subQueryNode->value1);
2010-12-19 22:42:32 +01:00
2011-02-17 15:25:56 +01:00
if ((derivedField = ExprNode::as<DerivedFieldNode>(derived_field)))
{
2012-05-03 18:43:29 +02:00
derivedField->value = select_item;
2011-02-17 15:25:56 +01:00
return derived_field;
}
}
else if ((mapNode = ExprNode::as<DsqlMapNode>(select_item)))
{
// Aggregate's have map on top.
2012-04-25 03:42:47 +02:00
ValueExprNode* derived_field = pass1_make_derived_field(tdbb, dsqlScratch, mapNode->map->map_node);
2010-12-19 22:42:32 +01:00
2011-02-17 15:25:56 +01:00
// If we had succesfully made a derived field node change it with orginal map.
if ((derivedField = ExprNode::as<DerivedFieldNode>(derived_field)))
{
2012-05-03 18:43:29 +02:00
derivedField->value = select_item;
2011-02-17 15:25:56 +01:00
derivedField->scope = dsqlScratch->scopeLevel;
2012-04-25 03:42:47 +02:00
derived_field->nodDesc = select_item->nodDesc;
2011-02-17 15:25:56 +01:00
return derived_field;
}
}
else if ((fieldNode = ExprNode::as<FieldNode>(select_item)))
{
// Create a derived field and hook in.
2010-12-19 22:42:32 +01:00
2011-02-17 15:25:56 +01:00
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
fieldNode->dsqlField->fld_name, dsqlScratch->scopeLevel, select_item);
2012-04-25 03:42:47 +02:00
newField->nodDesc = fieldNode->nodDesc;
return newField;
2011-02-17 15:25:56 +01:00
}
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
{
// Create a derived field that points to a derived field.
2011-02-17 15:25:56 +01:00
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
derivedField->name, dsqlScratch->scopeLevel, select_item);
2012-04-25 03:42:47 +02:00
newField->nodDesc = select_item->nodDesc;
return newField;
}
return select_item;
2003-08-24 04:36:46 +02:00
}
// Prepare a relation name for processing. Allocate a new relation node.
2012-04-25 03:42:47 +02:00
RecordSourceNode* PASS1_relation(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* input)
2001-05-23 15:26:42 +02:00
{
thread_db* tdbb = JRD_get_thread_data();
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2001-05-23 15:26:42 +02:00
dsql_ctx* context = PASS1_make_context(dsqlScratch, input);
RecordSourceNode* node = NULL;
2001-05-23 15:26:42 +02:00
if (context->ctx_relation)
{
RelationSourceNode* relNode = FB_NEW(*tdbb->getDefaultPool()) RelationSourceNode(
*tdbb->getDefaultPool(), context->ctx_relation->rel_name);
relNode->dsqlContext = context;
2012-04-25 03:42:47 +02:00
return relNode;
}
else if (context->ctx_procedure)
{
ProcedureSourceNode* procNode = FB_NEW(*tdbb->getDefaultPool()) ProcedureSourceNode(
*tdbb->getDefaultPool(), context->ctx_procedure->prc_name);
procNode->dsqlContext = context;
2012-04-25 03:42:47 +02:00
return procNode;
}
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
fb_assert(false);
return NULL;
2001-05-23 15:26:42 +02:00
}
// Concatenate 2 input strings together for a new alias string
// Note: Both input params can be empty.
static string pass1_alias_concat(const string& input1, const string& input2)
2001-05-23 15:26:42 +02:00
{
string output;
2001-05-23 15:26:42 +02:00
if (input1.hasData())
output.append(input1);
2001-05-23 15:26:42 +02:00
if (input2.hasData())
{
if (output.hasData())
output.append(" ");
output.append(input2);
}
2003-08-20 01:34:23 +02:00
return output;
}
2001-05-23 15:26:42 +02:00
2012-02-10 04:06:57 +01:00
// Wrapper for pass1_rse_impl. Substitute recursive CTE alias (if needed) and call pass1_rse_impl.
2012-04-25 03:42:47 +02:00
static RseNode* pass1_rse(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* input,
ValueListNode* order, RowsClause* rows, bool updateLock, USHORT flags)
{
string save_alias;
2012-04-25 03:42:47 +02:00
RseNode* rseNode = input->as<RseNode>();
const bool isRecursive = rseNode && (rseNode->dsqlFlags & RecordSourceNode::DFLAG_RECURSIVE);
2006-08-02 03:22:11 +02:00
if (isRecursive)
{
fb_assert(dsqlScratch->recursiveCtx);
save_alias = dsqlScratch->recursiveCtx->ctx_alias;
dsqlScratch->recursiveCtx->ctx_alias = *dsqlScratch->getNextCTEAlias();
}
RseNode* ret = pass1_rse_impl(dsqlScratch, input, order, rows, updateLock, flags);
if (isRecursive)
dsqlScratch->recursiveCtx->ctx_alias = save_alias;
return ret;
}
2012-02-10 04:06:57 +01:00
// Compile a record selection expression. The input node may either be a "select_expression"
// or a "list" (an implicit union) or a "query specification".
2012-04-25 03:42:47 +02:00
static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* input,
ValueListNode* order, RowsClause* rows, bool updateLock, USHORT flags)
2001-05-23 15:26:42 +02:00
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
SelectExprNode* selNode = input->as<SelectExprNode>();
UnionSourceNode* unionNode = input->as<UnionSourceNode>();
if (selNode)
{
WithClause* withClause = selNode->withClause;
2008-12-05 02:20:14 +01:00
try
{
if (withClause)
dsqlScratch->addCTEs(withClause);
2012-04-25 03:42:47 +02:00
RseNode* ret = pass1_rse(dsqlScratch, selNode->querySpec, selNode->orderClause,
2012-04-07 06:21:46 +02:00
selNode->rowsClause, updateLock, selNode->dsqlFlags);
if (withClause)
{
dsqlScratch->checkUnusedCTEs();
dsqlScratch->clearCTEs();
}
return ret;
2008-12-05 02:20:14 +01:00
}
2012-04-25 03:42:47 +02:00
catch (const Exception&)
2009-11-19 10:37:10 +01:00
{
if (withClause)
dsqlScratch->clearCTEs();
throw;
}
}
else if (unionNode)
{
2012-05-03 18:43:29 +02:00
fb_assert(unionNode->dsqlClauses->items.hasData());
return pass1_union(dsqlScratch, unionNode, order, rows, updateLock, flags);
2001-05-23 15:26:42 +02:00
}
2008-01-16 07:52:43 +01:00
2012-04-25 03:42:47 +02:00
RseNode* inputRse = input->as<RseNode>();
fb_assert(inputRse);
2001-05-23 15:26:42 +02:00
2005-05-28 00:45:31 +02:00
// Save the original base of the context stack and process relations
2001-05-23 15:26:42 +02:00
RseNode* targetRse = FB_NEW(pool) RseNode(pool);
RseNode* rse = targetRse;
2012-02-10 04:06:57 +01:00
if (updateLock)
rse->flags |= RseNode::FLAG_WRITELOCK;
2012-04-25 03:42:47 +02:00
rse->dsqlStreams = Node::doDsqlPass(dsqlScratch, inputRse->dsqlFrom, false);
RecSourceListNode* streamList = rse->dsqlStreams;
2001-05-23 15:26:42 +02:00
{ // scope block
RelationSourceNode* relNode;
2005-05-20 01:41:17 +02:00
const dsql_rel* relation;
2012-02-10 04:06:57 +01:00
if (updateLock &&
2012-05-03 18:43:29 +02:00
(streamList->items.getCount() != 1 ||
!(relNode = streamList->items[0]->as<RelationSourceNode>()) ||
!(relation = relNode->dsqlContext->ctx_relation) ||
2009-01-09 02:50:54 +01:00
(relation->rel_flags & REL_view) || (relation->rel_flags & REL_external)))
2005-05-20 01:41:17 +02:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str("WITH LOCK"));
2005-05-20 01:41:17 +02:00
}
} // end scope block
2005-05-28 00:45:31 +02:00
// Process LIMIT and/or ROWS, if any
if ((inputRse->dsqlFirst || inputRse->dsqlSkip) && rows)
2009-01-07 10:30:57 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str("ROWS"));
}
else if (rows)
2012-04-07 06:21:46 +02:00
PASS1_limit(dsqlScratch, rows->length, rows->skip, rse);
else if (inputRse->dsqlFirst || inputRse->dsqlSkip)
PASS1_limit(dsqlScratch, inputRse->dsqlFirst, inputRse->dsqlSkip, rse);
2005-05-28 00:45:31 +02:00
// Process boolean, if any
2001-05-23 15:26:42 +02:00
if (inputRse->dsqlWhere)
2009-01-07 10:30:57 +01:00
{
++dsqlScratch->inWhereClause;
2012-04-25 03:42:47 +02:00
rse->dsqlWhere = Node::doDsqlPass(dsqlScratch, inputRse->dsqlWhere, false);
--dsqlScratch->inWhereClause;
2009-04-19 12:06:07 +02:00
// AB: An aggregate pointing to it's own parent_context isn't
// allowed, HAVING should be used instead
2010-01-29 02:16:34 +01:00
if (Aggregate2Finder::find(dsqlScratch->scopeLevel, FIELD_MATCH_TYPE_EQUAL, false,
rse->dsqlWhere))
2008-07-01 03:12:02 +02:00
{
// Cannot use an aggregate in a WHERE clause, use HAVING instead
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_where_err));
2008-07-01 03:12:02 +02:00
}
}
#ifdef DSQL_DEBUG
2009-11-19 10:37:10 +01:00
if (DSQL_debug & 16)
{
dsql_trace("PASS1_rse input tree:");
2012-04-25 03:42:47 +02:00
//// TODO: Implement Node::print correctly.
}
#endif
2005-05-28 00:45:31 +02:00
// Process select list, if any. If not, generate one
2012-04-25 03:42:47 +02:00
ValueListNode* selectList = inputRse->dsqlSelectList;
// First expand select list, this will expand nodes with asterisk.
++dsqlScratch->inSelectList;
selectList = pass1_expand_select_list(dsqlScratch, selectList, rse->dsqlStreams);
2012-04-25 03:42:47 +02:00
if ((flags & RecordSourceNode::DFLAG_VALUE) &&
2012-05-03 18:43:29 +02:00
(!selectList || selectList->items.getCount() > 1))
{
// More than one column (or asterisk) is specified in column_singleton
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_count_mismatch));
}
// Pass select list
rse->dsqlSelectList = pass1_sel_list(dsqlScratch, selectList);
--dsqlScratch->inSelectList;
2005-05-28 00:45:31 +02:00
// Process ORDER clause, if any
2009-11-19 10:37:10 +01:00
if (order)
{
++dsqlScratch->inOrderByClause;
rse->dsqlOrder = PASS1_sort(dsqlScratch, order, selectList);
--dsqlScratch->inOrderByClause;
}
2005-05-28 00:45:31 +02:00
// A GROUP BY, HAVING, or any aggregate function in the select list
// will force an aggregate
dsql_ctx* parent_context = NULL;
RseNode* parentRse = NULL;
AggregateSourceNode* aggregate = NULL;
if (inputRse->dsqlGroup ||
inputRse->dsqlHaving ||
(rse->dsqlSelectList && AggregateFinder::find(dsqlScratch, false, rse->dsqlSelectList)) ||
(rse->dsqlOrder && AggregateFinder::find(dsqlScratch, false, rse->dsqlOrder)))
{
// dimitr: don't allow WITH LOCK for aggregates
2012-02-10 04:06:57 +01:00
if (updateLock)
2009-11-19 10:37:10 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str("WITH LOCK"));
}
2009-01-07 10:30:57 +01:00
parent_context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
parent_context->ctx_scope_level = dsqlScratch->scopeLevel;
// When we're in a outer-join part mark context for it.
if (dsqlScratch->inOuterJoin)
parent_context->ctx_flags |= CTX_outer_join;
parent_context->ctx_in_outer_join = dsqlScratch->inOuterJoin;
aggregate = FB_NEW(pool) AggregateSourceNode(pool);
aggregate->dsqlContext = parent_context;
2012-04-25 03:42:47 +02:00
aggregate->dsqlRse = rse;
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
2012-04-25 03:42:47 +02:00
parentRse->dsqlStreams = (streamList = FB_NEW(pool) RecSourceListNode(pool, 1));
2012-05-03 18:43:29 +02:00
streamList->items[0] = aggregate;
if (rse->dsqlFirst)
2009-11-19 10:37:10 +01:00
{
parentRse->dsqlFirst = rse->dsqlFirst;
rse->dsqlFirst = NULL;
}
if (rse->dsqlSkip)
2009-11-19 10:37:10 +01:00
{
parentRse->dsqlSkip = rse->dsqlSkip;
rse->dsqlSkip = NULL;
}
dsqlScratch->context->push(parent_context);
2003-05-05 00:02:42 +02:00
// replace original contexts with parent context
remap_streams_to_parent_context(rse->dsqlStreams, parent_context);
2001-05-23 15:26:42 +02:00
}
// Process GROUP BY clause, if any
if (inputRse->dsqlGroup)
2009-01-07 10:30:57 +01:00
{
2005-05-28 00:45:31 +02:00
// if there are positions in the group by clause then replace them
// by the (newly pass) items from the select_list
++dsqlScratch->inGroupByClause;
aggregate->dsqlGroup = pass1_group_by_list(dsqlScratch, inputRse->dsqlGroup, selectList);
--dsqlScratch->inGroupByClause;
// AB: An field pointing to another parent_context isn't
// allowed and GROUP BY items can't contain aggregates
if (FieldFinder::find(dsqlScratch->scopeLevel, FIELD_MATCH_TYPE_LOWER, aggregate->dsqlGroup) ||
2010-01-29 02:16:34 +01:00
Aggregate2Finder::find(dsqlScratch->scopeLevel, FIELD_MATCH_TYPE_LOWER_EQUAL,
false, aggregate->dsqlGroup))
{
2008-07-01 03:12:02 +02:00
// Cannot use an aggregate in a GROUP BY clause
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_group_err));
}
}
2001-05-23 15:26:42 +02:00
// Parse a user-specified access PLAN
2012-04-25 03:42:47 +02:00
if (inputRse->rse_plan)
{
PsqlChanger changer(dsqlScratch, false);
rse->rse_plan = inputRse->rse_plan->dsqlPass(dsqlScratch);
}
2001-05-23 15:26:42 +02:00
2005-05-28 00:45:31 +02:00
// AB: Pass select-items for distinct operation again, because for
2008-05-21 18:02:25 +02:00
// sub-selects a new context number should be generated
if (inputRse->dsqlDistinct)
2009-01-07 10:30:57 +01:00
{
2012-02-10 04:06:57 +01:00
if (updateLock)
2009-11-19 10:37:10 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str("WITH LOCK"));
}
++dsqlScratch->inSelectList;
targetRse->dsqlDistinct = pass1_sel_list(dsqlScratch, selectList);
--dsqlScratch->inSelectList;
// sort, group and distinct have the same limit for now
2012-05-03 18:43:29 +02:00
if (selectList->items.getCount() > MAX_SORT_ITEMS)
{
// Cannot have more than 255 items in DISTINCT list.
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_max_distinct_items));
}
}
2001-05-23 15:26:42 +02:00
if (parent_context)
{
2012-04-25 03:42:47 +02:00
FieldRemapper remapper(dsqlScratch, parent_context, false);
// Reset context of select items to point to the parent stream
2012-04-25 03:42:47 +02:00
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlSelectList, rse->dsqlSelectList);
rse->dsqlSelectList = NULL;
// AB: Check for invalid constructions inside selected-items list
2012-04-25 03:42:47 +02:00
ValueListNode* valueList = parentRse->dsqlSelectList;
{ // scope block
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = valueList->items.begin();
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
{
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
{
// Invalid expression in the select list
// (not contained in either an aggregate or the GROUP BY clause)
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("select list"));
}
}
} // end scope block
// Reset context of order items to point to the parent stream
2009-11-19 10:37:10 +01:00
if (order)
{
2012-04-25 03:42:47 +02:00
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlOrder, rse->dsqlOrder);
rse->dsqlOrder = NULL;
// AB: Check for invalid contructions inside the ORDER BY clause
2012-04-25 03:42:47 +02:00
ValueListNode* valueList = targetRse->dsqlOrder;
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = valueList->items.begin();
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
2005-05-20 01:41:17 +02:00
{
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
{
// Invalid expression in the ORDER BY clause
// (not contained in either an aggregate or the GROUP BY clause)
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("ORDER BY clause"));
}
2005-05-20 01:41:17 +02:00
}
}
// And, of course, reduction clauses must also apply to the parent
if (inputRse->dsqlDistinct)
2012-04-25 03:42:47 +02:00
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlDistinct);
// Process HAVING clause, if any
if (inputRse->dsqlHaving)
{
++dsqlScratch->inHavingClause;
2012-04-25 03:42:47 +02:00
parentRse->dsqlWhere = Node::doDsqlPass(dsqlScratch, inputRse->dsqlHaving, false);
--dsqlScratch->inHavingClause;
2012-04-25 03:42:47 +02:00
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlWhere);
// AB: Check for invalid contructions inside the HAVING clause
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup,
parentRse->dsqlWhere))
{
// Invalid expression in the HAVING clause
// (neither an aggregate nor contained in the GROUP BY clause)
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_having_err) << Arg::Str("HAVING clause"));
}
if (AggregateFinder::find(dsqlScratch, true, parentRse->dsqlWhere))
2003-10-05 08:37:26 +02:00
{
// Cannot use an aggregate in a WHERE clause, use HAVING instead
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_where_err));
2003-10-05 08:37:26 +02:00
}
}
rse = parentRse;
parent_context->ctx_context = dsqlScratch->contextNumber++;
}
const bool sortWindow = rse->dsqlOrder && AggregateFinder::find(dsqlScratch, true, rse->dsqlOrder);
2009-10-23 02:42:40 +02:00
// WINDOW functions
if ((rse->dsqlSelectList && AggregateFinder::find(dsqlScratch, true, rse->dsqlSelectList)) ||
sortWindow)
2009-01-07 10:30:57 +01:00
{
AutoSetRestore<bool> autoProcessingWindow(&dsqlScratch->processingWindow, true);
parent_context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
parent_context->ctx_scope_level = dsqlScratch->scopeLevel;
AggregateSourceNode* window = FB_NEW(pool) AggregateSourceNode(pool);
window->dsqlContext = parent_context;
2012-04-25 03:42:47 +02:00
window->dsqlRse = rse;
window->dsqlWindow = true;
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
2012-04-25 03:42:47 +02:00
parentRse->dsqlStreams = streamList = FB_NEW(pool) RecSourceListNode(pool, 1);
2012-05-03 18:43:29 +02:00
streamList->items[0] = window;
if (rse->flags & RseNode::FLAG_WRITELOCK)
{
parentRse->flags |= RseNode::FLAG_WRITELOCK;
rse->flags &= ~RseNode::FLAG_WRITELOCK;
}
if (rse->dsqlFirst)
{
parentRse->dsqlFirst = rse->dsqlFirst;
rse->dsqlFirst = NULL;
}
if (rse->dsqlSkip)
{
parentRse->dsqlSkip = rse->dsqlSkip;
rse->dsqlSkip = NULL;
}
dsqlScratch->context->push(parent_context);
// replace original contexts with parent context
remap_streams_to_parent_context(rse->dsqlStreams, parent_context);
if (aggregate)
{
// Check for invalid contructions inside selected-items list
2012-04-25 03:42:47 +02:00
ValueListNode* valueList = rse->dsqlSelectList;
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = valueList->items.begin();
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
2003-10-05 08:37:26 +02:00
{
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
{
// Invalid expression in the select list
// (not contained in either an aggregate or the GROUP BY clause)
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("select list"));
}
2003-10-05 08:37:26 +02:00
}
}
2012-04-25 03:42:47 +02:00
FieldRemapper remapper(dsqlScratch, parent_context, true);
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlSelectList, rse->dsqlSelectList);
// Remap the nodes to the partition context.
for (size_t i = 0, mapCount = parent_context->ctx_win_maps.getCount(); i < mapCount; ++i)
{
PartitionMap* partitionMap = parent_context->ctx_win_maps[i];
if (partitionMap->partition)
{
2012-04-25 03:42:47 +02:00
partitionMap->partitionRemapped = Node::doDsqlPass(dsqlScratch, partitionMap->partition);
FieldRemapper remapper2(dsqlScratch, parent_context, true, partitionMap->partition,
partitionMap->order);
2012-04-25 03:42:47 +02:00
ExprNode::doDsqlFieldRemapper(remapper2, partitionMap->partitionRemapped);
}
}
rse->dsqlSelectList = NULL;
if (order)
{
if (aggregate)
2003-10-05 08:37:26 +02:00
{
// Check for invalid contructions inside the order-by list
2012-04-25 03:42:47 +02:00
ValueListNode* valueList = rse->dsqlOrder;
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = valueList->items.begin();
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
{
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
{
// Invalid expression in the ORDER BY list
// (not contained in either an aggregate or the GROUP BY clause)
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("ORDER BY list"));
}
}
2003-10-05 08:37:26 +02:00
}
2012-04-25 03:42:47 +02:00
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlOrder, rse->dsqlOrder);
rse->dsqlOrder = NULL;
}
// And, of course, reduction clauses must also apply to the parent
if (rse->dsqlDistinct)
{
2012-04-25 03:42:47 +02:00
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlDistinct, rse->dsqlDistinct);
rse->dsqlDistinct = NULL;
}
rse = parentRse;
2001-05-23 15:26:42 +02:00
}
rse->dsqlFlags = flags;
return rse;
2001-05-23 15:26:42 +02:00
}
/**
2005-05-28 00:45:31 +02:00
pass1_sel_list
2005-05-28 00:45:31 +02:00
@brief Compile a select list.
@param dsqlScratch
@param input
**/
2012-04-25 03:42:47 +02:00
static ValueListNode* pass1_sel_list(DsqlCompilerScratch* dsqlScratch, ValueListNode* input)
2001-05-23 15:26:42 +02:00
{
2012-04-25 03:42:47 +02:00
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
ValueListNode* retList = FB_NEW(pool) ValueListNode(pool, 0u);
2001-05-23 15:26:42 +02:00
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = input->items.begin();
for (const NestConst<ValueExprNode>* const end = input->items.end(); ptr != end; ++ptr)
2012-04-25 03:42:47 +02:00
retList->add(Node::doDsqlPass(dsqlScratch, *ptr, false));
return retList;
2001-05-23 15:26:42 +02:00
}
// Process ORDER BY list, which may contain an ordinal or alias which references the select list.
2012-04-25 03:42:47 +02:00
ValueListNode* PASS1_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* input, ValueListNode* selectList)
2001-05-23 15:26:42 +02:00
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(input, dsql_type_nod);
DEV_BLKCHK(selectList, dsql_type_nod);
2001-05-23 15:26:42 +02:00
2012-04-08 06:15:09 +02:00
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
2012-04-25 03:42:47 +02:00
if (!input)
2009-11-19 10:37:10 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
// invalid ORDER BY clause
Arg::Gds(isc_order_by_err));
}
2001-05-23 15:26:42 +02:00
2012-05-03 18:43:29 +02:00
if (input->items.getCount() > MAX_SORT_ITEMS)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_order_by_err) <<
// invalid ORDER BY clause, cannot sort on more than 255 items
Arg::Gds(isc_dsql_max_sort_items));
}
2005-05-20 01:41:17 +02:00
// Node is simply to be rebuilt -- just recurse merrily
2001-05-23 15:26:42 +02:00
2012-05-03 18:43:29 +02:00
NestConst<ValueListNode> node = FB_NEW(pool) ValueListNode(pool, input->items.getCount());
NestConst<ValueExprNode>* ptr2 = node->items.begin();
2008-12-05 02:20:14 +01:00
2012-05-20 12:00:52 +02:00
for (size_t sortloop = 0; sortloop < input->items.getCount(); ++sortloop)
{
2012-05-03 18:43:29 +02:00
DEV_BLKCHK(input->items[sortloop], dsql_type_nod);
NestConst<OrderNode> node1 = input->items[sortloop]->as<OrderNode>();
2012-04-08 06:15:09 +02:00
if (!node1)
2009-11-19 10:37:10 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
// invalid ORDER BY clause
Arg::Gds(isc_order_by_err));
2005-05-28 00:45:31 +02:00
}
// get node of value to be ordered by
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode> orderValue = node1->value;
2012-05-03 18:43:29 +02:00
NestConst<CollateNode> collateNode = orderValue->as<CollateNode>();
if (collateNode)
2009-11-19 10:37:10 +01:00
{
// substitute CollateNode with its argument (real value)
2012-05-03 18:43:29 +02:00
orderValue = collateNode->arg;
}
FieldNode* field;
2010-10-24 02:26:00 +02:00
LiteralNode* literal;
2012-05-03 18:43:29 +02:00
if ((field = orderValue->as<FieldNode>()))
2009-11-19 10:37:10 +01:00
{
2012-04-25 03:42:47 +02:00
ValueExprNode* aliasNode = NULL;
// check for alias or field node
if (selectList && field->dsqlQualifier.isEmpty() && field->dsqlName.hasData())
{
// AB: Check first against the select list for matching column.
// When no matches at all are found we go on with our
// normal way of field name lookup.
aliasNode = PASS1_lookup_alias(dsqlScratch, field->dsqlName, selectList, true);
}
2012-04-08 06:15:09 +02:00
orderValue = aliasNode ? aliasNode : field->internalDsqlPass(dsqlScratch, NULL);
2005-05-28 00:45:31 +02:00
}
2012-05-03 18:43:29 +02:00
else if ((literal = orderValue->as<LiteralNode>()) && literal->litDesc.dsc_dtype == dtype_long)
{
2010-10-24 02:26:00 +02:00
const ULONG position = literal->getSlong();
2012-05-03 18:43:29 +02:00
if (position < 1 || !selectList || position > (ULONG) selectList->items.getCount())
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Invalid column position used in the ORDER BY clause
Arg::Gds(isc_dsql_column_pos_err) << Arg::Str("ORDER BY"));
}
2010-10-24 02:26:00 +02:00
// substitute ordinal with appropriate field
2012-05-03 18:43:29 +02:00
orderValue = Node::doDsqlPass(dsqlScratch, selectList->items[position - 1], false);
}
2010-10-24 02:26:00 +02:00
else
2012-04-25 03:42:47 +02:00
orderValue = Node::doDsqlPass(dsqlScratch, orderValue, false);
2001-05-23 15:26:42 +02:00
if (collateNode)
2009-11-19 10:37:10 +01:00
{
// Finally apply collation order, if necessary.
2012-04-25 03:42:47 +02:00
orderValue = CollateNode::pass1Collate(dsqlScratch, orderValue, collateNode->collation);
2001-05-23 15:26:42 +02:00
}
2012-04-08 06:15:09 +02:00
OrderNode* node2 = FB_NEW(pool) OrderNode(pool, orderValue);
node2->descending = node1->descending;
node2->nullsPlacement = node1->nullsPlacement;
// store actual value to be ordered by
2012-04-25 03:42:47 +02:00
*ptr2++ = node2;
2001-05-23 15:26:42 +02:00
}
return node;
}
2012-02-10 04:06:57 +01:00
// Handle a UNION of substreams, generating a mapping of all the fields and adding an implicit
// PROJECT clause to ensure that all the records returned are unique.
static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* input,
2012-04-25 03:42:47 +02:00
ValueListNode* orderList, RowsClause* rows, bool updateLock, USHORT flags)
2001-05-23 15:26:42 +02:00
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
2001-05-23 15:26:42 +02:00
// set up the rse node for the union.
UnionSourceNode* unionSource = FB_NEW(pool) UnionSourceNode(pool);
unionSource->dsqlAll = input->dsqlAll;
unionSource->recursive = input->recursive;
RseNode* unionRse = FB_NEW(pool) RseNode(pool);
2012-04-25 03:42:47 +02:00
unionRse->dsqlStreams = FB_NEW(pool) RecSourceListNode(pool, 1);
2012-05-03 18:43:29 +02:00
unionRse->dsqlStreams->items[0] = unionSource;
2012-04-25 03:42:47 +02:00
unionSource->dsqlParentRse = unionRse;
// generate a context for the union itself.
2009-01-07 10:30:57 +01:00
dsql_ctx* union_context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
if (input->recursive)
union_context->ctx_context = dsqlScratch->recursiveCtxId;
else
union_context->ctx_context = dsqlScratch->contextNumber++;
2001-05-23 15:26:42 +02:00
union_context->ctx_scope_level = dsqlScratch->scopeLevel;
// When we're in a outer-join part mark context for it.
if (dsqlScratch->inOuterJoin)
union_context->ctx_flags |= CTX_outer_join;
union_context->ctx_in_outer_join = dsqlScratch->inOuterJoin;
dsqlScratch->context->push(union_context);
2012-04-25 03:42:47 +02:00
unionSource->dsqlClauses = FB_NEW(pool) RecSourceListNode(pool,
2012-05-03 18:43:29 +02:00
input->dsqlClauses->items.getCount());
// process all the sub-rse's.
{ // scope block
2012-05-03 18:43:29 +02:00
NestConst<RecordSourceNode>* uptr = unionSource->dsqlClauses->items.begin();
const DsqlContextStack::const_iterator base(*dsqlScratch->context);
2012-05-03 18:43:29 +02:00
NestConst<RecordSourceNode>* ptr = input->dsqlClauses->items.begin();
2012-05-03 18:43:29 +02:00
for (const NestConst<RecordSourceNode>* const end = input->dsqlClauses->items.end();
2012-04-25 03:42:47 +02:00
ptr != end;
++ptr, ++uptr)
{
dsqlScratch->scopeLevel++;
2012-04-25 03:42:47 +02:00
*uptr = pass1_rse(dsqlScratch, *ptr, NULL, NULL, false, 0);
dsqlScratch->scopeLevel--;
while (*(dsqlScratch->context) != base)
dsqlScratch->unionContext.push(dsqlScratch->context->pop());
// Push recursive context after initial select has been processed.
// Corresponding pop occurs in pass1_derived_table
2012-05-03 18:43:29 +02:00
if (input->recursive && (ptr == input->dsqlClauses->items.begin()))
dsqlScratch->context->push(dsqlScratch->recursiveCtx);
2005-05-20 01:41:17 +02:00
}
} // end scope block
2001-05-23 15:26:42 +02:00
// generate the list of fields to select.
2012-05-03 18:43:29 +02:00
ValueListNode* items = unionSource->dsqlClauses->items[0]->as<RseNode>()->dsqlSelectList;
2001-05-23 15:26:42 +02:00
2005-05-28 00:45:31 +02:00
// loop through the list nodes, checking to be sure that they have the
2003-08-15 02:02:18 +02:00
// same number of items
2003-10-05 08:37:26 +02:00
2012-05-20 12:00:52 +02:00
for (size_t i = 1; i < unionSource->dsqlClauses->items.getCount(); ++i)
2009-01-07 10:30:57 +01:00
{
2012-05-03 18:43:29 +02:00
const ValueListNode* nod1 = unionSource->dsqlClauses->items[i]->as<RseNode>()->dsqlSelectList;
2012-05-03 18:43:29 +02:00
if (items->items.getCount() != nod1->items.getCount())
2009-11-19 10:37:10 +01:00
{
2009-01-07 10:30:57 +01:00
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
// overload of msg
Arg::Gds(isc_dsql_count_mismatch));
2003-08-15 02:02:18 +02:00
}
2009-01-07 10:30:57 +01:00
}
2001-05-23 15:26:42 +02:00
2009-04-19 12:06:07 +02:00
// Comment below belongs to the old code (way a union was handled).
2009-04-19 12:06:07 +02:00
// SQL II, section 9.3, pg 195 governs which data types
// are considered equivalent for a UNION
// The following restriction is in some ways more restrictive
// (cannot UNION CHAR with VARCHAR for instance)
// (or cannot union CHAR of different lengths)
// and in someways less restrictive
// (SCALE is not looked at)
// Workaround: use a direct CAST() dsqlScratch in the SQL
// dsqlScratch to force desired datatype.
2003-08-15 02:02:18 +02:00
// loop through the list nodes and cast whenever possible.
2012-04-25 03:42:47 +02:00
ValueListNode* tmp_list = FB_NEW(pool) ValueListNode(
2012-05-03 18:43:29 +02:00
pool, unionSource->dsqlClauses->items.getCount());
2012-05-20 12:00:52 +02:00
for (size_t j = 0; j < items->items.getCount(); ++j)
2009-01-07 10:30:57 +01:00
{
2012-05-20 12:00:52 +02:00
for (size_t i = 0; i < unionSource->dsqlClauses->items.getCount(); ++i)
2009-11-19 10:37:10 +01:00
{
2012-05-03 18:43:29 +02:00
ValueListNode* nod1 = unionSource->dsqlClauses->items[i]->as<RseNode>()->dsqlSelectList;
MAKE_desc(dsqlScratch, &nod1->items[j]->nodDesc, nod1->items[j]);
tmp_list->items[i] = nod1->items[j];
// We look only at the items->nod_arg[] when creating the
// output descriptors. Make sure that the sub-rses
2012-04-25 03:42:47 +02:00
// descriptor flags are copied onto items->nod_arg[]->nodDesc.
// Bug 65584.
// -Sudesh 07/28/1999.
2009-11-19 10:37:10 +01:00
if (i > 0)
{
2012-05-03 18:43:29 +02:00
if (nod1->items[j]->nodDesc.dsc_flags & DSC_nullable)
items->items[j]->nodDesc.dsc_flags |= DSC_nullable;
2003-08-15 02:02:18 +02:00
}
}
2003-10-05 08:37:26 +02:00
dsc desc;
MAKE_desc_from_list(dsqlScratch, &desc, tmp_list, "UNION");
// Only mark upper node as a NULL node when all sub-nodes are NULL
2012-05-03 18:43:29 +02:00
items->items[j]->nodDesc.dsc_flags &= ~DSC_null;
items->items[j]->nodDesc.dsc_flags |= (desc.dsc_flags & DSC_null);
2012-04-25 03:42:47 +02:00
pass1_union_auto_cast(dsqlScratch, unionSource->dsqlClauses, desc, j);
2001-05-23 15:26:42 +02:00
}
2012-05-03 18:43:29 +02:00
items = unionSource->dsqlClauses->items[0]->as<RseNode>()->dsqlSelectList;
2001-05-23 15:26:42 +02:00
// Create mappings for union.
2012-05-03 18:43:29 +02:00
ValueListNode* union_items = FB_NEW(pool) ValueListNode(pool, items->items.getCount());
{ // scope block
2011-01-28 18:22:44 +01:00
USHORT count = 0;
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* uptr = items->items.begin();
NestConst<ValueExprNode>* ptr = union_items->items.begin();
2012-05-03 18:43:29 +02:00
for (const NestConst<ValueExprNode>* const end = union_items->items.end(); ptr != end; ++ptr)
2005-05-20 01:41:17 +02:00
{
// Set up the dsql_map* between the sub-rses and the union context.
dsql_map* map = union_context->ctx_map = FB_NEW(*tdbb->getDefaultPool()) dsql_map;
2005-05-20 01:41:17 +02:00
map->map_position = count++;
2012-04-10 04:37:34 +02:00
fb_assert(count != 0); // no wrap, please!
2005-05-20 01:41:17 +02:00
map->map_node = *uptr++;
map->map_next = union_context->ctx_map;
map->map_partition = NULL;
2012-04-25 03:42:47 +02:00
*ptr = FB_NEW(pool) DsqlMapNode(pool, union_context, map);
2005-05-20 01:41:17 +02:00
}
unionRse->dsqlSelectList = union_items;
} // end scope block
2001-05-23 15:26:42 +02:00
// Process ORDER clause, if any.
2012-04-25 03:42:47 +02:00
if (orderList)
2009-01-07 10:30:57 +01:00
{
2012-05-03 18:43:29 +02:00
ValueListNode* sort = FB_NEW(pool) ValueListNode(pool, orderList->items.getCount());
NestConst<ValueExprNode>* uptr = sort->items.begin();
NestConst<ValueExprNode>* ptr = orderList->items.begin();
2012-05-03 18:43:29 +02:00
for (const NestConst<ValueExprNode>* const end = orderList->items.end();
2012-04-25 03:42:47 +02:00
ptr != end;
++ptr, ++uptr)
2003-10-05 08:37:26 +02:00
{
2012-04-25 03:42:47 +02:00
OrderNode* order1 = (*ptr)->as<OrderNode>();
2012-05-03 18:43:29 +02:00
const ValueExprNode* position = order1->value;
2012-04-25 03:42:47 +02:00
const CollateNode* collateNode = position->as<CollateNode>();
if (collateNode)
2012-05-03 18:43:29 +02:00
position = collateNode->arg;
2012-04-25 03:42:47 +02:00
const LiteralNode* literal = position->as<LiteralNode>();
2010-10-24 02:26:00 +02:00
if (!literal || literal->litDesc.dsc_dtype != dtype_long)
2003-10-05 08:37:26 +02:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
// invalid ORDER BY clause.
Arg::Gds(isc_order_by_err));
}
2010-10-24 02:26:00 +02:00
const SLONG number = literal->getSlong();
2012-05-20 12:00:52 +02:00
if (number < 1 || ULONG(number) > union_items->items.getCount())
2009-11-19 10:37:10 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
// invalid ORDER BY clause.
Arg::Gds(isc_order_by_err));
}
2001-05-23 15:26:42 +02:00
// make a new order node pointing at the Nth item in the select list.
2012-05-03 18:43:29 +02:00
OrderNode* order2 = FB_NEW(pool) OrderNode(pool, union_items->items[number - 1]);
2012-04-25 03:42:47 +02:00
*uptr = order2;
2012-04-08 06:15:09 +02:00
order2->descending = order1->descending;
2010-10-24 02:26:00 +02:00
if (collateNode)
2009-11-19 10:37:10 +01:00
{
2012-05-03 18:43:29 +02:00
order2->value = CollateNode::pass1Collate(dsqlScratch,
order2->value, collateNode->collation);
}
2010-10-24 02:26:00 +02:00
2012-04-08 06:15:09 +02:00
order2->nullsPlacement = order1->nullsPlacement;
2001-05-23 15:26:42 +02:00
}
2010-10-24 02:26:00 +02:00
unionRse->dsqlOrder = sort;
2001-05-23 15:26:42 +02:00
}
2009-01-07 10:30:57 +01:00
if (rows)
2012-04-07 06:21:46 +02:00
PASS1_limit(dsqlScratch, rows->length, rows->skip, unionRse);
// PROJECT on all the select items unless UNION ALL was specified.
if (!input->dsqlAll)
{
2012-02-10 04:06:57 +01:00
if (updateLock)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str("WITH LOCK"));
}
unionRse->dsqlDistinct = union_items;
}
2001-05-23 15:26:42 +02:00
2012-02-10 04:06:57 +01:00
if (updateLock)
unionRse->flags |= RseNode::FLAG_WRITELOCK;
unionRse->dsqlFlags = flags;
return unionRse;
2001-05-23 15:26:42 +02:00
}
2003-08-15 02:02:18 +02:00
/**
2005-05-28 00:45:31 +02:00
2003-08-15 02:02:18 +02:00
pass1_union_auto_cast
2005-05-28 00:45:31 +02:00
@brief Auto cast types to the same type by the rules from
2003-08-15 02:02:18 +02:00
MAKE_desc_from_list. SELECT X1 FROM .. UNION SELECT X2 FROM ..
Items X1..Xn are collected together to make the cast-descriptor, this
was done by the caller (param desc and input is the collection).
Then is a cast generated (or reused) for every X item if it has
another descriptor than the param desc.
Position tells us which column-nr we are processing.
@param input
@param desc
@param position
**/
2012-04-25 03:42:47 +02:00
static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* input,
2012-05-20 12:00:52 +02:00
const dsc& desc, size_t position)
2003-08-15 02:02:18 +02:00
{
thread_db* tdbb = JRD_get_thread_data();
2012-04-25 03:42:47 +02:00
RecSourceListNode* recSourceList;
RseNode* rseNode;
UnionSourceNode* unionNode;
2003-08-15 02:02:18 +02:00
2012-04-25 03:42:47 +02:00
if ((recSourceList = input->as<RecSourceListNode>()))
2005-05-22 05:11:41 +02:00
{
2012-05-03 18:43:29 +02:00
NestConst<RecordSourceNode>* ptr = recSourceList->items.begin();
for (const NestConst<RecordSourceNode>* const end = recSourceList->items.end(); ptr != end; ++ptr)
2012-04-25 03:42:47 +02:00
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
}
else if ((rseNode = input->as<RseNode>()) && !rseNode->dsqlExplicitJoin &&
!rseNode->dsqlContext) // not derived table
{
pass1_union_auto_cast(dsqlScratch, rseNode->dsqlStreams, desc, position);
2012-05-03 18:43:29 +02:00
if (rseNode->dsqlStreams->items.getCount() == 1 &&
(unionNode = rseNode->dsqlStreams->items[0]->as<UnionSourceNode>()) &&
2012-04-25 03:42:47 +02:00
unionNode->dsqlParentRse == rseNode)
{
// We're now in a UNION under a UNION so don't change the existing mappings.
// Only replace the node where the map points to, because they could be changed.
2012-05-03 18:43:29 +02:00
ValueListNode* sub_rse_items =
unionNode->dsqlClauses->items[0]->as<RseNode>()->dsqlSelectList;
dsql_map* map = rseNode->dsqlSelectList->items[position]->as<DsqlMapNode>()->map;
map->map_node = sub_rse_items->items[position];
rseNode->dsqlSelectList->items[position]->nodDesc = desc;
2012-04-25 03:42:47 +02:00
}
else
{
ValueListNode* list = rseNode->dsqlSelectList;
2012-05-20 12:00:52 +02:00
if (position >= list->items.getCount())
2005-05-22 05:11:41 +02:00
{
2012-04-25 03:42:47 +02:00
// Internal dsql error: column position out of range in pass1_union_auto_cast
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_auto_field_bad_pos));
}
else
{
2012-05-03 18:43:29 +02:00
ValueExprNode* select_item = list->items[position];
2012-04-25 03:42:47 +02:00
MAKE_desc(dsqlScratch, &select_item->nodDesc, select_item);
2012-04-25 03:42:47 +02:00
if (select_item->nodDesc.dsc_dtype != desc.dsc_dtype ||
select_item->nodDesc.dsc_length != desc.dsc_length ||
select_item->nodDesc.dsc_scale != desc.dsc_scale ||
select_item->nodDesc.dsc_sub_type != desc.dsc_sub_type)
{
// Because this select item has a different descriptor then
// our finally descriptor CAST it.
CastNode* castNode = NULL;
DsqlAliasNode* newAliasNode = NULL;
DsqlAliasNode* aliasNode;
DerivedFieldNode* derivedField;
// Pick a existing cast if available else make a new one.
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)) &&
2012-05-03 18:43:29 +02:00
aliasNode->value && (castNode = aliasNode->value->as<CastNode>()))
2003-10-05 08:37:26 +02:00
{
2012-04-25 03:42:47 +02:00
}
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)) &&
2012-05-03 18:43:29 +02:00
(castNode = derivedField->value->as<CastNode>()))
2012-04-25 03:42:47 +02:00
{
}
else if ((castNode = ExprNode::as<CastNode>(select_item)))
{
}
else
{
castNode = FB_NEW(*tdbb->getDefaultPool()) CastNode(
*tdbb->getDefaultPool());
castNode->dsqlField = FB_NEW(*tdbb->getDefaultPool()) dsql_fld(
*tdbb->getDefaultPool());
// We want to leave the ALIAS node on his place, because a UNION
// uses the select_items from the first sub-rse to determine the
// columnname.
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)))
2012-05-03 18:43:29 +02:00
castNode->source = aliasNode->value;
2012-04-25 03:42:47 +02:00
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
2012-05-03 18:43:29 +02:00
castNode->source = derivedField->value;
2009-01-07 10:30:57 +01:00
else
2012-05-03 18:43:29 +02:00
castNode->source = select_item;
2005-05-28 00:45:31 +02:00
2012-04-25 03:42:47 +02:00
// When a cast is created we're losing our fieldname, thus
// create an alias to keep it.
const ValueExprNode* name_node = select_item;
const DsqlMapNode* mapNode;
2012-04-25 03:42:47 +02:00
while ((mapNode = ExprNode::as<DsqlMapNode>(name_node)))
2009-11-19 10:37:10 +01:00
{
2012-04-25 03:42:47 +02:00
// Skip all the DsqlMapNodes.
name_node = mapNode->map->map_node;
2003-08-15 02:02:18 +02:00
}
2012-04-25 03:42:47 +02:00
const FieldNode* fieldNode;
2012-04-25 03:42:47 +02:00
if ((fieldNode = ExprNode::as<FieldNode>(name_node)))
2009-11-19 10:37:10 +01:00
{
2012-04-25 03:42:47 +02:00
// Create new node for alias and copy fieldname.
newAliasNode = FB_NEW(*tdbb->getDefaultPool()) DsqlAliasNode(
*tdbb->getDefaultPool(), fieldNode->dsqlField->fld_name, NULL);
// The alias value will be assigned a bit later.
2003-08-15 02:02:18 +02:00
}
}
2012-04-25 03:42:47 +02:00
dsql_fld* field = castNode->dsqlField;
// Copy the descriptor to a field, because the gen_cast
// uses a dsql field type.
field->dtype = desc.dsc_dtype;
field->scale = desc.dsc_scale;
field->subType = desc.dsc_sub_type;
field->length = desc.dsc_length;
field->flags = (desc.dsc_flags & DSC_nullable) ? FLD_nullable : 0;
2003-08-15 02:02:18 +02:00
2012-04-25 03:42:47 +02:00
if (desc.dsc_dtype <= dtype_any_text)
{
field->textType = desc.dsc_sub_type;
field->charSetId = INTL_GET_CHARSET(&desc);
field->collationId = INTL_GET_COLLATE(&desc);
2012-04-25 03:42:47 +02:00
}
else if (desc.dsc_dtype == dtype_blob)
{
field->charSetId = desc.dsc_scale;
field->collationId = desc.dsc_flags >> 8;
2012-04-25 03:42:47 +02:00
}
2012-04-25 03:42:47 +02:00
// Finally copy the descriptors to the root nodes and swap
// the necessary nodes.
castNode->nodDesc = desc;
2012-04-25 03:42:47 +02:00
if (select_item->nodDesc.dsc_flags & DSC_nullable)
castNode->nodDesc.dsc_flags |= DSC_nullable;
2012-04-25 03:42:47 +02:00
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)))
{
aliasNode->value = castNode;
aliasNode->value->nodDesc = desc;
select_item->nodDesc = desc;
}
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
{
2012-05-03 18:43:29 +02:00
derivedField->value = castNode;
derivedField->value->nodDesc = desc;
2012-04-25 03:42:47 +02:00
select_item->nodDesc = desc;
}
else
{
// If a new alias was created for keeping original field-name
// make the alias the "top" node.
if (newAliasNode)
{
newAliasNode->value = castNode;
newAliasNode->value->nodDesc = castNode->nodDesc;
2012-05-03 18:43:29 +02:00
list->items[position] = newAliasNode;
2012-04-25 03:42:47 +02:00
}
else
2012-05-03 18:43:29 +02:00
list->items[position] = castNode;
2012-04-25 03:42:47 +02:00
}
}
}
2012-04-25 03:42:47 +02:00
}
}
else if ((unionNode = input->as<UnionSourceNode>()))
{
recSourceList = unionNode->dsqlClauses;
2003-08-15 02:02:18 +02:00
2012-05-03 18:43:29 +02:00
for (NestConst<RecordSourceNode>* ptr = recSourceList->items.begin();
ptr != recSourceList->items.end();
2012-04-25 03:42:47 +02:00
++ptr)
{
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
}
2003-08-15 02:02:18 +02:00
}
}
// Post an item to a map for a context.
2012-04-25 03:42:47 +02:00
DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node, dsql_ctx* context,
ValueListNode* partitionNode, ValueListNode* orderNode)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(node, dsql_type_nod);
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
PartitionMap* partitionMap = NULL;
dsql_map* map = NULL;
if (dsqlScratch->processingWindow)
{
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, orderNode);
map = partitionMap->map;
}
else
map = context->ctx_map;
2011-01-28 18:22:44 +01:00
USHORT count = 0;
while (map)
2008-05-21 18:02:25 +02:00
{
if (PASS1_node_match(node, map->map_node, false))
2001-05-23 15:26:42 +02:00
break;
++count;
map = map->map_next;
2008-05-21 18:02:25 +02:00
}
2001-05-23 15:26:42 +02:00
2008-05-21 18:02:25 +02:00
if (!map)
{
dsql_map** next = partitionMap ? &partitionMap->map : &context->ctx_map;
if (*next)
{
while (*(next = &(*next)->map_next))
;
}
map = *next = FB_NEW(*tdbb->getDefaultPool()) dsql_map;
2011-01-28 18:22:44 +01:00
map->map_position = count;
2003-08-30 18:49:15 +02:00
map->map_node = node;
map->map_partition = partitionMap;
2001-05-23 15:26:42 +02:00
}
2012-04-25 03:42:47 +02:00
MAKE_desc(dsqlScratch, &node->nodDesc, node);
DsqlMapNode* mapNode = FB_NEW(*tdbb->getDefaultPool()) DsqlMapNode(*tdbb->getDefaultPool(),
context, map);
2012-04-25 03:42:47 +02:00
mapNode->nodDesc = node->nodDesc;
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
return mapNode;
2001-05-23 15:26:42 +02:00
}
2012-04-25 03:42:47 +02:00
// For each relation in the list, flag the relation's context as having a parent context.
// Be sure to handle joins (Bug 6674).
static void remap_streams_to_parent_context(ExprNode* input, dsql_ctx* parent_context)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(parent_context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
RecSourceListNode* listNode;
ProcedureSourceNode* procNode;
RelationSourceNode* relNode;
RseNode* rseNode;
UnionSourceNode* unionNode;
2012-04-25 03:42:47 +02:00
if ((listNode = input->as<RecSourceListNode>()))
{
2012-05-03 18:43:29 +02:00
NestConst<RecordSourceNode>* ptr = listNode->items.begin();
for (const NestConst<RecordSourceNode>* const end = listNode->items.end(); ptr != end; ++ptr)
2012-04-25 03:42:47 +02:00
remap_streams_to_parent_context(*ptr, parent_context);
}
else if ((procNode = input->as<ProcedureSourceNode>()))
{
DEV_BLKCHK(procNode->dsqlContext, dsql_type_ctx);
procNode->dsqlContext->ctx_parent = parent_context;
2001-05-23 15:26:42 +02:00
}
2012-04-25 03:42:47 +02:00
else if ((relNode = input->as<RelationSourceNode>()))
{
DEV_BLKCHK(relNode->dsqlContext, dsql_type_ctx);
relNode->dsqlContext->ctx_parent = parent_context;
}
else if ((rseNode = input->as<RseNode>()))
remap_streams_to_parent_context(rseNode->dsqlStreams, parent_context);
else if ((unionNode = input->as<UnionSourceNode>()))
remap_streams_to_parent_context(unionNode->dsqlClauses, parent_context);
else
fb_assert(input->as<AggregateSourceNode>());
2001-05-23 15:26:42 +02:00
}
2012-04-25 03:42:47 +02:00
// Setup the datatype of a parameter.
bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, ValueExprNode* inNode,
const dsc* desc, bool force_varchar)
2001-05-23 15:26:42 +02:00
{
2012-04-25 03:42:47 +02:00
return inNode && inNode->setParameterType(dsqlScratch, desc, force_varchar);
}
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
// Setup the datatype of a parameter.
bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, ValueExprNode* inNode,
ValueExprNode* node, bool force_varchar)
{
if (!inNode)
return false;
2001-05-23 15:26:42 +02:00
2012-04-25 03:42:47 +02:00
MAKE_desc(dsqlScratch, &node->nodDesc, node);
return inNode->setParameterType(dsqlScratch, &node->nodDesc, force_varchar);
2001-05-23 15:26:42 +02:00
}
// Returns false for hidden fields and true for non-hidden.
// For non-hidden, change "node" if the field is part of an
// implicit join.
2012-05-03 18:43:29 +02:00
bool dsql_ctx::getImplicitJoinField(const MetaName& name, NestConst<ValueExprNode>& node)
{
ImplicitJoin* impJoin;
if (ctx_imp_join.get(name, impJoin))
{
if (impJoin->visibleInContext == this)
{
node = impJoin->value;
return true;
}
2008-01-16 07:52:43 +01:00
return false;
}
return true;
}
2008-02-28 14:48:16 +01:00
// Returns (creating, if necessary) the PartitionMap of a given partition (that may be NULL).
2012-04-25 03:42:47 +02:00
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueListNode* partitionNode,
ValueListNode* orderNode)
{
thread_db* tdbb = JRD_get_thread_data();
PartitionMap* partitionMap = NULL;
for (Array<PartitionMap*>::iterator i = ctx_win_maps.begin();
!partitionMap && i != ctx_win_maps.end();
++i)
{
if (PASS1_node_match((*i)->partition, partitionNode, false) &&
PASS1_node_match((*i)->order, orderNode, false))
{
partitionMap = *i;
}
}
if (!partitionMap)
{
partitionMap = FB_NEW(*tdbb->getDefaultPool()) PartitionMap(partitionNode, orderNode);
ctx_win_maps.add(partitionMap);
partitionMap->context = dsqlScratch->contextNumber++;
}
return partitionMap;
}