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-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;
|
2008-08-15 13:21:47 +02:00
|
|
|
using namespace Firebird;
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
|
2012-12-25 18:34:50 +01:00
|
|
|
static string pass1_alias_concat(const string&, const string&);
|
2008-10-04 04:48:35 +02:00
|
|
|
static void pass1_expand_contexts(DsqlContextStack& contexts, dsql_ctx* context);
|
2012-04-25 03:42:47 +02:00
|
|
|
static ValueListNode* pass1_expand_select_list(DsqlCompilerScratch*, ValueListNode*, RecSourceListNode*);
|
|
|
|
static ValueListNode* pass1_group_by_list(DsqlCompilerScratch*, ValueListNode*, ValueListNode*);
|
|
|
|
static ValueExprNode* pass1_make_derived_field(thread_db*, DsqlCompilerScratch*, ValueExprNode*);
|
|
|
|
static RseNode* pass1_rse(DsqlCompilerScratch*, RecordSourceNode*, ValueListNode*, RowsClause*, bool, USHORT);
|
|
|
|
static RseNode* pass1_rse_impl(DsqlCompilerScratch*, RecordSourceNode*, ValueListNode*, RowsClause*, bool, USHORT);
|
|
|
|
static ValueListNode* pass1_sel_list(DsqlCompilerScratch*, ValueListNode*);
|
|
|
|
static RseNode* pass1_union(DsqlCompilerScratch*, UnionSourceNode*, ValueListNode*, RowsClause*, bool, USHORT);
|
2012-05-20 12:00:52 +02:00
|
|
|
static void pass1_union_auto_cast(DsqlCompilerScratch*, ExprNode*, const dsc&, size_t position);
|
2012-04-25 03:42:47 +02:00
|
|
|
static void remap_streams_to_parent_context(ExprNode*, dsql_ctx*);
|
|
|
|
|
|
|
|
|
|
|
|
AggregateFinder::AggregateFinder(DsqlCompilerScratch* aDsqlScratch, bool aWindow)
|
|
|
|
: dsqlScratch(aDsqlScratch),
|
2010-02-13 21:29:29 +01:00
|
|
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
bool AggregateFinder::find(DsqlCompilerScratch* dsqlScratch, bool window, ExprNode* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
AggregateFinder visitor(dsqlScratch, window);
|
|
|
|
return visitor.visit(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AggregateFinder::visit(ExprNode* node)
|
|
|
|
{
|
|
|
|
return node && node->dsqlAggregateFinder(*this);
|
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)
|
2012-04-25 03:42:47 +02:00
|
|
|
: checkScopeLevel(aCheckScopeLevel),
|
2010-02-13 21:29:29 +01:00
|
|
|
matchType(aMatchType),
|
|
|
|
windowOnly(aWindowOnly),
|
|
|
|
currentScopeLevelEqual(true)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
bool Aggregate2Finder::find(USHORT checkScopeLevel, FieldMatchType matchType, bool windowOnly,
|
|
|
|
ExprNode* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
Aggregate2Finder visitor(checkScopeLevel, matchType, windowOnly);
|
|
|
|
return visitor.visit(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Aggregate2Finder::visit(ExprNode* node)
|
|
|
|
{
|
|
|
|
return node && node->dsqlAggregate2Finder(*this);
|
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)
|
2012-04-25 03:42:47 +02:00
|
|
|
: checkScopeLevel(aCheckScopeLevel),
|
2010-02-13 21:29:29 +01:00
|
|
|
matchType(aMatchType),
|
|
|
|
field(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
bool FieldFinder::find(USHORT checkScopeLevel, FieldMatchType matchType, ExprNode* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
FieldFinder visitor(checkScopeLevel, matchType);
|
|
|
|
return visitor.visit(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FieldFinder::visit(ExprNode* node)
|
|
|
|
{
|
|
|
|
return node && node->dsqlFieldFinder(*this);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
InvalidReferenceFinder::InvalidReferenceFinder(const dsql_ctx* aContext, const ValueListNode* aList)
|
|
|
|
: context(aContext),
|
2010-02-13 21:29:29 +01:00
|
|
|
list(aList),
|
|
|
|
insideOwnMap(false),
|
|
|
|
insideHigherMap(false)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(list, dsql_type_nod);
|
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
bool InvalidReferenceFinder::find(const dsql_ctx* context, const ValueListNode* list, ExprNode* node)
|
|
|
|
{
|
|
|
|
InvalidReferenceFinder visitor(context, list);
|
|
|
|
return visitor.visit(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InvalidReferenceFinder::visit(ExprNode* node)
|
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
|
2012-05-03 18:43:29 +02:00
|
|
|
const NestConst<ValueExprNode>* ptr = list->items.begin();
|
|
|
|
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
return node->dsqlInvalidReferenceFinder(*this);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
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,
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* aPartitionNode, ValueListNode* aOrderNode)
|
|
|
|
: dsqlScratch(aDsqlScratch),
|
2010-02-13 21:29:29 +01:00
|
|
|
context(aContext),
|
|
|
|
window(aWindow),
|
|
|
|
partitionNode(aPartitionNode),
|
|
|
|
orderNode(aOrderNode),
|
|
|
|
currentLevel(dsqlScratch->scopeLevel)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode* FieldRemapper::visit(ExprNode* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
return node ? node->dsqlFieldRemapper(*this) : NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
bool SubSelectFinder::visit(ExprNode* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
return node && node->dsqlSubSelectFinder(*this);
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
@param relationNode
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2012-05-03 18:43:29 +02:00
|
|
|
dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* relationNode)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
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* 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;
|
2012-05-03 18:43:29 +02:00
|
|
|
ProcedureSourceNode* procNode = NULL;
|
|
|
|
RelationSourceNode* relNode = NULL;
|
|
|
|
SelectExprNode* selNode = NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((procNode = relationNode->as<ProcedureSourceNode>()))
|
2012-04-07 05:03:28 +02:00
|
|
|
relation_name = procNode->dsqlName.identifier;
|
2012-04-25 03:42:47 +02:00
|
|
|
else if ((relNode = relationNode->as<RelationSourceNode>()))
|
2012-04-07 05:03:28 +02:00
|
|
|
relation_name = relNode->dsqlName;
|
2012-04-25 03:42:47 +02:00
|
|
|
else if ((selNode = relationNode->as<SelectExprNode>()))
|
2012-04-07 05:03:28 +02:00
|
|
|
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.
|
|
|
|
}
|
2012-05-03 18:43:29 +02:00
|
|
|
else if (procNode && (procNode->dsqlName.package.hasData() || procNode->sourceList))
|
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()) <<
|
2012-04-25 03:42:47 +02:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relationNode->line) <<
|
|
|
|
Arg::Num(relationNode->column));
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((cte = dsqlScratch->findCTE(relation_name)))
|
2012-04-25 03:42:47 +02:00
|
|
|
relationNode = 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) <<
|
2012-04-25 03:42:47 +02:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relationNode->line) <<
|
|
|
|
Arg::Num(relationNode->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()) <<
|
2012-04-25 03:42:47 +02:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relationNode->line) <<
|
|
|
|
Arg::Num(relationNode->column));
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
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.
|
2012-12-25 18:34:50 +01:00
|
|
|
string str;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((procNode = relationNode->as<ProcedureSourceNode>()))
|
2012-12-25 18:34:50 +01:00
|
|
|
str = procNode->alias;
|
2012-04-25 03:42:47 +02:00
|
|
|
else if ((relNode = relationNode->as<RelationSourceNode>()))
|
2012-12-25 18:34:50 +01:00
|
|
|
str = relNode->alias;
|
2012-04-25 03:42:47 +02:00
|
|
|
else if ((selNode = relationNode->as<SelectExprNode>()))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-12-25 18:34:50 +01:00
|
|
|
str = selNode->alias;
|
2012-04-25 03:42:47 +02:00
|
|
|
// ASF: In the case of a UNION contained in a CTE, selNode->querySpec will be a
|
|
|
|
// UnionSourceNode instead of a RseNode. Since ctx_rse is a RseNode and is always accessed
|
|
|
|
// as one, I'll leave this assignment here. It will be set in PASS1_derived_table anyway.
|
|
|
|
///context->ctx_rse = selNode->querySpec;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
2012-12-25 18:34:50 +01:00
|
|
|
if (str.hasData())
|
|
|
|
context->ctx_internal_alias = str;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-12-25 18:34:50 +01:00
|
|
|
if (dsqlScratch->aliasRelationPrefix.hasData() && !selNode)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-12-25 18:34:50 +01:00
|
|
|
if (str.hasData())
|
|
|
|
str = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, str);
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2012-12-25 18:34:50 +01:00
|
|
|
str = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, relation_name.c_str());
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
2012-12-25 18:34:50 +01:00
|
|
|
if (str.hasData())
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-12-25 18:34:50 +01:00
|
|
|
context->ctx_alias = str;
|
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
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (procNode->sourceList)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
context->ctx_proc_inputs = Node::doDsqlPass(dsqlScratch, procNode->sourceList, false);
|
|
|
|
count = context->ctx_proc_inputs->items.getCount();
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
dsc desc_node;
|
|
|
|
ValueListNode* inputList = context->ctx_proc_inputs;
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* input = inputList->items.begin();
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
for (dsql_fld* field = procedure->prc_inputs;
|
2012-05-03 18:43:29 +02:00
|
|
|
input != inputList->items.end();
|
2012-04-25 03:42:47 +02:00
|
|
|
++input, field = field->fld_next)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-10-03 17:35:49 +02:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2012-04-25 03:42:47 +02:00
|
|
|
MAKE_desc_from_field(&desc_node, field);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, *input, &desc_node, false);
|
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
|
|
|
|
2012-02-10 04:06:57 +01:00
|
|
|
// Compile a record selection expression, bumping up the statement scope level everytime an rse is
|
|
|
|
// seen. The scope level controls parsing of aliases.
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* PASS1_rse(DsqlCompilerScratch* dsqlScratch, SelectExprNode* input, bool updateLock)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
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
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* 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.
|
2012-04-25 03:42:47 +02:00
|
|
|
BoolExprNode* PASS1_compose(BoolExprNode* expr1, BoolExprNode* 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();
|
|
|
|
|
|
|
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
return FB_NEW(*tdbb->getDefaultPool()) BinaryBoolNode(
|
2010-09-17 05:15:32 +02:00
|
|
|
*tdbb->getDefaultPool(), blrOp, expr1, expr2);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
If ignoreMapCast 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
|
2012-04-25 03:42:47 +02:00
|
|
|
@param ignoreMapCast
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2012-04-25 03:42:47 +02:00
|
|
|
bool PASS1_node_match(const ExprNode* node1, const ExprNode* node2, bool ignoreMapCast)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
const CastNode* castNode1 = node1->as<CastNode>();
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (ignoreMapCast && castNode1)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
const CastNode* castNode2 = node2->as<CastNode>();
|
2010-11-02 00:57:31 +01:00
|
|
|
|
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
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
return PASS1_node_match(castNode1->source, castNode2->source, ignoreMapCast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2009-04-19 12:06:07 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
return PASS1_node_match(castNode1->source, node2, ignoreMapCast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
const DsqlMapNode* mapNode1 = node1->as<DsqlMapNode>();
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (ignoreMapCast && mapNode1)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
const DsqlMapNode* mapNode2 = node2->as<DsqlMapNode>();
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
return PASS1_node_match(mapNode1->map->map_node, mapNode2->map->map_node, ignoreMapCast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
return PASS1_node_match(mapNode1->map->map_node, node2, ignoreMapCast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
const DsqlAliasNode* aliasNode1 = node1->as<DsqlAliasNode>();
|
|
|
|
const DsqlAliasNode* aliasNode2 = node2->as<DsqlAliasNode>();
|
2011-02-17 15:25:56 +01:00
|
|
|
|
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)
|
2012-04-25 03:42:47 +02:00
|
|
|
return PASS1_node_match(aliasNode1->value, aliasNode2->value, ignoreMapCast);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if (aliasNode1)
|
2012-04-25 03:42:47 +02:00
|
|
|
return PASS1_node_match(aliasNode1->value, node2, ignoreMapCast);
|
2011-02-17 15:25:56 +01:00
|
|
|
|
|
|
|
if (aliasNode2)
|
2012-04-25 03:42:47 +02:00
|
|
|
return PASS1_node_match(node1, aliasNode2->value, ignoreMapCast);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Handle derived fields.
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
const DerivedFieldNode* derivedField1 = node1->as<DerivedFieldNode>();
|
|
|
|
const DerivedFieldNode* derivedField2 = node2->as<DerivedFieldNode>();
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
return PASS1_node_match(derivedField1->value, derivedField2->value, ignoreMapCast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField1)
|
2012-05-03 18:43:29 +02:00
|
|
|
return PASS1_node_match(derivedField1->value, node2, ignoreMapCast);
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField2)
|
2012-05-03 18:43:29 +02:00
|
|
|
return PASS1_node_match(node1, derivedField2->value, ignoreMapCast);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
return node1->type == node2->type && node1->dsqlMatch(node2, ignoreMapCast);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2013-12-05 14:59:12 +01:00
|
|
|
if (context->ctx_relation || context->ctx_procedure ||
|
|
|
|
context->ctx_map || context->ctx_win_maps.hasData())
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
// Process derived table which is part of a from clause.
|
|
|
|
RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* input,
|
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;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// Create the context now, because we need to know it for the tables inside.
|
2012-04-25 03:42:47 +02:00
|
|
|
dsql_ctx* const context = PASS1_make_context(dsqlScratch, input);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// Save some values to restore after rse process.
|
|
|
|
DsqlContextStack* const req_base = dsqlScratch->context;
|
2013-05-21 23:46:28 +02:00
|
|
|
const string aliasRelationPrefix = dsqlScratch->aliasRelationPrefix;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// 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-12-25 18:34:50 +01:00
|
|
|
dsqlScratch->aliasRelationPrefix = pass1_alias_concat(aliasRelationPrefix, alias);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RecordSourceNode* query = input->querySpec;
|
|
|
|
UnionSourceNode* unionQuery = query->as<UnionSourceNode>();
|
|
|
|
RseNode* rse = NULL;
|
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-05-03 18:43:29 +02:00
|
|
|
RecordSourceNode* save = unionQuery->dsqlClauses->items.pop();
|
2012-04-07 05:03:28 +02:00
|
|
|
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-25 03:42:47 +02:00
|
|
|
rse = 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
|
|
|
|
2012-04-10 04:37:34 +02:00
|
|
|
// recursive union always has exactly 2 members
|
2012-05-03 18:43:29 +02:00
|
|
|
unionQuery->dsqlClauses->items.push(save);
|
2012-04-07 05:03:28 +02:00
|
|
|
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;
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* queryNode = query->as<RseNode>();
|
2011-01-09 22:58:56 +01:00
|
|
|
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);
|
2012-04-25 03:42:47 +02:00
|
|
|
unionExpr->dsqlClauses = FB_NEW(pool) RecSourceListNode(pool, 1);
|
2012-05-03 18:43:29 +02:00
|
|
|
unionExpr->dsqlClauses->items[0] = input;
|
2012-04-07 05:03:28 +02:00
|
|
|
unionExpr->dsqlAll = true;
|
2012-04-25 03:42:47 +02:00
|
|
|
rse = 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-25 03:42:47 +02:00
|
|
|
rse = PASS1_rse(dsqlScratch, input, false);
|
2005-02-10 22:14:52 +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.
|
2013-05-06 00:59:39 +02:00
|
|
|
pass1_expand_contexts(context->ctx_main_derived_contexts, childCtx);
|
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
|
|
|
// 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-25 03:42:47 +02:00
|
|
|
if (input->columns && input->columns->hasData())
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
// Do both lists have the same number of items?
|
2012-05-03 18:43:29 +02:00
|
|
|
if (input->columns->getCount() != rse->dsqlSelectList->items.getCount())
|
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;
|
2012-05-03 18:43:29 +02:00
|
|
|
if (input->columns->getCount() > rse->dsqlSelectList->items.getCount())
|
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.
|
2012-05-20 12:00:52 +02:00
|
|
|
for (size_t count = 0; count < input->columns->getCount(); ++count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueExprNode* select_item = rse->dsqlSelectList->items[count];
|
2012-04-25 03:42:47 +02:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nodDesc, select_item);
|
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,
|
2012-04-25 03:42:47 +02:00
|
|
|
(*input->columns)[count], dsqlScratch->scopeLevel, select_item);
|
|
|
|
derivedField->nodDesc = select_item->nodDesc;
|
2012-05-03 18:43:29 +02:00
|
|
|
rse->dsqlSelectList->items[count] = derivedField;
|
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.
|
2012-05-20 12:00:52 +02:00
|
|
|
for (size_t count = 0; count < rse->dsqlSelectList->items.getCount(); ++count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* select_item = pass1_make_derived_field(tdbb, dsqlScratch,
|
2012-05-03 18:43:29 +02:00
|
|
|
rse->dsqlSelectList->items[count]);
|
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
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nodDesc, select_item);
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Construct dummy fieldname
|
|
|
|
char fieldname[25];
|
2012-06-06 16:50:53 +02:00
|
|
|
sprintf(fieldname, "f%"SIZEFORMAT, 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);
|
2012-04-25 03:42:47 +02:00
|
|
|
derivedField->nodDesc = select_item->nodDesc;
|
|
|
|
select_item = derivedField;
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
rse->dsqlSelectList->items[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
|
|
|
|
2012-05-20 12:00:52 +02:00
|
|
|
size_t 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.
|
2012-05-03 18:43:29 +02:00
|
|
|
for (count = 0; count < rse->dsqlSelectList->items.getCount(); ++count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueExprNode* select_item = rse->dsqlSelectList->items[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.
|
2012-05-03 18:43:29 +02:00
|
|
|
for (count = 0; count < rse->dsqlSelectList->items.getCount(); ++count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DerivedFieldNode* selectItem1 =
|
2012-05-03 18:43:29 +02:00
|
|
|
rse->dsqlSelectList->items[count]->as<DerivedFieldNode>();
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2012-05-20 12:00:52 +02:00
|
|
|
for (size_t count2 = (count + 1); count2 < rse->dsqlSelectList->items.getCount(); ++count2)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DerivedFieldNode* selectItem2 =
|
2012-05-03 18:43:29 +02:00
|
|
|
rse->dsqlSelectList->items[count2]->as<DerivedFieldNode>();
|
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
|
|
|
{
|
2013-07-19 14:09:29 +02:00
|
|
|
dsql_ctx* const saveRecursiveCtx = dsqlScratch->recursiveCtx;
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->recursiveCtx = context;
|
|
|
|
dsqlScratch->context = &temp;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2013-07-19 14:09:29 +02:00
|
|
|
const string* const* saveCteAlias = dsqlScratch->currCteAlias;
|
|
|
|
dsqlScratch->resetCTEAlias(alias);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
rse = PASS1_rse(dsqlScratch, input, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2013-07-19 14:09:29 +02:00
|
|
|
if (saveCteAlias)
|
|
|
|
dsqlScratch->resetCTEAlias(**saveCteAlias);
|
|
|
|
dsqlScratch->recursiveCtx = saveRecursiveCtx;
|
|
|
|
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
rse->dsqlSelectList = 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.
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* items = rse->dsqlSelectList;
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueExprNode* map_item = items->items[0];
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(map_item)))
|
2012-05-03 18:43:29 +02:00
|
|
|
map_item = derivedField->value;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
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
|
|
|
// Restore our original values.
|
|
|
|
dsqlScratch->context = req_base;
|
|
|
|
dsqlScratch->aliasRelationPrefix = aliasRelationPrefix;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
rse->dsqlContext = context;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
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
|
|
|
**/
|
2012-04-25 03:42:47 +02:00
|
|
|
static ValueListNode* pass1_expand_select_list(DsqlCompilerScratch* dsqlScratch, ValueListNode* list,
|
|
|
|
RecSourceListNode* streams)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* retList = FB_NEW(pool) ValueListNode(pool, 0u);
|
2002-10-01 02:34:29 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (list)
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
for (NestConst<ValueExprNode>* ptr = list->items.begin(); ptr != list->items.end(); ++ptr)
|
2012-04-25 03:42:47 +02:00
|
|
|
PASS1_expand_select_node(dsqlScratch, *ptr, retList, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
for (NestConst<RecordSourceNode>* ptr = streams->items.begin(); ptr != streams->items.end(); ++ptr)
|
2012-04-25 03:42:47 +02:00
|
|
|
PASS1_expand_select_node(dsqlScratch, *ptr, retList, true);
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
return retList;
|
2011-01-22 21:40:04 +01:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Expand a select item node.
|
2012-04-25 03:42:47 +02:00
|
|
|
void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, ValueListNode* list,
|
2010-01-28 16:18:11 +01:00
|
|
|
bool hide_using)
|
|
|
|
{
|
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
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* sub_items = rseNode->dsqlSelectList;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if (sub_items) // AB: Derived table support
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = sub_items->items.begin();
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
for (const NestConst<ValueExprNode>* const end = sub_items->items.end(); ptr != end; ++ptr)
|
2011-01-22 21:40:04 +01:00
|
|
|
{
|
|
|
|
// Create a new alias else mappings would be mangled.
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode> 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;
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (!(derivedField = select_item->as<DerivedFieldNode>()))
|
2011-01-22 21:40:04 +01:00
|
|
|
{
|
|
|
|
// 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))
|
2012-04-25 03:42:47 +02:00
|
|
|
list->add(select_item);
|
2011-01-22 21:40:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else // joins
|
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
RecSourceListNode* streamList = rseNode->dsqlStreams;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
for (NestConst<RecordSourceNode>* ptr = streamList->items.begin();
|
|
|
|
ptr != streamList->items.end();
|
|
|
|
++ptr)
|
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
PASS1_expand_select_node(dsqlScratch, *ptr, list, true);
|
2012-05-03 18:43:29 +02:00
|
|
|
}
|
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
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode> select_item = NULL;
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
|
|
|
|
{
|
|
|
|
if (!select_item)
|
2012-04-25 03:42:47 +02:00
|
|
|
select_item = MAKE_field(context, field, NULL);
|
|
|
|
list->add(select_item);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode> select_item = NULL;
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
|
|
|
|
{
|
|
|
|
if (!select_item)
|
2012-04-25 03:42:47 +02:00
|
|
|
select_item = MAKE_field(context, field, NULL);
|
|
|
|
list->add(select_item);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-03-25 03:08:55 +02:00
|
|
|
else if ((fieldNode = ExprNode::as<FieldNode>(node)))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
RecordSourceNode* recSource = NULL;
|
|
|
|
ValueExprNode* value = fieldNode->internalDsqlPass(dsqlScratch, &recSource);
|
2010-10-22 17:00:22 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (recSource)
|
|
|
|
PASS1_expand_select_node(dsqlScratch, recSource, list, false);
|
2010-08-02 04:22:26 +02:00
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
list->add(value);
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2012-03-25 03:08:55 +02:00
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
{
|
|
|
|
fb_assert(node->kind == DmlNode::KIND_VALUE);
|
|
|
|
list->add(static_cast<ValueExprNode*>(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
|
|
|
|
|
|
|
|
**/
|
2012-04-25 03:42:47 +02:00
|
|
|
static ValueListNode* pass1_group_by_list(DsqlCompilerScratch* dsqlScratch, ValueListNode* input,
|
|
|
|
ValueListNode* selectList)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (input->items.getCount() > MAX_SORT_ITEMS) // sort, group and distinct have the same limit for now
|
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
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* retList = FB_NEW(pool) ValueListNode(pool, 0u);
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = input->items.begin();
|
|
|
|
for (const NestConst<ValueExprNode>* const end = input->items.end(); ptr != end; ++ptr)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* sub = (*ptr);
|
|
|
|
ValueExprNode* 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
|
2013-05-30 10:20:53 +02:00
|
|
|
if (selectList && field->dsqlQualifier.isEmpty() && field->dsqlName.hasData())
|
2012-03-25 03:08:55 +02:00
|
|
|
{
|
|
|
|
// 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();
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (position < 1 || !selectList || position > (ULONG) selectList->items.getCount())
|
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
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
frnode = Node::doDsqlPass(dsqlScratch, selectList->items[position - 1], false);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
frnode = Node::doDsqlPass(dsqlScratch, *ptr, false);
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
retList->add(frnode);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
// Finally make the complete list.
|
|
|
|
return retList;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
// Process the limit clause (FIRST/SKIP/ROWS)
|
2012-05-03 18:43:29 +02:00
|
|
|
void PASS1_limit(DsqlCompilerScratch* dsqlScratch, NestConst<ValueExprNode> firstNode,
|
|
|
|
NestConst<ValueExprNode> skipNode, RseNode* rse)
|
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);
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
dsc descNode;
|
2011-01-20 05:41:10 +01:00
|
|
|
|
|
|
|
if (dsqlScratch->clientDialect <= SQL_DIALECT_V5)
|
2012-04-25 03:42:47 +02:00
|
|
|
descNode.makeLong(0);
|
2011-01-20 05:41:10 +01:00
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
descNode.makeInt64(0);
|
2011-01-20 05:41:10 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
rse->dsqlFirst = Node::doDsqlPass(dsqlScratch, firstNode, false);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, rse->dsqlFirst, &descNode, false);
|
2011-01-20 05:41:10 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
rse->dsqlSkip = Node::doDsqlPass(dsqlScratch, skipNode, false);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, rse->dsqlSkip, &descNode, false);
|
2011-01-20 05:41:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* PASS1_lookup_alias(DsqlCompilerScratch* dsqlScratch, const MetaName& name,
|
|
|
|
ValueListNode* selectList, bool process)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* returnNode = NULL;
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = selectList->items.begin();
|
|
|
|
const NestConst<ValueExprNode>* const end = selectList->items.end();
|
2012-04-25 03:42:47 +02:00
|
|
|
for (; ptr < end; ++ptr)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode> matchingNode = NULL;
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* node = *ptr;
|
2011-02-17 15:25:56 +01:00
|
|
|
DsqlAliasNode* aliasNode;
|
2010-12-19 22:42:32 +01:00
|
|
|
FieldNode* fieldNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if ((aliasNode = node->as<DsqlAliasNode>()))
|
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;
|
|
|
|
}
|
2012-05-03 18:43:29 +02:00
|
|
|
else if ((fieldNode = node->as<FieldNode>()))
|
2011-02-17 15:25:56 +01:00
|
|
|
{
|
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;
|
|
|
|
}
|
2012-05-03 18:43:29 +02:00
|
|
|
else if ((derivedField = node->as<DerivedFieldNode>()))
|
2011-02-17 15:25:56 +01:00
|
|
|
{
|
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)
|
2012-04-25 03:42:47 +02:00
|
|
|
matchingNode = Node::doDsqlPass(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;
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (returnNode->is<DsqlAliasNode>())
|
2011-02-17 15:25:56 +01:00
|
|
|
strcat(buffer1, "an alias");
|
2012-05-03 18:43:29 +02:00
|
|
|
else if (returnNode->is<FieldNode>())
|
2011-02-17 15:25:56 +01:00
|
|
|
strcat(buffer1, "a field");
|
2012-05-03 18:43:29 +02:00
|
|
|
else if (returnNode->is<DerivedFieldNode>())
|
2011-02-17 15:25:56 +01:00
|
|
|
strcat(buffer1, "a derived field");
|
|
|
|
else
|
|
|
|
strcat(buffer1, "an item");
|
2005-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
TEXT buffer2[256];
|
|
|
|
buffer2[0] = 0;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (matchingNode->is<DsqlAliasNode>())
|
2011-02-17 15:25:56 +01:00
|
|
|
strcat(buffer2, "an alias");
|
2012-05-03 18:43:29 +02:00
|
|
|
else if (matchingNode->is<FieldNode>())
|
2011-02-17 15:25:56 +01:00
|
|
|
strcat(buffer2, "a field");
|
2012-05-03 18:43:29 +02:00
|
|
|
else if (matchingNode->is<DerivedFieldNode>())
|
2011-02-17 15:25:56 +01:00
|
|
|
strcat(buffer2, "a derived field");
|
|
|
|
else
|
|
|
|
strcat(buffer2, "an item");
|
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
|
|
|
|
|
|
|
**/
|
2012-04-25 03:42:47 +02:00
|
|
|
static ValueExprNode* pass1_make_derived_field(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
|
|
|
|
ValueExprNode* select_item)
|
2003-08-24 04:36:46 +02:00
|
|
|
{
|
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);
|
2012-04-25 03:42:47 +02:00
|
|
|
newField->nodDesc = aliasNode->value->nodDesc;
|
|
|
|
return newField;
|
2011-02-17 15:25:56 +01:00
|
|
|
}
|
|
|
|
else if ((subQueryNode = ExprNode::as<SubQueryNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Try to generate derived field from sub-select
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* derived_field = pass1_make_derived_field(tdbb, dsqlScratch,
|
2012-05-03 18:43:29 +02:00
|
|
|
subQueryNode->value1);
|
2010-12-19 22:42:32 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(derived_field)))
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
derivedField->value = select_item;
|
2011-02-17 15:25:56 +01:00
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((mapNode = ExprNode::as<DsqlMapNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Aggregate's have map on top.
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* derived_field = pass1_make_derived_field(tdbb, dsqlScratch, mapNode->map->map_node);
|
2010-12-19 22:42:32 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
// If we had succesfully made a derived field node change it with orginal map.
|
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(derived_field)))
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
derivedField->value = select_item;
|
2011-02-17 15:25:56 +01:00
|
|
|
derivedField->scope = dsqlScratch->scopeLevel;
|
2012-04-25 03:42:47 +02:00
|
|
|
derived_field->nodDesc = select_item->nodDesc;
|
2011-02-17 15:25:56 +01:00
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((fieldNode = ExprNode::as<FieldNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Create a derived field and hook in.
|
2010-12-19 22:42:32 +01:00
|
|
|
|
2011-02-17 15:25:56 +01:00
|
|
|
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
fieldNode->dsqlField->fld_name, dsqlScratch->scopeLevel, select_item);
|
2012-04-25 03:42:47 +02:00
|
|
|
newField->nodDesc = fieldNode->nodDesc;
|
|
|
|
return newField;
|
2011-02-17 15:25:56 +01:00
|
|
|
}
|
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Create a derived field that points to a derived field.
|
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);
|
2012-04-25 03:42:47 +02:00
|
|
|
newField->nodDesc = select_item->nodDesc;
|
|
|
|
return newField;
|
2003-09-04 17:02:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return select_item;
|
2003-08-24 04:36:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
// Prepare a relation name for processing. Allocate a new relation node.
|
2012-04-25 03:42:47 +02:00
|
|
|
RecordSourceNode* PASS1_relation(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
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-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;
|
2012-04-25 03:42:47 +02:00
|
|
|
return relNode;
|
2011-01-30 01:25:46 +01:00
|
|
|
}
|
|
|
|
else if (context->ctx_procedure)
|
|
|
|
{
|
|
|
|
ProcedureSourceNode* procNode = FB_NEW(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
|
|
|
*tdbb->getDefaultPool(), context->ctx_procedure->prc_name);
|
|
|
|
procNode->dsqlContext = context;
|
2012-04-25 03:42:47 +02:00
|
|
|
return procNode;
|
2011-01-30 01:25:46 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
fb_assert(false);
|
|
|
|
return NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-25 18:34:50 +01:00
|
|
|
// Concatenate 2 input strings together for a new alias string
|
|
|
|
// Note: Both input params can be empty.
|
|
|
|
static string pass1_alias_concat(const string& input1, const string& input2)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2012-12-25 18:34:50 +01:00
|
|
|
string output;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-12-25 18:34:50 +01:00
|
|
|
if (input1.hasData())
|
|
|
|
output.append(input1);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-12-25 18:34:50 +01:00
|
|
|
if (input2.hasData())
|
|
|
|
{
|
|
|
|
if (output.hasData())
|
|
|
|
output.append(" ");
|
|
|
|
output.append(input2);
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-02-10 04:06:57 +01:00
|
|
|
// Wrapper for pass1_rse_impl. Substitute recursive CTE alias (if needed) and call pass1_rse_impl.
|
2012-04-25 03:42:47 +02:00
|
|
|
static RseNode* pass1_rse(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* input,
|
|
|
|
ValueListNode* order, RowsClause* rows, bool updateLock, USHORT flags)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
string save_alias;
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* rseNode = input->as<RseNode>();
|
2012-04-07 05:03:28 +02:00
|
|
|
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-25 03:42:47 +02:00
|
|
|
static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* input,
|
|
|
|
ValueListNode* order, RowsClause* rows, bool updateLock, USHORT flags)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2011-01-09 22:58:56 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
SelectExprNode* selNode = input->as<SelectExprNode>();
|
|
|
|
UnionSourceNode* unionNode = input->as<UnionSourceNode>();
|
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-25 03:42:47 +02:00
|
|
|
RseNode* ret = pass1_rse(dsqlScratch, selNode->querySpec, selNode->orderClause,
|
2012-04-07 06:21:46 +02:00
|
|
|
selNode->rowsClause, updateLock, selNode->dsqlFlags);
|
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
|
|
|
}
|
2012-04-25 03:42:47 +02:00
|
|
|
catch (const Exception&)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
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-05-03 18:43:29 +02:00
|
|
|
fb_assert(unionNode->dsqlClauses->items.hasData());
|
2012-04-07 05:03:28 +02:00
|
|
|
return pass1_union(dsqlScratch, unionNode, order, rows, updateLock, flags);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RseNode* inputRse = input->as<RseNode>();
|
2011-01-09 22:58:56 +01:00
|
|
|
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;
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
rse->dsqlStreams = Node::doDsqlPass(dsqlScratch, inputRse->dsqlFrom, false);
|
|
|
|
RecSourceListNode* streamList = rse->dsqlStreams;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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 &&
|
2012-05-03 18:43:29 +02:00
|
|
|
(streamList->items.getCount() != 1 ||
|
|
|
|
!(relNode = streamList->items[0]->as<RelationSourceNode>()) ||
|
2011-01-09 22:58:56 +01:00
|
|
|
!(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;
|
2012-04-25 03:42:47 +02:00
|
|
|
rse->dsqlWhere = Node::doDsqlPass(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:");
|
2012-04-25 03:42:47 +02:00
|
|
|
//// TODO: Implement Node::print correctly.
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* 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-25 03:42:47 +02:00
|
|
|
if ((flags & RecordSourceNode::DFLAG_VALUE) &&
|
2012-05-03 18:43:29 +02:00
|
|
|
(!selectList || selectList->items.getCount() > 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-25 03:42:47 +02:00
|
|
|
aggregate->dsqlRse = rse;
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
|
2012-04-25 03:42:47 +02:00
|
|
|
parentRse->dsqlStreams = (streamList = FB_NEW(pool) RecSourceListNode(pool, 1));
|
2012-05-03 18:43:29 +02:00
|
|
|
streamList->items[0] = aggregate;
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
if (inputRse->rse_plan)
|
|
|
|
{
|
|
|
|
PsqlChanger changer(dsqlScratch, false);
|
|
|
|
rse->rse_plan = inputRse->rse_plan->dsqlPass(dsqlScratch);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// AB: Pass select-items for distinct operation again, because for
|
2008-05-21 18:02:25 +02:00
|
|
|
// sub-selects a new context number should be generated
|
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;
|
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
|
2012-05-03 18:43:29 +02:00
|
|
|
if (selectList->items.getCount() > MAX_SORT_ITEMS)
|
2009-06-06 04:21:23 +02:00
|
|
|
{
|
|
|
|
// 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
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
FieldRemapper remapper(dsqlScratch, parent_context, false);
|
|
|
|
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlSelectList, rse->dsqlSelectList);
|
2011-01-09 22:58:56 +01:00
|
|
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* valueList = parentRse->dsqlSelectList;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
{ // scope block
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = valueList->items.begin();
|
|
|
|
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
|
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)
|
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlOrder, rse->dsqlOrder);
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlOrder = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// AB: Check for invalid contructions inside the ORDER BY clause
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* valueList = targetRse->dsqlOrder;
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = valueList->items.begin();
|
|
|
|
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
|
2005-05-20 01:41:17 +02:00
|
|
|
{
|
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)
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlDistinct);
|
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;
|
2012-04-25 03:42:47 +02:00
|
|
|
parentRse->dsqlWhere = Node::doDsqlPass(dsqlScratch, inputRse->dsqlHaving, false);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inHavingClause;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode::doDsqlFieldRemapper(remapper, 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
|
|
|
|
2013-12-05 14:59:12 +01:00
|
|
|
// When we're in a outer-join part mark context for it.
|
|
|
|
if (dsqlScratch->inOuterJoin)
|
|
|
|
parent_context->ctx_flags |= CTX_outer_join;
|
|
|
|
parent_context->ctx_in_outer_join = dsqlScratch->inOuterJoin;
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
AggregateSourceNode* window = FB_NEW(pool) AggregateSourceNode(pool);
|
|
|
|
window->dsqlContext = parent_context;
|
2012-04-25 03:42:47 +02:00
|
|
|
window->dsqlRse = rse;
|
2011-01-09 22:58:56 +01:00
|
|
|
window->dsqlWindow = true;
|
|
|
|
|
|
|
|
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
|
2012-04-25 03:42:47 +02:00
|
|
|
parentRse->dsqlStreams = streamList = FB_NEW(pool) RecSourceListNode(pool, 1);
|
2012-05-03 18:43:29 +02:00
|
|
|
streamList->items[0] = window;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* valueList = rse->dsqlSelectList;
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = valueList->items.begin();
|
|
|
|
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
FieldRemapper remapper(dsqlScratch, parent_context, true);
|
|
|
|
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlSelectList, 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
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
partitionMap->partitionRemapped = Node::doDsqlPass(dsqlScratch, partitionMap->partition);
|
|
|
|
|
|
|
|
FieldRemapper remapper2(dsqlScratch, parent_context, true, partitionMap->partition,
|
2010-01-23 04:02:53 +01:00
|
|
|
partitionMap->order);
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode::doDsqlFieldRemapper(remapper2, partitionMap->partitionRemapped);
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* valueList = rse->dsqlOrder;
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = valueList->items.begin();
|
|
|
|
for (const NestConst<ValueExprNode>* const end = valueList->items.end(); ptr != end; ++ptr)
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlOrder, rse->dsqlOrder);
|
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
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
ExprNode::doDsqlFieldRemapper(remapper, parentRse->dsqlDistinct, rse->dsqlDistinct);
|
2011-01-09 22:58:56 +01:00
|
|
|
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-25 03:42:47 +02:00
|
|
|
static ValueListNode* pass1_sel_list(DsqlCompilerScratch* dsqlScratch, ValueListNode* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* retList = FB_NEW(pool) ValueListNode(pool, 0u);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* ptr = input->items.begin();
|
|
|
|
for (const NestConst<ValueExprNode>* const end = input->items.end(); ptr != end; ++ptr)
|
2012-04-25 03:42:47 +02:00
|
|
|
retList->add(Node::doDsqlPass(dsqlScratch, *ptr, false));
|
|
|
|
|
|
|
|
return retList;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-04 02:47:49 +01:00
|
|
|
// Process ORDER BY list, which may contain an ordinal or alias which references the select list.
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* PASS1_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* input, ValueListNode* selectList)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
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();
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (!input)
|
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));
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (input->items.getCount() > MAX_SORT_ITEMS)
|
2006-09-30 11:10:28 +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) <<
|
|
|
|
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
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueListNode> node = FB_NEW(pool) ValueListNode(pool, input->items.getCount());
|
|
|
|
NestConst<ValueExprNode>* ptr2 = node->items.begin();
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2012-05-20 12:00:52 +02:00
|
|
|
for (size_t sortloop = 0; sortloop < input->items.getCount(); ++sortloop)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
DEV_BLKCHK(input->items[sortloop], dsql_type_nod);
|
|
|
|
NestConst<OrderNode> node1 = input->items[sortloop]->as<OrderNode>();
|
2012-04-08 06:15:09 +02:00
|
|
|
if (!node1)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
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-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode> orderValue = node1->value;
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<CollateNode> collateNode = orderValue->as<CollateNode>();
|
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-05-03 18:43:29 +02:00
|
|
|
orderValue = collateNode->arg;
|
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-05-03 18:43:29 +02:00
|
|
|
if ((field = orderValue->as<FieldNode>()))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueExprNode* aliasNode = NULL;
|
2012-03-25 03:08:55 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// check for alias or field node
|
2013-05-30 10:20:53 +02:00
|
|
|
if (selectList && field->dsqlQualifier.isEmpty() && field->dsqlName.hasData())
|
2012-03-25 03:08:55 +02:00
|
|
|
{
|
|
|
|
// AB: Check first against the select list for matching column.
|
|
|
|
// When no matches at all are found we go on with our
|
|
|
|
// normal way of field name lookup.
|
|
|
|
aliasNode = PASS1_lookup_alias(dsqlScratch, field->dsqlName, selectList, true);
|
|
|
|
}
|
|
|
|
|
2012-04-08 06:15:09 +02:00
|
|
|
orderValue = aliasNode ? aliasNode : field->internalDsqlPass(dsqlScratch, NULL);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2012-05-03 18:43:29 +02:00
|
|
|
else if ((literal = orderValue->as<LiteralNode>()) && literal->litDesc.dsc_dtype == dtype_long)
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
2010-10-24 02:26:00 +02:00
|
|
|
const ULONG position = literal->getSlong();
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (position < 1 || !selectList || position > (ULONG) selectList->items.getCount())
|
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-05-03 18:43:29 +02:00
|
|
|
orderValue = Node::doDsqlPass(dsqlScratch, selectList->items[position - 1], false);
|
2008-04-09 15:47:15 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
else
|
2012-04-25 03:42:47 +02:00
|
|
|
orderValue = Node::doDsqlPass(dsqlScratch, orderValue, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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-25 03:42:47 +02:00
|
|
|
orderValue = 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-25 03:42:47 +02:00
|
|
|
*ptr2++ = node2;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-10 04:06:57 +01:00
|
|
|
// Handle a UNION of substreams, generating a mapping of all the fields and adding an implicit
|
|
|
|
// PROJECT clause to ensure that all the records returned are unique.
|
2012-04-07 05:03:28 +02:00
|
|
|
static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* input,
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* orderList, RowsClause* rows, bool updateLock, USHORT flags)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
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);
|
2012-04-25 03:42:47 +02:00
|
|
|
|
|
|
|
unionRse->dsqlStreams = FB_NEW(pool) RecSourceListNode(pool, 1);
|
2012-05-03 18:43:29 +02:00
|
|
|
unionRse->dsqlStreams->items[0] = unionSource;
|
2012-04-25 03:42:47 +02:00
|
|
|
unionSource->dsqlParentRse = unionRse;
|
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-25 03:42:47 +02:00
|
|
|
unionSource->dsqlClauses = FB_NEW(pool) RecSourceListNode(pool,
|
2012-05-03 18:43:29 +02:00
|
|
|
input->dsqlClauses->items.getCount());
|
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
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<RecordSourceNode>* uptr = unionSource->dsqlClauses->items.begin();
|
2009-12-20 22:01:10 +01:00
|
|
|
const DsqlContextStack::const_iterator base(*dsqlScratch->context);
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<RecordSourceNode>* ptr = input->dsqlClauses->items.begin();
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
for (const NestConst<RecordSourceNode>* const end = input->dsqlClauses->items.end();
|
2012-04-25 03:42:47 +02:00
|
|
|
ptr != end;
|
|
|
|
++ptr, ++uptr)
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
2012-04-25 03:42:47 +02:00
|
|
|
*uptr = 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-05-03 18:43:29 +02:00
|
|
|
if (input->recursive && (ptr == input->dsqlClauses->items.begin()))
|
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.
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueListNode* items = unionSource->dsqlClauses->items[0]->as<RseNode>()->dsqlSelectList;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// loop through the list nodes, checking to be sure that they have the
|
2003-08-15 02:02:18 +02:00
|
|
|
// same number of items
|
2003-10-05 08:37:26 +02:00
|
|
|
|
2012-05-20 12:00:52 +02:00
|
|
|
for (size_t i = 1; i < unionSource->dsqlClauses->items.getCount(); ++i)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
const ValueListNode* nod1 = unionSource->dsqlClauses->items[i]->as<RseNode>()->dsqlSelectList;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (items->items.getCount() != nod1->items.getCount())
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2009-01-07 10:30:57 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
// overload of msg
|
|
|
|
Arg::Gds(isc_dsql_count_mismatch));
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2009-01-07 10:30:57 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
// Comment below belongs to the old code (way a union was handled).
|
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.
|
2012-04-25 03:42:47 +02:00
|
|
|
ValueListNode* tmp_list = FB_NEW(pool) ValueListNode(
|
2012-05-03 18:43:29 +02:00
|
|
|
pool, unionSource->dsqlClauses->items.getCount());
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-05-20 12:00:52 +02:00
|
|
|
for (size_t j = 0; j < items->items.getCount(); ++j)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2012-05-20 12:00:52 +02:00
|
|
|
for (size_t i = 0; i < unionSource->dsqlClauses->items.getCount(); ++i)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueListNode* nod1 = unionSource->dsqlClauses->items[i]->as<RseNode>()->dsqlSelectList;
|
|
|
|
MAKE_desc(dsqlScratch, &nod1->items[j]->nodDesc, nod1->items[j]);
|
|
|
|
tmp_list->items[i] = nod1->items[j];
|
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
|
2012-04-25 03:42:47 +02:00
|
|
|
// descriptor flags are copied onto items->nod_arg[]->nodDesc.
|
2003-08-18 23:37:47 +02:00
|
|
|
// Bug 65584.
|
|
|
|
// -Sudesh 07/28/1999.
|
2009-11-19 10:37:10 +01:00
|
|
|
if (i > 0)
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
if (nod1->items[j]->nodDesc.dsc_flags & DSC_nullable)
|
|
|
|
items->items[j]->nodDesc.dsc_flags |= DSC_nullable;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
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
|
2012-05-03 18:43:29 +02:00
|
|
|
items->items[j]->nodDesc.dsc_flags &= ~DSC_null;
|
|
|
|
items->items[j]->nodDesc.dsc_flags |= (desc.dsc_flags & DSC_null);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
pass1_union_auto_cast(dsqlScratch, unionSource->dsqlClauses, desc, j);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
items = unionSource->dsqlClauses->items[0]->as<RseNode>()->dsqlSelectList;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// Create mappings for union.
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueListNode* union_items = FB_NEW(pool) ValueListNode(pool, items->items.getCount());
|
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;
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode>* uptr = items->items.begin();
|
|
|
|
NestConst<ValueExprNode>* ptr = union_items->items.begin();
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
for (const NestConst<ValueExprNode>* const end = union_items->items.end(); ptr != end; ++ptr)
|
2005-05-20 01:41:17 +02:00
|
|
|
{
|
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++;
|
2012-04-10 04:37:34 +02:00
|
|
|
fb_assert(count != 0); // no wrap, please!
|
2005-05-20 01:41:17 +02:00
|
|
|
map->map_node = *uptr++;
|
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
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
*ptr = FB_NEW(pool) DsqlMapNode(pool, union_context, map);
|
2005-05-20 01:41:17 +02:00
|
|
|
}
|
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.
|
2012-04-25 03:42:47 +02:00
|
|
|
if (orderList)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueListNode* sort = FB_NEW(pool) ValueListNode(pool, orderList->items.getCount());
|
|
|
|
NestConst<ValueExprNode>* uptr = sort->items.begin();
|
|
|
|
NestConst<ValueExprNode>* ptr = orderList->items.begin();
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
for (const NestConst<ValueExprNode>* const end = orderList->items.end();
|
2012-04-25 03:42:47 +02:00
|
|
|
ptr != end;
|
|
|
|
++ptr, ++uptr)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
OrderNode* order1 = (*ptr)->as<OrderNode>();
|
2012-05-03 18:43:29 +02:00
|
|
|
const ValueExprNode* position = order1->value;
|
2012-04-25 03:42:47 +02:00
|
|
|
const CollateNode* collateNode = position->as<CollateNode>();
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if (collateNode)
|
2012-05-03 18:43:29 +02:00
|
|
|
position = collateNode->arg;
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
const LiteralNode* literal = position->as<LiteralNode>();
|
2010-10-24 02:26:00 +02:00
|
|
|
|
|
|
|
if (!literal || literal->litDesc.dsc_dtype != dtype_long)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
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();
|
|
|
|
|
2012-05-20 12:00:52 +02:00
|
|
|
if (number < 1 || ULONG(number) > union_items->items.getCount())
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
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-05-03 18:43:29 +02:00
|
|
|
OrderNode* order2 = FB_NEW(pool) OrderNode(pool, union_items->items[number - 1]);
|
2012-04-25 03:42:47 +02:00
|
|
|
*uptr = order2;
|
2012-04-08 06:15:09 +02:00
|
|
|
order2->descending = order1->descending;
|
2010-10-24 02:26:00 +02:00
|
|
|
|
2012-03-25 03:08:55 +02:00
|
|
|
if (collateNode)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
order2->value = CollateNode::pass1Collate(dsqlScratch,
|
|
|
|
order2->value, collateNode->collation);
|
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
|
|
|
|
|
|
|
|
**/
|
2012-04-25 03:42:47 +02:00
|
|
|
static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* input,
|
2012-05-20 12:00:52 +02:00
|
|
|
const dsc& desc, size_t position)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RecSourceListNode* recSourceList;
|
|
|
|
RseNode* rseNode;
|
|
|
|
UnionSourceNode* unionNode;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((recSourceList = input->as<RecSourceListNode>()))
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<RecordSourceNode>* ptr = recSourceList->items.begin();
|
|
|
|
for (const NestConst<RecordSourceNode>* const end = recSourceList->items.end(); ptr != end; ++ptr)
|
2012-04-25 03:42:47 +02:00
|
|
|
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
|
|
|
|
}
|
|
|
|
else if ((rseNode = input->as<RseNode>()) && !rseNode->dsqlExplicitJoin &&
|
|
|
|
!rseNode->dsqlContext) // not derived table
|
|
|
|
{
|
|
|
|
pass1_union_auto_cast(dsqlScratch, rseNode->dsqlStreams, desc, position);
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (rseNode->dsqlStreams->items.getCount() == 1 &&
|
|
|
|
(unionNode = rseNode->dsqlStreams->items[0]->as<UnionSourceNode>()) &&
|
2012-04-25 03:42:47 +02:00
|
|
|
unionNode->dsqlParentRse == rseNode)
|
|
|
|
{
|
|
|
|
// We're now in a UNION under a UNION so don't change the existing mappings.
|
|
|
|
// Only replace the node where the map points to, because they could be changed.
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueListNode* sub_rse_items =
|
|
|
|
unionNode->dsqlClauses->items[0]->as<RseNode>()->dsqlSelectList;
|
|
|
|
dsql_map* map = rseNode->dsqlSelectList->items[position]->as<DsqlMapNode>()->map;
|
|
|
|
map->map_node = sub_rse_items->items[position];
|
|
|
|
rseNode->dsqlSelectList->items[position]->nodDesc = desc;
|
2012-04-25 03:42:47 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ValueListNode* list = rseNode->dsqlSelectList;
|
|
|
|
|
2012-05-20 12:00:52 +02:00
|
|
|
if (position >= list->items.getCount())
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
// Internal dsql error: column position out of range in pass1_union_auto_cast
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_auto_field_bad_pos));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueExprNode* select_item = list->items[position];
|
2012-04-25 03:42:47 +02:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nodDesc, select_item);
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (select_item->nodDesc.dsc_dtype != desc.dsc_dtype ||
|
|
|
|
select_item->nodDesc.dsc_length != desc.dsc_length ||
|
|
|
|
select_item->nodDesc.dsc_scale != desc.dsc_scale ||
|
|
|
|
select_item->nodDesc.dsc_sub_type != desc.dsc_sub_type)
|
|
|
|
{
|
|
|
|
// Because this select item has a different descriptor then
|
|
|
|
// our finally descriptor CAST it.
|
|
|
|
CastNode* castNode = NULL;
|
|
|
|
DsqlAliasNode* newAliasNode = NULL;
|
|
|
|
DsqlAliasNode* aliasNode;
|
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
|
|
|
// Pick a existing cast if available else make a new one.
|
|
|
|
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)) &&
|
2012-05-03 18:43:29 +02:00
|
|
|
aliasNode->value && (castNode = aliasNode->value->as<CastNode>()))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
}
|
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)) &&
|
2012-05-03 18:43:29 +02:00
|
|
|
(castNode = derivedField->value->as<CastNode>()))
|
2012-04-25 03:42:47 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
else if ((castNode = ExprNode::as<CastNode>(select_item)))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
castNode = FB_NEW(*tdbb->getDefaultPool()) CastNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
|
|
|
|
castNode->dsqlField = FB_NEW(*tdbb->getDefaultPool()) dsql_fld(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
|
|
|
|
// We want to leave the ALIAS node on his place, because a UNION
|
|
|
|
// uses the select_items from the first sub-rse to determine the
|
|
|
|
// columnname.
|
|
|
|
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)))
|
2012-05-03 18:43:29 +02:00
|
|
|
castNode->source = aliasNode->value;
|
2012-04-25 03:42:47 +02:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
2012-05-03 18:43:29 +02:00
|
|
|
castNode->source = derivedField->value;
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
2012-05-03 18:43:29 +02:00
|
|
|
castNode->source = select_item;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
// When a cast is created we're losing our fieldname, thus
|
|
|
|
// create an alias to keep it.
|
|
|
|
const ValueExprNode* name_node = select_item;
|
|
|
|
const DsqlMapNode* mapNode;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
while ((mapNode = ExprNode::as<DsqlMapNode>(name_node)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
// Skip all the DsqlMapNodes.
|
|
|
|
name_node = mapNode->map->map_node;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
const FieldNode* fieldNode;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((fieldNode = ExprNode::as<FieldNode>(name_node)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
// Create new node for alias and copy fieldname.
|
|
|
|
newAliasNode = FB_NEW(*tdbb->getDefaultPool()) DsqlAliasNode(
|
|
|
|
*tdbb->getDefaultPool(), fieldNode->dsqlField->fld_name, NULL);
|
|
|
|
// The alias value will be assigned a bit later.
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
dsql_fld* field = castNode->dsqlField;
|
|
|
|
// Copy the descriptor to a field, because the gen_cast
|
|
|
|
// uses a dsql field type.
|
2012-12-05 03:07:37 +01:00
|
|
|
field->dtype = desc.dsc_dtype;
|
|
|
|
field->scale = desc.dsc_scale;
|
|
|
|
field->subType = desc.dsc_sub_type;
|
|
|
|
field->length = desc.dsc_length;
|
|
|
|
field->flags = (desc.dsc_flags & DSC_nullable) ? FLD_nullable : 0;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (desc.dsc_dtype <= dtype_any_text)
|
|
|
|
{
|
2012-12-05 03:07:37 +01:00
|
|
|
field->textType = desc.dsc_sub_type;
|
|
|
|
field->charSetId = INTL_GET_CHARSET(&desc);
|
|
|
|
field->collationId = INTL_GET_COLLATE(&desc);
|
2012-04-25 03:42:47 +02:00
|
|
|
}
|
|
|
|
else if (desc.dsc_dtype == dtype_blob)
|
|
|
|
{
|
2012-12-05 03:07:37 +01:00
|
|
|
field->charSetId = desc.dsc_scale;
|
|
|
|
field->collationId = desc.dsc_flags >> 8;
|
2012-04-25 03:42:47 +02:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
// Finally copy the descriptors to the root nodes and swap
|
|
|
|
// the necessary nodes.
|
|
|
|
castNode->nodDesc = desc;
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if (select_item->nodDesc.dsc_flags & DSC_nullable)
|
|
|
|
castNode->nodDesc.dsc_flags |= DSC_nullable;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((aliasNode = ExprNode::as<DsqlAliasNode>(select_item)))
|
|
|
|
{
|
|
|
|
aliasNode->value = castNode;
|
|
|
|
aliasNode->value->nodDesc = desc;
|
|
|
|
select_item->nodDesc = desc;
|
|
|
|
}
|
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
derivedField->value = castNode;
|
|
|
|
derivedField->value->nodDesc = desc;
|
2012-04-25 03:42:47 +02:00
|
|
|
select_item->nodDesc = desc;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If a new alias was created for keeping original field-name
|
|
|
|
// make the alias the "top" node.
|
|
|
|
if (newAliasNode)
|
|
|
|
{
|
|
|
|
newAliasNode->value = castNode;
|
|
|
|
newAliasNode->value->nodDesc = castNode->nodDesc;
|
2012-05-03 18:43:29 +02:00
|
|
|
list->items[position] = newAliasNode;
|
2012-04-25 03:42:47 +02:00
|
|
|
}
|
|
|
|
else
|
2012-05-03 18:43:29 +02:00
|
|
|
list->items[position] = castNode;
|
2012-04-25 03:42:47 +02:00
|
|
|
}
|
2011-01-22 21:40:04 +01:00
|
|
|
}
|
|
|
|
}
|
2012-04-25 03:42:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((unionNode = input->as<UnionSourceNode>()))
|
|
|
|
{
|
|
|
|
recSourceList = unionNode->dsqlClauses;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
for (NestConst<RecordSourceNode>* ptr = recSourceList->items.begin();
|
|
|
|
ptr != recSourceList->items.end();
|
2012-04-25 03:42:47 +02:00
|
|
|
++ptr)
|
|
|
|
{
|
|
|
|
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
|
2011-01-09 22:58:56 +01:00
|
|
|
}
|
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.
|
2012-04-25 03:42:47 +02:00
|
|
|
DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node, dsql_ctx* context,
|
|
|
|
ValueListNode* partitionNode, ValueListNode* orderNode)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
MAKE_desc(dsqlScratch, &node->nodDesc, node);
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2013-07-16 18:18:22 +02:00
|
|
|
return FB_NEW(*tdbb->getDefaultPool()) DsqlMapNode(*tdbb->getDefaultPool(), context, map);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
// For each relation in the list, flag the relation's context as having a parent context.
|
|
|
|
// Be sure to handle joins (Bug 6674).
|
|
|
|
static void remap_streams_to_parent_context(ExprNode* input, dsql_ctx* parent_context)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(parent_context, dsql_type_ctx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RecSourceListNode* listNode;
|
|
|
|
ProcedureSourceNode* procNode;
|
|
|
|
RelationSourceNode* relNode;
|
|
|
|
RseNode* rseNode;
|
|
|
|
UnionSourceNode* unionNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
if ((listNode = input->as<RecSourceListNode>()))
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<RecordSourceNode>* ptr = listNode->items.begin();
|
|
|
|
for (const NestConst<RecordSourceNode>* const end = listNode->items.end(); ptr != end; ++ptr)
|
2012-04-25 03:42:47 +02:00
|
|
|
remap_streams_to_parent_context(*ptr, parent_context);
|
|
|
|
}
|
|
|
|
else if ((procNode = input->as<ProcedureSourceNode>()))
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(procNode->dsqlContext, dsql_type_ctx);
|
|
|
|
procNode->dsqlContext->ctx_parent = parent_context;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2012-04-25 03:42:47 +02:00
|
|
|
else if ((relNode = input->as<RelationSourceNode>()))
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(relNode->dsqlContext, dsql_type_ctx);
|
|
|
|
relNode->dsqlContext->ctx_parent = parent_context;
|
|
|
|
}
|
|
|
|
else if ((rseNode = input->as<RseNode>()))
|
|
|
|
remap_streams_to_parent_context(rseNode->dsqlStreams, parent_context);
|
|
|
|
else if ((unionNode = input->as<UnionSourceNode>()))
|
|
|
|
remap_streams_to_parent_context(unionNode->dsqlClauses, parent_context);
|
|
|
|
else
|
|
|
|
fb_assert(input->as<AggregateSourceNode>());
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
// Setup the datatype of a parameter.
|
|
|
|
bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, ValueExprNode* inNode,
|
|
|
|
const dsc* desc, bool force_varchar)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2012-04-25 03:42:47 +02:00
|
|
|
return inNode && inNode->setParameterType(dsqlScratch, desc, force_varchar);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
// Setup the datatype of a parameter.
|
|
|
|
bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, ValueExprNode* inNode,
|
|
|
|
ValueExprNode* node, bool force_varchar)
|
|
|
|
{
|
|
|
|
if (!inNode)
|
2003-11-07 15:10:16 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
MAKE_desc(dsqlScratch, &node->nodDesc, node);
|
|
|
|
return inNode->setParameterType(dsqlScratch, &node->nodDesc, force_varchar);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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.
|
2012-05-03 18:43:29 +02:00
|
|
|
bool dsql_ctx::getImplicitJoinField(const MetaName& name, NestConst<ValueExprNode>& 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).
|
2012-04-25 03:42:47 +02:00
|
|
|
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueListNode* partitionNode,
|
|
|
|
ValueListNode* 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
|
|
|
}
|