2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: Dynamic SQL runtime support
|
2003-09-29 14:43:14 +02:00
|
|
|
* MODULE: pass1.cpp
|
2008-05-24 05:19:52 +02:00
|
|
|
* 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-08-03 17:27:20 +02:00
|
|
|
*
|
|
|
|
* 2002.07.30 Arno Brinkman: Added pass1_coalesce, pass1_simple_case, pass1_searched_case
|
2011-01-22 21:40:04 +01:00
|
|
|
* and PASS1_put_args_on_stack
|
2002-08-11 10:04:54 +02:00
|
|
|
*
|
2010-02-13 21:29:29 +01:00
|
|
|
* 2002.08.04 Arno Brinkman: Added ignore_cast as parameter to PASS1_node_match,
|
2002-08-11 10:04:54 +02:00
|
|
|
* 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 16:04:35 +02:00
|
|
|
*
|
|
|
|
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
|
|
|
|
* exception handling in SPs/triggers,
|
|
|
|
* implemented ROWS_AFFECTED system variable
|
2002-09-29 01:52:36 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2011-03-04 02:47:49 +01:00
|
|
|
* Modified functions pass1_field, pass1_rse, copy_field, PASS1_sort.
|
2002-09-29 01:52:36 +02:00
|
|
|
* Functions pass1_found_aggregate and pass1_found_field added.
|
2002-10-25 10:29:12 +02:00
|
|
|
*
|
2002-10-29 21:20:44 +01: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 21:20:44 +01:00
|
|
|
*
|
|
|
|
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
|
2002-12-03 19:04:36 +01:00
|
|
|
*
|
|
|
|
* 2002.12.03 Dmitry Yemanov: Implemented ORDER BY clause in subqueries
|
2002-12-18 16:01:50 +01:00
|
|
|
*
|
|
|
|
* 2002.12.18 Dmitry Yemanov: Fixed bug with BREAK and partially implemented
|
|
|
|
* SQL-compliant labels and LEAVE statement
|
2003-01-11 03:49:13 +01:00
|
|
|
*
|
|
|
|
* 2003.01.11 Arno Brinkman: Reworked a lot of functions for bringing back backwards compatibilty
|
|
|
|
* with sub-selects and aggregates.
|
2003-01-15 13:00:33 +01:00
|
|
|
*
|
|
|
|
* 2003.01.14 Dmitry Yemanov: Fixed bug with cursors in triggers
|
|
|
|
*
|
|
|
|
* 2003.01.15 Dmitry Yemanov: Added support for parametrized events
|
2003-04-06 13:20:24 +02:00
|
|
|
*
|
|
|
|
* 2003.04.05 Dmitry Yemanov: Changed logic of ORDER BY with collations
|
|
|
|
* (because of the parser change)
|
2003-08-16 02:36:54 +02:00
|
|
|
*
|
|
|
|
* 2003.08.14 Arno Brinkman: Added derived table support.
|
|
|
|
*
|
|
|
|
* 2003.08.16 Arno Brinkman: Changed ambiguous column name checking.
|
2003-11-02 13:28:30 +01:00
|
|
|
*
|
|
|
|
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL.
|
2004-01-16 11:43:21 +01:00
|
|
|
*
|
2005-05-28 00:45:31 +02:00
|
|
|
* 2004.01.16 Vlad Horsun: added support for default parameters and
|
2004-01-16 11:43:21 +01:00
|
|
|
* EXECUTE BLOCK statement
|
2007-03-06 16:54:34 +01:00
|
|
|
*
|
|
|
|
* Adriano dos Santos Fernandes
|
|
|
|
*
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
|
2001-07-30 01:43:24 +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"
|
2008-02-28 14:48:16 +01:00
|
|
|
#include "../dsql/node.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../dsql/Nodes.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../dsql/BoolNodes.h"
|
Refactor a number of expression nodes: nod_add, nod_divide, nod_multiply, nod_negate, nod_user_name, nod_subtract, nod_current_date, nod_current_time, nod_current_timestamp, nod_add2, nod_subtract2, nod_multiply2, nod_divide2, nod_current_role, nod_internal_info
2010-09-04 23:36:41 +02:00
|
|
|
#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"
|
2008-03-01 20:14:46 +01:00
|
|
|
#include "../jrd/intl_classes.h"
|
2011-01-09 22:58:56 +01:00
|
|
|
#include "../jrd/RecordSourceNodes.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../dsql/DdlNodes.h"
|
2009-10-24 19:45:33 +02:00
|
|
|
#include "../dsql/StmtNodes.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/ddl_proto.h"
|
|
|
|
#include "../dsql/errd_proto.h"
|
2010-06-10 04:03:03 +02:00
|
|
|
#include "../dsql/gen_proto.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/make_proto.h"
|
|
|
|
#include "../dsql/metd_proto.h"
|
2005-06-10 04:03:08 +02:00
|
|
|
#include "../dsql/pass1_proto.h"
|
|
|
|
#include "../dsql/utld_proto.h"
|
2010-04-15 16:40:27 +02:00
|
|
|
#include "../dsql/DSqlDataTypeUtil.h"
|
2010-10-12 10:02:57 +02:00
|
|
|
#include "../common/dsc_proto.h"
|
2008-03-01 20:14:46 +01:00
|
|
|
#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"
|
2007-06-09 21:18:21 +02:00
|
|
|
#include "../jrd/SysFunction.h"
|
2004-01-16 11:43:21 +01:00
|
|
|
#include "../common/classes/array.h"
|
2006-05-19 17:17:02 +02:00
|
|
|
#include "../common/classes/auto.h"
|
2003-12-31 06:36:12 +01:00
|
|
|
#include "../common/utils_proto.h"
|
2007-11-29 18:53:38 +01:00
|
|
|
#include "../common/config/config.h"
|
2008-08-15 13:21:47 +02:00
|
|
|
#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 Dsql;
|
2008-08-15 13:21:47 +02:00
|
|
|
using namespace Firebird;
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef DEV_BUILD
|
2008-02-28 14:48:16 +01:00
|
|
|
static void DSQL_pretty(const dsql_nod*, int);
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
static dsql_ctx* pass1_alias_list(DsqlCompilerScratch*, dsql_nod*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_ctx* pass1_alias(DsqlCompilerScratch*, DsqlContextStack&, dsql_str*);
|
2011-01-30 01:25:46 +01:00
|
|
|
static dsql_str* pass1_alias_concat(const dsql_str*, const char*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_rel* pass1_base_table(DsqlCompilerScratch*, const dsql_rel*, const dsql_str*);
|
2008-10-04 04:48:35 +02:00
|
|
|
static void pass1_expand_contexts(DsqlContextStack& contexts, dsql_ctx* context);
|
2012-04-07 05:03:28 +02:00
|
|
|
static dsql_nod* pass1_derived_table(DsqlCompilerScratch*, SelectExprNode*, const char*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_expand_select_list(DsqlCompilerScratch*, dsql_nod*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_group_by_list(DsqlCompilerScratch*, dsql_nod*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_make_derived_field(DsqlCompilerScratch*, thread_db*, dsql_nod*);
|
2012-04-07 06:21:46 +02:00
|
|
|
static RseNode* pass1_rse(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, RowsClause*, bool, USHORT);
|
|
|
|
static RseNode* pass1_rse_impl(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, RowsClause*, bool, USHORT);
|
2012-04-07 05:03:28 +02:00
|
|
|
static dsql_nod* pass1_sel_list(DsqlCompilerScratch*, dsql_nod*);
|
2012-04-07 06:21:46 +02:00
|
|
|
static RseNode* pass1_union(DsqlCompilerScratch*, UnionSourceNode*, dsql_nod*, RowsClause*, bool, USHORT);
|
2010-11-01 14:45:52 +01:00
|
|
|
static void pass1_union_auto_cast(DsqlCompilerScratch*, dsql_nod*, const dsc&, SSHORT,
|
|
|
|
bool in_select_list = false);
|
2003-11-10 10:16:38 +01:00
|
|
|
static void remap_streams_to_parent_context(dsql_nod*, dsql_ctx*);
|
2010-06-10 04:03:03 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
AggregateFinder::AggregateFinder(const DsqlCompilerScratch* aDsqlScratch, bool aWindow)
|
|
|
|
: ConstDsqlNodeVisitor(false, false, &ExprNode::dsqlAggregateFinder),
|
|
|
|
dsqlScratch(aDsqlScratch),
|
|
|
|
window(aWindow),
|
|
|
|
currentLevel(dsqlScratch->scopeLevel),
|
|
|
|
deepestLevel(0),
|
|
|
|
ignoreSubSelects(false)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
2003-11-11 13:19:20 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
bool AggregateFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
Aggregate2Finder::Aggregate2Finder(USHORT aCheckScopeLevel, FieldMatchType aMatchType, bool aWindowOnly)
|
|
|
|
: ConstDsqlNodeVisitor(true, false, &ExprNode::dsqlAggregate2Finder),
|
|
|
|
checkScopeLevel(aCheckScopeLevel),
|
|
|
|
matchType(aMatchType),
|
|
|
|
windowOnly(aWindowOnly),
|
|
|
|
currentScopeLevelEqual(true)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Aggregate2Finder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2004-01-16 11:43:21 +01:00
|
|
|
|
2006-03-29 11:41:48 +02:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
FieldFinder::FieldFinder(USHORT aCheckScopeLevel, FieldMatchType aMatchType)
|
|
|
|
: ConstDsqlNodeVisitor(true, false, &ExprNode::dsqlFieldFinder),
|
|
|
|
checkScopeLevel(aCheckScopeLevel),
|
|
|
|
matchType(aMatchType),
|
|
|
|
field(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FieldFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
InvalidReferenceFinder::InvalidReferenceFinder(const dsql_ctx* aContext, const dsql_nod* aList)
|
|
|
|
: ConstDsqlNodeVisitor(true, false, &ExprNode::dsqlInvalidReferenceFinder),
|
|
|
|
context(aContext),
|
|
|
|
list(aList),
|
|
|
|
insideOwnMap(false),
|
|
|
|
insideHigherMap(false)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(list, dsql_type_nod);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InvalidReferenceFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool invalid = false;
|
|
|
|
|
2010-01-31 00:08:04 +01:00
|
|
|
// 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
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (list)
|
|
|
|
{
|
2010-01-29 12:07:42 +01:00
|
|
|
// Check if this node (with ignoring of CASTs) appears also
|
2010-01-28 16:18:11 +01:00
|
|
|
// in the list of group by. If yes then it's allowed
|
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (PASS1_node_match(node, *ptr, true))
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_plan_expr:
|
2011-02-06 19:13:12 +01:00
|
|
|
fb_assert(false);
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return visitChildren(node);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return invalid;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
FieldRemapper::FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
|
|
|
dsql_nod* aPartitionNode, dsql_nod* aOrderNode)
|
|
|
|
: NonConstDsqlNodeVisitor(false, false, &ExprNode::dsqlFieldRemapper),
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FieldRemapper::internalVisit(dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
SubSelectFinder::SubSelectFinder()
|
|
|
|
: ConstDsqlNodeVisitor(false, true, &ExprNode::dsqlSubSelectFinder)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SubSelectFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!node)
|
|
|
|
return false;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
return visitChildren(node);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
PASS1_make_context
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Generate a context for a dsqlScratch.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2010-01-28 16:18:11 +01:00
|
|
|
@param relation_node
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, const dsql_nod* relation_node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(relation_node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
thread_db* const tdbb = JRD_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_rel* relation = NULL;
|
|
|
|
dsql_prc* procedure = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// figure out whether this is a relation or a procedure
|
|
|
|
// and give an error if it is neither
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
MetaName relation_name;
|
|
|
|
const ProcedureSourceNode* procNode = NULL;
|
|
|
|
const RelationSourceNode* relNode = NULL;
|
2012-04-07 05:03:28 +02:00
|
|
|
const SelectExprNode* selNode = NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(relation_node)))
|
|
|
|
relation_name = procNode->dsqlName.identifier;
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(relation_node)))
|
|
|
|
relation_name = relNode->dsqlName;
|
|
|
|
else if ((selNode = ExprNode::as<SelectExprNode>(relation_node)))
|
|
|
|
relation_name = selNode->alias.c_str();
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* cte = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (selNode)
|
2011-01-09 22:58:56 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
// No processing needed here for derived tables.
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
else if (procNode && (procNode->dsqlName.package.hasData() || procNode->dsqlInputs))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-10-03 00:11:41 +02:00
|
|
|
if (procNode->dsqlName.package.isEmpty())
|
|
|
|
procedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
|
|
|
|
|
|
|
|
if (!procedure)
|
|
|
|
{
|
|
|
|
procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch,
|
|
|
|
procNode->dsqlName);
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
if (!procedure)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_dsql_procedure_err) <<
|
|
|
|
Arg::Gds(isc_random) <<
|
2011-01-30 01:25:46 +01:00
|
|
|
Arg::Str(procNode->dsqlName.toString()) <<
|
2010-01-28 16:18:11 +01:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) <<
|
|
|
|
Arg::Num(relation_node->nod_column));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((cte = dsqlScratch->findCTE(relation_name)))
|
2012-04-07 05:03:28 +02:00
|
|
|
relation_node = MAKE_class_node(cte);
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
|
|
|
{
|
2011-10-03 00:11:41 +02:00
|
|
|
if (procNode && procNode->dsqlName.package.isEmpty())
|
|
|
|
procedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
|
|
|
|
|
|
|
|
if (!procedure)
|
|
|
|
relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name);
|
2010-08-02 04:22:26 +02:00
|
|
|
|
2011-10-03 00:11:41 +02:00
|
|
|
if (!relation && !procedure && procNode)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
procedure = METD_get_procedure(dsqlScratch->getTransaction(),
|
|
|
|
dsqlScratch, procNode->dsqlName);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!relation && !procedure)
|
2005-07-20 12:05:57 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_dsql_relation_err) <<
|
2011-01-30 01:25:46 +01:00
|
|
|
Arg::Gds(isc_random) << Arg::Str(relation_name) <<
|
2010-01-28 16:18:11 +01:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) <<
|
|
|
|
Arg::Num(relation_node->nod_column));
|
2005-07-20 12:05:57 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2005-07-20 12:05:57 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
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()) <<
|
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) <<
|
|
|
|
Arg::Num(relation_node->nod_column));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Set up context block.
|
|
|
|
dsql_ctx* context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
|
|
|
|
context->ctx_relation = relation;
|
|
|
|
context->ctx_procedure = procedure;
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (selNode)
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_context = USHORT(MAX_UCHAR) + 1 + dsqlScratch->derivedContextNumber++;
|
|
|
|
else
|
|
|
|
context->ctx_context = dsqlScratch->contextNumber++;
|
|
|
|
|
|
|
|
context->ctx_scope_level = dsqlScratch->scopeLevel;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// When we're in a outer-join part mark context for it.
|
2011-01-30 01:25:46 +01:00
|
|
|
if (dsqlScratch->inOuterJoin)
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_flags |= CTX_outer_join;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_in_outer_join = dsqlScratch->inOuterJoin;
|
|
|
|
|
|
|
|
// find the context alias name, if it exists.
|
2011-01-30 01:25:46 +01:00
|
|
|
const char* string = NULL;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(relation_node)))
|
2011-02-01 02:27:51 +01:00
|
|
|
string = procNode->alias.nullStr();
|
2011-01-30 01:25:46 +01:00
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(relation_node)))
|
2011-02-01 02:27:51 +01:00
|
|
|
string = relNode->alias.nullStr();
|
2012-04-07 05:03:28 +02:00
|
|
|
else if ((selNode = ExprNode::as<SelectExprNode>(relation_node)))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
string = selNode->alias.nullStr();
|
|
|
|
context->ctx_rse = selNode->querySpec;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (string)
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_internal_alias = string;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (dsqlScratch->aliasRelationPrefix && !selNode)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (string)
|
2011-01-30 01:25:46 +01:00
|
|
|
string = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, string)->str_data;
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2011-01-30 01:25:46 +01:00
|
|
|
string = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, relation_name.c_str())->str_data;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (string)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_alias = string;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// 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
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_ctx* conflict = stack.object();
|
2009-12-20 22:01:10 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (conflict->ctx_scope_level != context->ctx_scope_level)
|
2010-01-28 16:18:11 +01:00
|
|
|
continue;
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const TEXT* conflict_name;
|
|
|
|
ISC_STATUS error_code;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (conflict->ctx_alias.hasData())
|
2008-05-17 18:51:07 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
conflict_name = conflict->ctx_alias.c_str();
|
2010-01-28 16:18:11 +01:00
|
|
|
error_code = isc_alias_conflict_err;
|
|
|
|
// alias %s conflicts with an alias in the same dsqlScratch.
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else if (conflict->ctx_procedure)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
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.
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else if (conflict->ctx_relation)
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01: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.
|
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2010-01-28 16:18:11 +01:00
|
|
|
continue;
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_alias == conflict_name)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +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
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (procedure)
|
|
|
|
{
|
|
|
|
USHORT count = 0;
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (procNode->dsqlInputs)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_proc_inputs = PASS1_node_psql(dsqlScratch, procNode->dsqlInputs, false);
|
2010-01-28 16:18:11 +01:00
|
|
|
count = context->ctx_proc_inputs->nod_count;
|
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2011-10-03 17:35:49 +02:00
|
|
|
if (count > procedure->prc_in_count ||
|
|
|
|
count < procedure->prc_in_count - procedure->prc_def_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-10-03 17:35:49 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_prcmismat) << Arg::Str(procNode->dsqlName.toString()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
{
|
|
|
|
// Initialize this stack variable, and make it look like a node
|
|
|
|
AutoPtr<dsql_nod> desc_node(FB_NEW_RPT(*tdbb->getDefaultPool(), 0) dsql_nod);
|
|
|
|
dsql_nod* const* input = context->ctx_proc_inputs->nod_arg;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2011-10-03 17:35:49 +02:00
|
|
|
for (dsql_fld* field = procedure->prc_inputs; *input; ++input, field = field->fld_next)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-10-03 17:35:49 +02:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
DEV_BLKCHK(*input, dsql_type_nod);
|
|
|
|
MAKE_desc_from_field(&desc_node->nod_desc, field);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, *input, desc_node, false);
|
2005-03-26 14:32:29 +01:00
|
|
|
}
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// push the context onto the dsqlScratch context stack
|
|
|
|
// for matching fields against
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->context->push(context);
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return context;
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
PASS1_node
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Compile a parsed statement into something more interesting.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param input
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
dsql_nod* PASS1_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|
|
|
{
|
2010-10-22 17:00:22 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-10-22 17:00:22 +02:00
|
|
|
if (!input)
|
2010-01-28 16:18:11 +01:00
|
|
|
return NULL;
|
2002-06-20 13:42:15 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* node;
|
|
|
|
dsql_nod* sub1;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Dispatch on node type. Fall thru on easy ones
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
case nod_class_exprnode:
|
|
|
|
{
|
|
|
|
ExprNode* exprNode = reinterpret_cast<ExprNode*>(input->nod_arg[0]);
|
2011-01-30 01:25:46 +01:00
|
|
|
ProcedureSourceNode* procNode = NULL;
|
|
|
|
RelationSourceNode* relNode = NULL;
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* selNode = NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
//// TODO: Move this to dsqlPass when possible.
|
|
|
|
if ((procNode = exprNode->as<ProcedureSourceNode>()) ||
|
|
|
|
(relNode = exprNode->as<RelationSourceNode>()))
|
|
|
|
{
|
|
|
|
bool couldBeCte = true;
|
|
|
|
MetaName rel_name;
|
|
|
|
string rel_alias;
|
|
|
|
|
|
|
|
if (procNode)
|
|
|
|
{
|
|
|
|
rel_name = procNode->dsqlName.identifier;
|
|
|
|
rel_alias = procNode->alias;
|
|
|
|
couldBeCte = !procNode->dsqlInputs && procNode->dsqlName.package.isEmpty();
|
|
|
|
}
|
|
|
|
else if (relNode)
|
|
|
|
{
|
|
|
|
rel_name = relNode->dsqlName;
|
|
|
|
rel_alias = relNode->alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rel_alias.isEmpty())
|
|
|
|
rel_alias = rel_name.c_str();
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* cte = couldBeCte ? dsqlScratch->findCTE(rel_name) : NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (!cte)
|
2011-03-04 02:47:49 +01:00
|
|
|
return PASS1_relation(dsqlScratch, input);
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
cte->dsqlFlags |= RecordSourceNode::DFLAG_DT_CTE_USED;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if ((dsqlScratch->flags & DsqlCompilerScratch::FLAG_RECURSIVE_CTE) &&
|
|
|
|
dsqlScratch->currCtes.hasData() &&
|
|
|
|
(dsqlScratch->currCtes.object() == cte))
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Recursive CTE member (%s) can refer itself only in FROM clause
|
|
|
|
Arg::Gds(isc_dsql_cte_wrong_reference) << rel_name);
|
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
for (Stack<SelectExprNode*>::const_iterator stack(dsqlScratch->currCtes); stack.hasData(); ++stack)
|
2011-01-30 01:25:46 +01:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* cte1 = stack.object();
|
2011-01-30 01:25:46 +01:00
|
|
|
if (cte1 == cte)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// CTE %s has cyclic dependencies
|
|
|
|
Arg::Gds(isc_dsql_cte_cycle) << rel_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
dsql_nod* const query = cte->querySpec;
|
|
|
|
UnionSourceNode* unionQuery = ExprNode::as<UnionSourceNode>(query);
|
|
|
|
const bool isRecursive = unionQuery && unionQuery->recursive;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
const string saveCteName = cte->alias;
|
2011-01-30 01:25:46 +01:00
|
|
|
if (!isRecursive)
|
2012-04-07 05:03:28 +02:00
|
|
|
cte->alias = rel_alias;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
dsqlScratch->currCtes.push(cte);
|
|
|
|
|
|
|
|
dsql_nod* derived_node = pass1_derived_table(dsqlScratch,
|
|
|
|
cte, (isRecursive ? rel_alias.c_str() : NULL));
|
|
|
|
|
|
|
|
if (!isRecursive)
|
2012-04-07 05:03:28 +02:00
|
|
|
cte->alias = saveCteName;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
dsqlScratch->currCtes.pop();
|
|
|
|
|
|
|
|
return derived_node;
|
|
|
|
}
|
2012-04-07 05:03:28 +02:00
|
|
|
else if ((selNode = exprNode->as<SelectExprNode>()))
|
|
|
|
{
|
|
|
|
fb_assert(selNode->dsqlFlags & RecordSourceNode::DFLAG_DERIVED);
|
|
|
|
return pass1_derived_table(dsqlScratch, selNode, NULL);
|
|
|
|
}
|
2010-02-13 21:29:29 +01:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
Node* newNode = exprNode->dsqlPass(dsqlScratch);
|
|
|
|
if (newNode != (Node*) input->nod_arg[0])
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
|
|
|
input = MAKE_node(input->nod_type, input->nod_count);
|
2012-03-25 03:08:55 +02:00
|
|
|
input->nod_arg[0] = (dsql_nod*) newNode;
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
2012-03-25 03:08:55 +02:00
|
|
|
|
|
|
|
input->nod_line = newNode->line;
|
|
|
|
input->nod_column = newNode->column;
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
|
|
|
return input;
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_delete:
|
|
|
|
case nod_select:
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err));
|
2008-06-08 22:42:27 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
// access plan node types
|
|
|
|
|
|
|
|
case nod_plan_item:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
dsql_ctx* context = pass1_alias_list(dsqlScratch, input->nod_arg[0]);
|
|
|
|
RecordSourceNode* recSource = NULL;
|
2003-11-07 15:10:16 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_relation)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
RelationSourceNode* relNode = FB_NEW(*tdbb->getDefaultPool()) RelationSourceNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
relNode->dsqlContext = context;
|
|
|
|
recSource = relNode;
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
else if (context->ctx_procedure)
|
2009-12-23 01:57:08 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
// ASF: Note that usage of procedure name in a PLAN clause causes errors when
|
|
|
|
// parsing the BLR.
|
|
|
|
ProcedureSourceNode* procNode = FB_NEW(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
procNode->dsqlContext = context;
|
|
|
|
recSource = procNode;
|
2009-12-23 01:57:08 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
fb_assert(recSource);
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
node = MAKE_node(input->nod_type, 2);
|
|
|
|
node->nod_arg[0] = sub1 = MAKE_node(nod_class_exprnode, 1);
|
2011-01-30 01:25:46 +01:00
|
|
|
sub1->nod_arg[0] = reinterpret_cast<dsql_nod*>(recSource);
|
2011-01-09 22:58:56 +01:00
|
|
|
node->nod_arg[1] = PASS1_node(dsqlScratch, input->nod_arg[1]);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_index:
|
|
|
|
node = MAKE_node(input->nod_type, 1);
|
|
|
|
node->nod_arg[0] = input->nod_arg[0];
|
|
|
|
return node;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_index_order:
|
|
|
|
node = MAKE_node(input->nod_type, 2);
|
|
|
|
node->nod_arg[0] = input->nod_arg[0];
|
|
|
|
node->nod_arg[1] = input->nod_arg[1];
|
|
|
|
return node;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Node is simply to be rebuilt -- just recurse merrily
|
2003-04-17 22:58:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
const dsql_nod** ptr2 = const_cast<const dsql_nod**>(node->nod_arg);
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
*ptr2++ = PASS1_node(dsqlScratch, *ptr);
|
|
|
|
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2003-08-16 02:36:54 +02:00
|
|
|
return node;
|
2002-06-29 08:56:51 +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.
|
|
|
|
dsql_nod* PASS1_rse(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, bool updateLock)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
fb_assert(ExprNode::as<SelectExprNode>(input));
|
2005-07-22 04:08:14 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
2012-04-07 05:03:28 +02:00
|
|
|
dsql_nod* node = MAKE_class_node(pass1_rse(dsqlScratch, input, NULL, NULL, updateLock, 0));
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->scopeLevel--;
|
2005-07-22 04:08:14 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-14 18:25:48 +01: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,
|
2012-03-25 03:08:55 +02:00
|
|
|
const MetaName& name, const DsqlContextStack& ambiguous_contexts)
|
2006-09-14 04:05:32 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
// If there are no relations or only 1 there's no ambiguity, thus return.
|
2010-11-14 18:25:48 +01:00
|
|
|
if (ambiguous_contexts.getCount() < 2)
|
|
|
|
return;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
TEXT buffer[1024];
|
|
|
|
USHORT loop = 0;
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
buffer[0] = 0;
|
|
|
|
TEXT* b = buffer;
|
2010-11-14 18:25:48 +01:00
|
|
|
TEXT* p = NULL;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
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.
|
2011-01-30 01:25:46 +01:00
|
|
|
if (++loop > 2)
|
2010-01-28 16:18:11 +01:00
|
|
|
strcat(buffer, "and ");
|
|
|
|
// Process relation when present.
|
|
|
|
if (relation)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
if (!(relation->rel_flags & REL_view))
|
2010-01-28 16:18:11 +01:00
|
|
|
strcat(buffer, "table ");
|
2011-01-30 01:25:46 +01:00
|
|
|
else
|
2010-01-28 16:18:11 +01:00
|
|
|
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 ");
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_alias.hasData())
|
|
|
|
strcat(b, context->ctx_alias.c_str());
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
strcat(buffer, " ");
|
2011-01-30 01:25:46 +01:00
|
|
|
if (!p)
|
2010-01-28 16:18:11 +01:00
|
|
|
p = b + strlen(b);
|
|
|
|
}
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (p)
|
2010-01-28 16:18:11 +01:00
|
|
|
*--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) <<
|
2012-03-25 03:08:55 +02:00
|
|
|
Arg::Gds(isc_random) << name);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(204) <<
|
|
|
|
Arg::Warning(isc_dsql_ambiguous_field_name) << Arg::Str(buffer) <<
|
|
|
|
Arg::Str(++p) <<
|
2012-03-25 03:08:55 +02:00
|
|
|
Arg::Warning(isc_random) << name);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
|
|
|
PASS1_check_unique_fields_names
|
|
|
|
|
|
|
|
check fields (params, variables, cursors etc) names against
|
|
|
|
sorted array
|
|
|
|
if success, add them into array
|
|
|
|
**/
|
2011-02-22 01:51:56 +01:00
|
|
|
void PASS1_check_unique_fields_names(StrArray& names, const CompoundStmtNode* fields)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
if (!fields)
|
|
|
|
return;
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
const NestConst<StmtNode>* ptr = fields->statements.begin();
|
|
|
|
const NestConst<StmtNode>* const end = fields->statements.end();
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
for (; ptr != end; ++ptr)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-03-06 03:48:34 +01:00
|
|
|
const char* name = NULL;
|
2011-02-26 22:51:45 +01:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
const DeclareVariableNode* varNode;
|
|
|
|
const DeclareCursorNode* cursorNode;
|
2011-02-26 22:51:45 +01:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
if ((varNode = (*ptr)->as<DeclareVariableNode>()))
|
|
|
|
name = varNode->dsqlDef->name.c_str();
|
|
|
|
else if ((cursorNode = (*ptr)->as<DeclareCursorNode>()))
|
|
|
|
name = cursorNode->dsqlName.c_str();
|
2011-10-16 22:36:07 +02:00
|
|
|
else if ((*ptr)->as<DeclareSubProcNode>() || (*ptr)->as<DeclareSubFuncNode>())
|
2011-10-03 00:11:41 +02:00
|
|
|
continue;
|
2008-09-07 23:50:00 +02:00
|
|
|
|
2011-03-06 03:48:34 +01:00
|
|
|
fb_assert(name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-23 04:02:53 +01:00
|
|
|
|
2010-08-27 04:18:00 +02:00
|
|
|
// Compose two booleans.
|
2010-09-17 05:15:32 +02:00
|
|
|
dsql_nod* PASS1_compose(dsql_nod* expr1, dsql_nod* expr2, UCHAR blrOp)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(expr1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(expr2, dsql_type_nod);
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
fb_assert(blrOp == blr_and || blrOp == blr_or);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!expr1)
|
|
|
|
return expr2;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!expr2)
|
|
|
|
return expr1;
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
BinaryBoolNode* binNode = FB_NEW(*tdbb->getDefaultPool()) BinaryBoolNode(
|
|
|
|
*tdbb->getDefaultPool(), blrOp, expr1, expr2);
|
|
|
|
|
|
|
|
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(binNode);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2003-04-17 22:58:36 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Report a field parsing recognition error.
|
|
|
|
void PASS1_field_unknown(const TEXT* qualifier_name, const TEXT* field_name,
|
2012-03-25 03:08:55 +02:00
|
|
|
const ExprNode* flawed_node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
TEXT field_buffer[MAX_SQL_IDENTIFIER_SIZE * 2];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01: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;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
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) <<
|
2012-03-25 03:08:55 +02:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(flawed_node->line) <<
|
|
|
|
Arg::Num(flawed_node->column));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-206) <<
|
|
|
|
Arg::Gds(isc_dsql_field_err) <<
|
2012-03-25 03:08:55 +02:00
|
|
|
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
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (field_name)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01: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
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +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
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2009-04-12 09:53:44 +02:00
|
|
|
|
2009-04-13 03:46:26 +02:00
|
|
|
|
2011-01-30 02:17:41 +01:00
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_node_match
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Compare two nodes for equality of value.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
[2002-08-04]--- Arno Brinkman
|
|
|
|
If ignore_map_cast is true and the node1 is
|
2010-02-13 21:29:29 +01:00
|
|
|
type nod_cast or nod_map then PASS1_node_match is
|
2010-01-28 16:18:11 +01:00
|
|
|
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)
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
@param node1
|
|
|
|
@param node2
|
|
|
|
@param ignore_map_cast
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2010-02-13 21:29:29 +01:00
|
|
|
bool PASS1_node_match(const dsql_nod* node1, const dsql_nod* node2, bool ignore_map_cast)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(node1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(node2, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
if (!node1 && !node2)
|
2010-01-28 16:18:11 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
if (!node1 || !node2)
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
const CastNode* castNode1 = ExprNode::as<CastNode>(node1);
|
|
|
|
|
|
|
|
if (ignore_map_cast && castNode1)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-02 00:57:31 +01:00
|
|
|
const CastNode* castNode2 = ExprNode::as<CastNode>(node2);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// If node2 is also cast and same type continue with both sources.
|
2010-11-02 00:57:31 +01:00
|
|
|
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)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-02 00:57:31 +01:00
|
|
|
return PASS1_node_match(castNode1->dsqlSource, castNode2->dsqlSource, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2009-04-19 12:06:07 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
return PASS1_node_match(castNode1->dsqlSource, node2, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
const DsqlMapNode* mapNode1 = ExprNode::as<DsqlMapNode>(node1);
|
|
|
|
|
|
|
|
if (ignore_map_cast && mapNode1)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DsqlMapNode* mapNode2 = ExprNode::as<DsqlMapNode>(node2);
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (mapNode2)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
if (mapNode1->context != mapNode2->context)
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
return PASS1_node_match(mapNode1->map->map_node, mapNode2->map->map_node, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
return PASS1_node_match(mapNode1->map->map_node, node2, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
const DsqlAliasNode* aliasNode1 = ExprNode::as<DsqlAliasNode>(node1);
|
|
|
|
const DsqlAliasNode* aliasNode2 = ExprNode::as<DsqlAliasNode>(node2);
|
|
|
|
|
2010-01-28 16:18:11 +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)
|
|
|
|
return PASS1_node_match(aliasNode1->value, aliasNode2->value, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if (aliasNode1)
|
|
|
|
return PASS1_node_match(aliasNode1->value, node2, ignore_map_cast);
|
|
|
|
|
|
|
|
if (aliasNode2)
|
|
|
|
return PASS1_node_match(node1, aliasNode2->value, ignore_map_cast);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Handle derived fields.
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
const DerivedFieldNode* derivedField1 = ExprNode::as<DerivedFieldNode>(node1);
|
|
|
|
const DerivedFieldNode* derivedField2 = ExprNode::as<DerivedFieldNode>(node2);
|
|
|
|
|
|
|
|
if (derivedField1 || derivedField2)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField1 && derivedField2)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2011-10-28 01:53:15 +02:00
|
|
|
if (derivedField1->context->ctx_context != derivedField2->context->ctx_context ||
|
2010-11-14 18:25:48 +01:00
|
|
|
derivedField1->name != derivedField2->name)
|
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2010-11-14 18:25:48 +01:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
return PASS1_node_match(derivedField1->dsqlValue, derivedField2->dsqlValue,
|
|
|
|
ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField1)
|
|
|
|
return PASS1_node_match(derivedField1->dsqlValue, node2, ignore_map_cast);
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField2)
|
|
|
|
return PASS1_node_match(node1, derivedField2->dsqlValue, ignore_map_cast);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (node1->nod_type != node2->nod_type || node1->nod_count != node2->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// This is to get rid of assertion failures when trying
|
2010-02-13 21:29:29 +01:00
|
|
|
// to PASS1_node_match nod_aggregate's children. This was happening because not
|
2010-01-28 16:18:11 +01:00
|
|
|
// all of the children are of type "dsql_nod". Pointer to the first child
|
|
|
|
// (argument) is actually a pointer to context structure.
|
|
|
|
// To compare two nodes of type nod_aggregate we need first to see if they
|
|
|
|
// both refer to same context structure. If they do not they are different
|
|
|
|
// nodes, if they point to the same context they are the same (because
|
|
|
|
// nod_aggregate is created for an rse that have aggregate expression,
|
|
|
|
// group by or having clause and each rse has its own context). But just in
|
|
|
|
// case we compare two other subtrees.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (node1->nod_type)
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
case nod_class_exprnode:
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
|
|
|
ExprNode* expr1Node = reinterpret_cast<ExprNode*>(node1->nod_arg[0]);
|
|
|
|
ExprNode* expr2Node = reinterpret_cast<ExprNode*>(node2->nod_arg[0]);
|
|
|
|
|
|
|
|
if (expr1Node->type != expr2Node->type)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return expr1Node->dsqlMatch(expr2Node, ignore_map_cast);
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
} // switch
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_nod* const* ptr1 = node1->nod_arg;
|
|
|
|
const dsql_nod* const* ptr2 = node2->nod_arg;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr1 + node1->nod_count; ptr1 < end; ptr1++, ptr2++)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (!PASS1_node_match(*ptr1, *ptr2, ignore_map_cast))
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
/**
|
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
PASS1_cursor_name
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
@brief Find a cursor.
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2011-03-04 02:47:49 +01:00
|
|
|
@param name
|
|
|
|
@param mask
|
|
|
|
@param existence_flag
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
**/
|
2011-03-04 02:47:49 +01:00
|
|
|
DeclareCursorNode* PASS1_cursor_name(DsqlCompilerScratch* dsqlScratch, const MetaName& name,
|
|
|
|
USHORT mask, bool existence_flag)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2011-03-04 02:47:49 +01:00
|
|
|
DeclareCursorNode* cursor = NULL;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
if (name.isEmpty())
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
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));
|
|
|
|
}
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
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))
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
|
|
|
cursor = NULL;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!cursor && existence_flag)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
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);
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else if (cursor && !existence_flag)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
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);
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Extract relation and procedure context and expand derived child contexts.
|
|
|
|
static void pass1_expand_contexts(DsqlContextStack& contexts, dsql_ctx* context)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
2011-02-21 09:04:08 +01:00
|
|
|
if (context->ctx_relation || context->ctx_procedure || context->ctx_map)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
if (context->ctx_parent)
|
|
|
|
context = context->ctx_parent;
|
2007-04-29 21:04:26 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
contexts.push(context);
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
for (DsqlContextStack::iterator i(context->ctx_childs_derived_table); i.hasData(); ++i)
|
|
|
|
pass1_expand_contexts(contexts, i.object());
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_derived_table
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Process derived table which is part of a from clause.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2012-04-07 05:03:28 +02:00
|
|
|
static dsql_nod* pass1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* input,
|
2011-01-30 01:25:46 +01:00
|
|
|
const char* cte_alias)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2010-11-14 18:25:48 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
const string& alias = input->alias;
|
|
|
|
dsql_nod* inputNod = MAKE_class_node(input);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// Create the context now, because we need to know it for the tables inside.
|
2012-04-07 05:03:28 +02:00
|
|
|
dsql_ctx* const context = PASS1_make_context(dsqlScratch, inputNod);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// Save some values to restore after rse process.
|
|
|
|
DsqlContextStack* const req_base = dsqlScratch->context;
|
|
|
|
dsql_str* const 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
|
|
|
{
|
2010-01-28 16:18:11 +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
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* baseContext = NULL;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
if (temp.hasData())
|
2010-01-28 16:18:11 +01:00
|
|
|
baseContext = temp.object();
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->context = &temp;
|
2012-04-07 05:03:28 +02:00
|
|
|
dsqlScratch->aliasRelationPrefix = pass1_alias_concat(aliasRelationPrefix, alias.nullStr());
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
dsql_nod* query = input->querySpec;
|
|
|
|
UnionSourceNode* unionQuery = ExprNode::as<UnionSourceNode>(query);
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* rse = NULL;
|
2012-04-07 05:03:28 +02:00
|
|
|
const bool isRecursive = unionQuery && unionQuery->recursive;
|
2010-01-28 16:18:11 +01:00
|
|
|
USHORT recursive_map_ctx = 0;
|
|
|
|
|
|
|
|
if (isRecursive)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +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-04-07 05:03:28 +02:00
|
|
|
unionQuery->dsqlClauses->nod_count = 1;
|
|
|
|
unionQuery->recursive = false;
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* baseUnionCtx = dsqlScratch->unionContext.hasData() ?
|
|
|
|
dsqlScratch->unionContext.object() : NULL;
|
2004-01-09 03:23:46 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// reserve extra context number for map's secondary context
|
|
|
|
recursive_map_ctx = dsqlScratch->contextNumber++;
|
2004-01-09 03:23:46 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->recursiveCtxId = dsqlScratch->contextNumber;
|
2012-04-07 05:03:28 +02:00
|
|
|
rse = MAKE_class_node(pass1_union(dsqlScratch, unionQuery, NULL, NULL, NULL, 0));
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->contextNumber = dsqlScratch->recursiveCtxId + 1;
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// recursive union always have exactly 2 members
|
2012-04-07 05:03:28 +02:00
|
|
|
unionQuery->dsqlClauses->nod_count = 2;
|
|
|
|
unionQuery->recursive = true;
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
while (dsqlScratch->unionContext.hasData() &&
|
|
|
|
dsqlScratch->unionContext.object() != baseUnionCtx)
|
|
|
|
{
|
|
|
|
dsqlScratch->unionContext.pop();
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
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;
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* queryNode = ExprNode::as<RseNode>(query);
|
|
|
|
if (queryNode)
|
|
|
|
foundSubSelect = SubSelectFinder::find(queryNode->dsqlSelectList);
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (foundSubSelect)
|
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
UnionSourceNode* unionExpr = FB_NEW(pool) UnionSourceNode(pool);
|
|
|
|
unionExpr->dsqlClauses = MAKE_node(nod_list, 1);
|
|
|
|
unionExpr->dsqlClauses->nod_arg[0] = inputNod;
|
|
|
|
unionExpr->dsqlAll = true;
|
|
|
|
rse = MAKE_class_node(pass1_union(dsqlScratch, unionExpr, NULL, NULL, NULL, 0));
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2010-08-07 04:10:08 +02:00
|
|
|
else
|
2012-04-07 05:03:28 +02:00
|
|
|
rse = PASS1_rse(dsqlScratch, inputNod, false);
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
USHORT minOuterJoin = MAX_USHORT;
|
2007-11-30 01:35:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01: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))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-08-07 04:43:29 +02:00
|
|
|
dsql_ctx* childCtx = temp.pop();
|
|
|
|
|
2011-02-21 09:04:08 +01:00
|
|
|
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
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Collect contexts that will be used for blr_derived_expr generation.
|
|
|
|
// We want all child contexts with minimum ctx_in_outer_join.
|
|
|
|
if (childCtx->ctx_in_outer_join <= minOuterJoin)
|
2007-11-29 18:53:38 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DsqlContextStack contexts;
|
|
|
|
pass1_expand_contexts(contexts, childCtx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (DsqlContextStack::iterator i(contexts); i.hasData(); ++i)
|
2007-11-29 18:53:38 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
if (i.object()->ctx_in_outer_join < minOuterJoin)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
minOuterJoin = i.object()->ctx_in_outer_join;
|
|
|
|
context->ctx_main_derived_contexts.clear();
|
2004-01-09 03:23:46 +01:00
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_main_derived_contexts.push(i.object());
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2008-06-03 08:19:21 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
while (temp.hasData())
|
|
|
|
temp.pop();
|
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
context->ctx_rse = rse;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// CVC: prepare a truncated alias for the derived table here
|
|
|
|
// because we need it several times.
|
|
|
|
TEXT aliasbuffer[100] = "";
|
|
|
|
const TEXT* aliasname = aliasbuffer;
|
2012-04-07 05:03:28 +02:00
|
|
|
if (alias.hasData())
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
int length = alias.length();
|
2010-01-28 16:18:11 +01:00
|
|
|
if (length > 99)
|
|
|
|
{
|
|
|
|
length = 99;
|
2012-04-07 05:03:28 +02:00
|
|
|
memcpy(aliasbuffer, alias.c_str(), length);
|
2010-01-28 16:18:11 +01:00
|
|
|
aliasbuffer[length] = 0;
|
|
|
|
}
|
|
|
|
else
|
2012-04-07 05:03:28 +02:00
|
|
|
aliasname = alias.c_str();
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
aliasname = "<unnamed>";
|
2007-05-06 18:45:49 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* selectList = ExprNode::as<RseNode>(rse)->dsqlSelectList;
|
|
|
|
|
|
|
|
// If an alias-list is specified, process it.
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
const bool ignoreColumnChecks =
|
|
|
|
(input->dsqlFlags & RecordSourceNode::DFLAG_DT_IGNORE_COLUMN_CHECK);
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (input->columns && input->columns->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
dsql_nod* list = input->columns;
|
2007-05-06 18:45:49 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Do both lists have the same number of items?
|
2011-01-09 22:58:56 +01:00
|
|
|
if (list->nod_count != selectList->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
// Column list by derived table %s [alias-name] has %s [more/fewer] columns
|
|
|
|
// than the number of items.
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
int errcode = isc_dsql_derived_table_less_columns;
|
2011-01-09 22:58:56 +01:00
|
|
|
if (list->nod_count > selectList->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
errcode = isc_dsql_derived_table_more_columns;
|
2007-05-06 18:45:49 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(errcode) << Arg::Str(aliasname));
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Generate derived fields and assign alias-name to them.
|
2010-01-28 16:18:11 +01:00
|
|
|
for (int count = 0; count < list->nod_count; count++)
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* select_item = selectList->nod_arg[count];
|
2010-11-01 14:45:52 +01:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nod_desc, select_item);
|
2004-01-09 03:23:46 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Make new derived field node.
|
|
|
|
|
|
|
|
DerivedFieldNode* derivedField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
((dsql_str*) list->nod_arg[count])->str_data, dsqlScratch->scopeLevel, select_item);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* nod = selectList->nod_arg[count] = MAKE_node(nod_class_exprnode, 1);
|
2010-11-14 18:25:48 +01:00
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(derivedField);
|
|
|
|
nod->nod_desc = select_item->nod_desc;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// For those select-items where no alias is specified try
|
|
|
|
// to generate one from the field_name.
|
2011-01-09 22:58:56 +01:00
|
|
|
for (int count = 0; count < selectList->nod_count; count++)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
dsql_nod* select_item = pass1_make_derived_field(dsqlScratch, tdbb,
|
2011-01-09 22:58:56 +01:00
|
|
|
selectList->nod_arg[count]);
|
2007-11-29 18:53:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Auto-create dummy column name for pass1_any()
|
2010-11-14 18:25:48 +01:00
|
|
|
if (ignoreColumnChecks && !ExprNode::is<DerivedFieldNode>(select_item))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nod_desc, select_item);
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Construct dummy fieldname
|
|
|
|
char fieldname[25];
|
2010-11-14 18:25:48 +01:00
|
|
|
sprintf(fieldname, "f%d", count);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Make new derived field node.
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
fieldname, dsqlScratch->scopeLevel, select_item);
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(derivedField);
|
|
|
|
nod->nod_desc = select_item->nod_desc;
|
|
|
|
|
|
|
|
select_item = nod;
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
selectList->nod_arg[count] = select_item;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2007-11-29 18:53:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
int count;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Check if all root select-items have a derived field else show a message.
|
2011-01-09 22:58:56 +01:00
|
|
|
for (count = 0; count < selectList->nod_count; count++)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* select_item = selectList->nod_arg[count];
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
2007-11-29 18:53:38 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
derivedField->context = context;
|
2010-01-28 16:18:11 +01:00
|
|
|
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));
|
2007-11-29 18:53:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Check for ambiguous column names inside this derived table.
|
2011-01-09 22:58:56 +01:00
|
|
|
for (count = 0; count < selectList->nod_count; ++count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DerivedFieldNode* selectItem1 =
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<DerivedFieldNode>(selectList->nod_arg[count]);
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
for (int count2 = (count + 1); count2 < selectList->nod_count; ++count2)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DerivedFieldNode* selectItem2 =
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<DerivedFieldNode>(selectList->nod_arg[count2]);
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
if (selectItem1->name == selectItem2->name)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
// 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) <<
|
2010-11-14 18:25:48 +01:00
|
|
|
Arg::Gds(isc_dsql_derived_field_dup_name) << selectItem1->name <<
|
|
|
|
aliasname);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// 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)
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->recursiveCtx = context;
|
|
|
|
dsqlScratch->context = &temp;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
dsqlScratch->resetCTEAlias(alias.c_str());
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
rse = PASS1_rse(dsqlScratch, inputNod, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01: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
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
temp.clear();
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<RseNode>(rse)->dsqlSelectList = ExprNode::as<RseNode>(context->ctx_rse)->dsqlSelectList;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_rse = rse;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
if (cte_alias)
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_alias = cte_alias;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->context = req_base;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Mark union's map context as recursive and assign secondary context number to it.
|
|
|
|
dsql_nod* items = ExprNode::as<RseNode>(rse)->dsqlSelectList;
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* map_item = items->nod_arg[0];
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(map_item)))
|
|
|
|
map_item = derivedField->dsqlValue;
|
|
|
|
|
|
|
|
dsql_ctx* map_context = ExprNode::as<DsqlMapNode>(map_item)->context;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
map_context->ctx_flags |= CTX_recursive;
|
|
|
|
map_context->ctx_recursive = recursive_map_ctx;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
delete dsqlScratch->aliasRelationPrefix;
|
|
|
|
// Restore our original values.
|
|
|
|
dsqlScratch->context = req_base;
|
|
|
|
dsqlScratch->aliasRelationPrefix = aliasRelationPrefix;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<RseNode>(rse)->dsqlContext = context;
|
|
|
|
|
|
|
|
return rse;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_expand_select_list
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Expand asterisk nodes into fields.
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param list
|
|
|
|
@param streams
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_expand_select_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* list,
|
|
|
|
dsql_nod* streams)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
list = streams;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DsqlNodStack stack;
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
2002-10-01 02:34:29 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
|
|
|
PASS1_expand_select_node(dsqlScratch, *ptr, stack, true);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
return MAKE_list(stack);
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Expand a select item node.
|
|
|
|
void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* node, DsqlNodStack& stack,
|
2010-01-28 16:18:11 +01:00
|
|
|
bool hide_using)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* rseNode;
|
2011-01-30 01:25:46 +01:00
|
|
|
ProcedureSourceNode* procNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
RelationSourceNode* relNode;
|
2012-03-25 03:08:55 +02:00
|
|
|
FieldNode* fieldNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if ((rseNode = ExprNode::as<RseNode>(node)))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* sub_items = rseNode->dsqlSelectList;
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if (sub_items) // AB: Derived table support
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod** ptr = sub_items->nod_arg;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + sub_items->nod_count; ptr != end; ++ptr)
|
|
|
|
{
|
|
|
|
// Create a new alias else mappings would be mangled.
|
|
|
|
dsql_nod* select_item = *ptr;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// select-item should always be a derived field!
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
|
|
|
if (!(derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
{
|
|
|
|
// 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);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if (!hide_using || context->getImplicitJoinField(derivedField->name, select_item))
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // joins
|
|
|
|
{
|
|
|
|
dsql_nod** ptr = rseNode->dsqlStreams->nod_arg;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + rseNode->dsqlStreams->nod_count;
|
|
|
|
ptr != end;
|
|
|
|
++ptr)
|
|
|
|
{
|
|
|
|
PASS1_expand_select_node(dsqlScratch, *ptr, stack, true);
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
else if ((procNode = ExprNode::as<ProcedureSourceNode>(node)))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
dsql_ctx* context = procNode->dsqlContext;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_procedure)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
for (dsql_fld* field = context->ctx_procedure->prc_outputs; field; field = field->fld_next)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* select_item = NULL;
|
|
|
|
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
|
|
|
|
{
|
|
|
|
if (!select_item)
|
|
|
|
select_item = MAKE_field(context, field, 0);
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
}
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(node)))
|
|
|
|
{
|
|
|
|
dsql_ctx* context = relNode->dsqlContext;
|
|
|
|
|
|
|
|
if (context->ctx_relation)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
for (dsql_fld* field = context->ctx_relation->rel_fields; field; field = field->fld_next)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2010-01-23 04:02:53 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* select_item = NULL;
|
|
|
|
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
|
|
|
|
{
|
|
|
|
if (!select_item)
|
|
|
|
select_item = MAKE_field(context, field, 0);
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-03-25 03:08:55 +02:00
|
|
|
else if ((fieldNode = ExprNode::as<FieldNode>(node)))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-03-25 03:08:55 +02:00
|
|
|
dsql_nod* list = NULL;
|
|
|
|
dsql_nod* value = fieldNode->internalDsqlPass(dsqlScratch, &list);
|
2010-10-22 17:00:22 +02:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if (list)
|
|
|
|
PASS1_expand_select_node(dsqlScratch, list, stack, false);
|
2010-08-02 04:22:26 +02:00
|
|
|
else
|
2012-03-25 03:08:55 +02:00
|
|
|
stack.push(value);
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2012-03-25 03:08:55 +02:00
|
|
|
else
|
|
|
|
stack.push(node);
|
2005-01-06 14:14:38 +01: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_group_by_list
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
@brief Process GROUP BY list, which may contain
|
2005-02-14 06:54:45 +01:00
|
|
|
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
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-08-15 02:02:18 +02:00
|
|
|
@param input
|
|
|
|
@param select_list
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_group_by_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* selectList)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2003-08-15 02:02:18 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2005-02-10 22:14:52 +01:00
|
|
|
DEV_BLKCHK(selectList, dsql_type_nod);
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2009-06-06 04:21:23 +02:00
|
|
|
if (input->nod_count > MAX_SORT_ITEMS) // sort, group and distinct have the same limit for now
|
2006-10-07 11:40:59 +02:00
|
|
|
{
|
2008-07-01 03:12:02 +02:00
|
|
|
// cannot group on more than 255 items
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_max_group_items));
|
2006-10-07 11:40:59 +02:00
|
|
|
}
|
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack stack;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub = (*ptr);
|
2006-10-08 03:57:21 +02:00
|
|
|
dsql_nod* frnode = NULL;
|
2012-03-25 03:08:55 +02:00
|
|
|
FieldNode* field;
|
2010-10-24 02:26:00 +02:00
|
|
|
LiteralNode* literal;
|
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if ((field = ExprNode::as<FieldNode>(sub)))
|
2007-10-17 16:35:31 +02:00
|
|
|
{
|
2005-02-10 22:14:52 +01:00
|
|
|
// check for alias or field node
|
2012-03-25 03:08:55 +02:00
|
|
|
if (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))
|
2006-10-07 11:40:59 +02:00
|
|
|
{
|
2010-10-24 02:26:00 +02:00
|
|
|
const ULONG position = literal->getSlong();
|
|
|
|
|
2010-10-27 02:32:58 +02:00
|
|
|
if (position < 1 || !selectList || position > (ULONG) selectList->nod_count)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-07-01 03:12:02 +02:00
|
|
|
// Invalid column position used in the GROUP BY clause
|
2008-08-15 13:21:47 +02:00
|
|
|
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
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
frnode = PASS1_node_psql(dsqlScratch, selectList->nod_arg[position - 1], false);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
frnode = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2006-10-07 11:40:59 +02:00
|
|
|
stack.push(frnode);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
// Finally make the complete list.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_list(stack);
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-02-22 17:00:49 +01:00
|
|
|
// Process loop interruption.
|
2011-01-22 22:32:29 +01:00
|
|
|
dsql_nod* PASS1_label(DsqlCompilerScratch* dsqlScratch, bool breakContinue, dsql_nod* label)
|
2003-08-26 09:13:33 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2010-02-22 17:00:49 +01:00
|
|
|
DEV_BLKCHK(label, dsql_type_nod);
|
|
|
|
|
2003-08-26 09:13:33 +02:00
|
|
|
// look for a label, if specified
|
|
|
|
|
2008-07-03 09:12:36 +02:00
|
|
|
const dsql_str* string = NULL;
|
2003-11-18 08:58:35 +01:00
|
|
|
USHORT position = 0;
|
2003-08-26 09:13:33 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (label)
|
|
|
|
{
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(label->nod_type == nod_label);
|
2003-11-10 10:16:38 +01:00
|
|
|
string = (dsql_str*) label->nod_arg[e_label_name];
|
2008-07-03 09:12:36 +02:00
|
|
|
const TEXT* label_string = string->str_data;
|
2009-12-20 22:01:10 +01:00
|
|
|
int index = dsqlScratch->loopLevel;
|
|
|
|
for (DsqlStrStack::iterator stack(dsqlScratch->labels); stack.hasData(); ++stack)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
const dsql_str* obj = stack.object();
|
2009-11-19 10:37:10 +01:00
|
|
|
if (obj)
|
|
|
|
{
|
2008-07-03 09:12:36 +02:00
|
|
|
const TEXT* obj_string = obj->str_data;
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!strcmp(label_string, obj_string))
|
|
|
|
{
|
2003-11-02 13:28:30 +01:00
|
|
|
position = index;
|
|
|
|
break;
|
|
|
|
}
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
USHORT number = 0;
|
2011-01-22 22:32:29 +01:00
|
|
|
|
|
|
|
if (breakContinue)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (position > 0)
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// break/continue the specified loop
|
2003-08-26 09:13:33 +02:00
|
|
|
number = position;
|
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else if (label)
|
|
|
|
{
|
2003-08-26 09:13:33 +02:00
|
|
|
// ERROR: Label %s is not found in the current scope
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
2008-12-05 02:20:14 +01:00
|
|
|
Arg::Gds(isc_dsql_invalid_label) << Arg::Str(string->str_data) <<
|
2008-08-15 13:21:47 +02:00
|
|
|
Arg::Str("is not found"));
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// break/continue the current loop
|
2009-12-20 22:01:10 +01:00
|
|
|
number = dsqlScratch->loopLevel;
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
2009-04-19 12:06:07 +02:00
|
|
|
else
|
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (position > 0)
|
|
|
|
{
|
2003-08-26 09:13:33 +02:00
|
|
|
// ERROR: Label %s already exists in the current scope
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
2008-12-05 02:20:14 +01:00
|
|
|
Arg::Gds(isc_dsql_invalid_label) << Arg::Str(string->str_data) <<
|
2008-08-15 13:21:47 +02:00
|
|
|
Arg::Str("already exists"));
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else
|
|
|
|
{
|
2003-08-26 09:13:33 +02:00
|
|
|
// store label name, if specified
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->labels.push(string);
|
|
|
|
number = dsqlScratch->loopLevel;
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
fb_assert(number > 0 && number <= dsqlScratch->loopLevel);
|
2003-08-26 09:13:33 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!label)
|
|
|
|
{
|
2003-09-28 13:12:03 +02:00
|
|
|
label = MAKE_node(nod_label, e_label_count);
|
|
|
|
// this label is unnamed, i.e. its nod_arg[e_label_name] is NULL
|
|
|
|
}
|
2010-02-22 17:00:49 +01:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
label->nod_arg[e_label_number] = (dsql_nod*) (IPTR) number;
|
2003-09-28 13:12:03 +02:00
|
|
|
|
|
|
|
return label;
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
// Process the limit clause (FIRST/SKIP/ROWS)
|
|
|
|
void PASS1_limit(DsqlCompilerScratch* dsqlScratch, dsql_nod* firstNode, dsql_nod* skipNode,
|
|
|
|
RseNode* rse)
|
2011-01-20 05:41:10 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(firstNode, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(skipNode, dsql_type_nod);
|
|
|
|
|
|
|
|
// Initialize this stack variable, and make it look like a node
|
|
|
|
AutoPtr<dsql_nod> descNode(FB_NEW_RPT(*getDefaultMemoryPool(), 0) dsql_nod);
|
|
|
|
|
|
|
|
if (dsqlScratch->clientDialect <= SQL_DIALECT_V5)
|
|
|
|
descNode->nod_desc.makeLong(0);
|
|
|
|
else
|
|
|
|
descNode->nod_desc.makeInt64(0);
|
|
|
|
|
|
|
|
rse->dsqlFirst = PASS1_node_psql(dsqlScratch, firstNode, false);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, rse->dsqlFirst, descNode, false);
|
|
|
|
|
|
|
|
rse->dsqlSkip = PASS1_node_psql(dsqlScratch, skipNode, false);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, rse->dsqlSkip, descNode, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// 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-03-25 03:08:55 +02:00
|
|
|
dsql_nod* PASS1_lookup_alias(DsqlCompilerScratch* dsqlScratch, const MetaName& name,
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* selectList, bool process)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
dsql_nod* returnNode = NULL;
|
2005-02-10 22:14:52 +01:00
|
|
|
dsql_nod** ptr = selectList->nod_arg;
|
|
|
|
const dsql_nod* const* const end = ptr + selectList->nod_count;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (; ptr < end; ptr++)
|
|
|
|
{
|
2005-02-10 22:14:52 +01:00
|
|
|
dsql_nod* matchingNode = NULL;
|
|
|
|
dsql_nod* node = *ptr;
|
2011-02-17 15:25:56 +01:00
|
|
|
DsqlAliasNode* aliasNode;
|
2010-12-19 22:42:32 +01:00
|
|
|
FieldNode* fieldNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if ((aliasNode = ExprNode::as<DsqlAliasNode>(node)))
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2012-03-25 03:08:55 +02:00
|
|
|
if (aliasNode->name == name)
|
2011-02-17 15:25:56 +01:00
|
|
|
matchingNode = node;
|
|
|
|
}
|
|
|
|
else if ((fieldNode = ExprNode::as<FieldNode>(node)))
|
|
|
|
{
|
2012-03-25 03:08:55 +02:00
|
|
|
if (fieldNode->dsqlField->fld_name == name.c_str())
|
2011-02-17 15:25:56 +01:00
|
|
|
matchingNode = node;
|
|
|
|
}
|
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(node)))
|
|
|
|
{
|
2012-03-25 03:08:55 +02:00
|
|
|
if (derivedField->name == name)
|
2011-02-17 15:25:56 +01:00
|
|
|
matchingNode = node;
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (matchingNode)
|
|
|
|
{
|
2007-04-29 21:04:26 +02:00
|
|
|
if (process)
|
2009-12-20 22:01:10 +01:00
|
|
|
matchingNode = PASS1_node_psql(dsqlScratch, matchingNode, false);
|
2007-04-29 21:04:26 +02:00
|
|
|
|
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.
|
2005-02-10 22:14:52 +01:00
|
|
|
TEXT buffer1[256];
|
|
|
|
buffer1[0] = 0;
|
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if (ExprNode::is<DsqlAliasNode>(returnNode))
|
|
|
|
strcat(buffer1, "an alias");
|
|
|
|
else if (ExprNode::is<FieldNode>(returnNode))
|
|
|
|
strcat(buffer1, "a field");
|
|
|
|
else if (ExprNode::is<DerivedFieldNode>(returnNode))
|
|
|
|
strcat(buffer1, "a derived field");
|
|
|
|
else
|
|
|
|
strcat(buffer1, "an item");
|
2005-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
TEXT buffer2[256];
|
|
|
|
buffer2[0] = 0;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if (ExprNode::is<DsqlAliasNode>(matchingNode))
|
|
|
|
strcat(buffer2, "an alias");
|
|
|
|
else if (ExprNode::is<FieldNode>(matchingNode))
|
|
|
|
strcat(buffer2, "a field");
|
|
|
|
else if (ExprNode::is<DerivedFieldNode>(matchingNode))
|
|
|
|
strcat(buffer2, "a derived field");
|
|
|
|
else
|
|
|
|
strcat(buffer2, "an item");
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
strcat(buffer2, " in the select list with name");
|
|
|
|
|
2008-08-15 13:21:47 +02:00
|
|
|
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) <<
|
2008-08-15 13:21:47 +02:00
|
|
|
Arg::Str(buffer2) <<
|
2012-03-25 03:08:55 +02:00
|
|
|
Arg::Gds(isc_random) << name);
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
returnNode = matchingNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-03 09:12:36 +02:00
|
|
|
return returnNode;
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
|
2003-08-24 04:36:46 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
pass1_make_derived_field
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-09-04 17:02:22 +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
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2008-02-28 14:48:16 +01:00
|
|
|
@param tdbb
|
2005-01-25 00:02:08 +01:00
|
|
|
@param select_item
|
2003-08-24 04:36:46 +02:00
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_make_derived_field(DsqlCompilerScratch* dsqlScratch, thread_db* tdbb,
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_nod* select_item)
|
2003-08-24 04:36:46 +02:00
|
|
|
{
|
2003-09-04 17:02:22 +02:00
|
|
|
DEV_BLKCHK(select_item, dsql_type_nod);
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2011-02-17 15:25:56 +01:00
|
|
|
DsqlAliasNode* aliasNode;
|
|
|
|
SubQueryNode* subQueryNode;
|
|
|
|
DsqlMapNode* mapNode;
|
|
|
|
FieldNode* fieldNode;
|
|
|
|
DerivedFieldNode* derivedField;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
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);
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(newField);
|
|
|
|
nod->nod_desc = aliasNode->value->nod_desc;
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
return nod;
|
|
|
|
}
|
|
|
|
else if ((subQueryNode = ExprNode::as<SubQueryNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Try to generate derived field from sub-select
|
|
|
|
dsql_nod* derived_field = pass1_make_derived_field(dsqlScratch, tdbb,
|
|
|
|
subQueryNode->dsqlValue1);
|
2010-12-19 22:42:32 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(derived_field)))
|
|
|
|
{
|
|
|
|
derivedField->dsqlValue = select_item;
|
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((mapNode = ExprNode::as<DsqlMapNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Aggregate's have map on top.
|
|
|
|
dsql_nod* derived_field = pass1_make_derived_field(dsqlScratch, tdbb, 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)))
|
|
|
|
{
|
|
|
|
derivedField->dsqlValue = select_item;
|
|
|
|
derivedField->scope = dsqlScratch->scopeLevel;
|
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
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);
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(newField);
|
|
|
|
nod->nod_desc = fieldNode->dsqlDesc;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
return nod;
|
|
|
|
}
|
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Create a derived field that points to a derived field.
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
derivedField->name, dsqlScratch->scopeLevel, select_item);
|
2010-02-26 02:03:54 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(newField);
|
|
|
|
nod->nod_desc = select_item->nod_desc;
|
2005-01-25 00:02:08 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
return nod;
|
2003-09-04 17:02:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return select_item;
|
2003-08-24 04:36:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
// Changes dsqlScratch->isPsql() value, calls PASS1_node and restore dsqlScratch->isPsql().
|
|
|
|
dsql_nod* PASS1_node_psql(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, bool psql)
|
2008-05-17 18:51:07 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
PsqlChanger changer(dsqlScratch, psql);
|
|
|
|
return PASS1_node(dsqlScratch, input);
|
2008-05-17 18:51:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Put recursive non list nodes on the stack.
|
|
|
|
void PASS1_put_args_on_stack( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, DsqlNodStack& stack)
|
2002-08-03 17:27:20 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2002-08-03 17:27:20 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (input->nod_type != nod_list)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
stack.push(PASS1_node(dsqlScratch, input));
|
2002-08-03 17:27:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2011-01-22 21:40:04 +01:00
|
|
|
PASS1_put_args_on_stack(dsqlScratch, *ptr, stack);
|
2002-08-03 17:27:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
// Prepare a relation name for processing. Allocate a new relation node.
|
|
|
|
dsql_nod* PASS1_relation(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
dsql_ctx* context = PASS1_make_context(dsqlScratch, input);
|
|
|
|
RecordSourceNode* node = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_relation)
|
|
|
|
{
|
|
|
|
RelationSourceNode* relNode = FB_NEW(*tdbb->getDefaultPool()) RelationSourceNode(
|
|
|
|
*tdbb->getDefaultPool(), context->ctx_relation->rel_name);
|
|
|
|
relNode->dsqlContext = context;
|
|
|
|
node = relNode;
|
|
|
|
}
|
|
|
|
else if (context->ctx_procedure)
|
|
|
|
{
|
|
|
|
ProcedureSourceNode* procNode = FB_NEW(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
|
|
|
*tdbb->getDefaultPool(), context->ctx_procedure->prc_name);
|
|
|
|
procNode->dsqlContext = context;
|
|
|
|
node = procNode;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
fb_assert(node);
|
|
|
|
|
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(node);
|
|
|
|
|
|
|
|
return nod;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_alias_list
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief The passed alias list fully specifies a relation.
|
|
|
|
The first alias represents a relation specified in
|
|
|
|
the from list at this scope levels. Subsequent
|
|
|
|
contexts, if there are any, represent base relations
|
2005-05-28 00:45:31 +02:00
|
|
|
in a view stack. They are used to fully specify a
|
|
|
|
base relation of a view. The aliases used in the
|
2003-02-15 04:01:51 +01:00
|
|
|
view stack are those used in the view definition.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param alias_list
|
|
|
|
|
|
|
|
**/
|
2011-01-09 22:58:56 +01:00
|
|
|
static dsql_ctx* pass1_alias_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* alias_list)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(alias_list, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** arg = alias_list->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* const* const end = arg + alias_list->nod_count;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
// Loop through every alias and find the context for that alias.
|
2005-08-04 06:03:01 +02:00
|
|
|
// All aliases should have a corresponding context.
|
2005-07-26 13:54:37 +02:00
|
|
|
int aliasCount = alias_list->nod_count;
|
2009-12-20 22:01:10 +01:00
|
|
|
USHORT savedScopeLevel = dsqlScratch->scopeLevel;
|
2005-07-26 13:54:37 +02:00
|
|
|
dsql_ctx* context = NULL;
|
|
|
|
while (aliasCount > 0)
|
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
if (context)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
if (context->ctx_rse && !context->ctx_relation && !context->ctx_procedure)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
|
|
|
// Derived table
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
|
|
|
context = pass1_alias(dsqlScratch, context->ctx_childs_derived_table, (dsql_str*) *arg);
|
2005-07-26 13:54:37 +02:00
|
|
|
}
|
2010-07-02 12:43:33 +02:00
|
|
|
else if (context->ctx_relation)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
|
|
|
// This must be a VIEW
|
|
|
|
dsql_nod** startArg = arg;
|
|
|
|
dsql_rel* relation = context->ctx_relation;
|
|
|
|
// find the base table using the specified alias list, skipping the first one
|
|
|
|
// since we already matched it to the context.
|
|
|
|
for (; arg < end; arg++, aliasCount--)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
if (!(relation = pass1_base_table(dsqlScratch, relation, (dsql_str*) *arg)))
|
2005-07-26 13:54:37 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
// Found base relation
|
|
|
|
if ((aliasCount == 0) && relation)
|
|
|
|
{
|
|
|
|
// AB: Pretty ugly huh?
|
|
|
|
// make up a dummy context to hold the resultant relation.
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2009-01-07 10:30:57 +01:00
|
|
|
dsql_ctx* new_context =
|
|
|
|
FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
|
2005-07-26 13:54:37 +02:00
|
|
|
new_context->ctx_context = context->ctx_context;
|
|
|
|
new_context->ctx_relation = relation;
|
|
|
|
|
|
|
|
// concatenate all the contexts to form the alias name;
|
|
|
|
// calculate the length leaving room for spaces and a null
|
|
|
|
USHORT alias_length = alias_list->nod_count;
|
|
|
|
dsql_nod** aliasArg = startArg;
|
2008-12-05 02:20:14 +01:00
|
|
|
for (; aliasArg < end; aliasArg++)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(*aliasArg, dsql_type_str);
|
|
|
|
alias_length += static_cast<USHORT>(((dsql_str*) *aliasArg)->str_length);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
dsql_str* alias = FB_NEW_RPT(*tdbb->getDefaultPool(), alias_length) dsql_str;
|
2005-07-26 13:54:37 +02:00
|
|
|
alias->str_length = alias_length;
|
2009-01-07 10:30:57 +01:00
|
|
|
new_context->ctx_alias = alias->str_data;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
TEXT* p = alias->str_data;
|
2008-12-05 02:20:14 +01:00
|
|
|
for (aliasArg = startArg; aliasArg < end; aliasArg++)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const TEXT* q = ((dsql_str*) *aliasArg)->str_data; *q;)
|
2005-07-26 13:54:37 +02:00
|
|
|
*p++ = *q++;
|
|
|
|
*p++ = ' ';
|
|
|
|
}
|
|
|
|
p[-1] = 0;
|
|
|
|
|
|
|
|
context = new_context;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2008-12-05 02:20:14 +01:00
|
|
|
else
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
context = NULL;
|
|
|
|
}
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2010-07-02 12:43:33 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
context = NULL;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
else
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
context = pass1_alias(dsqlScratch, *dsqlScratch->context, (dsql_str*) *arg);
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
if (!context) {
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
|
|
|
|
arg++;
|
|
|
|
aliasCount--;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel = savedScopeLevel;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!context)
|
|
|
|
{
|
2003-08-20 01:34:23 +02:00
|
|
|
// there is no alias or table named %s at this scope level.
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_no_relation_alias) << Arg::Str(((dsql_str*) *arg)->str_data));
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
return context;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_alias
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief The passed relation or alias represents
|
2003-02-15 04:01:51 +01:00
|
|
|
a context which was previously specified
|
2005-05-28 00:45:31 +02:00
|
|
|
in the from list. Find and return the
|
2003-02-15 04:01:51 +01:00
|
|
|
proper context.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param alias
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_ctx* pass1_alias(DsqlCompilerScratch* dsqlScratch, DsqlContextStack& stack, dsql_str* alias)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_ctx* relation_context = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(alias, dsql_type_str);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
// CVC: Getting rid of trailing spaces.
|
|
|
|
if (alias && alias->str_data) {
|
2009-01-07 10:30:57 +01:00
|
|
|
fb_utils::exact_name(alias->str_data);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
// look through all contexts at this scope level
|
|
|
|
// to find one that has a relation name or alias
|
2005-05-28 00:45:31 +02:00
|
|
|
// name which matches the identifier passed.
|
2009-01-07 10:30:57 +01:00
|
|
|
for (DsqlContextStack::iterator itr(stack); itr.hasData(); ++itr)
|
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
dsql_ctx* context = itr.object();
|
2009-12-20 22:01:10 +01:00
|
|
|
if (context->ctx_scope_level != dsqlScratch->scopeLevel) {
|
2001-05-23 15:26:42 +02:00
|
|
|
continue;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
// check for matching alias.
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_internal_alias.hasData())
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_internal_alias == alias->str_data)
|
2005-01-21 15:09:02 +01:00
|
|
|
return context;
|
2011-04-02 06:51:27 +02:00
|
|
|
|
2005-01-21 15:09:02 +01:00
|
|
|
continue;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2008-06-03 08:19:21 +02:00
|
|
|
|
|
|
|
// If an unnamed derived table and empty alias
|
2009-01-07 10:30:57 +01:00
|
|
|
if (context->ctx_rse && !context->ctx_relation && !context->ctx_procedure &&
|
2011-01-30 01:25:46 +01:00
|
|
|
alias->str_length == 0)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2008-06-03 08:19:21 +02:00
|
|
|
relation_context = context;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
// check for matching relation name; aliases take priority so
|
|
|
|
// save the context in case there is an alias of the same name;
|
|
|
|
// also to check that there is no self-join in the query.
|
2009-01-07 10:30:57 +01:00
|
|
|
if (context->ctx_relation && context->ctx_relation->rel_name == alias->str_data)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (relation_context)
|
|
|
|
{
|
2009-04-19 12:06:07 +02:00
|
|
|
// the table %s is referenced twice; use aliases to differentiate
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_self_join) << Arg::Str(alias->str_data));
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
relation_context = context;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return relation_context;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
pass1_alias_concat
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
@brief Concatenate 2 input strings together for
|
2005-05-28 00:45:31 +02:00
|
|
|
a new alias string.
|
2003-08-20 01:34:23 +02:00
|
|
|
Note: Both input params can be empty.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
|
|
|
|
@param input1
|
|
|
|
@param input2
|
|
|
|
|
|
|
|
**/
|
2011-01-30 01:25:46 +01:00
|
|
|
static dsql_str* pass1_alias_concat(const dsql_str* input1, const char* input2)
|
2003-08-20 01:34:23 +02:00
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2003-08-20 01:34:23 +02:00
|
|
|
|
|
|
|
DEV_BLKCHK(input1, dsql_type_str);
|
|
|
|
DEV_BLKCHK(input2, dsql_type_str);
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
int length = (input1 ? input1->str_length : 0);
|
|
|
|
|
|
|
|
if (input1 && input1->str_length && input2)
|
|
|
|
++length; // Room for space character.
|
|
|
|
|
|
|
|
if (input2)
|
|
|
|
length += strlen(input2);
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
dsql_str* output = FB_NEW_RPT(*tdbb->getDefaultPool(), length) dsql_str;
|
2003-08-20 01:34:23 +02:00
|
|
|
output->str_length = length;
|
2003-10-05 08:37:26 +02:00
|
|
|
TEXT* ptr = output->str_data;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (input1)
|
2003-08-20 01:34:23 +02:00
|
|
|
strcat(ptr, input1->str_data);
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (input1 && input1->str_length && input2)
|
2003-08-20 01:34:23 +02:00
|
|
|
strcat(ptr, " ");
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (input2)
|
|
|
|
strcat(ptr, input2);
|
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_base_table
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Check if the relation in the passed context
|
|
|
|
has a base table which matches the passed alias.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param relation
|
|
|
|
@param alias
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_rel* pass1_base_table( DsqlCompilerScratch* dsqlScratch, const dsql_rel* relation,
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* alias)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(relation, dsql_type_dsql_rel);
|
|
|
|
DEV_BLKCHK(alias, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
return METD_get_view_relation(dsqlScratch->getTransaction(), dsqlScratch,
|
|
|
|
relation->rel_name.c_str(), alias->str_data);
|
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-07 05:03:28 +02:00
|
|
|
static RseNode* pass1_rse(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* order,
|
2012-04-07 06:21:46 +02:00
|
|
|
RowsClause* rows, bool updateLock, USHORT flags)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
string save_alias;
|
2012-04-07 05:03:28 +02:00
|
|
|
RseNode* rseNode = ExprNode::as<RseNode>(input);
|
|
|
|
const bool isRecursive = rseNode && (rseNode->dsqlFlags & RecordSourceNode::DFLAG_RECURSIVE);
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
if (isRecursive)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
fb_assert(dsqlScratch->recursiveCtx);
|
|
|
|
save_alias = dsqlScratch->recursiveCtx->ctx_alias;
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
dsqlScratch->recursiveCtx->ctx_alias = *dsqlScratch->getNextCTEAlias();
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
RseNode* ret = pass1_rse_impl(dsqlScratch, input, order, rows, updateLock, flags);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (isRecursive)
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->recursiveCtx->ctx_alias = save_alias;
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
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-07 05:03:28 +02:00
|
|
|
static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* order,
|
2012-04-07 06:21:46 +02:00
|
|
|
RowsClause* rows, bool updateLock, USHORT flags)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(order, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2011-01-09 22:58:56 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
SelectExprNode* selNode = ExprNode::as<SelectExprNode>(input);
|
|
|
|
UnionSourceNode* unionNode = ExprNode::as<UnionSourceNode>(input);
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (selNode)
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
WithClause* withClause = selNode->withClause;
|
2008-12-05 02:20:14 +01:00
|
|
|
try
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
if (withClause)
|
|
|
|
dsqlScratch->addCTEs(withClause);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
RseNode* ret = pass1_rse(dsqlScratch, selNode->querySpec, selNode->order,
|
2012-04-07 06:21:46 +02:00
|
|
|
selNode->rowsClause, updateLock, selNode->dsqlFlags);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (withClause)
|
2008-03-17 11:27:01 +01:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->checkUnusedCTEs();
|
|
|
|
dsqlScratch->clearCTEs();
|
2008-03-17 11:27:01 +01:00
|
|
|
}
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
return ret;
|
2008-12-05 02:20:14 +01:00
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
if (withClause)
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->clearCTEs();
|
2006-08-01 22:37:58 +02:00
|
|
|
throw;
|
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
2012-04-07 05:03:28 +02:00
|
|
|
else if (unionNode)
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
2012-04-07 05:03:28 +02:00
|
|
|
fb_assert(unionNode->dsqlClauses->nod_count > 1);
|
|
|
|
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
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* inputRse = ExprNode::as<RseNode>(input);
|
|
|
|
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
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* targetRse = FB_NEW(pool) RseNode(pool);
|
|
|
|
RseNode* rse = targetRse;
|
2004-03-21 02:48:29 +01:00
|
|
|
|
2012-02-10 04:06:57 +01:00
|
|
|
if (updateLock)
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->flags |= RseNode::FLAG_WRITELOCK;
|
|
|
|
|
|
|
|
dsql_nod* list = rse->dsqlStreams = PASS1_node_psql(dsqlScratch, inputRse->dsqlFrom, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2011-01-09 22:58:56 +01:00
|
|
|
RelationSourceNode* relNode;
|
2005-05-20 01:41:17 +02:00
|
|
|
const dsql_rel* relation;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-02-10 04:06:57 +01:00
|
|
|
if (updateLock &&
|
2011-01-09 22:58:56 +01:00
|
|
|
(list->nod_count != 1 || !(relNode = ExprNode::as<RelationSourceNode>(list->nod_arg[0])) ||
|
|
|
|
!(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
|
|
|
{
|
2008-08-15 13:21:47 +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
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
// Process LIMIT and/or ROWS, if any
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((inputRse->dsqlFirst || inputRse->dsqlSkip) && rows)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Token unknown
|
|
|
|
Arg::Gds(isc_token_err) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str("ROWS"));
|
2003-11-07 15:10:16 +01:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else if (rows)
|
2012-04-07 06:21:46 +02:00
|
|
|
PASS1_limit(dsqlScratch, rows->length, rows->skip, rse);
|
2011-01-09 22:58:56 +01:00
|
|
|
else if (inputRse->dsqlFirst || inputRse->dsqlSkip)
|
2011-03-04 02:47:49 +01:00
|
|
|
PASS1_limit(dsqlScratch, inputRse->dsqlFirst, inputRse->dsqlSkip, rse);
|
2003-11-07 15:10:16 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process boolean, if any
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlWhere)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inWhereClause;
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlWhere = PASS1_node_psql(dsqlScratch, inputRse->dsqlWhere, false);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inWhereClause;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
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,
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlWhere))
|
2008-07-01 03:12:02 +02:00
|
|
|
{
|
|
|
|
// Cannot use an aggregate in a WHERE clause, use HAVING instead
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_where_err));
|
2008-07-01 03:12:02 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
2003-09-28 23:36:05 +02:00
|
|
|
#ifdef DSQL_DEBUG
|
2009-11-19 10:37:10 +01:00
|
|
|
if (DSQL_debug & 16)
|
|
|
|
{
|
2003-09-28 23:36:05 +02:00
|
|
|
dsql_trace("PASS1_rse input tree:");
|
2003-01-11 03:49:13 +01:00
|
|
|
DSQL_pretty(input, 0);
|
2003-09-28 23:36:05 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
#endif
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process select list, if any. If not, generate one
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* selectList = inputRse->dsqlSelectList;
|
2005-02-10 22:14:52 +01:00
|
|
|
// First expand select list, this will expand nodes with asterisk.
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inSelectList;
|
2011-01-09 22:58:56 +01:00
|
|
|
selectList = pass1_expand_select_list(dsqlScratch, selectList, rse->dsqlStreams);
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if ((flags & RecordSourceNode::DFLAG_VALUE) && (!selectList || selectList->nod_count > 1))
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
|
|
|
// More than one column (or asterisk) is specified in column_singleton
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_count_mismatch));
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// Pass select list
|
2012-04-07 05:03:28 +02:00
|
|
|
rse->dsqlSelectList = pass1_sel_list(dsqlScratch, selectList);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inSelectList;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process ORDER clause, if any
|
2009-11-19 10:37:10 +01:00
|
|
|
if (order)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inOrderByClause;
|
2011-03-04 02:47:49 +01:00
|
|
|
rse->dsqlOrder = PASS1_sort(dsqlScratch, order, selectList);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inOrderByClause;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// A GROUP BY, HAVING, or any aggregate function in the select list
|
2005-02-10 22:14:52 +01:00
|
|
|
// will force an aggregate
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_ctx* parent_context = NULL;
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* parentRse = NULL;
|
|
|
|
AggregateSourceNode* aggregate = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlGroup ||
|
|
|
|
inputRse->dsqlHaving ||
|
|
|
|
(rse->dsqlSelectList && AggregateFinder::find(dsqlScratch, false, rse->dsqlSelectList)) ||
|
|
|
|
(rse->dsqlOrder && AggregateFinder::find(dsqlScratch, false, rse->dsqlOrder)))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-06-12 11:38:01 +02:00
|
|
|
// 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
|
|
|
{
|
2008-08-15 13:21:47 +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"));
|
2003-06-12 11:38:01 +02:00
|
|
|
}
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
parent_context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
|
2009-12-20 22:01:10 +01:00
|
|
|
parent_context->ctx_scope_level = dsqlScratch->scopeLevel;
|
2009-10-04 23:47:52 +02:00
|
|
|
|
|
|
|
// When we're in a outer-join part mark context for it.
|
2009-12-20 22:01:10 +01:00
|
|
|
if (dsqlScratch->inOuterJoin)
|
2009-10-04 23:47:52 +02:00
|
|
|
parent_context->ctx_flags |= CTX_outer_join;
|
2009-12-20 22:01:10 +01:00
|
|
|
parent_context->ctx_in_outer_join = dsqlScratch->inOuterJoin;
|
2009-10-04 23:47:52 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
aggregate = FB_NEW(pool) AggregateSourceNode(pool);
|
|
|
|
aggregate->dsqlContext = parent_context;
|
2012-04-07 05:03:28 +02:00
|
|
|
aggregate->dsqlRse = MAKE_class_node(rse);
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
|
|
|
|
parentRse->dsqlStreams = list = MAKE_node(nod_list, 1);
|
|
|
|
|
|
|
|
dsql_nod* aggregateNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
aggregateNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(aggregate);
|
|
|
|
list->nod_arg[0] = aggregateNod;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlFirst)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlFirst = rse->dsqlFirst;
|
|
|
|
rse->dsqlFirst = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
if (rse->dsqlSkip)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSkip = rse->dsqlSkip;
|
|
|
|
rse->dsqlSkip = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(parent_context);
|
2003-05-05 00:02:42 +02:00
|
|
|
// replace original contexts with parent context
|
2011-01-09 22:58:56 +01:00
|
|
|
remap_streams_to_parent_context(rse->dsqlStreams, parent_context);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-11-17 15:50:33 +01:00
|
|
|
// Process GROUP BY clause, if any
|
2011-01-09 22:58:56 +01:00
|
|
|
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
|
2005-02-10 22:14:52 +01:00
|
|
|
// by the (newly pass) items from the select_list
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inGroupByClause;
|
2011-01-09 22:58:56 +01:00
|
|
|
aggregate->dsqlGroup = pass1_group_by_list(dsqlScratch, inputRse->dsqlGroup, selectList);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inGroupByClause;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// AB: An field pointing to another parent_context isn't
|
|
|
|
// allowed and GROUP BY items can't contain aggregates
|
2011-01-09 22:58:56 +01:00
|
|
|
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,
|
2011-01-09 22:58:56 +01:00
|
|
|
false, aggregate->dsqlGroup))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-07-01 03:12:02 +02:00
|
|
|
// Cannot use an aggregate in a GROUP BY clause
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_group_err));
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2008-04-09 15:47:15 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// Parse a user-specified access PLAN
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlPlan = PASS1_node_psql(dsqlScratch, inputRse->dsqlPlan, false);
|
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
|
2011-01-09 22:58:56 +01:00
|
|
|
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
|
|
|
{
|
2008-08-15 13:21:47 +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"));
|
2003-06-30 16:31:00 +02:00
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inSelectList;
|
2008-09-01 15:18:02 +02:00
|
|
|
// ASF: We pass false to viewFields parameter here because these expressions are
|
|
|
|
// generated inside the view body, and not in view fields.
|
2012-04-07 05:03:28 +02:00
|
|
|
targetRse->dsqlDistinct = pass1_sel_list(dsqlScratch, selectList);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inSelectList;
|
2009-06-06 04:21:23 +02:00
|
|
|
|
|
|
|
// sort, group and distinct have the same limit for now
|
|
|
|
if (selectList->nod_count > 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));
|
|
|
|
}
|
2003-02-23 02:36:22 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
if (parent_context)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Reset context of select items to point to the parent stream
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSelectList = FieldRemapper::remap(dsqlScratch, parent_context, false,
|
|
|
|
rse->dsqlSelectList);
|
|
|
|
rse->dsqlSelectList = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
// AB: Check for invalid constructions inside selected-items list
|
2011-01-09 22:58:56 +01:00
|
|
|
list = parentRse->dsqlSelectList;
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
{ // scope block
|
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// 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
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// Reset context of order items to point to the parent stream
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (order)
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlOrder = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
false, rse->dsqlOrder);
|
|
|
|
rse->dsqlOrder = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// AB: Check for invalid contructions inside the ORDER BY clause
|
2011-01-09 22:58:56 +01:00
|
|
|
list = targetRse->dsqlOrder;
|
2009-10-21 02:42:38 +02:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2005-05-20 01:41:17 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// 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
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// And, of course, reduction clauses must also apply to the parent
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlDistinct)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlDistinct = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
false, parentRse->dsqlDistinct);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// Process HAVING clause, if any
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlHaving)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inHavingClause;
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlWhere = PASS1_node_psql(dsqlScratch, inputRse->dsqlHaving, false);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inHavingClause;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlWhere = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
false, parentRse->dsqlWhere);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// AB: Check for invalid contructions inside the HAVING clause
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup,
|
|
|
|
parentRse->dsqlWhere))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
// 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"));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (AggregateFinder::find(dsqlScratch, true, parentRse->dsqlWhere))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Cannot use an aggregate in a WHERE clause, use HAVING instead
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
2009-10-21 02:42:38 +02:00
|
|
|
Arg::Gds(isc_dsql_agg_where_err));
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse = parentRse;
|
2010-01-19 19:26:28 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
parent_context->ctx_context = dsqlScratch->contextNumber++;
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
const bool sortWindow = rse->dsqlOrder && AggregateFinder::find(dsqlScratch, true, rse->dsqlOrder);
|
2009-10-23 02:42:40 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// WINDOW functions
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((rse->dsqlSelectList && AggregateFinder::find(dsqlScratch, true, rse->dsqlSelectList)) ||
|
2009-10-21 02:42:38 +02:00
|
|
|
sortWindow)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
AutoSetRestore<bool> autoProcessingWindow(&dsqlScratch->processingWindow, true);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
parent_context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
|
2009-12-20 22:01:10 +01:00
|
|
|
parent_context->ctx_scope_level = dsqlScratch->scopeLevel;
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
AggregateSourceNode* window = FB_NEW(pool) AggregateSourceNode(pool);
|
|
|
|
window->dsqlContext = parent_context;
|
2012-04-07 05:03:28 +02:00
|
|
|
window->dsqlRse = MAKE_class_node(rse);
|
2011-01-09 22:58:56 +01:00
|
|
|
window->dsqlWindow = true;
|
|
|
|
|
|
|
|
dsql_nod* windowNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
windowNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(window);
|
|
|
|
|
|
|
|
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
|
|
|
|
parentRse->dsqlStreams = list = MAKE_node(nod_list, 1);
|
|
|
|
list->nod_arg[0] = windowNod;
|
|
|
|
|
|
|
|
if (rse->flags & RseNode::FLAG_WRITELOCK)
|
2009-12-11 15:47:41 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->flags |= RseNode::FLAG_WRITELOCK;
|
|
|
|
rse->flags &= ~RseNode::FLAG_WRITELOCK;
|
2009-12-11 15:47:41 +01:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlFirst)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlFirst = rse->dsqlFirst;
|
|
|
|
rse->dsqlFirst = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlSkip)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSkip = rse->dsqlSkip;
|
|
|
|
rse->dsqlSkip = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(parent_context);
|
2009-10-21 02:42:38 +02:00
|
|
|
// replace original contexts with parent context
|
2011-01-09 22:58:56 +01:00
|
|
|
remap_streams_to_parent_context(rse->dsqlStreams, parent_context);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (aggregate)
|
2004-10-14 18:35:13 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Check for invalid contructions inside selected-items list
|
2011-01-09 22:58:56 +01:00
|
|
|
list = rse->dsqlSelectList;
|
2009-10-21 02:42:38 +02:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// 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
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSelectList = FieldRemapper::remap(dsqlScratch, parent_context, true,
|
|
|
|
rse->dsqlSelectList);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
// Remap the nodes to the partition context.
|
2010-01-21 19:33:18 +01:00
|
|
|
for (size_t i = 0, mapCount = parent_context->ctx_win_maps.getCount(); i < mapCount; ++i)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
PartitionMap* partitionMap = parent_context->ctx_win_maps[i];
|
|
|
|
if (partitionMap->partition)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
partitionMap->partitionRemapped = PASS1_node(dsqlScratch, partitionMap->partition);
|
2010-01-28 16:18:11 +01:00
|
|
|
partitionMap->partitionRemapped = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
true, partitionMap->partitionRemapped, partitionMap->partition,
|
2010-01-23 04:02:53 +01:00
|
|
|
partitionMap->order);
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlSelectList = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (order)
|
2004-10-14 18:35:13 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
if (aggregate)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Check for invalid contructions inside the order-by list
|
2011-01-09 22:58:56 +01:00
|
|
|
list = rse->dsqlOrder;
|
2009-10-21 02:42:38 +02:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// 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
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlOrder = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
true, rse->dsqlOrder);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlOrder = NULL;
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// And, of course, reduction clauses must also apply to the parent
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlDistinct)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlDistinct = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
true, rse->dsqlDistinct);
|
|
|
|
rse->dsqlDistinct = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse = parentRse;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
rse->dsqlFlags = flags;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
return rse;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_sel_list
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
@brief Compile a select list.
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2012-04-07 05:03:28 +02:00
|
|
|
static dsql_nod* pass1_sel_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack stack;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2009-12-20 22:01:10 +01:00
|
|
|
stack.push(PASS1_node_psql(dsqlScratch, *ptr, false));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_list(stack);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
// Process ORDER BY list, which may contain an ordinal or alias which references the select list.
|
|
|
|
dsql_nod* PASS1_sort( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* selectList)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2005-02-10 22:14:52 +01:00
|
|
|
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();
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (input->nod_type != nod_list)
|
|
|
|
{
|
2008-08-15 13:21:47 +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));
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-30 11:10:28 +02:00
|
|
|
if (input->nod_count > MAX_SORT_ITEMS)
|
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
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));
|
2006-09-30 11:10:28 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
dsql_nod** ptr2 = node->nod_arg;
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2006-10-07 11:40:59 +02:00
|
|
|
for (int sortloop = 0; sortloop < input->nod_count; sortloop++)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2006-10-07 11:40:59 +02:00
|
|
|
DEV_BLKCHK(input->nod_arg[sortloop], dsql_type_nod);
|
2012-04-08 06:15:09 +02:00
|
|
|
OrderNode* node1 = ExprNode::as<OrderNode>(input->nod_arg[sortloop]);
|
|
|
|
if (!node1)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2008-08-15 13:21:47 +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));
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-04-06 13:20:24 +02:00
|
|
|
|
|
|
|
// get node of value to be ordered by
|
2012-04-08 06:15:09 +02:00
|
|
|
dsql_nod* orderValue = node1->dsqlValue;
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2012-04-08 06:15:09 +02:00
|
|
|
const CollateNode* collateNode = ExprNode::as<CollateNode>(orderValue);
|
2012-03-25 03:08:55 +02:00
|
|
|
|
|
|
|
if (collateNode)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-03-25 03:08:55 +02:00
|
|
|
// substitute CollateNode with its argument (real value)
|
2012-04-08 06:15:09 +02:00
|
|
|
orderValue = collateNode->dsqlArg;
|
2003-04-06 13:20:24 +02:00
|
|
|
}
|
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
FieldNode* field;
|
2010-10-24 02:26:00 +02:00
|
|
|
LiteralNode* literal;
|
|
|
|
|
2012-04-08 06:15:09 +02:00
|
|
|
if ((field = ExprNode::as<FieldNode>(orderValue)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-03-25 03:08:55 +02:00
|
|
|
dsql_nod* aliasNode = NULL;
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// check for alias or field node
|
2012-03-25 03:08:55 +02:00
|
|
|
if (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-04-08 06:15:09 +02:00
|
|
|
else if ((literal = ExprNode::as<LiteralNode>(orderValue)) && literal->litDesc.dsc_dtype == dtype_long)
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
2010-10-24 02:26:00 +02:00
|
|
|
const ULONG position = literal->getSlong();
|
|
|
|
|
2009-01-09 02:50:54 +01:00
|
|
|
if (position < 1 || !selectList || position > (ULONG) selectList->nod_count)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
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"));
|
2003-01-09 17:50:24 +01:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
|
2003-04-06 13:20:24 +02:00
|
|
|
// substitute ordinal with appropriate field
|
2012-04-08 06:15:09 +02:00
|
|
|
orderValue = PASS1_node_psql(dsqlScratch, selectList->nod_arg[position - 1], false);
|
2008-04-09 15:47:15 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
else
|
2012-04-08 06:15:09 +02:00
|
|
|
orderValue = PASS1_node_psql(dsqlScratch, orderValue, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if (collateNode)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-03-25 03:08:55 +02:00
|
|
|
// Finally apply collation order, if necessary.
|
2012-04-08 06:15:09 +02:00
|
|
|
orderValue = MAKE_class_node(
|
|
|
|
CollateNode::pass1Collate(dsqlScratch, orderValue, collateNode->collation));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-04-06 13:20:24 +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;
|
|
|
|
|
2003-04-06 13:20:24 +02:00
|
|
|
// store actual value to be ordered by
|
2012-04-08 06:15:09 +02:00
|
|
|
*ptr2++ = MAKE_class_node(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.
|
2012-04-07 05:03:28 +02:00
|
|
|
static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* input,
|
2012-04-07 06:21:46 +02:00
|
|
|
dsql_nod* order_list, RowsClause* rows, bool updateLock, USHORT flags)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(order_list, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2011-01-09 22:58:56 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// set up the rse node for the union.
|
2012-04-07 05:03:28 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
UnionSourceNode* unionSource = FB_NEW(pool) UnionSourceNode(pool);
|
2012-04-07 05:03:28 +02:00
|
|
|
unionSource->dsqlAll = input->dsqlAll;
|
|
|
|
unionSource->recursive = input->recursive;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
RseNode* unionRse = FB_NEW(pool) RseNode(pool);
|
|
|
|
unionRse->dsqlStreams = MAKE_class_node(unionSource);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
// 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());
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
if (input->recursive)
|
2009-12-20 22:01:10 +01:00
|
|
|
union_context->ctx_context = dsqlScratch->recursiveCtxId;
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
union_context->ctx_context = dsqlScratch->contextNumber++;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-02-21 09:04:08 +01: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-07 05:03:28 +02:00
|
|
|
unionSource->dsqlClauses = MAKE_node(nod_list, input->dsqlClauses->nod_count);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// process all the sub-rse's.
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod** uptr = unionSource->dsqlClauses->nod_arg;
|
2009-12-20 22:01:10 +01:00
|
|
|
const DsqlContextStack::const_iterator base(*dsqlScratch->context);
|
2012-04-07 05:03:28 +02:00
|
|
|
dsql_nod** ptr = input->dsqlClauses->nod_arg;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->dsqlClauses->nod_count; ptr < end; ++ptr, ++uptr)
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
2012-04-07 05:03:28 +02:00
|
|
|
*uptr = MAKE_class_node(pass1_rse(dsqlScratch, *ptr, NULL, NULL, false, 0));
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel--;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
while (*(dsqlScratch->context) != base)
|
|
|
|
dsqlScratch->unionContext.push(dsqlScratch->context->pop());
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
// Push recursive context after initial select has been processed.
|
|
|
|
// Corresponding pop occurs in pass1_derived_table
|
2012-04-07 05:03:28 +02:00
|
|
|
if (input->recursive && (ptr == input->dsqlClauses->nod_arg))
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(dsqlScratch->recursiveCtx);
|
2005-05-20 01:41:17 +02:00
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// generate the list of fields to select.
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* items = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[0])->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
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (int i = 1; i < unionSource->dsqlClauses->nod_count; ++i)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
const dsql_nod* nod1 = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[i])->dsqlSelectList;
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (items->nod_count != nod1->nod_count)
|
|
|
|
{
|
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).
|
2003-08-18 23:37:47 +02:00
|
|
|
|
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)
|
2009-12-20 22:01:10 +01:00
|
|
|
// Workaround: use a direct CAST() dsqlScratch in the SQL
|
|
|
|
// dsqlScratch to force desired datatype.
|
2003-08-18 23:37:47 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
// loop through the list nodes and cast whenever possible.
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* tmp_list = MAKE_node(nod_list, unionSource->dsqlClauses->nod_count);
|
|
|
|
|
|
|
|
for (int j = 0; j < items->nod_count; ++j)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
for (int i = 0; i < unionSource->dsqlClauses->nod_count; ++i)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* nod1 = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[i])->dsqlSelectList;
|
2010-11-01 01:42:12 +01:00
|
|
|
MAKE_desc(dsqlScratch, &nod1->nod_arg[j]->nod_desc, nod1->nod_arg[j]);
|
2003-08-15 02:02:18 +02:00
|
|
|
tmp_list->nod_arg[i] = nod1->nod_arg[j];
|
2003-08-18 23:37:47 +02:00
|
|
|
|
|
|
|
// We look only at the items->nod_arg[] when creating the
|
|
|
|
// output descriptors. Make sure that the sub-rses
|
|
|
|
// descriptor flags are copied onto items->nod_arg[]->nod_desc.
|
|
|
|
// Bug 65584.
|
|
|
|
// -Sudesh 07/28/1999.
|
2009-11-19 10:37:10 +01:00
|
|
|
if (i > 0)
|
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
if (nod1->nod_arg[j]->nod_desc.dsc_flags & DSC_nullable)
|
2003-08-25 00:15:23 +02:00
|
|
|
items->nod_arg[j]->nod_desc.dsc_flags |= DSC_nullable;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
dsc desc;
|
2010-11-01 01:42:12 +01:00
|
|
|
MAKE_desc_from_list(dsqlScratch, &desc, tmp_list, "UNION");
|
2005-04-20 15:06:42 +02:00
|
|
|
// Only mark upper node as a NULL node when all sub-nodes are NULL
|
|
|
|
items->nod_arg[j]->nod_desc.dsc_flags &= ~DSC_null;
|
|
|
|
items->nod_arg[j]->nod_desc.dsc_flags |= (desc.dsc_flags & DSC_null);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < unionSource->dsqlClauses->nod_count; ++i)
|
|
|
|
pass1_union_auto_cast(dsqlScratch, unionSource->dsqlClauses->nod_arg[i], desc, j);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
items = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[0])->dsqlSelectList;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// Create mappings for union.
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* union_items = MAKE_node(nod_list, items->nod_count);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2011-01-28 18:22:44 +01:00
|
|
|
USHORT count = 0;
|
2005-05-20 01:41:17 +02:00
|
|
|
dsql_nod** uptr = items->nod_arg;
|
|
|
|
dsql_nod** ptr = union_items->nod_arg;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + union_items->nod_count; ptr < end; ptr++)
|
2005-05-20 01:41:17 +02:00
|
|
|
{
|
2010-11-14 18:25:48 +01: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++;
|
|
|
|
map->map_node = *uptr++;
|
2010-01-21 19:33:18 +01:00
|
|
|
map->map_next = union_context->ctx_map;
|
|
|
|
map->map_partition = NULL;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
DsqlMapNode* mapNode = FB_NEW(*tdbb->getDefaultPool()) DsqlMapNode(*tdbb->getDefaultPool(),
|
|
|
|
union_context, map);
|
|
|
|
|
|
|
|
*ptr = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
(*ptr)->nod_arg[0] = reinterpret_cast<dsql_nod*>(mapNode);
|
2005-05-20 01:41:17 +02:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
unionRse->dsqlSelectList = union_items;
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// Process ORDER clause, if any.
|
2009-01-07 10:30:57 +01:00
|
|
|
if (order_list)
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sort = MAKE_node(nod_list, order_list->nod_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod** uptr = sort->nod_arg;
|
|
|
|
dsql_nod** ptr = order_list->nod_arg;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + order_list->nod_count; ptr < end; ptr++, uptr++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2012-04-08 06:15:09 +02:00
|
|
|
OrderNode* order1 = ExprNode::as<OrderNode>(*ptr);
|
|
|
|
const dsql_nod* position = order1->dsqlValue;
|
2012-03-25 03:08:55 +02:00
|
|
|
const CollateNode* collateNode = ExprNode::as<CollateNode>(position);
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if (collateNode)
|
|
|
|
position = collateNode->dsqlArg;
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2010-10-24 02:26:00 +02:00
|
|
|
const LiteralNode* literal = ExprNode::as<LiteralNode>(position);
|
|
|
|
|
|
|
|
if (!literal || literal->litDesc.dsc_dtype != dtype_long)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2008-08-15 13:21:47 +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));
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
|
|
|
|
const SLONG number = literal->getSlong();
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (number < 1 || number > union_items->nod_count)
|
|
|
|
{
|
2008-08-15 13:21:47 +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));
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// make a new order node pointing at the Nth item in the select list.
|
2012-04-08 06:15:09 +02:00
|
|
|
OrderNode* order2 = FB_NEW(pool) OrderNode(pool, union_items->nod_arg[number - 1]);
|
|
|
|
*uptr = MAKE_class_node(order2);
|
|
|
|
order2->descending = order1->descending;
|
2010-10-24 02:26:00 +02:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if (collateNode)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-04-08 06:15:09 +02:00
|
|
|
order2->dsqlValue = MAKE_class_node(CollateNode::pass1Collate(
|
|
|
|
dsqlScratch, order2->dsqlValue, collateNode->collation));
|
2003-04-06 13:20:24 +02:00
|
|
|
}
|
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
|
|
|
|
2011-01-09 22:58:56 +01: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);
|
2003-11-07 15:10:16 +01:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// PROJECT on all the select items unless UNION ALL was specified.
|
2012-04-07 05:03:28 +02:00
|
|
|
if (!input->dsqlAll)
|
2010-02-16 17:28:54 +01:00
|
|
|
{
|
2012-02-10 04:06:57 +01:00
|
|
|
if (updateLock)
|
2010-02-16 17:28:54 +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"));
|
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
unionRse->dsqlDistinct = union_items;
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-02-10 04:06:57 +01:00
|
|
|
if (updateLock)
|
2011-01-09 22:58:56 +01:00
|
|
|
unionRse->flags |= RseNode::FLAG_WRITELOCK;
|
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
unionRse->dsqlFlags = flags;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-07 05:03:28 +02:00
|
|
|
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
|
|
|
|
@param in_select_list
|
|
|
|
|
|
|
|
**/
|
2010-11-01 14:45:52 +01:00
|
|
|
static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, dsql_nod* input,
|
|
|
|
const dsc& desc, SSHORT position, bool in_select_list)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
case nod_list:
|
2005-05-22 05:11:41 +02:00
|
|
|
if (in_select_list)
|
|
|
|
{
|
2009-01-09 02:50:54 +01:00
|
|
|
if (position < 0 || position >= input->nod_count)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2006-10-17 08:03:21 +02:00
|
|
|
// Internal dsql error: column position out of range in pass1_union_auto_cast
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_auto_field_bad_pos));
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* select_item = input->nod_arg[position];
|
2010-11-01 14:45:52 +01:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nod_desc, select_item);
|
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
if (select_item->nod_desc.dsc_dtype != desc.dsc_dtype ||
|
|
|
|
select_item->nod_desc.dsc_length != desc.dsc_length ||
|
|
|
|
select_item->nod_desc.dsc_scale != desc.dsc_scale ||
|
|
|
|
select_item->nod_desc.dsc_sub_type != desc.dsc_sub_type)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
// Because this select item has a different descriptor then
|
|
|
|
// our finally descriptor CAST it.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* cast_node = NULL;
|
2011-02-17 15:25:56 +01:00
|
|
|
DsqlAliasNode* newAliasNode = NULL;
|
|
|
|
DsqlAliasNode* aliasNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
// Pick a existing cast if available else make a new one.
|
2011-02-17 15:25:56 +01:00
|
|
|
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)) &&
|
|
|
|
aliasNode->value && ExprNode::is<CastNode>(aliasNode->value))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2011-02-17 15:25:56 +01:00
|
|
|
cast_node = aliasNode->value;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)) &&
|
|
|
|
ExprNode::is<CastNode>(derivedField->dsqlValue))
|
2005-11-29 00:06:53 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
cast_node = derivedField->dsqlValue;
|
2005-11-29 00:06:53 +01:00
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
else if (ExprNode::is<CastNode>(select_item))
|
2003-08-15 02:02:18 +02:00
|
|
|
cast_node = select_item;
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
|
|
|
{
|
2010-11-02 00:57:31 +01:00
|
|
|
CastNode* castNode = FB_NEW(*tdbb->getDefaultPool()) CastNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
|
|
|
|
castNode->dsqlField = FB_NEW(*tdbb->getDefaultPool()) dsql_fld(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
|
|
|
|
cast_node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
cast_node->nod_arg[0] = reinterpret_cast<dsql_nod*>(castNode);
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
// 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.
|
2011-02-17 15:25:56 +01:00
|
|
|
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)))
|
|
|
|
castNode->dsqlSource = aliasNode->value;
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
castNode->dsqlSource = derivedField->dsqlValue;
|
2010-11-02 00:57:31 +01:00
|
|
|
else
|
|
|
|
castNode->dsqlSource = select_item;
|
|
|
|
|
2004-06-30 00:15:10 +02:00
|
|
|
// When a cast is created we're losing our fieldname, thus
|
2007-03-31 08:50:01 +02:00
|
|
|
// create an alias to keep it.
|
2007-03-28 11:27:54 +02:00
|
|
|
const dsql_nod* name_node = select_item;
|
2010-11-14 18:25:48 +01:00
|
|
|
const DsqlMapNode* mapNode;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
while ((mapNode = ExprNode::as<DsqlMapNode>(name_node)))
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
// Skip all the DsqlMapNodes.
|
|
|
|
name_node = mapNode->map->map_node;
|
2007-03-28 11:27:54 +02:00
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
const FieldNode* fieldNode;
|
|
|
|
|
|
|
|
if ((fieldNode = ExprNode::as<FieldNode>(name_node)))
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2011-02-17 15:25:56 +01: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.
|
2004-06-30 00:15:10 +02:00
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
dsql_fld* field = ExprNode::as<CastNode>(cast_node)->dsqlField;
|
2003-08-15 02:02:18 +02:00
|
|
|
// Copy the descriptor to a field, because the gen_cast
|
|
|
|
// uses a dsql field type.
|
|
|
|
field->fld_dtype = desc.dsc_dtype;
|
|
|
|
field->fld_scale = desc.dsc_scale;
|
|
|
|
field->fld_sub_type = desc.dsc_sub_type;
|
|
|
|
field->fld_length = desc.dsc_length;
|
|
|
|
field->fld_flags = (desc.dsc_flags & DSC_nullable) ? FLD_nullable : 0;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (desc.dsc_dtype <= dtype_any_text)
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
field->fld_ttype = desc.dsc_sub_type;
|
|
|
|
field->fld_character_set_id = INTL_GET_CHARSET(&desc);
|
|
|
|
field->fld_collation_id = INTL_GET_COLLATE(&desc);
|
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else if (desc.dsc_dtype == dtype_blob)
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
field->fld_character_set_id = desc.dsc_scale;
|
2005-05-28 00:45:31 +02:00
|
|
|
field->fld_collation_id = desc.dsc_flags >> 8;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finally copy the descriptors to the root nodes and swap
|
|
|
|
// the necessary nodes.
|
|
|
|
cast_node->nod_desc = desc;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
|
|
|
if (select_item->nod_desc.dsc_flags & DSC_nullable)
|
2003-08-15 02:02:18 +02:00
|
|
|
cast_node->nod_desc.dsc_flags |= DSC_nullable;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-02-17 15:25:56 +01:00
|
|
|
aliasNode->value = cast_node;
|
|
|
|
aliasNode->value->nod_desc = desc;
|
2011-04-05 16:23:15 +02:00
|
|
|
select_item->nod_desc = desc;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
derivedField->dsqlValue = cast_node;
|
2011-04-05 16:23:15 +02:00
|
|
|
derivedField->dsqlValue->nod_desc = desc;
|
2005-11-29 00:06:53 +01:00
|
|
|
select_item->nod_desc = desc;
|
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else
|
|
|
|
{
|
2004-06-30 00:15:10 +02:00
|
|
|
// If a new alias was created for keeping original field-name
|
|
|
|
// make the alias the "top" node.
|
2011-02-17 15:25:56 +01:00
|
|
|
if (newAliasNode)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-02-17 15:25:56 +01:00
|
|
|
newAliasNode->value = cast_node;
|
|
|
|
newAliasNode->value->nod_desc = cast_node->nod_desc;
|
|
|
|
input->nod_arg[position] = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
input->nod_arg[position]->nod_arg[0] =
|
|
|
|
reinterpret_cast<dsql_nod*>(newAliasNode);
|
2004-06-30 00:15:10 +02:00
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
else
|
2004-06-30 00:15:10 +02:00
|
|
|
input->nod_arg[position] = cast_node;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2010-11-01 14:45:52 +01:00
|
|
|
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
case nod_class_exprnode:
|
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
RseNode* rseNode;
|
|
|
|
UnionSourceNode* unionNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-05-14 04:49:02 +02:00
|
|
|
if ((rseNode = ExprNode::as<RseNode>(input)) && !rseNode->dsqlExplicitJoin &&
|
|
|
|
!rseNode->dsqlContext) // not derived table
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
pass1_union_auto_cast(dsqlScratch, rseNode->dsqlStreams, desc, position);
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if ((unionNode = ExprNode::as<UnionSourceNode>(rseNode->dsqlStreams)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
// 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.
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* sub_rse_items = ExprNode::as<RseNode>(
|
2011-01-22 21:40:04 +01:00
|
|
|
unionNode->dsqlClauses->nod_arg[0])->dsqlSelectList;
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_map* map = ExprNode::as<DsqlMapNode>(rseNode->dsqlSelectList->nod_arg[position])->map;
|
2003-08-30 18:49:15 +02:00
|
|
|
map->map_node = sub_rse_items->nod_arg[position];
|
2011-01-09 22:58:56 +01:00
|
|
|
rseNode->dsqlSelectList->nod_arg[position]->nod_desc = desc;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2010-11-01 14:45:52 +01:00
|
|
|
else
|
2011-01-09 22:58:56 +01:00
|
|
|
pass1_union_auto_cast(dsqlScratch, rseNode->dsqlSelectList, desc, position, true);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2011-01-22 21:40:04 +01:00
|
|
|
else if ((unionNode = ExprNode::as<UnionSourceNode>(input)))
|
|
|
|
{
|
|
|
|
dsql_nod** ptr = unionNode->dsqlClauses->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const end = ptr + unionNode->dsqlClauses->nod_count;
|
|
|
|
ptr != end;
|
|
|
|
++ptr)
|
|
|
|
{
|
|
|
|
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
|
|
|
|
}
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
break;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Post an item to a map for a context.
|
|
|
|
dsql_nod* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node, dsql_ctx* context,
|
|
|
|
dsql_nod* partitionNode, dsql_nod* orderNode)
|
|
|
|
{
|
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(node);
|
|
|
|
return PASS1_post_map(dsqlScratch, nod, context, partitionNode, orderNode);
|
|
|
|
}
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Post an item to a map for a context.
|
2010-02-13 21:29:29 +01:00
|
|
|
dsql_nod* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, dsql_nod* node, dsql_ctx* context,
|
2010-01-23 04:02:53 +01:00
|
|
|
dsql_nod* partitionNode, dsql_nod* 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
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
PartitionMap* partitionMap = NULL;
|
2010-01-18 22:37:47 +01:00
|
|
|
dsql_map* map = NULL;
|
2010-01-21 19:33:18 +01:00
|
|
|
|
|
|
|
if (dsqlScratch->processingWindow)
|
|
|
|
{
|
2010-01-23 04:02:53 +01:00
|
|
|
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, orderNode);
|
2010-01-21 19:33:18 +01:00
|
|
|
map = partitionMap->map;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
map = context->ctx_map;
|
|
|
|
|
2011-01-28 18:22:44 +01:00
|
|
|
USHORT count = 0;
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
while (map)
|
2008-05-21 18:02:25 +02:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (PASS1_node_match(node, map->map_node, false))
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2010-01-21 19:33:18 +01:00
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
++count;
|
2010-01-21 19:33:18 +01:00
|
|
|
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)
|
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
dsql_map** next = partitionMap ? &partitionMap->map : &context->ctx_map;
|
2010-01-18 22:37:47 +01:00
|
|
|
|
|
|
|
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;
|
2010-01-21 19:33:18 +01:00
|
|
|
map->map_partition = partitionMap;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-11-01 14:45:52 +01:00
|
|
|
MAKE_desc(dsqlScratch, &node->nod_desc, node);
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
DsqlMapNode* mapNode = FB_NEW(*tdbb->getDefaultPool()) DsqlMapNode(*tdbb->getDefaultPool(),
|
|
|
|
context, map);
|
|
|
|
|
|
|
|
dsql_nod* new_node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
new_node->nod_arg[0] = reinterpret_cast<dsql_nod*>(mapNode);
|
2001-05-23 15:26:42 +02:00
|
|
|
new_node->nod_desc = node->nod_desc;
|
|
|
|
|
|
|
|
return new_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
remap_streams_to_parent_context
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief For each relation in the list, flag the relation's context
|
|
|
|
as having a parent context. Be sure to handle joins
|
|
|
|
(Bug 6674).
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param input
|
|
|
|
@param parent_context
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static void remap_streams_to_parent_context( dsql_nod* input, dsql_ctx* parent_context)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(parent_context, dsql_type_ctx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_list:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2009-03-13 16:30:33 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2003-11-18 08:58:35 +01:00
|
|
|
remap_streams_to_parent_context(*ptr, parent_context);
|
2009-03-13 16:26:50 +01:00
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2011-01-09 22:58:56 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
ProcedureSourceNode* procNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
RelationSourceNode* relNode;
|
|
|
|
RseNode* rseNode;
|
2011-01-22 21:40:04 +01:00
|
|
|
UnionSourceNode* unionNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(input)))
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(procNode->dsqlContext, dsql_type_ctx);
|
|
|
|
procNode->dsqlContext->ctx_parent = parent_context;
|
|
|
|
}
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(input)))
|
2011-01-09 22:58:56 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(relNode->dsqlContext, dsql_type_ctx);
|
|
|
|
relNode->dsqlContext->ctx_parent = parent_context;
|
|
|
|
}
|
|
|
|
else if ((rseNode = ExprNode::as<RseNode>(input)))
|
|
|
|
remap_streams_to_parent_context(rseNode->dsqlStreams, parent_context);
|
2011-01-22 21:40:04 +01:00
|
|
|
else if ((unionNode = ExprNode::as<UnionSourceNode>(input)))
|
|
|
|
{
|
|
|
|
dsql_nod** rse = unionNode->dsqlClauses->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = rse + unionNode->dsqlClauses->nod_count; rse != end; ++rse)
|
|
|
|
remap_streams_to_parent_context(ExprNode::as<RseNode>(*rse)->dsqlStreams, parent_context);
|
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else if (!ExprNode::as<AggregateSourceNode>(input))
|
|
|
|
{
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Setup the datatype of a parameter.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param in_node
|
|
|
|
@param node
|
|
|
|
@param force_varchar
|
|
|
|
|
|
|
|
**/
|
2010-02-13 21:29:29 +01:00
|
|
|
bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, dsql_nod* in_node,
|
2008-07-03 09:12:36 +02:00
|
|
|
dsql_nod* node, bool force_varchar)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(in_node, dsql_type_nod);
|
2002-04-04 18:41:41 +02:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-02-06 19:13:12 +01:00
|
|
|
if (!in_node || in_node->nod_type != nod_class_exprnode)
|
2003-11-07 15:10:16 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-02-06 19:13:12 +01:00
|
|
|
ValueExprNode* exprNode = reinterpret_cast<ValueExprNode*>(in_node->nod_arg[0]);
|
|
|
|
if (exprNode->kind == DmlNode::KIND_VALUE)
|
|
|
|
return exprNode->setParameterType(dsqlScratch, node, force_varchar);
|
2011-04-02 06:51:27 +02:00
|
|
|
|
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 21:04:26 +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.
|
2008-01-16 07:52:43 +01:00
|
|
|
bool dsql_ctx::getImplicitJoinField(const Firebird::MetaName& name, dsql_nod*& node)
|
2007-04-29 21:04:26 +02:00
|
|
|
{
|
|
|
|
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;
|
2007-04-29 21:04:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
// Returns (creating, if necessary) the PartitionMap of a given partition (that may be NULL).
|
2010-01-23 04:02:53 +01:00
|
|
|
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, dsql_nod* partitionNode,
|
|
|
|
dsql_nod* orderNode)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
PartitionMap* partitionMap = NULL;
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
for (Array<PartitionMap*>::iterator i = ctx_win_maps.begin();
|
|
|
|
!partitionMap && i != ctx_win_maps.end();
|
|
|
|
++i)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (PASS1_node_match((*i)->partition, partitionNode, false) &&
|
|
|
|
PASS1_node_match((*i)->order, orderNode, false))
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
partitionMap = *i;
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
if (!partitionMap)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-23 04:02:53 +01:00
|
|
|
partitionMap = FB_NEW(*tdbb->getDefaultPool()) PartitionMap(partitionNode, orderNode);
|
2010-01-21 19:33:18 +01:00
|
|
|
ctx_win_maps.add(partitionMap);
|
|
|
|
partitionMap->context = dsqlScratch->contextNumber++;
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
return partitionMap;
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
#ifdef DSQL_DEBUG
|
|
|
|
|
2008-08-27 14:20:47 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
static void trace_line(const char* message, ...)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
char buffer[1024];
|
|
|
|
va_list params;
|
|
|
|
va_start(params, message);
|
|
|
|
VSNPRINTF(buffer, sizeof(buffer), message, params);
|
|
|
|
va_end(params);
|
|
|
|
buffer[sizeof(buffer) - 1] = 0;
|
|
|
|
gds__trace_raw(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
DSQL_pretty
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
@brief Pretty print a node tree.
|
|
|
|
|
|
|
|
|
|
|
|
@param node
|
|
|
|
@param column
|
|
|
|
|
|
|
|
**/
|
|
|
|
void DSQL_pretty(const dsql_nod* node, int column)
|
|
|
|
{
|
|
|
|
TEXT buffer[1024];
|
|
|
|
|
|
|
|
TEXT* p = buffer;
|
|
|
|
p += sprintf(p, "%p ", (void*) node);
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (column)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
USHORT l = column * 3;
|
|
|
|
do {
|
|
|
|
*p++ = ' ';
|
|
|
|
} while (--l);
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = 0;
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!node)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%s *** null ***\n", buffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (node->getType())
|
|
|
|
{
|
2009-01-10 12:11:56 +01:00
|
|
|
case dsql_type_str:
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%sSTRING: \"%s\"\n", buffer, ((dsql_str*) node)->str_data);
|
|
|
|
return;
|
|
|
|
|
2009-01-10 12:11:56 +01:00
|
|
|
case dsql_type_fld:
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%sFIELD: %s\n", buffer, ((dsql_fld*) node)->fld_name.c_str());
|
|
|
|
return;
|
|
|
|
|
2009-01-10 12:11:56 +01:00
|
|
|
case dsql_type_nod:
|
2008-02-28 14:48:16 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
trace_line("%sUNKNOWN BLOCK TYPE\n", buffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEXT s[64];
|
|
|
|
const dsql_str* string;
|
2008-05-21 17:22:22 +02:00
|
|
|
Firebird::string verb;
|
2008-02-28 14:48:16 +01:00
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
2008-05-19 15:47:48 +02:00
|
|
|
const dsql_nod* const* end = ptr + node->nod_count;
|
|
|
|
Firebird::Array<dsql_nod*> subNodes;
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_all:
|
|
|
|
verb = "all";
|
|
|
|
break;
|
|
|
|
case nod_delete:
|
|
|
|
verb = "delete";
|
|
|
|
break;
|
|
|
|
case nod_execute:
|
|
|
|
verb = "execute";
|
|
|
|
break;
|
|
|
|
case nod_flag:
|
|
|
|
verb = "flag";
|
|
|
|
break;
|
|
|
|
case nod_insert:
|
|
|
|
verb = "insert";
|
|
|
|
break;
|
|
|
|
case nod_list:
|
|
|
|
verb = "list";
|
|
|
|
break;
|
|
|
|
case nod_procedure_name:
|
|
|
|
verb = "procedure name";
|
|
|
|
break;
|
2009-10-24 21:07:35 +02:00
|
|
|
case nod_package_name:
|
|
|
|
verb = "package name";
|
|
|
|
break;
|
2009-12-24 11:42:32 +01:00
|
|
|
case nod_function_name:
|
|
|
|
verb = "function_name";
|
|
|
|
break;
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_select:
|
|
|
|
verb = "select";
|
|
|
|
break;
|
|
|
|
case nod_update:
|
|
|
|
verb = "update";
|
|
|
|
break;
|
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
// IOL: missing node types
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_plan_expr:
|
|
|
|
verb = "plan";
|
|
|
|
break;
|
|
|
|
case nod_index:
|
|
|
|
verb = "index";
|
|
|
|
break;
|
|
|
|
case nod_index_order:
|
|
|
|
verb = "order";
|
|
|
|
break;
|
|
|
|
case nod_plan_item:
|
|
|
|
verb = "item";
|
|
|
|
break;
|
|
|
|
case nod_natural:
|
|
|
|
verb = "natural";
|
|
|
|
break;
|
2008-12-05 02:20:14 +01:00
|
|
|
// SKIDDER: some more missing node types
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_references:
|
|
|
|
verb = "references";
|
|
|
|
break;
|
|
|
|
case nod_proc_obj:
|
|
|
|
verb = "proc_obj";
|
|
|
|
break;
|
2009-12-21 18:23:07 +01:00
|
|
|
case nod_func_obj:
|
|
|
|
verb = "func_obj";
|
|
|
|
break;
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_trig_obj:
|
|
|
|
verb = "trig_obj";
|
|
|
|
break;
|
|
|
|
case nod_view_obj:
|
|
|
|
verb = "view_obj";
|
|
|
|
break;
|
|
|
|
case nod_user_group:
|
|
|
|
verb = "user_group";
|
|
|
|
break;
|
|
|
|
case nod_table_lock:
|
|
|
|
verb = "table_lock";
|
|
|
|
break;
|
|
|
|
case nod_lock_mode:
|
|
|
|
verb = "lock_mode";
|
|
|
|
break;
|
|
|
|
case nod_role_name:
|
|
|
|
verb = "role_name";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_label:
|
|
|
|
verb = "label";
|
|
|
|
DSQL_pretty(node->nod_arg[e_label_name], column + 1);
|
|
|
|
trace_line("%s number %d\n", buffer,
|
|
|
|
(int)(IPTR)node->nod_arg[e_label_number]);
|
|
|
|
return;
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
case nod_class_exprnode:
|
2008-05-21 17:22:22 +02:00
|
|
|
reinterpret_cast<Node*>(node->nod_arg[0])->print(verb, subNodes);
|
|
|
|
ptr = subNodes.begin();
|
|
|
|
end = subNodes.end();
|
|
|
|
break;
|
2008-04-15 04:18:38 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
default:
|
|
|
|
sprintf(s, "unknown type %d", node->nod_type);
|
|
|
|
verb = s;
|
|
|
|
}
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (node->nod_desc.dsc_dtype)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%s%s (%d,%d,%p)\n",
|
2008-05-21 17:22:22 +02:00
|
|
|
buffer, verb.c_str(),
|
2009-01-07 10:30:57 +01:00
|
|
|
node->nod_desc.dsc_dtype, node->nod_desc.dsc_length, node->nod_desc.dsc_address);
|
2008-02-28 14:48:16 +01:00
|
|
|
}
|
2008-05-21 17:22:22 +02:00
|
|
|
else
|
|
|
|
trace_line("%s%s\n", buffer, verb.c_str());
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
++column;
|
|
|
|
|
2008-05-21 17:22:22 +02:00
|
|
|
while (ptr < end)
|
2008-02-28 14:48:16 +01:00
|
|
|
DSQL_pretty(*ptr++, column);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|