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.
|
2002-09-29 01:52:36 +02:00
|
|
|
* Modified functions pass1_field, pass1_rse, copy_field, pass1_sort.
|
|
|
|
* Functions pass1_found_aggregate and pass1_found_field added.
|
2002-10-25 10:29:12 +02:00
|
|
|
*
|
2002-10-29 21:20:44 +01:00
|
|
|
* 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks
|
|
|
|
*
|
2002-10-25 10:29:12 +02:00
|
|
|
* 2002.10.25 Dmitry Yemanov: Re-allowed plans in triggers
|
2002-10-29 21:20:44 +01:00
|
|
|
*
|
|
|
|
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
|
2002-12-03 19:04:36 +01:00
|
|
|
*
|
|
|
|
* 2002.12.03 Dmitry Yemanov: Implemented ORDER BY clause in subqueries
|
2002-12-18 16:01:50 +01:00
|
|
|
*
|
|
|
|
* 2002.12.18 Dmitry Yemanov: Fixed bug with BREAK and partially implemented
|
|
|
|
* SQL-compliant labels and LEAVE statement
|
2003-01-11 03:49:13 +01:00
|
|
|
*
|
|
|
|
* 2003.01.11 Arno Brinkman: Reworked a lot of functions for bringing back backwards compatibilty
|
|
|
|
* with sub-selects and aggregates.
|
2003-01-15 13:00:33 +01:00
|
|
|
*
|
|
|
|
* 2003.01.14 Dmitry Yemanov: Fixed bug with cursors in triggers
|
|
|
|
*
|
|
|
|
* 2003.01.15 Dmitry Yemanov: Added support for parametrized events
|
2003-04-06 13:20:24 +02:00
|
|
|
*
|
|
|
|
* 2003.04.05 Dmitry Yemanov: Changed logic of ORDER BY with collations
|
|
|
|
* (because of the parser change)
|
2003-08-16 02:36:54 +02:00
|
|
|
*
|
|
|
|
* 2003.08.14 Arno Brinkman: Added derived table support.
|
|
|
|
*
|
|
|
|
* 2003.08.16 Arno Brinkman: Changed ambiguous column name checking.
|
2003-11-02 13:28:30 +01:00
|
|
|
*
|
|
|
|
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL.
|
2004-01-16 11:43:21 +01:00
|
|
|
*
|
2005-05-28 00:45:31 +02:00
|
|
|
* 2004.01.16 Vlad Horsun: added support for default parameters and
|
2004-01-16 11:43:21 +01:00
|
|
|
* EXECUTE BLOCK statement
|
2007-03-06 16:54:34 +01:00
|
|
|
*
|
|
|
|
* Adriano dos Santos Fernandes
|
|
|
|
*
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
|
2001-07-30 01:43:24 +02:00
|
|
|
#include "firebird.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include <string.h>
|
2004-04-29 00:00:03 +02:00
|
|
|
#include <stdio.h>
|
2003-11-08 00:27:24 +01:00
|
|
|
#include "../jrd/ibase.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/dsql.h"
|
2008-02-28 14:48:16 +01:00
|
|
|
#include "../dsql/node.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../dsql/Nodes.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../dsql/BoolNodes.h"
|
Refactor a number of expression nodes: nod_add, nod_divide, nod_multiply, nod_negate, nod_user_name, nod_subtract, nod_current_date, nod_current_time, nod_current_timestamp, nod_add2, nod_subtract2, nod_multiply2, nod_divide2, nod_current_role, nod_internal_info
2010-09-04 23:36:41 +02:00
|
|
|
#include "../dsql/ExprNodes.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/intl.h"
|
|
|
|
#include "../jrd/blr.h"
|
2008-02-28 14:48:16 +01:00
|
|
|
#include "../jrd/jrd.h"
|
2005-05-28 00:45:31 +02:00
|
|
|
#include "../jrd/constants.h"
|
2008-03-01 20:14:46 +01:00
|
|
|
#include "../jrd/intl_classes.h"
|
2011-01-09 22:58:56 +01:00
|
|
|
#include "../jrd/RecordSourceNodes.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../dsql/DdlNodes.h"
|
2009-10-24 19:45:33 +02:00
|
|
|
#include "../dsql/StmtNodes.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/ddl_proto.h"
|
|
|
|
#include "../dsql/errd_proto.h"
|
2010-06-10 04:03:03 +02:00
|
|
|
#include "../dsql/gen_proto.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/hsh_proto.h"
|
|
|
|
#include "../dsql/make_proto.h"
|
|
|
|
#include "../dsql/metd_proto.h"
|
2005-06-10 04:03:08 +02:00
|
|
|
#include "../dsql/pass1_proto.h"
|
|
|
|
#include "../dsql/utld_proto.h"
|
2010-04-15 16:40:27 +02:00
|
|
|
#include "../dsql/DSqlDataTypeUtil.h"
|
2010-10-12 10:02:57 +02:00
|
|
|
#include "../common/dsc_proto.h"
|
2008-03-01 20:14:46 +01:00
|
|
|
#include "../jrd/intl_proto.h"
|
2008-02-28 14:48:16 +01:00
|
|
|
#include "../jrd/jrd_proto.h"
|
2005-05-28 00:45:31 +02:00
|
|
|
#include "../jrd/thread_proto.h"
|
2010-10-12 10:02:57 +02:00
|
|
|
#include "../yvalve/why_proto.h"
|
2007-06-09 21:18:21 +02:00
|
|
|
#include "../jrd/SysFunction.h"
|
2004-01-16 11:43:21 +01:00
|
|
|
#include "../common/classes/array.h"
|
2006-05-19 17:17:02 +02:00
|
|
|
#include "../common/classes/auto.h"
|
2003-12-31 06:36:12 +01:00
|
|
|
#include "../common/utils_proto.h"
|
2007-11-29 18:53:38 +01:00
|
|
|
#include "../common/config/config.h"
|
2008-08-15 13:21:47 +02:00
|
|
|
#include "../common/StatusArg.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
using namespace Jrd;
|
|
|
|
using namespace Dsql;
|
2008-08-15 13:21:47 +02:00
|
|
|
using namespace Firebird;
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef DEV_BUILD
|
2008-02-28 14:48:16 +01:00
|
|
|
static void DSQL_pretty(const dsql_nod*, int);
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
static void assign_fld_dtype_from_dsc(dsql_fld*, const dsc*);
|
2006-09-03 03:09:23 +02:00
|
|
|
static dsql_nod* explode_fields(dsql_rel*);
|
2006-09-14 04:05:32 +02:00
|
|
|
static void field_appears_once(const dsql_nod*, const dsql_nod*, const bool, const char*);
|
2006-09-03 03:09:23 +02:00
|
|
|
static void field_duplication(const TEXT*, const TEXT*, const dsql_nod*, const char*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_par* find_dbkey(const dsql_req*, const dsql_nod*);
|
|
|
|
static dsql_par* find_record_version(const dsql_req*, const dsql_nod*);
|
2006-09-14 04:05:32 +02:00
|
|
|
static dsql_ctx* get_context(const dsql_nod* node);
|
2011-01-30 02:17:41 +01:00
|
|
|
static void get_contexts(DsqlContextStack& contexts, const dsql_nod* node);
|
2010-06-10 04:03:03 +02:00
|
|
|
static dsql_nod* nullify_returning(DsqlCompilerScratch*, dsql_nod* input, dsql_nod** list = NULL);
|
2011-01-09 22:58:56 +01:00
|
|
|
static dsql_ctx* pass1_alias_list(DsqlCompilerScratch*, dsql_nod*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_ctx* pass1_alias(DsqlCompilerScratch*, DsqlContextStack&, dsql_str*);
|
2011-01-30 01:25:46 +01:00
|
|
|
static dsql_str* pass1_alias_concat(const dsql_str*, const char*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_rel* pass1_base_table(DsqlCompilerScratch*, const dsql_rel*, const dsql_str*);
|
|
|
|
static void pass1_blob(DsqlCompilerScratch*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_collate(DsqlCompilerScratch*, dsql_nod*, const dsql_str*);
|
|
|
|
static dsql_ctx* pass1_cursor_context(DsqlCompilerScratch*, const dsql_nod*, const dsql_nod*);
|
|
|
|
static dsql_nod* pass1_cursor_reference(DsqlCompilerScratch*, const dsql_nod*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_delete(DsqlCompilerScratch*, dsql_nod*);
|
2008-10-04 04:48:35 +02:00
|
|
|
static void pass1_expand_contexts(DsqlContextStack& contexts, dsql_ctx* context);
|
2011-01-30 01:25:46 +01:00
|
|
|
static dsql_nod* pass1_derived_table(DsqlCompilerScratch*, dsql_nod*, const char*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_expand_select_list(DsqlCompilerScratch*, dsql_nod*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_field(DsqlCompilerScratch*, dsql_nod*, const bool, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_group_by_list(DsqlCompilerScratch*, dsql_nod*, dsql_nod*);
|
2011-02-06 19:13:12 +01:00
|
|
|
static dsql_nod* pass1_hidden_variable(DsqlCompilerScratch* dsqlScratch, dsql_nod* expr);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_insert(DsqlCompilerScratch*, dsql_nod*, bool);
|
2011-01-20 05:41:10 +01:00
|
|
|
static void pass1_limit(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, RseNode*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_make_derived_field(DsqlCompilerScratch*, thread_db*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_merge(DsqlCompilerScratch*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_relation(DsqlCompilerScratch*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_returning(DsqlCompilerScratch*, const dsql_nod*);
|
|
|
|
static dsql_nod* pass1_rse(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
|
|
|
static dsql_nod* pass1_rse_impl(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
|
|
|
static dsql_nod* pass1_sel_list(DsqlCompilerScratch*, dsql_nod*, bool);
|
|
|
|
static dsql_nod* pass1_sort(DsqlCompilerScratch*, dsql_nod*, dsql_nod*);
|
2010-02-16 17:28:54 +01:00
|
|
|
static dsql_nod* pass1_union(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
2010-11-01 14:45:52 +01:00
|
|
|
static void pass1_union_auto_cast(DsqlCompilerScratch*, dsql_nod*, const dsc&, SSHORT,
|
|
|
|
bool in_select_list = false);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_update(DsqlCompilerScratch*, dsql_nod*, bool);
|
|
|
|
static dsql_nod* pass1_update_or_insert(DsqlCompilerScratch*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_variable(DsqlCompilerScratch*, dsql_nod*);
|
2003-11-10 10:16:38 +01:00
|
|
|
static void remap_streams_to_parent_context(dsql_nod*, dsql_ctx*);
|
2010-08-02 04:22:26 +02:00
|
|
|
static dsql_fld* resolve_context(DsqlCompilerScratch*, const dsql_str*, dsql_ctx*, bool);
|
2003-11-18 08:58:35 +01:00
|
|
|
static void set_parameters_name(dsql_nod*, const dsql_nod*);
|
|
|
|
static void set_parameter_name(dsql_nod*, const dsql_nod*, const dsql_rel*);
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_savepoint(const DsqlCompilerScratch*, dsql_nod*);
|
|
|
|
static dsql_nod* process_returning(DsqlCompilerScratch*, dsql_nod*);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class ReturningProcessor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
// Play with contexts for RETURNING purposes.
|
|
|
|
// Its assumed that oldContext is already on the stack.
|
|
|
|
// Changes oldContext name to "OLD".
|
|
|
|
ReturningProcessor(DsqlCompilerScratch* aScratch, dsql_ctx* oldContext, dsql_ctx* modContext)
|
|
|
|
: scratch(aScratch),
|
2011-01-30 01:25:46 +01:00
|
|
|
autoAlias(&oldContext->ctx_alias, OLD_CONTEXT),
|
2010-06-10 04:03:03 +02:00
|
|
|
autoInternalAlias(&oldContext->ctx_internal_alias, oldContext->ctx_alias),
|
|
|
|
autoFlags(&oldContext->ctx_flags, oldContext->ctx_flags | CTX_system | CTX_returning),
|
|
|
|
hasModContext(modContext != NULL)
|
|
|
|
{
|
|
|
|
// Clone the modify/old context and push with name "NEW" in a greater scope level.
|
|
|
|
|
|
|
|
dsql_ctx* newContext = FB_NEW(scratch->getPool()) dsql_ctx(scratch->getPool());
|
|
|
|
|
|
|
|
if (modContext)
|
|
|
|
{
|
|
|
|
// push the modify context in the same scope level
|
|
|
|
scratch->context->push(modContext);
|
|
|
|
*newContext = *modContext;
|
|
|
|
newContext->ctx_flags |= CTX_system;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This is NEW in the context of a DELETE. Mark it as NULL.
|
|
|
|
*newContext = *oldContext;
|
|
|
|
newContext->ctx_flags |= CTX_null;
|
|
|
|
|
|
|
|
// Remove the system flag, so unqualified fields could be resolved to this context.
|
|
|
|
oldContext->ctx_flags &= ~CTX_system;
|
|
|
|
}
|
|
|
|
|
|
|
|
newContext->ctx_alias = newContext->ctx_internal_alias =
|
|
|
|
MAKE_cstring(NEW_CONTEXT)->str_data;
|
|
|
|
newContext->ctx_flags |= CTX_returning;
|
|
|
|
scratch->context->push(newContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
~ReturningProcessor()
|
|
|
|
{
|
|
|
|
// Restore the context stack.
|
|
|
|
scratch->context->pop();
|
|
|
|
if (hasModContext)
|
|
|
|
scratch->context->pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process the RETURNING clause.
|
|
|
|
dsql_nod* process(dsql_nod* node)
|
|
|
|
{
|
|
|
|
return process_returning(scratch, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clone a RETURNING node without create duplicate parameters.
|
|
|
|
static dsql_nod* clone(dsql_nod* unprocessed, dsql_nod* processed)
|
|
|
|
{
|
|
|
|
if (!processed)
|
|
|
|
return unprocessed;
|
|
|
|
|
|
|
|
// nod_returning was already processed
|
|
|
|
fb_assert(processed->nod_type == nod_list);
|
|
|
|
|
|
|
|
// And we create a RETURNING node where the targets are already processed.
|
|
|
|
dsql_nod* newNode = MAKE_node(nod_list, processed->nod_count);
|
|
|
|
|
|
|
|
dsql_nod** srcPtr = unprocessed->nod_arg[e_ret_source]->nod_arg;
|
|
|
|
dsql_nod** dstPtr = processed->nod_arg;
|
|
|
|
dsql_nod** ptr = newNode->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const end = ptr + processed->nod_count;
|
|
|
|
ptr < end; ++srcPtr, ++dstPtr, ++ptr)
|
|
|
|
{
|
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
temp->nod_arg[e_asgn_value] = *srcPtr;
|
|
|
|
temp->nod_arg[e_asgn_field] = (*dstPtr)->nod_arg[1];
|
|
|
|
*ptr = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DsqlCompilerScratch* scratch;
|
2011-01-30 01:25:46 +01:00
|
|
|
AutoSetRestore<string> autoAlias;
|
|
|
|
AutoSetRestore<string> autoInternalAlias;
|
2010-06-10 04:03:03 +02:00
|
|
|
AutoSetRestore<USHORT> autoFlags;
|
|
|
|
bool hasModContext;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
AggregateFinder::AggregateFinder(const DsqlCompilerScratch* aDsqlScratch, bool aWindow)
|
|
|
|
: ConstDsqlNodeVisitor(false, false, &ExprNode::dsqlAggregateFinder),
|
|
|
|
dsqlScratch(aDsqlScratch),
|
|
|
|
window(aWindow),
|
|
|
|
currentLevel(dsqlScratch->scopeLevel),
|
|
|
|
deepestLevel(0),
|
|
|
|
ignoreSubSelects(false)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
2003-11-11 13:19:20 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
bool AggregateFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
Aggregate2Finder::Aggregate2Finder(USHORT aCheckScopeLevel, FieldMatchType aMatchType, bool aWindowOnly)
|
|
|
|
: ConstDsqlNodeVisitor(true, false, &ExprNode::dsqlAggregate2Finder),
|
|
|
|
checkScopeLevel(aCheckScopeLevel),
|
|
|
|
matchType(aMatchType),
|
|
|
|
windowOnly(aWindowOnly),
|
|
|
|
currentScopeLevelEqual(true)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Aggregate2Finder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2004-01-16 11:43:21 +01:00
|
|
|
|
2006-03-29 11:41:48 +02:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
FieldFinder::FieldFinder(USHORT aCheckScopeLevel, FieldMatchType aMatchType)
|
|
|
|
: ConstDsqlNodeVisitor(true, false, &ExprNode::dsqlFieldFinder),
|
|
|
|
checkScopeLevel(aCheckScopeLevel),
|
|
|
|
matchType(aMatchType),
|
|
|
|
field(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FieldFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
InvalidReferenceFinder::InvalidReferenceFinder(const dsql_ctx* aContext, const dsql_nod* aList)
|
|
|
|
: ConstDsqlNodeVisitor(true, false, &ExprNode::dsqlInvalidReferenceFinder),
|
|
|
|
context(aContext),
|
|
|
|
list(aList),
|
|
|
|
insideOwnMap(false),
|
|
|
|
insideHigherMap(false)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(list, dsql_type_nod);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InvalidReferenceFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool invalid = false;
|
|
|
|
|
2010-01-31 00:08:04 +01:00
|
|
|
// ASF: What we do in this function is the verification of all fields/dbkeys (or any parent
|
|
|
|
// expression involving them) are present in the passed node list.
|
|
|
|
// That makes valid:
|
|
|
|
// select n + 0 from table group by n => The n item is present in the list
|
|
|
|
// select n + 0 from table group by n + 0 => The n + 0 item is present in the list
|
|
|
|
// And makes invalid:
|
|
|
|
// select n + 1 from table group by n + 0 => The n + 1 item is not present in the list
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (list)
|
|
|
|
{
|
2010-01-29 12:07:42 +01:00
|
|
|
// Check if this node (with ignoring of CASTs) appears also
|
2010-01-28 16:18:11 +01:00
|
|
|
// in the list of group by. If yes then it's allowed
|
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (PASS1_node_match(node, *ptr, true))
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_plan_expr:
|
2011-02-06 19:13:12 +01:00
|
|
|
fb_assert(false);
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return visitChildren(node);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return invalid;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
FieldRemapper::FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
|
|
|
dsql_nod* aPartitionNode, dsql_nod* aOrderNode)
|
|
|
|
: NonConstDsqlNodeVisitor(false, false, &ExprNode::dsqlFieldRemapper),
|
|
|
|
dsqlScratch(aDsqlScratch),
|
|
|
|
context(aContext),
|
|
|
|
window(aWindow),
|
|
|
|
partitionNode(aPartitionNode),
|
|
|
|
orderNode(aOrderNode),
|
|
|
|
currentLevel(dsqlScratch->scopeLevel)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FieldRemapper::internalVisit(dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2011-02-06 19:13:12 +01:00
|
|
|
return node && visitChildren(node);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
SubSelectFinder::SubSelectFinder()
|
|
|
|
: ConstDsqlNodeVisitor(false, true, &ExprNode::dsqlSubSelectFinder)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SubSelectFinder::internalVisit(const dsql_nod* node)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!node)
|
|
|
|
return false;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_field_name:
|
|
|
|
return false;
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_order:
|
|
|
|
fb_assert(false);
|
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
default:
|
|
|
|
return visitChildren(node);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
PASS1_make_context
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Generate a context for a dsqlScratch.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2010-01-28 16:18:11 +01:00
|
|
|
@param relation_node
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, const dsql_nod* relation_node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(relation_node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
thread_db* const tdbb = JRD_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_rel* relation = NULL;
|
|
|
|
dsql_prc* procedure = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// figure out whether this is a relation or a procedure
|
|
|
|
// and give an error if it is neither
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
MetaName relation_name;
|
|
|
|
const ProcedureSourceNode* procNode = NULL;
|
|
|
|
const RelationSourceNode* relNode = NULL;
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (relation_node->nod_type)
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
case nod_select_expr:
|
2011-01-30 01:25:46 +01:00
|
|
|
if (relation_node->nod_arg[e_sel_alias])
|
|
|
|
relation_name = ((dsql_str*) relation_node->nod_arg[e_sel_alias])->str_data;
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
|
|
|
default:
|
2011-01-30 01:25:46 +01:00
|
|
|
{
|
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(relation_node)))
|
|
|
|
relation_name = procNode->dsqlName.identifier;
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(relation_node)))
|
|
|
|
relation_name = relNode->dsqlName;
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
|
|
|
}
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_nod* cte = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (relation_node->nod_type == nod_select_expr)
|
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
// No processing needed here for derived tables.
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
else if (procNode && (procNode->dsqlName.package.hasData() || procNode->dsqlInputs))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch,
|
2011-01-30 01:25:46 +01:00
|
|
|
procNode->dsqlName);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
if (!procedure)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_dsql_procedure_err) <<
|
|
|
|
Arg::Gds(isc_random) <<
|
2011-01-30 01:25:46 +01:00
|
|
|
Arg::Str(procNode->dsqlName.toString()) <<
|
2010-01-28 16:18:11 +01:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) <<
|
|
|
|
Arg::Num(relation_node->nod_column));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((cte = dsqlScratch->findCTE(relation_name)))
|
|
|
|
relation_node = cte;
|
|
|
|
else
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name);
|
2010-08-02 04:22:26 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (!relation && procNode)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
procedure = METD_get_procedure(dsqlScratch->getTransaction(),
|
|
|
|
dsqlScratch, procNode->dsqlName);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!relation && !procedure)
|
2005-07-20 12:05:57 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_dsql_relation_err) <<
|
2011-01-30 01:25:46 +01:00
|
|
|
Arg::Gds(isc_random) << Arg::Str(relation_name) <<
|
2010-01-28 16:18:11 +01:00
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) <<
|
|
|
|
Arg::Num(relation_node->nod_column));
|
2005-07-20 12:05:57 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2005-07-20 12:05:57 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (procedure && !procedure->prc_out_count)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-84) <<
|
|
|
|
Arg::Gds(isc_dsql_procedure_use_err) << Arg::Str(procedure->prc_name.toString()) <<
|
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) <<
|
|
|
|
Arg::Num(relation_node->nod_column));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Set up context block.
|
|
|
|
dsql_ctx* context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
|
|
|
|
context->ctx_relation = relation;
|
|
|
|
context->ctx_procedure = procedure;
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (relation_node->nod_type == nod_select_expr)
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_context = USHORT(MAX_UCHAR) + 1 + dsqlScratch->derivedContextNumber++;
|
|
|
|
else
|
|
|
|
context->ctx_context = dsqlScratch->contextNumber++;
|
|
|
|
|
|
|
|
context->ctx_scope_level = dsqlScratch->scopeLevel;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// When we're in a outer-join part mark context for it.
|
2011-01-30 01:25:46 +01:00
|
|
|
if (dsqlScratch->inOuterJoin)
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_flags |= CTX_outer_join;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_in_outer_join = dsqlScratch->inOuterJoin;
|
|
|
|
|
|
|
|
// find the context alias name, if it exists.
|
2011-01-30 01:25:46 +01:00
|
|
|
const char* string = NULL;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(relation_node)))
|
2011-02-01 02:27:51 +01:00
|
|
|
string = procNode->alias.nullStr();
|
2011-01-30 01:25:46 +01:00
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(relation_node)))
|
2011-02-01 02:27:51 +01:00
|
|
|
string = relNode->alias.nullStr();
|
2011-01-09 22:58:56 +01:00
|
|
|
else if (relation_node->nod_type == nod_select_expr)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
if (relation_node->nod_arg[e_sel_alias])
|
|
|
|
string = ((dsql_str*) relation_node->nod_arg[e_sel_alias])->str_data;
|
2011-01-09 22:58:56 +01:00
|
|
|
context->ctx_rse = relation_node->nod_arg[e_sel_query_spec];
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (string)
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_internal_alias = string;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
if (dsqlScratch->aliasRelationPrefix && relation_node->nod_type != nod_select_expr)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (string)
|
2011-01-30 01:25:46 +01:00
|
|
|
string = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, string)->str_data;
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2011-01-30 01:25:46 +01:00
|
|
|
string = pass1_alias_concat(dsqlScratch->aliasRelationPrefix, relation_name.c_str())->str_data;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (string)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_alias = string;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// check to make sure the context is not already used at this same
|
|
|
|
// query level (if there are no subqueries, this checks that the
|
|
|
|
// alias is not used twice in the dsqlScratch).
|
|
|
|
for (DsqlContextStack::iterator stack(*dsqlScratch->context); stack.hasData(); ++stack)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_ctx* conflict = stack.object();
|
2009-12-20 22:01:10 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (conflict->ctx_scope_level != context->ctx_scope_level)
|
2010-01-28 16:18:11 +01:00
|
|
|
continue;
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const TEXT* conflict_name;
|
|
|
|
ISC_STATUS error_code;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (conflict->ctx_alias.hasData())
|
2008-05-17 18:51:07 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
conflict_name = conflict->ctx_alias.c_str();
|
2010-01-28 16:18:11 +01:00
|
|
|
error_code = isc_alias_conflict_err;
|
|
|
|
// alias %s conflicts with an alias in the same dsqlScratch.
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else if (conflict->ctx_procedure)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
conflict_name = conflict->ctx_procedure->prc_name.identifier.c_str();
|
|
|
|
error_code = isc_procedure_conflict_error;
|
|
|
|
// alias %s conflicts with a procedure in the same dsqlScratch.
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else if (conflict->ctx_relation)
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
conflict_name = conflict->ctx_relation->rel_name.c_str();
|
|
|
|
error_code = isc_relation_conflict_err;
|
|
|
|
// alias %s conflicts with a relation in the same dsqlScratch.
|
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2010-01-28 16:18:11 +01:00
|
|
|
continue;
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_alias == conflict_name)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(error_code) << Arg::Str(conflict_name));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (procedure)
|
|
|
|
{
|
|
|
|
USHORT count = 0;
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (procNode->dsqlInputs)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_proc_inputs = PASS1_node_psql(dsqlScratch, procNode->dsqlInputs, false);
|
2010-01-28 16:18:11 +01:00
|
|
|
count = context->ctx_proc_inputs->nod_count;
|
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_PROCEDURE))
|
|
|
|
{
|
|
|
|
if (count > procedure->prc_in_count ||
|
|
|
|
count < procedure->prc_in_count - procedure->prc_def_count)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_prcmismat) << Arg::Str(procNode->dsqlName.toString()));
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
if (count)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
// Initialize this stack variable, and make it look like a node
|
2011-01-30 01:25:46 +01:00
|
|
|
AutoPtr<dsql_nod> desc_node(FB_NEW_RPT(*tdbb->getDefaultPool(), 0) dsql_nod);
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* const* input = context->ctx_proc_inputs->nod_arg;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
for (dsql_fld* field = procedure->prc_inputs; *input; ++input, field = field->fld_next)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
DEV_BLKCHK(*input, dsql_type_nod);
|
|
|
|
MAKE_desc_from_field(&desc_node->nod_desc, field);
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, *input, desc_node, false);
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2005-03-26 14:32:29 +01:00
|
|
|
}
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// push the context onto the dsqlScratch context stack
|
|
|
|
// for matching fields against
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->context->push(context);
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return context;
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
PASS1_node
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Compile a parsed statement into something more interesting.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param input
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
dsql_nod* PASS1_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|
|
|
{
|
2010-10-22 17:00:22 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-10-22 17:00:22 +02:00
|
|
|
if (!input)
|
2010-01-28 16:18:11 +01:00
|
|
|
return NULL;
|
2002-06-20 13:42:15 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* node;
|
|
|
|
dsql_nod* sub1;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Dispatch on node type. Fall thru on easy ones
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
case nod_class_exprnode:
|
|
|
|
{
|
|
|
|
ExprNode* exprNode = reinterpret_cast<ExprNode*>(input->nod_arg[0]);
|
2011-01-30 01:25:46 +01:00
|
|
|
ProcedureSourceNode* procNode = NULL;
|
|
|
|
RelationSourceNode* relNode = NULL;
|
|
|
|
|
|
|
|
//// TODO: Move this to dsqlPass when possible.
|
|
|
|
if ((procNode = exprNode->as<ProcedureSourceNode>()) ||
|
|
|
|
(relNode = exprNode->as<RelationSourceNode>()))
|
|
|
|
{
|
|
|
|
bool couldBeCte = true;
|
|
|
|
MetaName rel_name;
|
|
|
|
string rel_alias;
|
|
|
|
|
|
|
|
if (procNode)
|
|
|
|
{
|
|
|
|
rel_name = procNode->dsqlName.identifier;
|
|
|
|
rel_alias = procNode->alias;
|
|
|
|
couldBeCte = !procNode->dsqlInputs && procNode->dsqlName.package.isEmpty();
|
|
|
|
}
|
|
|
|
else if (relNode)
|
|
|
|
{
|
|
|
|
rel_name = relNode->dsqlName;
|
|
|
|
rel_alias = relNode->alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rel_alias.isEmpty())
|
|
|
|
rel_alias = rel_name.c_str();
|
|
|
|
|
|
|
|
dsql_nod* cte = couldBeCte ? dsqlScratch->findCTE(rel_name) : NULL;
|
|
|
|
|
|
|
|
if (!cte)
|
|
|
|
return pass1_relation(dsqlScratch, input);
|
|
|
|
|
|
|
|
cte->nod_flags |= NOD_SELECT_EXPR_DT_CTE_USED;
|
|
|
|
|
|
|
|
if ((dsqlScratch->flags & DsqlCompilerScratch::FLAG_RECURSIVE_CTE) &&
|
|
|
|
dsqlScratch->currCtes.hasData() &&
|
|
|
|
(dsqlScratch->currCtes.object() == cte))
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Recursive CTE member (%s) can refer itself only in FROM clause
|
|
|
|
Arg::Gds(isc_dsql_cte_wrong_reference) << rel_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (DsqlNodStack::const_iterator stack(dsqlScratch->currCtes); stack.hasData(); ++stack)
|
|
|
|
{
|
|
|
|
dsql_nod* cte1 = stack.object();
|
|
|
|
if (cte1 == cte)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// CTE %s has cyclic dependencies
|
|
|
|
Arg::Gds(isc_dsql_cte_cycle) << rel_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* const query = cte->nod_arg[e_sel_query_spec];
|
|
|
|
const bool isRecursive =
|
|
|
|
(query->nod_type == nod_list) && (query->nod_flags & NOD_UNION_RECURSIVE);
|
|
|
|
|
|
|
|
dsql_str* cte_name = (dsql_str*) cte->nod_arg[e_sel_alias];
|
|
|
|
if (!isRecursive)
|
|
|
|
cte->nod_arg[e_sel_alias] = (dsql_nod*) MAKE_cstring(rel_alias.c_str());
|
|
|
|
|
|
|
|
dsqlScratch->currCtes.push(cte);
|
|
|
|
|
|
|
|
dsql_nod* derived_node = pass1_derived_table(dsqlScratch,
|
|
|
|
cte, (isRecursive ? rel_alias.c_str() : NULL));
|
|
|
|
|
|
|
|
if (!isRecursive)
|
|
|
|
cte->nod_arg[e_sel_alias] = (dsql_nod*) cte_name;
|
|
|
|
|
|
|
|
dsqlScratch->currCtes.pop();
|
|
|
|
|
|
|
|
return derived_node;
|
|
|
|
}
|
2010-02-13 21:29:29 +01:00
|
|
|
|
|
|
|
node = reinterpret_cast<dsql_nod*>(exprNode->dsqlPass(dsqlScratch));
|
|
|
|
if (node != input->nod_arg[0])
|
|
|
|
{
|
|
|
|
input = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
input->nod_arg[0] = node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return input;
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_alias:
|
|
|
|
node = MAKE_node(input->nod_type, e_alias_count);
|
|
|
|
node->nod_arg[e_alias_value] = sub1 = PASS1_node(dsqlScratch, input->nod_arg[e_alias_value]);
|
|
|
|
node->nod_arg[e_alias_alias] = input->nod_arg[e_alias_alias];
|
2010-11-01 14:45:52 +01:00
|
|
|
MAKE_desc(dsqlScratch, &sub1->nod_desc, sub1);
|
2010-01-28 16:18:11 +01:00
|
|
|
node->nod_desc = sub1->nod_desc;
|
|
|
|
return node;
|
2003-03-01 20:19:23 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_collate:
|
|
|
|
sub1 = PASS1_node(dsqlScratch, input->nod_arg[e_coll_source]);
|
|
|
|
node = pass1_collate(dsqlScratch, sub1, (dsql_str*) input->nod_arg[e_coll_target]);
|
|
|
|
return node;
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_delete:
|
|
|
|
case nod_insert:
|
|
|
|
case nod_merge:
|
|
|
|
case nod_update_or_insert:
|
|
|
|
case nod_select:
|
|
|
|
case nod_with:
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err));
|
2008-06-08 22:42:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_select_expr:
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (input->nod_flags & NOD_SELECT_EXPR_DERIVED)
|
|
|
|
return pass1_derived_table(dsqlScratch, input, NULL);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const DsqlContextStack::iterator base(*dsqlScratch->context);
|
2010-11-07 03:18:58 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* rseNod = PASS1_rse(dsqlScratch, input, NULL);
|
|
|
|
RseNode* rse = ExprNode::as<RseNode>(rseNod);
|
2010-10-22 17:00:22 +02:00
|
|
|
|
2010-11-07 03:18:58 +01:00
|
|
|
SubQueryNode* subQueryNode = FB_NEW(*tdbb->getDefaultPool()) SubQueryNode(*tdbb->getDefaultPool(),
|
2011-01-09 22:58:56 +01:00
|
|
|
blr_via, rseNod, rse->dsqlSelectList->nod_arg[0], MAKE_node(nod_class_exprnode, 1));
|
2010-11-07 03:18:58 +01:00
|
|
|
subQueryNode->dsqlValue2->nod_arg[0] = reinterpret_cast<dsql_nod*>(
|
2010-10-22 17:00:22 +02:00
|
|
|
FB_NEW(*tdbb->getDefaultPool()) NullNode(*tdbb->getDefaultPool()));
|
|
|
|
|
2010-11-07 03:18:58 +01:00
|
|
|
node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(subQueryNode);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Finish off by cleaning up contexts
|
|
|
|
dsqlScratch->context->clear(base);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
|
|
|
}
|
2008-06-08 22:42:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_field_name:
|
|
|
|
if (dsqlScratch->isPsql())
|
|
|
|
return pass1_variable(dsqlScratch, input);
|
|
|
|
return pass1_field(dsqlScratch, input, false, NULL);
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_array:
|
|
|
|
if (dsqlScratch->isPsql())
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
2010-01-28 16:18:11 +01:00
|
|
|
Arg::Gds(isc_dsql_invalid_array));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
|
|
|
return pass1_field(dsqlScratch, input, false, NULL);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_var_name:
|
|
|
|
return pass1_variable(dsqlScratch, input);
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
// access plan node types
|
|
|
|
|
|
|
|
case nod_plan_item:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
dsql_ctx* context = pass1_alias_list(dsqlScratch, input->nod_arg[0]);
|
|
|
|
RecordSourceNode* recSource = NULL;
|
2003-11-07 15:10:16 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_relation)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
RelationSourceNode* relNode = FB_NEW(*tdbb->getDefaultPool()) RelationSourceNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
relNode->dsqlContext = context;
|
|
|
|
recSource = relNode;
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
else if (context->ctx_procedure)
|
2009-12-23 01:57:08 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
// ASF: Note that usage of procedure name in a PLAN clause causes errors when
|
|
|
|
// parsing the BLR.
|
|
|
|
ProcedureSourceNode* procNode = FB_NEW(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
procNode->dsqlContext = context;
|
|
|
|
recSource = procNode;
|
2009-12-23 01:57:08 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
fb_assert(recSource);
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
node = MAKE_node(input->nod_type, 2);
|
|
|
|
node->nod_arg[0] = sub1 = MAKE_node(nod_class_exprnode, 1);
|
2011-01-30 01:25:46 +01:00
|
|
|
sub1->nod_arg[0] = reinterpret_cast<dsql_nod*>(recSource);
|
2011-01-09 22:58:56 +01:00
|
|
|
node->nod_arg[1] = PASS1_node(dsqlScratch, input->nod_arg[1]);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_index:
|
|
|
|
node = MAKE_node(input->nod_type, 1);
|
|
|
|
node->nod_arg[0] = input->nod_arg[0];
|
|
|
|
return node;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_index_order:
|
|
|
|
node = MAKE_node(input->nod_type, 2);
|
|
|
|
node->nod_arg[0] = input->nod_arg[0];
|
|
|
|
node->nod_arg[1] = input->nod_arg[1];
|
|
|
|
return node;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_returning:
|
|
|
|
return pass1_returning(dsqlScratch, input);
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Node is simply to be rebuilt -- just recurse merrily
|
2003-04-17 22:58:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
const dsql_nod** ptr2 = const_cast<const dsql_nod**>(node->nod_arg);
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
*ptr2++ = PASS1_node(dsqlScratch, *ptr);
|
|
|
|
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Try to match parameters against things of known data type.
|
|
|
|
dsql_nod* sub2 = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_assign:
|
|
|
|
sub1 = node->nod_arg[0];
|
|
|
|
sub2 = node->nod_arg[1];
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Try to force sub1 to be same type as sub2 eg: ? = FIELD case
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, sub1, sub2, false);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Try to force sub2 to be same type as sub1 eg: FIELD = ? case
|
|
|
|
// Try even when the above call succeeded, because "sub2" may
|
|
|
|
// have sub-expressions that should be resolved.
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, sub2, sub1, false);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
|
|
|
|
return node;
|
2002-06-29 08:56:51 +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_rse
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Compile a record selection expression,
|
|
|
|
bumping up the statement scope level
|
|
|
|
everytime an rse is seen. The scope
|
|
|
|
level controls parsing of aliases.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param input
|
|
|
|
@param update_lock
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* PASS1_rse(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* update_lock)
|
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);
|
|
|
|
DEV_BLKCHK(update_lock, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
fb_assert(input->nod_type == nod_select_expr);
|
2005-07-22 04:08:14 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
|
|
|
dsql_nod* node = pass1_rse(dsqlScratch, input, NULL, NULL, update_lock, 0);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
/**
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
PASS1_statement
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Compile a parsed statement into something more interesting.
|
2004-01-28 08:50:41 +01:00
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param input
|
2004-01-28 08:50:41 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
dsql_nod* PASS1_statement(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|
|
|
{
|
|
|
|
if (!input)
|
|
|
|
return NULL;
|
2004-01-28 08:50:41 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2004-01-28 08:50:41 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
#ifdef DSQL_DEBUG
|
|
|
|
if (DSQL_debug & 2)
|
|
|
|
{
|
|
|
|
dsql_trace("Node tree at DSQL pass1 entry:");
|
|
|
|
DSQL_pretty(input, 0);
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
#endif
|
2004-01-28 08:50:41 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* node = NULL;
|
|
|
|
const DsqlContextStack::iterator base(*dsqlScratch->context);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Dispatch on node type. Fall thru on easy ones
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
|
|
|
case nod_def_database:
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_CREATE_DB);
|
|
|
|
return input;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_def_index:
|
|
|
|
case nod_mod_index:
|
|
|
|
case nod_del_index:
|
|
|
|
case nod_def_constraint:
|
|
|
|
case nod_grant:
|
|
|
|
case nod_revoke:
|
|
|
|
case nod_mod_database:
|
|
|
|
case nod_del_generator:
|
|
|
|
case nod_def_role:
|
|
|
|
case nod_del_role:
|
|
|
|
case nod_def_filter:
|
|
|
|
case nod_del_filter:
|
|
|
|
case nod_def_domain:
|
|
|
|
case nod_def_udf:
|
|
|
|
case nod_del_udf:
|
|
|
|
case nod_def_shadow:
|
|
|
|
case nod_del_shadow:
|
|
|
|
case nod_set_statistics:
|
|
|
|
case nod_mod_udf:
|
|
|
|
case nod_mod_role:
|
|
|
|
case nod_add_user:
|
|
|
|
case nod_mod_user:
|
|
|
|
case nod_del_user:
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL);
|
|
|
|
return input;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
case nod_class_stmtnode:
|
2010-01-28 16:18:11 +01:00
|
|
|
node = reinterpret_cast<dsql_nod*>(
|
|
|
|
reinterpret_cast<Node*>(input->nod_arg[0])->dsqlPass(dsqlScratch));
|
|
|
|
if (node != input->nod_arg[0])
|
|
|
|
{
|
|
|
|
input = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
input->nod_arg[0] = node;
|
|
|
|
}
|
|
|
|
return input;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_assign:
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_arg[e_asgn_value] = PASS1_node(dsqlScratch, input->nod_arg[e_asgn_value]);
|
|
|
|
node->nod_arg[e_asgn_field] = PASS1_node(dsqlScratch, input->nod_arg[e_asgn_field]);
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_commit:
|
|
|
|
if ((input->nod_arg[e_commit_retain]) &&
|
|
|
|
(input->nod_arg[e_commit_retain]->nod_type == nod_retain))
|
|
|
|
{
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_COMMIT_RETAIN);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_COMMIT);
|
|
|
|
return input;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_rollback:
|
|
|
|
if ((input->nod_arg[e_rollback_retain]) &&
|
|
|
|
(input->nod_arg[e_rollback_retain]->nod_type == nod_retain))
|
|
|
|
{
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_ROLLBACK);
|
|
|
|
return input;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_delete:
|
|
|
|
node = pass1_savepoint(dsqlScratch, pass1_delete(dsqlScratch, input));
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_get_segment:
|
|
|
|
case nod_put_segment:
|
|
|
|
pass1_blob(dsqlScratch, input);
|
|
|
|
return input;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_insert:
|
|
|
|
node = pass1_savepoint(dsqlScratch, pass1_insert(dsqlScratch, input, false));
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_merge:
|
|
|
|
node = pass1_savepoint(dsqlScratch, pass1_merge(dsqlScratch, input));
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_block:
|
|
|
|
if (input->nod_arg[e_blk_errs])
|
|
|
|
dsqlScratch->errorHandlers++;
|
|
|
|
else if (input->nod_arg[e_blk_action])
|
|
|
|
{
|
|
|
|
input->nod_count = 1;
|
|
|
|
if (!dsqlScratch->errorHandlers)
|
|
|
|
input->nod_type = nod_list;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
input->nod_count = 0;
|
|
|
|
input->nod_type = nod_list;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_list:
|
|
|
|
{
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
const dsql_nod** ptr2 = const_cast<const dsql_nod**>(node->nod_arg);
|
|
|
|
dsql_nod* const* ptr = input->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
if ((*ptr)->nod_type == nod_assign)
|
|
|
|
*ptr2++ = PASS1_node(dsqlScratch, *ptr);
|
|
|
|
else
|
|
|
|
*ptr2++ = PASS1_statement(dsqlScratch, *ptr);
|
|
|
|
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
|
|
|
|
}
|
|
|
|
if (input->nod_type == nod_block && input->nod_arg[e_blk_errs])
|
|
|
|
dsqlScratch->errorHandlers--;
|
|
|
|
return node;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_on_error:
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_arg[e_err_errs] = input->nod_arg[e_err_errs];
|
|
|
|
node->nod_arg[e_err_action] = PASS1_statement(dsqlScratch, input->nod_arg[e_err_action]);
|
|
|
|
return node;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_select:
|
2008-07-01 03:12:02 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
node = PASS1_rse(dsqlScratch, input->nod_arg[e_select_expr], input->nod_arg[e_select_lock]);
|
|
|
|
|
|
|
|
if (input->nod_arg[e_select_update])
|
|
|
|
{
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SELECT_UPD);
|
|
|
|
dsqlScratch->getStatement()->addFlags(DsqlCompiledStatement::FLAG_NO_BATCH);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is a union without ALL or order by or a select distinct
|
|
|
|
// buffering is OK even if stored procedure occurs in the select
|
|
|
|
// list. In these cases all of stored procedure is executed under
|
|
|
|
// savepoint for open cursor.
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* rseNode = ExprNode::as<RseNode>(node);
|
|
|
|
|
|
|
|
if (rseNode->dsqlOrder || rseNode->dsqlDistinct)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
dsqlScratch->getStatement()->setFlags(dsqlScratch->getStatement()->getFlags() &
|
|
|
|
~DsqlCompiledStatement::FLAG_NO_BATCH);
|
|
|
|
}
|
2008-07-01 03:12:02 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_trans:
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_START_TRANS);
|
|
|
|
return input;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_update:
|
|
|
|
node = pass1_savepoint(dsqlScratch, pass1_update(dsqlScratch, input, false));
|
|
|
|
break;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_while:
|
|
|
|
{
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_arg[e_while_cond] = PASS1_node(dsqlScratch, input->nod_arg[e_while_cond]);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// CVC: loop numbers should be incremented before analyzing the body
|
|
|
|
// to preserve nesting <==> increasing level number
|
|
|
|
dsqlScratch->loopLevel++;
|
2011-01-22 22:32:29 +01:00
|
|
|
node->nod_arg[e_while_label] = PASS1_label(dsqlScratch, false, input->nod_arg[e_while_label]);
|
2010-01-28 16:18:11 +01:00
|
|
|
node->nod_arg[e_while_action] = PASS1_statement(dsqlScratch, input->nod_arg[e_while_action]);
|
|
|
|
dsqlScratch->loopLevel--;
|
|
|
|
dsqlScratch->labels.pop();
|
|
|
|
}
|
|
|
|
break;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_exception:
|
|
|
|
case nod_sqlcode:
|
|
|
|
case nod_gdscode:
|
|
|
|
return input;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_set_generator:
|
|
|
|
node = MAKE_node(input->nod_type, e_gen_id_count);
|
|
|
|
node->nod_arg[e_gen_id_value] = PASS1_node(dsqlScratch, input->nod_arg[e_gen_id_value]);
|
|
|
|
node->nod_arg[e_gen_id_name] = input->nod_arg[e_gen_id_name];
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SET_GENERATOR);
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_set_generator2:
|
|
|
|
node = MAKE_node(input->nod_type, e_gen_id_count);
|
|
|
|
node->nod_arg[e_gen_id_value] = PASS1_node(dsqlScratch, input->nod_arg[e_gen_id_value]);
|
|
|
|
node->nod_arg[e_gen_id_name] = input->nod_arg[e_gen_id_name];
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SET_GENERATOR);
|
|
|
|
break;
|
2009-12-19 19:50:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_cursor:
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
fb_assert(input->nod_flags > 0);
|
|
|
|
// make sure the cursor doesn't exist
|
2010-12-23 19:42:06 +01:00
|
|
|
PASS1_cursor_name(dsqlScratch, ((dsql_str*) input->nod_arg[e_cur_name])->str_data,
|
|
|
|
NOD_CURSOR_ALL, false);
|
2010-01-28 16:18:11 +01:00
|
|
|
// temporarily hide unnecessary contexts and process our RSE
|
|
|
|
DsqlContextStack* const base_context = dsqlScratch->context;
|
|
|
|
DsqlContextStack temp;
|
|
|
|
dsqlScratch->context = &temp;
|
|
|
|
const dsql_nod* select = input->nod_arg[e_cur_rse];
|
|
|
|
input->nod_arg[e_cur_rse] =
|
|
|
|
PASS1_rse(dsqlScratch, select->nod_arg[e_select_expr], select->nod_arg[e_select_lock]);
|
|
|
|
dsqlScratch->context->clear();
|
|
|
|
dsqlScratch->context = base_context;
|
|
|
|
// assign number and store in the dsqlScratch stack
|
|
|
|
input->nod_arg[e_cur_number] = (dsql_nod*) (IPTR) dsqlScratch->cursorNumber++;
|
|
|
|
dsqlScratch->cursors.push(input);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
return input;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_src_info:
|
|
|
|
{
|
|
|
|
input->nod_line = (USHORT) (IPTR) input->nod_arg[e_src_info_line];
|
|
|
|
input->nod_column = (USHORT) (IPTR) input->nod_arg[e_src_info_column];
|
|
|
|
input->nod_arg[e_src_info_stmt] = PASS1_statement(dsqlScratch, input->nod_arg[e_src_info_stmt]);
|
|
|
|
return input;
|
|
|
|
}
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_update_or_insert:
|
|
|
|
node = pass1_savepoint(dsqlScratch, pass1_update_or_insert(dsqlScratch, input));
|
|
|
|
break;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
default:
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
// Unsupported DSQL construct
|
|
|
|
Arg::Gds(isc_dsql_construct_err));
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Finish off by cleaning up contexts
|
|
|
|
dsqlScratch->context->clear(base);
|
2009-12-19 19:50:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
#ifdef DSQL_DEBUG
|
|
|
|
if (DSQL_debug & 1)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_trace("Node tree at DSQL pass1 exit:");
|
|
|
|
DSQL_pretty(node, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
#endif
|
2009-12-19 19:50:38 +01: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,
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_str* 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) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str(name->str_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(204) <<
|
|
|
|
Arg::Warning(isc_dsql_ambiguous_field_name) << Arg::Str(buffer) <<
|
|
|
|
Arg::Str(++p) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str(name->str_data));
|
2006-09-14 04:05:32 +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
|
|
|
assign_fld_dtype_from_dsc
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Set a field's descriptor from a DSC
|
|
|
|
(If dsql_fld* is ever redefined this can be removed)
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param field
|
|
|
|
@param nod_desc
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
static void assign_fld_dtype_from_dsc( dsql_fld* field, const dsc* nod_desc)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
field->fld_dtype = nod_desc->dsc_dtype;
|
|
|
|
field->fld_scale = nod_desc->dsc_scale;
|
|
|
|
field->fld_sub_type = nod_desc->dsc_sub_type;
|
|
|
|
field->fld_length = nod_desc->dsc_length;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (nod_desc->dsc_dtype <= dtype_any_text)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
field->fld_collation_id = DSC_GET_COLLATE(nod_desc);
|
|
|
|
field->fld_character_set_id = DSC_GET_CHARSET(nod_desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else if (nod_desc->dsc_dtype == dtype_blob)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
field->fld_character_set_id = nod_desc->dsc_scale;
|
|
|
|
field->fld_collation_id = nod_desc->dsc_flags >> 8;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (nod_desc->dsc_flags & DSC_nullable)
|
|
|
|
field->fld_flags |= FLD_nullable;
|
|
|
|
}
|
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
|
|
|
|
**/
|
|
|
|
void PASS1_check_unique_fields_names(StrArray& names, const dsql_nod* fields)
|
|
|
|
{
|
|
|
|
if (!fields)
|
|
|
|
return;
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_nod* const* ptr = fields->nod_arg;
|
|
|
|
const dsql_nod* const* const end = ptr + fields->nod_count;
|
|
|
|
const dsql_fld* field;
|
|
|
|
const dsql_str* str;
|
|
|
|
const char* name = NULL;
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
switch ((*ptr)->nod_type)
|
|
|
|
{
|
|
|
|
case nod_def_field:
|
|
|
|
field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
name = field->fld_name.c_str();
|
2010-06-18 18:48:33 +02:00
|
|
|
break;
|
2008-09-07 23:50:00 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
case nod_cursor:
|
|
|
|
str = (dsql_str*) (*ptr)->nod_arg[e_cur_name];
|
|
|
|
DEV_BLKCHK(str, dsql_type_str);
|
|
|
|
name = str->str_data;
|
2010-06-18 18:48:33 +02:00
|
|
|
break;
|
2008-09-07 23:50:00 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
size_t pos;
|
|
|
|
if (!names.find(name, pos))
|
|
|
|
names.insert(pos, name);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
|
|
|
|
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-23 04:02:53 +01:00
|
|
|
|
2010-08-27 04:18:00 +02:00
|
|
|
// Compose two booleans.
|
2010-09-17 05:15:32 +02:00
|
|
|
dsql_nod* PASS1_compose(dsql_nod* expr1, dsql_nod* expr2, UCHAR blrOp)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(expr1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(expr2, dsql_type_nod);
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
fb_assert(blrOp == blr_and || blrOp == blr_or);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!expr1)
|
|
|
|
return expr2;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!expr2)
|
|
|
|
return expr1;
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
BinaryBoolNode* binNode = FB_NEW(*tdbb->getDefaultPool()) BinaryBoolNode(
|
|
|
|
*tdbb->getDefaultPool(), blrOp, expr1, expr2);
|
|
|
|
|
|
|
|
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(binNode);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2003-04-17 22:58:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
explode_fields
|
|
|
|
|
|
|
|
@brief Generate a field list that correspond to table fields.
|
|
|
|
|
|
|
|
|
|
|
|
@param relation
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* explode_fields(dsql_rel* relation)
|
|
|
|
{
|
|
|
|
DsqlNodStack stack;
|
|
|
|
|
|
|
|
for (dsql_fld* field = relation->rel_fields; field; field = field->fld_next)
|
|
|
|
{
|
|
|
|
// CVC: Ann Harrison requested to skip COMPUTED fields in INSERT w/o field list.
|
|
|
|
if (field->fld_flags & FLD_computed)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
stack.push(MAKE_field_name(field->fld_name.c_str()));
|
2008-09-01 15:18:02 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return MAKE_list(stack);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
field_appears_once
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Check that a field is named only once in INSERT or UPDATE statements.
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param fields
|
|
|
|
@param old_fields
|
|
|
|
@param is_insert
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
static void field_appears_once(const dsql_nod* fields, const dsql_nod* old_fields,
|
|
|
|
const bool is_insert, const char* dsqlScratch)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < fields->nod_count; ++i)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_nod* elem1 = fields->nod_arg[i];
|
|
|
|
if (elem1->nod_type == nod_assign && !is_insert)
|
|
|
|
elem1 = elem1->nod_arg[e_asgn_field];
|
2002-10-04 19:53:35 +02:00
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
const FieldNode* fieldNode1 = ExprNode::as<FieldNode>(elem1);
|
|
|
|
|
|
|
|
if (fieldNode1)
|
2006-03-14 11:57:11 +01:00
|
|
|
{
|
2010-12-19 22:42:32 +01:00
|
|
|
const Firebird::MetaName& n1 = fieldNode1->dsqlField->fld_name;
|
2006-03-14 11:57:11 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (int j = i + 1; j < fields->nod_count; ++j)
|
|
|
|
{
|
|
|
|
const dsql_nod* elem2 = fields->nod_arg[j];
|
|
|
|
if (elem2->nod_type == nod_assign && !is_insert)
|
|
|
|
elem2 = elem2->nod_arg[e_asgn_field];
|
2006-03-14 11:57:11 +01:00
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
const FieldNode* fieldNode2 = ExprNode::as<FieldNode>(elem2);
|
|
|
|
|
|
|
|
if (fieldNode2)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-12-19 22:42:32 +01:00
|
|
|
const Firebird::MetaName& n2 = fieldNode2->dsqlField->fld_name;
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (n1 == n2)
|
|
|
|
{
|
2010-12-19 22:42:32 +01:00
|
|
|
const dsql_ctx* tmp_ctx = fieldNode2->dsqlContext;
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_rel* bad_rel = tmp_ctx ? tmp_ctx->ctx_relation : 0;
|
2010-12-19 22:42:32 +01:00
|
|
|
field_duplication((bad_rel ? bad_rel->rel_name.c_str() : 0),
|
|
|
|
n2.c_str(),
|
|
|
|
(is_insert ? old_fields->nod_arg[j] : old_fields->nod_arg[j]->nod_arg[1]),
|
|
|
|
dsqlScratch);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-09-04 17:02:22 +02:00
|
|
|
}
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
field_duplication
|
|
|
|
|
|
|
|
@brief Report a field duplication error in INSERT or UPDATE statements.
|
|
|
|
|
|
|
|
|
|
|
|
@param qualifier_name
|
|
|
|
@param field_name
|
|
|
|
@param flawed_node
|
|
|
|
@param is_insert
|
|
|
|
|
|
|
|
**/
|
|
|
|
static void field_duplication(const TEXT* qualifier_name, const TEXT* field_name,
|
|
|
|
const dsql_nod* flawed_node, const char* dsqlScratch)
|
|
|
|
{
|
|
|
|
TEXT field_buffer[MAX_SQL_IDENTIFIER_SIZE * 2];
|
|
|
|
|
|
|
|
if (qualifier_name)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
sprintf(field_buffer, "%.*s.%.*s", (int) MAX_SQL_IDENTIFIER_LEN, qualifier_name,
|
|
|
|
(int) MAX_SQL_IDENTIFIER_LEN, field_name);
|
|
|
|
field_name = field_buffer;
|
|
|
|
}
|
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_no_dup_name) << Arg::Str(field_name) << Arg::Str(dsqlScratch) <<
|
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(flawed_node->nod_line) <<
|
|
|
|
Arg::Num(flawed_node->nod_column));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2002-08-11 10:04:54 +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,
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_nod* flawed_node)
|
|
|
|
{
|
|
|
|
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) <<
|
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(flawed_node->nod_line) <<
|
|
|
|
Arg::Num(flawed_node->nod_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) <<
|
|
|
|
Arg::Gds(isc_dsql_line_col_error) << Arg::Num(flawed_node->nod_line) <<
|
|
|
|
Arg::Num(flawed_node->nod_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
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2009-04-12 09:53:44 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
find_dbkey
|
2009-04-12 09:53:44 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Find dbkey for named relation in statement's saved dbkeys.
|
2009-04-12 09:53:44 +02:00
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param relation_name
|
2005-05-04 11:53:37 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
static dsql_par* find_dbkey(const dsql_req* request, const dsql_nod* relation_name)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_msg* message = request->getStatement()->getReceiveMsg();
|
|
|
|
dsql_par* candidate = NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
const MetaName& relName = ExprNode::as<RelationSourceNode>(relation_name)->dsqlName;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < message->msg_parameters.getCount(); ++i)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_par* parameter = message->msg_parameters[i];
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (parameter->par_dbkey_relname.hasData() && parameter->par_dbkey_relname == relName)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
if (candidate)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
candidate = parameter;
|
2002-10-19 02:32:23 +02:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return candidate;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-24 17:22:05 +02:00
|
|
|
/**
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
find_record_version
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Find record version for relation in statement's saved record version
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
@param dsqlScratch
|
|
|
|
@param relation_name
|
2007-04-24 17:22:05 +02:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
static dsql_par* find_record_version(const dsql_req* request, const dsql_nod* relation_name)
|
2007-04-24 17:22:05 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_msg* message = request->getStatement()->getReceiveMsg();
|
|
|
|
dsql_par* candidate = NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
const MetaName& relName = ExprNode::as<RelationSourceNode>(relation_name)->dsqlName;
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (size_t i = 0; i < message->msg_parameters.getCount(); ++i)
|
2007-05-02 08:57:18 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_par* parameter = message->msg_parameters[i];
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (parameter->par_rec_version_relname.hasData() &&
|
2011-01-30 01:25:46 +01:00
|
|
|
parameter->par_rec_version_relname == relName)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
if (candidate)
|
|
|
|
return NULL;
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
candidate = parameter;
|
|
|
|
}
|
2007-04-24 17:22:05 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return candidate;
|
2007-04-24 17:22:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-30 02:17:41 +01:00
|
|
|
// Get the context of a relation, procedure or derived table.
|
2010-01-28 16:18:11 +01:00
|
|
|
static dsql_ctx* get_context(const dsql_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
const ProcedureSourceNode* procNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
const RelationSourceNode* relNode;
|
|
|
|
const RseNode* rseNode;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(node)))
|
|
|
|
return procNode->dsqlContext;
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(node)))
|
2011-01-09 22:58:56 +01:00
|
|
|
return relNode->dsqlContext;
|
|
|
|
else if ((rseNode = ExprNode::as<RseNode>(node)))
|
|
|
|
return rseNode->dsqlContext;
|
2006-01-22 20:14:27 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
fb_assert(false);
|
|
|
|
return NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-30 02:17:41 +01:00
|
|
|
// Get the contexts of a relation, procedure, derived table or a list of joins.
|
|
|
|
static void get_contexts(DsqlContextStack& contexts, const dsql_nod* node)
|
|
|
|
{
|
|
|
|
const ProcedureSourceNode* procNode;
|
|
|
|
const RelationSourceNode* relNode;
|
|
|
|
const RseNode* rseNode;
|
|
|
|
|
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(node)))
|
|
|
|
contexts.push(procNode->dsqlContext);
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(node)))
|
|
|
|
contexts.push(relNode->dsqlContext);
|
|
|
|
else if ((rseNode = ExprNode::as<RseNode>(node)))
|
|
|
|
{
|
|
|
|
if (rseNode->dsqlContext) // derived table
|
|
|
|
contexts.push(rseNode->dsqlContext);
|
|
|
|
else // joins
|
|
|
|
{
|
|
|
|
dsql_nod** ptr = rseNode->dsqlStreams->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const end = ptr + rseNode->dsqlStreams->nod_count;
|
|
|
|
ptr != end;
|
|
|
|
++ptr)
|
|
|
|
{
|
|
|
|
get_contexts(contexts, *ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_node_match
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Compare two nodes for equality of value.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
[2002-08-04]--- Arno Brinkman
|
|
|
|
If ignore_map_cast is true and the node1 is
|
2010-02-13 21:29:29 +01:00
|
|
|
type nod_cast or nod_map then PASS1_node_match is
|
2010-01-28 16:18:11 +01:00
|
|
|
calling itselfs again with the node1
|
|
|
|
CASTs source or map->node.
|
|
|
|
This is for allow CAST to other datatypes
|
|
|
|
without complaining that it's an unknown
|
|
|
|
column reference. (Aggregate functions)
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
@param node1
|
|
|
|
@param node2
|
|
|
|
@param ignore_map_cast
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2010-02-13 21:29:29 +01:00
|
|
|
bool PASS1_node_match(const dsql_nod* node1, const dsql_nod* node2, bool ignore_map_cast)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(node1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(node2, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
if (!node1 && !node2)
|
2010-01-28 16:18:11 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
if (!node1 || !node2)
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
const CastNode* castNode1 = ExprNode::as<CastNode>(node1);
|
|
|
|
|
|
|
|
if (ignore_map_cast && castNode1)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-02 00:57:31 +01:00
|
|
|
const CastNode* castNode2 = ExprNode::as<CastNode>(node2);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// If node2 is also cast and same type continue with both sources.
|
2010-11-02 00:57:31 +01:00
|
|
|
if (castNode2 &&
|
|
|
|
castNode1->castDesc.dsc_dtype == castNode2->castDesc.dsc_dtype &&
|
|
|
|
castNode1->castDesc.dsc_scale == castNode2->castDesc.dsc_scale &&
|
|
|
|
castNode1->castDesc.dsc_length == castNode2->castDesc.dsc_length &&
|
|
|
|
castNode1->castDesc.dsc_sub_type == castNode2->castDesc.dsc_sub_type)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-02 00:57:31 +01:00
|
|
|
return PASS1_node_match(castNode1->dsqlSource, castNode2->dsqlSource, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2009-04-19 12:06:07 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
return PASS1_node_match(castNode1->dsqlSource, node2, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
const DsqlMapNode* mapNode1 = ExprNode::as<DsqlMapNode>(node1);
|
|
|
|
|
|
|
|
if (ignore_map_cast && mapNode1)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DsqlMapNode* mapNode2 = ExprNode::as<DsqlMapNode>(node2);
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (mapNode2)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
if (mapNode1->context != mapNode2->context)
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
return PASS1_node_match(mapNode1->map->map_node, mapNode2->map->map_node, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
return PASS1_node_match(mapNode1->map->map_node, node2, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// We don't care about the alias itself but only about its field.
|
|
|
|
if ((node1->nod_type == nod_alias) || (node2->nod_type == nod_alias))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
if ((node1->nod_type == nod_alias) && (node2->nod_type == nod_alias))
|
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
return PASS1_node_match(node1->nod_arg[e_alias_value],
|
2010-01-28 16:18:11 +01:00
|
|
|
node2->nod_arg[e_alias_value], ignore_map_cast);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
if (node1->nod_type == nod_alias) {
|
2010-02-13 21:29:29 +01:00
|
|
|
return PASS1_node_match(node1->nod_arg[e_alias_value], node2, ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
if (node2->nod_type == nod_alias) {
|
2010-02-13 21:29:29 +01:00
|
|
|
return PASS1_node_match(node1, node2->nod_arg[e_alias_value], ignore_map_cast);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Handle derived fields.
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
const DerivedFieldNode* derivedField1 = ExprNode::as<DerivedFieldNode>(node1);
|
|
|
|
const DerivedFieldNode* derivedField2 = ExprNode::as<DerivedFieldNode>(node2);
|
|
|
|
|
|
|
|
if (derivedField1 || derivedField2)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField1 && derivedField2)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField1->scope != derivedField2->scope ||
|
|
|
|
derivedField1->name != derivedField2->name)
|
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2010-11-14 18:25:48 +01:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
return PASS1_node_match(derivedField1->dsqlValue, derivedField2->dsqlValue,
|
|
|
|
ignore_map_cast);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField1)
|
|
|
|
return PASS1_node_match(derivedField1->dsqlValue, node2, ignore_map_cast);
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (derivedField2)
|
|
|
|
return PASS1_node_match(node1, derivedField2->dsqlValue, ignore_map_cast);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (node1->nod_type != node2->nod_type || node1->nod_count != node2->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// This is to get rid of assertion failures when trying
|
2010-02-13 21:29:29 +01:00
|
|
|
// to PASS1_node_match nod_aggregate's children. This was happening because not
|
2010-01-28 16:18:11 +01:00
|
|
|
// all of the children are of type "dsql_nod". Pointer to the first child
|
|
|
|
// (argument) is actually a pointer to context structure.
|
|
|
|
// To compare two nodes of type nod_aggregate we need first to see if they
|
|
|
|
// both refer to same context structure. If they do not they are different
|
|
|
|
// nodes, if they point to the same context they are the same (because
|
|
|
|
// nod_aggregate is created for an rse that have aggregate expression,
|
|
|
|
// group by or having clause and each rse has its own context). But just in
|
|
|
|
// case we compare two other subtrees.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (node1->nod_type)
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
case nod_class_exprnode:
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
|
|
|
ExprNode* expr1Node = reinterpret_cast<ExprNode*>(node1->nod_arg[0]);
|
|
|
|
ExprNode* expr2Node = reinterpret_cast<ExprNode*>(node2->nod_arg[0]);
|
|
|
|
|
|
|
|
if (expr1Node->type != expr2Node->type)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return expr1Node->dsqlMatch(expr2Node, ignore_map_cast);
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
} // switch
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_nod* const* ptr1 = node1->nod_arg;
|
|
|
|
const dsql_nod* const* ptr2 = node2->nod_arg;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr1 + node1->nod_count; ptr1 < end; ptr1++, ptr2++)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (!PASS1_node_match(*ptr1, *ptr2, ignore_map_cast))
|
2010-01-28 16:18:11 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
// Create a compound statement to initialize returning parameters.
|
|
|
|
static dsql_nod* nullify_returning(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod** list)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2010-10-22 17:00:22 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2006-03-10 01:08:44 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (list)
|
|
|
|
*list = input;
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* returning = NULL;
|
2007-06-09 21:31:10 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
|
|
|
case nod_store:
|
|
|
|
returning = input->nod_arg[e_sto_return];
|
|
|
|
break;
|
|
|
|
case nod_modify:
|
|
|
|
returning = input->nod_arg[e_mod_return];
|
|
|
|
break;
|
|
|
|
case nod_erase:
|
|
|
|
returning = input->nod_arg[e_era_return];
|
|
|
|
break;
|
2010-06-10 04:03:03 +02:00
|
|
|
case nod_modify_current:
|
|
|
|
returning = input->nod_arg[e_mdc_return];
|
|
|
|
break;
|
|
|
|
case nod_erase_current:
|
|
|
|
returning = input->nod_arg[e_erc_return];
|
|
|
|
break;
|
2010-01-28 16:18:11 +01:00
|
|
|
default:
|
|
|
|
fb_assert(false);
|
2007-06-09 21:31:10 +02:00
|
|
|
}
|
2003-02-12 20:28:13 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (dsqlScratch->isPsql() || !returning)
|
2010-06-10 04:03:03 +02:00
|
|
|
return NULL;
|
2006-03-10 01:08:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// If this is a RETURNING in DSQL, we need to initialize the output
|
|
|
|
// parameters with NULL, to return in case of empty resultset.
|
|
|
|
// Note: this may be changed in the future, i.e. return empty resultset
|
|
|
|
// instead of NULLs. In this case, I suppose this function could be
|
|
|
|
// completely removed.
|
2003-02-12 20:28:13 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// nod_returning was already processed
|
|
|
|
fb_assert(returning->nod_type == nod_list);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* null_assign = MAKE_node(nod_list, returning->nod_count);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod** ret_ptr = returning->nod_arg;
|
|
|
|
dsql_nod** null_ptr = null_assign->nod_arg;
|
|
|
|
dsql_nod* temp;
|
2008-03-01 20:14:46 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (const dsql_nod* const* const end = ret_ptr + returning->nod_count; ret_ptr < end;
|
|
|
|
++ret_ptr, ++null_ptr)
|
2008-03-01 20:14:46 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
temp = MAKE_node(nod_assign, e_asgn_count);
|
2010-10-22 17:00:22 +02:00
|
|
|
temp->nod_arg[e_asgn_value] = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
temp->nod_arg[e_asgn_value]->nod_arg[0] = reinterpret_cast<dsql_nod*>(
|
|
|
|
FB_NEW(*tdbb->getDefaultPool()) NullNode(*tdbb->getDefaultPool()));
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
temp->nod_arg[e_asgn_field] = (*ret_ptr)->nod_arg[1];
|
|
|
|
*null_ptr = temp;
|
2005-09-14 21:06:28 +02:00
|
|
|
}
|
2005-06-06 10:30:03 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
// If asked for, return a compound statement with the initialization and the
|
|
|
|
// original statement.
|
|
|
|
if (list)
|
|
|
|
{
|
|
|
|
*list = MAKE_node(nod_list, 2);
|
|
|
|
(*list)->nod_arg[0] = null_assign;
|
|
|
|
(*list)->nod_arg[1] = input;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
return null_assign; // return the initialization statement
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_blob
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Process a blob get or put segment.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2010-01-28 16:18:11 +01:00
|
|
|
@param input
|
2003-11-02 13:28:30 +01:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
static void pass1_blob( DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2003-11-02 13:28:30 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2004-11-17 15:50:33 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
PASS1_make_context(dsqlScratch, input->nod_arg[e_blb_relation]);
|
2010-12-19 22:42:32 +01:00
|
|
|
FieldNode* field = ExprNode::as<FieldNode>(
|
|
|
|
pass1_field(dsqlScratch, input->nod_arg[e_blb_field], false, NULL));
|
|
|
|
|
|
|
|
if (field->dsqlDesc.dsc_dtype != dtype_blob)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-206) <<
|
|
|
|
Arg::Gds(isc_dsql_blob_err));
|
2003-11-02 13:28:30 +01:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->getStatement()->setType(input->nod_type == nod_get_segment ?
|
|
|
|
DsqlCompiledStatement::TYPE_GET_SEGMENT : DsqlCompiledStatement::TYPE_PUT_SEGMENT);
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_blb* blob = FB_NEW(*tdbb->getDefaultPool()) dsql_blb;
|
|
|
|
dsqlScratch->getStatement()->setBlob(blob);
|
|
|
|
//blob->blb_field = field;
|
|
|
|
blob->blb_open_in_msg = dsqlScratch->getStatement()->getSendMsg();
|
|
|
|
blob->blb_open_out_msg = FB_NEW(*tdbb->getDefaultPool()) dsql_msg(*tdbb->getDefaultPool());
|
|
|
|
blob->blb_segment_msg = dsqlScratch->getStatement()->getReceiveMsg();
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Create a parameter for the blob segment
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_par* parameter = MAKE_parameter(blob->blb_segment_msg, true, true, 0, NULL);
|
|
|
|
blob->blb_segment = parameter;
|
|
|
|
parameter->par_desc.dsc_dtype = dtype_text;
|
|
|
|
parameter->par_desc.dsc_ttype() = ttype_binary;
|
2010-12-19 22:42:32 +01:00
|
|
|
parameter->par_desc.dsc_length = field->dsqlField->fld_seg_length;
|
|
|
|
DEV_BLKCHK(field->dsqlField, dsql_type_fld);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// The Null indicator is used to pass back the segment length,
|
|
|
|
// set DSC_nullable so that the SQL_type is set to SQL_TEXT+1 instead
|
|
|
|
// of SQL_TEXT.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (input->nod_type == nod_get_segment)
|
|
|
|
parameter->par_desc.dsc_flags |= DSC_nullable;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Create a parameter for the blob id
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_msg* temp_msg = (input->nod_type == nod_get_segment) ?
|
|
|
|
blob->blb_open_in_msg : blob->blb_open_out_msg;
|
|
|
|
blob->blb_blob_id = parameter = MAKE_parameter(temp_msg, true, true, 0, NULL);
|
2010-12-19 22:42:32 +01:00
|
|
|
field->make(dsqlScratch, ¶meter->par_desc);
|
2010-01-28 16:18:11 +01:00
|
|
|
parameter->par_desc.dsc_dtype = dtype_quad;
|
|
|
|
parameter->par_desc.dsc_scale = 0;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* list = input->nod_arg[e_blb_filter];
|
|
|
|
if (list)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
if (list->nod_arg[0]) {
|
|
|
|
blob->blb_from = PASS1_node_psql(dsqlScratch, list->nod_arg[0], false);
|
|
|
|
}
|
|
|
|
if (list->nod_arg[1]) {
|
|
|
|
blob->blb_to = PASS1_node_psql(dsqlScratch, list->nod_arg[1], false);
|
|
|
|
}
|
2003-11-28 07:48:34 +01:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!blob->blb_from) {
|
|
|
|
blob->blb_from = MAKE_const_slong(0);
|
|
|
|
}
|
|
|
|
if (!blob->blb_to) {
|
|
|
|
blob->blb_to = MAKE_const_slong(0);
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (size_t i = 0; i < blob->blb_open_in_msg->msg_parameters.getCount(); ++i)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_par* parameter = blob->blb_open_in_msg->msg_parameters[i];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (parameter->par_index > ((input->nod_type == nod_get_segment) ? 1 : 0))
|
|
|
|
{
|
|
|
|
parameter->par_desc.dsc_dtype = dtype_short;
|
|
|
|
parameter->par_desc.dsc_scale = 0;
|
|
|
|
parameter->par_desc.dsc_length = sizeof(SSHORT);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2008-03-21 20:05:24 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_collate
|
2008-04-02 16:27:17 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Turn a collate clause into a cast clause.
|
|
|
|
If the source is not already text, report an error.
|
|
|
|
(SQL 92: Section 13.1, pg 308, item 11)
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param sub1
|
|
|
|
@param collation
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_collate( DsqlCompilerScratch* dsqlScratch, dsql_nod* sub1,
|
|
|
|
const dsql_str* collation)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(sub1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(collation, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
|
|
|
dsql_fld* field = FB_NEW(*tdbb->getDefaultPool()) dsql_fld(*tdbb->getDefaultPool());
|
2010-11-02 00:57:31 +01:00
|
|
|
CastNode* castNode = FB_NEW(*tdbb->getDefaultPool()) CastNode(*tdbb->getDefaultPool(),
|
|
|
|
sub1, field);
|
|
|
|
|
2010-11-01 01:42:12 +01:00
|
|
|
MAKE_desc(dsqlScratch, &sub1->nod_desc, sub1);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(castNode);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (sub1->nod_desc.dsc_dtype <= dtype_any_text ||
|
|
|
|
(sub1->nod_desc.dsc_dtype == dtype_blob && sub1->nod_desc.dsc_sub_type == isc_blob_text))
|
|
|
|
{
|
|
|
|
assign_fld_dtype_from_dsc(field, &sub1->nod_desc);
|
|
|
|
field->fld_character_length = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_dsql_datatype_err) <<
|
|
|
|
Arg::Gds(isc_collation_requires_text));
|
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DDL_resolve_intl_type(dsqlScratch, field, collation);
|
|
|
|
MAKE_desc_from_field(&node->nod_desc, field);
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
/**
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_cursor_context
|
|
|
|
|
|
|
|
@brief Turn a cursor reference into a record selection expression.
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2010-01-28 16:18:11 +01:00
|
|
|
@param cursor
|
|
|
|
@param relation_name
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
static dsql_ctx* pass1_cursor_context( DsqlCompilerScratch* dsqlScratch, const dsql_nod* cursor,
|
|
|
|
const dsql_nod* relation_name)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(cursor, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
const MetaName& relName = ExprNode::as<RelationSourceNode>(relation_name)->dsqlName;
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_str* string = (dsql_str*) cursor->nod_arg[e_cur_name];
|
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// this function must throw an error if no cursor was found
|
2010-12-23 19:42:06 +01:00
|
|
|
const dsql_nod* node = PASS1_cursor_name(dsqlScratch, string->str_data, NOD_CURSOR_ALL, true);
|
2010-01-28 16:18:11 +01:00
|
|
|
fb_assert(node);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
const RseNode* rse = ExprNode::as<RseNode>(node->nod_arg[e_cur_rse]);
|
2010-01-28 16:18:11 +01:00
|
|
|
fb_assert(rse);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlDistinct)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
// cursor with DISTINCT is not updatable
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-510) <<
|
|
|
|
Arg::Gds(isc_dsql_cursor_update_err) << Arg::Str(string->str_data));
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
const dsql_nod* temp = rse->dsqlStreams;
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* context = NULL;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
|
|
|
if (temp->nod_type != nod_class_exprnode)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* const* ptr = temp->nod_arg;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + temp->nod_count; ptr != end; ++ptr)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
dsql_nod* r_node = *ptr;
|
|
|
|
RelationSourceNode* relNode = ExprNode::as<RelationSourceNode>(r_node);
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if (relNode)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_ctx* candidate = relNode->dsqlContext;
|
|
|
|
DEV_BLKCHK(candidate, dsql_type_ctx);
|
|
|
|
const dsql_rel* relation = candidate->ctx_relation;
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (relation->rel_name == relName)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
if (context)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
|
|
|
|
Arg::Gds(isc_dsql_cursor_err) <<
|
2011-01-30 01:25:46 +01:00
|
|
|
Arg::Gds(isc_dsql_cursor_rel_ambiguous) << Arg::Str(relName) <<
|
2011-01-22 21:40:04 +01:00
|
|
|
Arg::Str(string->str_data));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
context = candidate;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2011-01-22 21:40:04 +01:00
|
|
|
else if (ExprNode::as<AggregateSourceNode>(r_node))
|
|
|
|
{
|
|
|
|
// cursor with aggregation is not updatable
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-510) <<
|
|
|
|
Arg::Gds(isc_dsql_cursor_update_err) << Arg::Str(string->str_data));
|
|
|
|
}
|
|
|
|
// note that UnionSourceNode and joins will cause the error below,
|
|
|
|
// as well as derived tables. Some cases deserve fixing in the future
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!context)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
|
|
|
|
Arg::Gds(isc_dsql_cursor_err) <<
|
2011-01-30 01:25:46 +01:00
|
|
|
Arg::Gds(isc_dsql_cursor_rel_not_found) << Arg::Str(relName) <<
|
2010-01-28 16:18:11 +01:00
|
|
|
Arg::Str(string->str_data));
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return context;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
2010-02-22 17:00:49 +01:00
|
|
|
PASS1_cursor_name
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
@brief Find a cursor.
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2010-01-28 16:18:11 +01:00
|
|
|
@param string
|
|
|
|
@param mask
|
|
|
|
@param existence_flag
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
**/
|
2010-12-23 19:42:06 +01:00
|
|
|
dsql_nod* PASS1_cursor_name(DsqlCompilerScratch* dsqlScratch, const MetaName& string,
|
2010-01-28 16:18:11 +01:00
|
|
|
USHORT mask, bool existence_flag)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
|
|
|
dsql_nod* cursor = NULL;
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2010-12-23 19:42:06 +01:00
|
|
|
if (string.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
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (DsqlNodStack::iterator itr(dsqlScratch->cursors); itr.hasData(); ++itr)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
cursor = itr.object();
|
|
|
|
const dsql_str* cname = (dsql_str*) cursor->nod_arg[e_cur_name];
|
2010-12-23 19:42:06 +01:00
|
|
|
if (strcmp(string.c_str(), cname->str_data) == 0 && (cursor->nod_flags & 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) <<
|
2010-12-23 19:42:06 +01:00
|
|
|
Arg::Gds(isc_dsql_cursor_not_found) << Arg::Str(string.c_str()));
|
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) <<
|
2010-12-23 19:42:06 +01:00
|
|
|
Arg::Gds(isc_dsql_cursor_exists) << Arg::Str(string.c_str()));
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_cursor_reference
|
|
|
|
|
|
|
|
@brief Turn a cursor reference into a record selection expression.
|
|
|
|
|
|
|
|
|
|
|
|
@param dsqlScratch
|
|
|
|
@param cursor
|
|
|
|
@param relation_name
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_cursor_reference( DsqlCompilerScratch* dsqlScratch, const dsql_nod* cursor,
|
|
|
|
dsql_nod* relation_name)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(cursor, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Lookup parent dsqlScratch
|
|
|
|
|
|
|
|
const dsql_str* string = (dsql_str*) cursor->nod_arg[e_cur_name];
|
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
|
|
|
|
|
|
|
const dsql_sym* symbol = HSHD_lookup(dsqlScratch->getAttachment(), string->str_data,
|
|
|
|
static_cast<SSHORT>(string->str_length), SYM_cursor, 0);
|
|
|
|
|
|
|
|
if (!symbol)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
// cursor is not found
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
|
|
|
|
Arg::Gds(isc_dsql_cursor_err) <<
|
|
|
|
Arg::Gds(isc_dsql_cursor_not_found) << Arg::Str(string->str_data));
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_req* parent = (dsql_req*) symbol->sym_object;
|
|
|
|
|
|
|
|
// Verify that the cursor is appropriate and updatable
|
|
|
|
|
|
|
|
dsql_par* rv_source = find_record_version(parent, relation_name);
|
|
|
|
|
|
|
|
dsql_par* source;
|
|
|
|
if (parent->getStatement()->getType() != DsqlCompiledStatement::TYPE_SELECT_UPD ||
|
|
|
|
!(source = find_dbkey(parent, relation_name)) || !rv_source)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
// cursor is not updatable
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-510) <<
|
|
|
|
Arg::Gds(isc_dsql_cursor_update_err) << Arg::Str(string->str_data));
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->getStatement()->setParentRequest(parent);
|
|
|
|
dsqlScratch->getStatement()->setParentDbKey(source);
|
|
|
|
dsqlScratch->getStatement()->setParentRecVersion(rv_source);
|
|
|
|
parent->cursors.add(dsqlScratch->getStatement());
|
2006-08-05 23:56:04 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Build record selection expression
|
2009-01-07 10:30:57 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* rse = FB_NEW(pool) RseNode(pool);
|
|
|
|
dsql_nod* temp = rse->dsqlStreams = MAKE_node(nod_list, 1);
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* relation_node = pass1_relation(dsqlScratch, relation_name);
|
|
|
|
temp->nod_arg[0] = relation_node;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
ComparativeBoolNode* eqlNode = FB_NEW(pool) ComparativeBoolNode(pool,
|
2010-11-14 18:25:48 +01:00
|
|
|
blr_eql, MAKE_node(nod_class_exprnode, 1), MAKE_node(nod_class_exprnode, 1));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(eqlNode);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlWhere = node;
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
RecordKeyNode* dbKeyNode = FB_NEW(pool) RecordKeyNode(pool, blr_dbkey);
|
|
|
|
dbKeyNode->dsqlRelation = relation_node;
|
|
|
|
eqlNode->dsqlArg1->nod_count = 0;
|
|
|
|
eqlNode->dsqlArg1->nod_arg[0] = reinterpret_cast<dsql_nod*>(dbKeyNode);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_par* parameter = MAKE_parameter(dsqlScratch->getStatement()->getSendMsg(),
|
|
|
|
false, false, 0, NULL);
|
|
|
|
dsqlScratch->getStatement()->setDbKey(parameter);
|
2010-10-09 03:57:37 +02:00
|
|
|
|
|
|
|
ParameterNode* paramNode = FB_NEW(pool) ParameterNode(pool);
|
2010-11-14 18:25:48 +01:00
|
|
|
eqlNode->dsqlArg2->nod_count = 0;
|
2010-10-09 03:57:37 +02:00
|
|
|
eqlNode->dsqlArg2->nod_arg[0] = reinterpret_cast<dsql_nod*>(paramNode);
|
|
|
|
paramNode->dsqlParameterIndex = parameter->par_index;
|
|
|
|
paramNode->dsqlParameter = parameter;
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
parameter->par_desc = source->par_desc;
|
|
|
|
|
|
|
|
// record version will be set only for V4 - for the parent select cursor
|
|
|
|
if (rv_source)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
eqlNode = FB_NEW(pool) ComparativeBoolNode(pool,
|
2010-11-14 18:25:48 +01:00
|
|
|
blr_eql, MAKE_node(nod_class_exprnode, 1), MAKE_node(nod_class_exprnode, 1));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(eqlNode);
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
dbKeyNode = FB_NEW(pool) RecordKeyNode(pool, blr_record_version);
|
|
|
|
dbKeyNode->dsqlRelation = relation_node;
|
|
|
|
eqlNode->dsqlArg1->nod_count = 0;
|
|
|
|
eqlNode->dsqlArg1->nod_arg[0] = reinterpret_cast<dsql_nod*>(dbKeyNode);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
parameter = MAKE_parameter(dsqlScratch->getStatement()->getSendMsg(),
|
|
|
|
false, false, 0, NULL);
|
|
|
|
dsqlScratch->getStatement()->setRecVersion(parameter);
|
2010-10-09 03:57:37 +02:00
|
|
|
|
|
|
|
paramNode = FB_NEW(pool) ParameterNode(pool);
|
2010-11-14 18:25:48 +01:00
|
|
|
eqlNode->dsqlArg2->nod_count = 0;
|
2010-10-09 03:57:37 +02:00
|
|
|
eqlNode->dsqlArg2->nod_arg[0] = reinterpret_cast<dsql_nod*>(paramNode);
|
|
|
|
paramNode->dsqlParameterIndex = parameter->par_index;
|
|
|
|
paramNode->dsqlParameter = parameter;
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
parameter->par_desc = rv_source->par_desc;
|
2006-08-05 23:56:04 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlWhere = PASS1_compose(rse->dsqlWhere, node, blr_and);
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* rseNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
rseNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(rse);
|
|
|
|
|
|
|
|
return rseNod;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_delete
|
2009-12-10 00:06:29 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Process DELETE statement.
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2006-08-01 22:37:58 +02:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
static dsql_nod* pass1_delete( DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
const dsql_nod* cursor = input->nod_arg[e_del_cursor];
|
|
|
|
dsql_nod* relation = input->nod_arg[e_del_relation];
|
|
|
|
if (cursor && dsqlScratch->isPsql())
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* anode = MAKE_node(nod_erase_current, e_erc_count);
|
|
|
|
anode->nod_arg[e_erc_context] = (dsql_nod*) pass1_cursor_context(dsqlScratch, cursor, relation);
|
|
|
|
anode->nod_arg[e_erc_return] = process_returning(dsqlScratch, input->nod_arg[e_del_return]);
|
|
|
|
return anode;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->getStatement()->setType(
|
|
|
|
cursor ? DsqlCompiledStatement::TYPE_DELETE_CURSOR : DsqlCompiledStatement::TYPE_DELETE);
|
|
|
|
dsql_nod* node = MAKE_node(nod_erase, e_era_count);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Generate record selection expression
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* rseNod;
|
2010-01-28 16:18:11 +01:00
|
|
|
if (cursor)
|
2011-01-09 22:58:56 +01:00
|
|
|
rseNod = pass1_cursor_reference(dsqlScratch, cursor, relation);
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* rse = FB_NEW(*tdbb->getDefaultPool()) RseNode(*tdbb->getDefaultPool());
|
|
|
|
|
|
|
|
rseNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
rseNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(rse);
|
|
|
|
|
|
|
|
dsql_nod* temp = rse->dsqlStreams = MAKE_node(nod_list, 1);
|
2010-01-28 16:18:11 +01:00
|
|
|
temp->nod_arg[0] = PASS1_node_psql(dsqlScratch, relation, false);
|
2009-12-10 00:06:29 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_del_boolean]))
|
|
|
|
rse->dsqlWhere = PASS1_node_psql(dsqlScratch, temp, false);
|
2009-12-10 00:06:29 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_del_plan]))
|
|
|
|
rse->dsqlPlan = PASS1_node_psql(dsqlScratch, temp, false);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_del_sort]))
|
|
|
|
rse->dsqlOrder = pass1_sort(dsqlScratch, temp, NULL);
|
2009-12-10 00:06:29 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_del_rows]))
|
2011-01-20 05:41:10 +01:00
|
|
|
pass1_limit(dsqlScratch, temp->nod_arg[e_rows_length], temp->nod_arg[e_rows_skip], rse);
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (input->nod_arg[e_del_return])
|
2011-01-09 22:58:56 +01:00
|
|
|
rseNod->nod_flags |= NOD_SELECT_EXPR_SINGLETON;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
node->nod_arg[e_era_rse] = rseNod;
|
|
|
|
node->nod_arg[e_era_relation] = ExprNode::as<RseNode>(rseNod)->dsqlStreams->nod_arg[0];
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
node->nod_arg[e_era_return] = process_returning(dsqlScratch, input->nod_arg[e_del_return]);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
nullify_returning(dsqlScratch, node, &node);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->context->pop();
|
2006-08-01 22:37:58 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
process_returning
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Compile a RETURNING clause (nod_returning or not).
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2010-01-28 16:18:11 +01:00
|
|
|
@param input
|
2005-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
static dsql_nod* process_returning(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* node;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!input || input->nod_type == nod_returning)
|
|
|
|
node = PASS1_node(dsqlScratch, input);
|
|
|
|
else
|
|
|
|
node = PASS1_node_psql(dsqlScratch, input, false);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (input && !dsqlScratch->isPsql())
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_EXEC_PROCEDURE);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// 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
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
if (context->ctx_relation || context->ctx_procedure)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
if (context->ctx_parent)
|
|
|
|
context = context->ctx_parent;
|
2007-04-29 21:04:26 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
contexts.push(context);
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
for (DsqlContextStack::iterator i(context->ctx_childs_derived_table); i.hasData(); ++i)
|
|
|
|
pass1_expand_contexts(contexts, i.object());
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_derived_table
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Process derived table which is part of a from clause.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2011-01-30 01:25:46 +01:00
|
|
|
static dsql_nod* pass1_derived_table(DsqlCompilerScratch* dsqlScratch, dsql_nod* input,
|
|
|
|
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-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
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
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_str* alias = (dsql_str*) input->nod_arg[e_sel_alias];
|
2010-01-28 16:18:11 +01:00
|
|
|
|
|
|
|
// Create the context now, because we need to know it for the tables inside.
|
2011-01-09 22:58:56 +01: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;
|
|
|
|
dsql_str* const aliasRelationPrefix = dsqlScratch->aliasRelationPrefix;
|
|
|
|
|
|
|
|
// Change context, because when we are processing the derived table rse
|
|
|
|
// it may not reference to other streams in the same scope_level.
|
|
|
|
DsqlContextStack temp;
|
|
|
|
// Put special contexts (NEW/OLD) also on the stack
|
|
|
|
for (DsqlContextStack::iterator stack(*dsqlScratch->context); stack.hasData(); ++stack)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* local_context = stack.object();
|
|
|
|
if ((local_context->ctx_scope_level < dsqlScratch->scopeLevel) ||
|
|
|
|
(local_context->ctx_flags & CTX_system))
|
|
|
|
{
|
|
|
|
temp.push(local_context);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* baseContext = NULL;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
if (temp.hasData())
|
2010-01-28 16:18:11 +01:00
|
|
|
baseContext = temp.object();
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->context = &temp;
|
2011-01-30 01:25:46 +01:00
|
|
|
dsqlScratch->aliasRelationPrefix = pass1_alias_concat(
|
|
|
|
aliasRelationPrefix, (alias ? alias->str_data : NULL));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* query = input->nod_arg[e_sel_query_spec];
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* rse = NULL;
|
|
|
|
const bool isRecursive = (query->nod_type == nod_list) && (query->nod_flags & NOD_UNION_RECURSIVE);
|
|
|
|
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
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
query->nod_count = 1;
|
|
|
|
query->nod_flags &= ~NOD_UNION_RECURSIVE;
|
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;
|
2010-02-16 17:28:54 +01:00
|
|
|
rse = pass1_union(dsqlScratch, query, NULL, NULL, NULL, 0);
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->contextNumber = dsqlScratch->recursiveCtxId + 1;
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// recursive union always have exactly 2 members
|
|
|
|
query->nod_count = 2;
|
|
|
|
query->nod_flags |= NOD_UNION_RECURSIVE;
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
while (dsqlScratch->unionContext.hasData() &&
|
|
|
|
dsqlScratch->unionContext.object() != baseUnionCtx)
|
|
|
|
{
|
|
|
|
dsqlScratch->unionContext.pop();
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// AB: 2005-01-06
|
|
|
|
// If our derived table contains a single query with a sub-select buried
|
|
|
|
// inside the select items then we need a special handling, because we don't
|
|
|
|
// want creating a new sub-select for every reference outside the derived
|
|
|
|
// table to that sub-select.
|
|
|
|
// To handle this we simple create a UNION ALL with derived table inside it.
|
|
|
|
// Due this mappings are created and we simple reference to these mappings.
|
|
|
|
// Optimizer effects:
|
|
|
|
// Good thing is that only 1 recordstream is made for the sub-select, but
|
|
|
|
// the worse thing is that a UNION currently can't be used in
|
|
|
|
// deciding the JOIN order.
|
|
|
|
bool foundSubSelect = false;
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* queryNode = ExprNode::as<RseNode>(query);
|
|
|
|
if (queryNode)
|
|
|
|
foundSubSelect = SubSelectFinder::find(queryNode->dsqlSelectList);
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2010-08-07 04:43:29 +02:00
|
|
|
int unionContexts = 0;
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (foundSubSelect)
|
|
|
|
{
|
2010-08-07 04:10:08 +02:00
|
|
|
DsqlContextStack::const_iterator baseUnion(dsqlScratch->unionContext);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* union_expr = MAKE_node(nod_list, 1);
|
2011-01-09 22:58:56 +01:00
|
|
|
union_expr->nod_arg[0] = input;
|
2010-01-28 16:18:11 +01:00
|
|
|
union_expr->nod_flags = NOD_UNION_ALL;
|
2010-02-16 17:28:54 +01:00
|
|
|
rse = pass1_union(dsqlScratch, union_expr, NULL, NULL, NULL, 0);
|
2010-08-07 04:10:08 +02:00
|
|
|
|
|
|
|
for (DsqlContextStack::const_iterator i(dsqlScratch->unionContext);
|
|
|
|
i.hasData() && i != baseUnion;
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
temp.push(i.object());
|
2010-08-07 04:43:29 +02:00
|
|
|
++unionContexts;
|
2010-08-07 04:10:08 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2010-08-07 04:10:08 +02:00
|
|
|
else
|
2011-01-09 22:58:56 +01:00
|
|
|
rse = PASS1_rse(dsqlScratch, input, NULL);
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
USHORT minOuterJoin = MAX_USHORT;
|
2007-11-30 01:35:44 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Finish off by cleaning up contexts and put them into derivedContext
|
|
|
|
// so create view (ddl) can deal with it.
|
|
|
|
// Also add the used contexts into the childs stack.
|
|
|
|
while (temp.hasData() && (temp.object() != baseContext))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-08-07 04:43:29 +02:00
|
|
|
dsql_ctx* childCtx = temp.pop();
|
|
|
|
|
|
|
|
// Do not put the just pushed union contexts in derivedContext. Otherwise duplicate
|
|
|
|
// records will appear in RDB$VIEW_RELATIONS.
|
|
|
|
if (--unionContexts < 0)
|
|
|
|
dsqlScratch->derivedContext.push(childCtx);
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-08-07 04:43:29 +02:00
|
|
|
context->ctx_childs_derived_table.push(childCtx);
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Collect contexts that will be used for blr_derived_expr generation.
|
|
|
|
// We want all child contexts with minimum ctx_in_outer_join.
|
|
|
|
if (childCtx->ctx_in_outer_join <= minOuterJoin)
|
2007-11-29 18:53:38 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
DsqlContextStack contexts;
|
|
|
|
pass1_expand_contexts(contexts, childCtx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (DsqlContextStack::iterator i(contexts); i.hasData(); ++i)
|
2007-11-29 18:53:38 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
if (i.object()->ctx_in_outer_join < minOuterJoin)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
minOuterJoin = i.object()->ctx_in_outer_join;
|
|
|
|
context->ctx_main_derived_contexts.clear();
|
2004-01-09 03:23:46 +01:00
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_main_derived_contexts.push(i.object());
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2008-06-03 08:19:21 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
while (temp.hasData())
|
|
|
|
temp.pop();
|
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
context->ctx_rse = rse;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// CVC: prepare a truncated alias for the derived table here
|
|
|
|
// because we need it several times.
|
|
|
|
TEXT aliasbuffer[100] = "";
|
|
|
|
const TEXT* aliasname = aliasbuffer;
|
|
|
|
if (alias)
|
|
|
|
{
|
|
|
|
int length = alias->str_length;
|
|
|
|
if (length > 99)
|
|
|
|
{
|
|
|
|
length = 99;
|
|
|
|
memcpy(aliasbuffer, alias->str_data, length);
|
|
|
|
aliasbuffer[length] = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
aliasname = alias->str_data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
aliasname = "<unnamed>";
|
2007-05-06 18:45:49 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* selectList = ExprNode::as<RseNode>(rse)->dsqlSelectList;
|
|
|
|
|
|
|
|
// If an alias-list is specified, process it.
|
|
|
|
|
|
|
|
const bool ignoreColumnChecks = (input->nod_flags & NOD_SELECT_EXPR_DT_IGNORE_COLUMN_CHECK);
|
|
|
|
|
|
|
|
if (input->nod_arg[e_sel_columns] && input->nod_arg[e_sel_columns]->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* list = input->nod_arg[e_sel_columns];
|
2007-05-06 18:45:49 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Do both lists have the same number of items?
|
2011-01-09 22:58:56 +01:00
|
|
|
if (list->nod_count != selectList->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
// Column list by derived table %s [alias-name] has %s [more/fewer] columns
|
|
|
|
// than the number of items.
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
int errcode = isc_dsql_derived_table_less_columns;
|
2011-01-09 22:58:56 +01:00
|
|
|
if (list->nod_count > selectList->nod_count)
|
2010-01-28 16:18:11 +01:00
|
|
|
errcode = isc_dsql_derived_table_more_columns;
|
2007-05-06 18:45:49 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(errcode) << Arg::Str(aliasname));
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Generate derived fields and assign alias-name to them.
|
2010-01-28 16:18:11 +01:00
|
|
|
for (int count = 0; count < list->nod_count; count++)
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* select_item = selectList->nod_arg[count];
|
2010-11-01 14:45:52 +01:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nod_desc, select_item);
|
2004-01-09 03:23:46 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Make new derived field node.
|
|
|
|
|
|
|
|
DerivedFieldNode* derivedField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
((dsql_str*) list->nod_arg[count])->str_data, dsqlScratch->scopeLevel, select_item);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* nod = selectList->nod_arg[count] = MAKE_node(nod_class_exprnode, 1);
|
2010-11-14 18:25:48 +01:00
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(derivedField);
|
|
|
|
nod->nod_desc = select_item->nod_desc;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// For those select-items where no alias is specified try
|
|
|
|
// to generate one from the field_name.
|
2011-01-09 22:58:56 +01:00
|
|
|
for (int count = 0; count < selectList->nod_count; count++)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
dsql_nod* select_item = pass1_make_derived_field(dsqlScratch, tdbb,
|
2011-01-09 22:58:56 +01:00
|
|
|
selectList->nod_arg[count]);
|
2007-11-29 18:53:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Auto-create dummy column name for pass1_any()
|
2010-11-14 18:25:48 +01:00
|
|
|
if (ignoreColumnChecks && !ExprNode::is<DerivedFieldNode>(select_item))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nod_desc, select_item);
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Construct dummy fieldname
|
|
|
|
char fieldname[25];
|
2010-11-14 18:25:48 +01:00
|
|
|
sprintf(fieldname, "f%d", count);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Make new derived field node.
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
fieldname, dsqlScratch->scopeLevel, select_item);
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(derivedField);
|
|
|
|
nod->nod_desc = select_item->nod_desc;
|
|
|
|
|
|
|
|
select_item = nod;
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
selectList->nod_arg[count] = select_item;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2007-11-29 18:53:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
int count;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Check if all root select-items have a derived field else show a message.
|
2011-01-09 22:58:56 +01:00
|
|
|
for (count = 0; count < selectList->nod_count; count++)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* select_item = selectList->nod_arg[count];
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
2007-11-29 18:53:38 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
derivedField->context = context;
|
2010-01-28 16:18:11 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// no column name specified for column number %d in derived table %s
|
|
|
|
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_derived_field_unnamed) << Arg::Num(count + 1) <<
|
|
|
|
Arg::Str(aliasname));
|
2007-11-29 18:53:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Check for ambiguous column names inside this derived table.
|
2011-01-09 22:58:56 +01:00
|
|
|
for (count = 0; count < selectList->nod_count; ++count)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DerivedFieldNode* selectItem1 =
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<DerivedFieldNode>(selectList->nod_arg[count]);
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
for (int count2 = (count + 1); count2 < selectList->nod_count; ++count2)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
const DerivedFieldNode* selectItem2 =
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<DerivedFieldNode>(selectList->nod_arg[count2]);
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
if (selectItem1->name == selectItem2->name)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
// column %s was specified multiple times for derived table %s
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
2010-11-14 18:25:48 +01:00
|
|
|
Arg::Gds(isc_dsql_derived_field_dup_name) << selectItem1->name <<
|
|
|
|
aliasname);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// If we used a dummy rse before, replace it with the real one now.
|
|
|
|
// We cannot do this earlier, because recursive processing needs a fully
|
|
|
|
// developed context block.
|
|
|
|
if (isRecursive)
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->recursiveCtx = context;
|
|
|
|
dsqlScratch->context = &temp;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
dsqlScratch->resetCTEAlias(alias->str_data);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse = PASS1_rse(dsqlScratch, input, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Finish off by cleaning up contexts and put them into derivedContext
|
|
|
|
// so create view (ddl) can deal with it.
|
|
|
|
// Also add the used contexts into the childs stack.
|
|
|
|
while (temp.hasData() && (temp.object() != baseContext))
|
|
|
|
{
|
|
|
|
dsqlScratch->derivedContext.push(temp.object());
|
|
|
|
context->ctx_childs_derived_table.push(temp.pop());
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
temp.clear();
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<RseNode>(rse)->dsqlSelectList = ExprNode::as<RseNode>(context->ctx_rse)->dsqlSelectList;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
context->ctx_rse = rse;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
if (cte_alias)
|
2011-01-30 01:25:46 +01:00
|
|
|
context->ctx_alias = cte_alias;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsqlScratch->context = req_base;
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Mark union's map context as recursive and assign secondary context number to it.
|
|
|
|
dsql_nod* items = ExprNode::as<RseNode>(rse)->dsqlSelectList;
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* map_item = items->nod_arg[0];
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(map_item)))
|
|
|
|
map_item = derivedField->dsqlValue;
|
|
|
|
|
|
|
|
dsql_ctx* map_context = ExprNode::as<DsqlMapNode>(map_item)->context;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
map_context->ctx_flags |= CTX_recursive;
|
|
|
|
map_context->ctx_recursive = recursive_map_ctx;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
delete dsqlScratch->aliasRelationPrefix;
|
|
|
|
// Restore our original values.
|
|
|
|
dsqlScratch->context = req_base;
|
|
|
|
dsqlScratch->aliasRelationPrefix = aliasRelationPrefix;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
ExprNode::as<RseNode>(rse)->dsqlContext = context;
|
|
|
|
|
|
|
|
return rse;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/**
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_expand_select_list
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Expand asterisk nodes into fields.
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param list
|
|
|
|
@param streams
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_expand_select_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* list,
|
|
|
|
dsql_nod* streams)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
list = streams;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DsqlNodStack stack;
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
2002-10-01 02:34:29 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
|
|
|
PASS1_expand_select_node(dsqlScratch, *ptr, stack, true);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
return MAKE_list(stack);
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Expand a select item node.
|
|
|
|
void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* node, DsqlNodStack& stack,
|
2010-01-28 16:18:11 +01:00
|
|
|
bool hide_using)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* rseNode;
|
2011-01-30 01:25:46 +01:00
|
|
|
ProcedureSourceNode* procNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
RelationSourceNode* relNode;
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if ((rseNode = ExprNode::as<RseNode>(node)))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* sub_items = rseNode->dsqlSelectList;
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if (sub_items) // AB: Derived table support
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod** ptr = sub_items->nod_arg;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + sub_items->nod_count; ptr != end; ++ptr)
|
|
|
|
{
|
|
|
|
// Create a new alias else mappings would be mangled.
|
|
|
|
dsql_nod* select_item = *ptr;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// select-item should always be a derived field!
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
|
|
|
if (!(derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Internal dsql error: alias type expected by PASS1_expand_select_node
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_derived_alias_select));
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_ctx* context = derivedField->context;
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if (!hide_using || context->getImplicitJoinField(derivedField->name, select_item))
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // joins
|
|
|
|
{
|
|
|
|
dsql_nod** ptr = rseNode->dsqlStreams->nod_arg;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + rseNode->dsqlStreams->nod_count;
|
|
|
|
ptr != end;
|
|
|
|
++ptr)
|
|
|
|
{
|
|
|
|
PASS1_expand_select_node(dsqlScratch, *ptr, stack, true);
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
else if ((procNode = ExprNode::as<ProcedureSourceNode>(node)))
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
dsql_ctx* context = procNode->dsqlContext;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_procedure)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
for (dsql_fld* field = context->ctx_procedure->prc_outputs; field; field = field->fld_next)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* select_item = NULL;
|
|
|
|
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
|
|
|
|
{
|
|
|
|
if (!select_item)
|
|
|
|
select_item = MAKE_field(context, field, 0);
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
}
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(node)))
|
|
|
|
{
|
|
|
|
dsql_ctx* context = relNode->dsqlContext;
|
|
|
|
|
|
|
|
if (context->ctx_relation)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
for (dsql_fld* field = context->ctx_relation->rel_fields; field; field = field->fld_next)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2010-01-23 04:02:53 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* select_item = NULL;
|
|
|
|
if (!hide_using || context->getImplicitJoinField(field->fld_name, select_item))
|
|
|
|
{
|
|
|
|
if (!select_item)
|
|
|
|
select_item = MAKE_field(context, field, 0);
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (node->nod_type == nod_field_name)
|
|
|
|
{
|
|
|
|
dsql_nod* select_item = pass1_field(dsqlScratch, node, true, NULL);
|
|
|
|
// The node could be a relation so call recursively.
|
2011-01-22 21:40:04 +01:00
|
|
|
PASS1_expand_select_node(dsqlScratch, select_item, stack, false);
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2010-01-28 16:18:11 +01:00
|
|
|
stack.push(node);
|
2002-09-29 01:52:36 +02:00
|
|
|
}
|
|
|
|
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
pass1_field
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@brief Resolve a field name to an available context.
|
|
|
|
If list is true, then this function can detect and
|
|
|
|
return a relation node if there is no name. This
|
|
|
|
is used for cases of "SELECT <table_name>. ...".
|
|
|
|
CVC: The function attempts to detect
|
|
|
|
if an unqualified field appears in more than one context
|
|
|
|
and hence it returns the number of occurrences. This was
|
|
|
|
added to allow the caller to detect ambiguous commands like
|
|
|
|
select from t1 join t2 on t1.f = t2.f order by common_field.
|
|
|
|
While inoffensive on inner joins, it changes the result on outer joins.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
@param dsqlScratch
|
|
|
|
@param input
|
|
|
|
@param list
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2010-01-28 16:18:11 +01:00
|
|
|
static dsql_nod* pass1_field(DsqlCompilerScratch* dsqlScratch, dsql_nod* input,
|
|
|
|
const bool list, dsql_nod* select_list)
|
2002-09-29 01:52:36 +02:00
|
|
|
{
|
2010-10-22 17:00:22 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// handle an array element.
|
|
|
|
dsql_nod* indices;
|
|
|
|
if (input->nod_type == nod_array)
|
|
|
|
{
|
|
|
|
indices = input->nod_arg[e_ary_indices];
|
|
|
|
input = input->nod_arg[e_ary_array];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
indices = NULL;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_str* name;
|
|
|
|
const dsql_str* qualifier; // We assume the qualifier was stripped elsewhere.
|
|
|
|
if (input->nod_count == 1)
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2010-01-28 16:18:11 +01:00
|
|
|
name = (dsql_str*) input->nod_arg[0];
|
|
|
|
qualifier = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
name = (dsql_str*) input->nod_arg[1];
|
|
|
|
qualifier = (dsql_str*) input->nod_arg[0];
|
|
|
|
}
|
|
|
|
DEV_BLKCHK(name, dsql_type_str);
|
|
|
|
DEV_BLKCHK(qualifier, dsql_type_str);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// CVC: Let's strip trailing blanks or comparisons may fail in dialect 3.
|
|
|
|
if (name && name->str_data) {
|
|
|
|
fb_utils::exact_name(name->str_data);
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
/* CVC: PLEASE READ THIS EXPLANATION IF YOU NEED TO CHANGE THIS CODE.
|
|
|
|
You should ensure that this function:
|
|
|
|
1.- Never returns NULL. In such case, it such fall back to an invocation
|
2010-11-14 18:25:48 +01:00
|
|
|
to PASS1_field_unknown() near the end of this function. None of the multiple callers
|
2010-01-28 16:18:11 +01:00
|
|
|
of this function (inside this same module) expect a null pointer, hence they
|
|
|
|
will crash the engine in such case.
|
|
|
|
2.- Doesn't allocate more than one field in "node". Either you put a break,
|
|
|
|
keep the current "continue" or call ALLD_release if you don't want nor the
|
|
|
|
continue neither the break if node is already allocated. If it isn't evident,
|
|
|
|
but this variable is initialized to zero in the declaration above. You
|
|
|
|
may write an explicit line to set it to zero here, before the loop.
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
3.- Doesn't waste cycles if qualifier is not null. The problem is not the cycles
|
|
|
|
themselves, but the fact that you'll detect an ambiguity that doesn't exist: if
|
|
|
|
the field appears in more than one context but it's always qualified, then
|
|
|
|
there's no ambiguity. There's PASS1_make_context() that prevents a context's
|
|
|
|
alias from being reused. However, other places in the code don't check that you
|
|
|
|
don't create a join or subselect with the same context without disambiguating it
|
|
|
|
with different aliases. This is the place where resolve_context() is called for
|
|
|
|
that purpose. In the future, it will be fine if we force the use of the alias as
|
|
|
|
the only allowed qualifier if the alias exists. Hopefully, we will eliminate
|
|
|
|
some day this construction: "select table.field from table t" because it
|
|
|
|
should be "t.field" instead.
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
AB: 2004-01-09
|
|
|
|
The explained query directly above doesn't work anymore, thus the day has come ;-)
|
|
|
|
It's allowed to use the same fieldname between different scope levels (sub-queries)
|
|
|
|
without being hit by the ambiguity check. The field uses the first match starting
|
|
|
|
from it's own level (of course ambiguity-check on each level is done).
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
4.- Doesn't verify code derived automatically from check constraints. They are
|
|
|
|
ill-formed by nature but making that code generation more orthodox is not a
|
|
|
|
priority. Typically, they only check a field against a contant. The problem
|
|
|
|
appears when they check a field against a subselect, for example. For now,
|
|
|
|
allow the user to write ambiguous subselects in check() statements.
|
|
|
|
Claudio Valderrama - 2001.1.29.
|
|
|
|
*/
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (select_list && !qualifier && name && name->str_data)
|
|
|
|
{
|
|
|
|
// 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.
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* node = PASS1_lookup_alias(dsqlScratch, name, select_list, true);
|
|
|
|
if (node)
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
|
|
|
}
|
2009-10-30 11:43:42 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Try to resolve field against various contexts;
|
|
|
|
// if there is an alias, check only against the first matching
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* node = NULL; // This var must be initialized.
|
|
|
|
DsqlContextStack ambiguous_ctx_stack;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
bool resolve_by_alias = true;
|
|
|
|
const bool relaxedAliasChecking = Config::getRelaxedAliasChecking();
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// AB: Loop through the scope_levels starting by its own.
|
|
|
|
bool done = false;
|
|
|
|
USHORT current_scope_level = dsqlScratch->scopeLevel + 1;
|
|
|
|
for (; (current_scope_level > 0) && !done; current_scope_level--)
|
|
|
|
{
|
2006-08-04 06:32:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// If we've found a node we're done.
|
2011-01-09 22:58:56 +01:00
|
|
|
if (node)
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
2006-08-04 06:32:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (DsqlContextStack::iterator stack(*dsqlScratch->context); stack.hasData(); ++stack)
|
|
|
|
{
|
|
|
|
// resolve_context() checks the type of the
|
|
|
|
// given context, so the cast to dsql_ctx* is safe.
|
2006-08-04 06:32:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_ctx* context = stack.object();
|
2006-08-04 06:32:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (context->ctx_scope_level != (current_scope_level - 1)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-08-02 04:22:26 +02:00
|
|
|
dsql_fld* field = resolve_context(dsqlScratch, qualifier, context, resolve_by_alias);
|
2006-08-04 06:32:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// AB: When there's no relation and no procedure then we have a derived table.
|
|
|
|
const bool is_derived_table =
|
|
|
|
(!context->ctx_procedure && !context->ctx_relation && context->ctx_rse);
|
2006-08-04 06:32:27 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (field)
|
|
|
|
{
|
|
|
|
// If there's no name then we have most probable an asterisk that
|
|
|
|
// needs to be exploded. This should be handled by the caller and
|
|
|
|
// when the caller can handle this, list is true.
|
|
|
|
if (!name)
|
|
|
|
{
|
|
|
|
if (list)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
dsql_ctx* stackContext = stack.object();
|
|
|
|
|
|
|
|
RecordSourceNode* recSource = NULL;
|
|
|
|
|
|
|
|
if (context->ctx_relation)
|
|
|
|
{
|
|
|
|
RelationSourceNode* relNode = FB_NEW(*tdbb->getDefaultPool())
|
|
|
|
RelationSourceNode(*tdbb->getDefaultPool());
|
|
|
|
relNode->dsqlContext = stackContext;
|
|
|
|
recSource = relNode;
|
|
|
|
}
|
|
|
|
else if (context->ctx_procedure)
|
|
|
|
{
|
|
|
|
ProcedureSourceNode* procNode = FB_NEW(*tdbb->getDefaultPool())
|
|
|
|
ProcedureSourceNode(*tdbb->getDefaultPool());
|
|
|
|
procNode->dsqlContext = stackContext;
|
|
|
|
recSource = procNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(recSource);
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
node = MAKE_node(nod_class_exprnode, 1);
|
2011-01-30 01:25:46 +01:00
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(recSource);
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
return node;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* using_field = NULL;
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (; field; field = field->fld_next)
|
|
|
|
{
|
|
|
|
if (field->fld_name == name->str_data)
|
|
|
|
{
|
|
|
|
if (!qualifier)
|
|
|
|
{
|
|
|
|
if (!context->getImplicitJoinField(field->fld_name, using_field))
|
|
|
|
{
|
|
|
|
field = NULL;
|
|
|
|
break;
|
|
|
|
}
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (using_field)
|
|
|
|
field = NULL;
|
|
|
|
}
|
2010-01-23 04:02:53 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
ambiguous_ctx_stack.push(context);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (qualifier && !field)
|
|
|
|
{
|
|
|
|
// If a qualifier was present and we don't have found
|
|
|
|
// a matching field then we should stop searching.
|
|
|
|
// Column unknown error will be raised at bottom of function.
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (field || using_field)
|
|
|
|
{
|
|
|
|
// Intercept any reference to a field with datatype that
|
|
|
|
// did not exist prior to V6 and post an error
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (dsqlScratch->clientDialect <= SQL_DIALECT_V5 && field &&
|
|
|
|
(field->fld_dtype == dtype_sql_date ||
|
|
|
|
field->fld_dtype == dtype_sql_time || field->fld_dtype == dtype_int64))
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-206) <<
|
|
|
|
Arg::Gds(isc_dsql_field_err) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str(field->fld_name) <<
|
|
|
|
Arg::Gds(isc_sql_dialect_datatype_unsupport) <<
|
|
|
|
Arg::Num(dsqlScratch->clientDialect) <<
|
|
|
|
Arg::Str(DSC_dtype_tostring(static_cast<UCHAR>(field->fld_dtype))));
|
|
|
|
return NULL;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// CVC: Stop here if this is our second or third iteration.
|
|
|
|
// Anyway, we can't report more than one ambiguity to the status vector.
|
|
|
|
// AB: But only if we're on different scope level, because a
|
|
|
|
// node inside the same context should have priority.
|
2010-10-22 17:00:22 +02:00
|
|
|
if (node)
|
2010-01-28 16:18:11 +01:00
|
|
|
continue;
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-10-22 17:00:22 +02:00
|
|
|
if (indices)
|
2010-01-28 16:18:11 +01:00
|
|
|
indices = PASS1_node_psql(dsqlScratch, indices, false);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (context->ctx_flags & CTX_null)
|
2010-10-22 17:00:22 +02:00
|
|
|
{
|
|
|
|
node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(
|
|
|
|
FB_NEW(*tdbb->getDefaultPool()) NullNode(*tdbb->getDefaultPool()));
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
else if (field)
|
|
|
|
node = MAKE_field(context, field, indices);
|
|
|
|
else
|
|
|
|
node = list ? using_field : PASS1_node_psql(dsqlScratch, using_field, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (is_derived_table)
|
|
|
|
{
|
|
|
|
// if an qualifier is present check if we have the same derived
|
|
|
|
// table else continue;
|
|
|
|
if (qualifier)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_alias.hasData())
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_alias != qualifier->str_data)
|
2010-01-28 16:18:11 +01:00
|
|
|
continue;
|
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else
|
2010-01-28 16:18:11 +01:00
|
|
|
continue;
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// If there's no name then we have most probable a asterisk that
|
|
|
|
// needs to be exploded. This should be handled by the caller and
|
|
|
|
// when the caller can handle this, list is true.
|
|
|
|
if (!name)
|
|
|
|
{
|
|
|
|
if (list)
|
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
// Return node which PASS1_expand_select_node() can deal with it.
|
2011-01-09 22:58:56 +01:00
|
|
|
return context->ctx_rse;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Because every select item has an alias we can just walk
|
|
|
|
// through the list and return the correct node when found.
|
2011-01-09 22:58:56 +01:00
|
|
|
const dsql_nod* rse_items = ExprNode::as<RseNode>(context->ctx_rse)->dsqlSelectList;
|
2010-01-28 16:18:11 +01:00
|
|
|
dsql_nod* const* ptr = rse_items->nod_arg;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + rse_items->nod_count;
|
2010-11-14 18:25:48 +01:00
|
|
|
ptr < end; ptr++)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* selectItem = ExprNode::as<DerivedFieldNode>(*ptr);
|
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// select-item should always be a alias!
|
2010-11-14 18:25:48 +01:00
|
|
|
if (selectItem)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
|
|
|
dsql_nod* using_field = NULL;
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!qualifier)
|
|
|
|
{
|
|
|
|
if (!context->getImplicitJoinField(name->str_data, using_field))
|
|
|
|
break;
|
|
|
|
}
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (!strcmp(name->str_data, selectItem->name.c_str()) || using_field)
|
2010-01-28 16:18:11 +01:00
|
|
|
{
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// This is a matching item so add the context to the ambiguous list.
|
|
|
|
ambiguous_ctx_stack.push(context);
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Stop here if this is our second or more iteration.
|
2010-11-14 18:25:48 +01:00
|
|
|
if (node)
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
node = using_field ? using_field : *ptr;
|
2010-01-28 16:18:11 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Internal dsql error: alias type expected by pass1_field
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_derived_alias_field));
|
|
|
|
}
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (!node && qualifier)
|
|
|
|
{
|
|
|
|
// If a qualifier was present and we don't have found
|
|
|
|
// a matching field then we should stop searching.
|
|
|
|
// Column unknown error will be raised at bottom of function.
|
|
|
|
done = true;
|
|
|
|
break;
|
2005-01-06 14:14:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (node)
|
|
|
|
break;
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-08-02 04:22:26 +02:00
|
|
|
if (resolve_by_alias && !dsqlScratch->checkConstraintTrigger && relaxedAliasChecking)
|
2010-01-28 16:18:11 +01:00
|
|
|
resolve_by_alias = false;
|
2010-08-02 04:22:26 +02:00
|
|
|
else
|
2005-01-06 14:14:38 +01:00
|
|
|
break;
|
2010-01-28 16:18:11 +01:00
|
|
|
}
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// CVC: We can't return blindly if this is a check constraint, because there's
|
|
|
|
// the possibility of an invalid field that wasn't found. The multiple places that
|
|
|
|
// call this function pass1_field() don't expect a NULL pointer, hence will crash.
|
|
|
|
// Don't check ambiguity if we don't have a field.
|
2005-11-26 17:15:47 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if (node && name)
|
|
|
|
PASS1_ambiguity_check(dsqlScratch, name, ambiguous_ctx_stack);
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
// Clean up stack
|
|
|
|
ambiguous_ctx_stack.clear();
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-01-28 16:18:11 +01:00
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
return node;
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2005-01-06 14:14:38 +01:00
|
|
|
|
2010-12-18 03:17:06 +01:00
|
|
|
PASS1_field_unknown((qualifier ? qualifier->str_data : NULL),
|
|
|
|
(name ? name->str_data : NULL), input);
|
2010-01-28 16:18:11 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// CVC: PASS1_field_unknown() calls ERRD_post() that never returns, so the next line
|
2010-01-28 16:18:11 +01:00
|
|
|
// is only to make the compiler happy.
|
|
|
|
return NULL;
|
2005-01-06 14:14:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
pass1_group_by_list
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
@brief Process GROUP BY list, which may contain
|
2005-02-14 06:54:45 +01:00
|
|
|
an ordinal or alias which references the
|
2005-05-28 00:45:31 +02:00
|
|
|
select list.
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-08-15 02:02:18 +02:00
|
|
|
@param input
|
|
|
|
@param select_list
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_group_by_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* selectList)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2003-08-15 02:02:18 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2005-02-10 22:14:52 +01:00
|
|
|
DEV_BLKCHK(selectList, dsql_type_nod);
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2009-06-06 04:21:23 +02:00
|
|
|
if (input->nod_count > MAX_SORT_ITEMS) // sort, group and distinct have the same limit for now
|
2006-10-07 11:40:59 +02:00
|
|
|
{
|
2008-07-01 03:12:02 +02:00
|
|
|
// cannot group on more than 255 items
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_max_group_items));
|
2006-10-07 11:40:59 +02:00
|
|
|
}
|
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack stack;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub = (*ptr);
|
2006-10-08 03:57:21 +02:00
|
|
|
dsql_nod* frnode = NULL;
|
2010-10-24 02:26:00 +02:00
|
|
|
LiteralNode* literal;
|
|
|
|
|
2008-12-05 02:20:14 +01:00
|
|
|
if (sub->nod_type == nod_field_name)
|
2007-10-17 16:35:31 +02:00
|
|
|
{
|
2005-02-10 22:14:52 +01:00
|
|
|
// check for alias or field node
|
2009-12-20 22:01:10 +01:00
|
|
|
frnode = pass1_field(dsqlScratch, sub, false, selectList);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
else if ((literal = ExprNode::as<LiteralNode>(sub)) && (literal->litDesc.dsc_dtype == dtype_long))
|
2006-10-07 11:40:59 +02:00
|
|
|
{
|
2010-10-24 02:26:00 +02:00
|
|
|
const ULONG position = literal->getSlong();
|
|
|
|
|
2010-10-27 02:32:58 +02:00
|
|
|
if (position < 1 || !selectList || position > (ULONG) selectList->nod_count)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-07-01 03:12:02 +02:00
|
|
|
// Invalid column position used in the GROUP BY clause
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_column_pos_err) << Arg::Str("GROUP BY"));
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
frnode = PASS1_node_psql(dsqlScratch, selectList->nod_arg[position - 1], false);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
frnode = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2006-10-07 11:40:59 +02:00
|
|
|
stack.push(frnode);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
// Finally make the complete list.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_list(stack);
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2008-08-15 18:32:42 +02:00
|
|
|
// Create (if necessary) a hidden variable to store a temporary value.
|
2011-02-06 19:13:12 +01:00
|
|
|
static dsql_nod* pass1_hidden_variable(DsqlCompilerScratch* dsqlScratch, dsql_nod* expr)
|
2008-08-15 18:32:42 +02:00
|
|
|
{
|
2011-01-31 01:13:15 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2008-08-15 18:32:42 +02:00
|
|
|
// For some node types, it's better to not create temporary value.
|
2010-12-19 22:42:32 +01:00
|
|
|
if (expr->nod_type == nod_class_exprnode)
|
|
|
|
{
|
|
|
|
switch (ExprNode::fromLegacy(expr)->type)
|
|
|
|
{
|
|
|
|
case ExprNode::TYPE_CURRENT_DATE:
|
|
|
|
case ExprNode::TYPE_CURRENT_TIME:
|
|
|
|
case ExprNode::TYPE_CURRENT_TIMESTAMP:
|
|
|
|
case ExprNode::TYPE_CURRENT_ROLE:
|
|
|
|
case ExprNode::TYPE_CURRENT_USER:
|
|
|
|
case ExprNode::TYPE_FIELD:
|
|
|
|
case ExprNode::TYPE_INTERNAL_INFO:
|
|
|
|
case ExprNode::TYPE_LITERAL:
|
|
|
|
case ExprNode::TYPE_NULL:
|
|
|
|
case ExprNode::TYPE_PARAMETER:
|
|
|
|
case ExprNode::TYPE_RECORD_KEY:
|
|
|
|
case ExprNode::TYPE_VARIABLE:
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-08-15 18:32:42 +02:00
|
|
|
}
|
|
|
|
|
2011-01-31 01:13:15 +01:00
|
|
|
VariableNode* varNode = FB_NEW(*tdbb->getDefaultPool()) VariableNode(*tdbb->getDefaultPool());
|
2011-01-31 15:47:41 +01:00
|
|
|
varNode->dsqlVar = dsqlScratch->makeVariable(NULL, "", dsql_var::TYPE_HIDDEN,
|
|
|
|
0, 0, dsqlScratch->hiddenVarsNumber++);
|
2010-11-02 18:05:01 +01:00
|
|
|
|
|
|
|
dsql_nod* varNod = MAKE_node(nod_class_exprnode, 1);
|
2011-01-31 01:13:15 +01:00
|
|
|
varNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(varNode);
|
2010-11-02 18:05:01 +01:00
|
|
|
|
2011-01-31 15:47:41 +01:00
|
|
|
MAKE_desc(dsqlScratch, &varNode->dsqlVar->desc, expr);
|
|
|
|
varNod->nod_desc = varNode->dsqlVar->desc;
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2010-11-02 18:05:01 +01:00
|
|
|
return varNod;
|
2008-08-15 18:32: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_insert
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2009-12-22 01:08:49 +01:00
|
|
|
@brief Process INSERT statement.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_insert( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, bool insert_or_update)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_store, e_sto_count);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
// Process SELECT expression, if present
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* values;
|
|
|
|
dsql_nod* rse = input->nod_arg[e_ins_select];
|
2009-01-07 10:30:57 +01:00
|
|
|
if (rse)
|
|
|
|
{
|
2007-04-24 17:22:05 +02:00
|
|
|
if (input->nod_arg[e_ins_return])
|
|
|
|
rse->nod_flags |= NOD_SELECT_EXPR_SINGLETON;
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
node->nod_arg[e_sto_rse] = rse = PASS1_rse(dsqlScratch, rse, NULL);
|
2011-01-09 22:58:56 +01:00
|
|
|
values = ExprNode::as<RseNode>(rse)->dsqlSelectList;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
values = PASS1_node_psql(dsqlScratch, input->nod_arg[e_ins_values], false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
// Process relation
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_nod* temp_rel = pass1_relation(dsqlScratch, input->nod_arg[e_ins_relation]);
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_sto_relation] = temp_rel;
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_ctx* context = ExprNode::as<RelationSourceNode>(temp_rel)->dsqlContext;
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_rel* relation = context->ctx_relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
// If there isn't a field list, generate one
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* fields = input->nod_arg[e_ins_fields];
|
2008-04-09 15:47:15 +02:00
|
|
|
if (fields)
|
|
|
|
{
|
2005-10-06 08:08:10 +02:00
|
|
|
const dsql_nod* old_fields = fields; // for error reporting.
|
2009-12-20 22:01:10 +01:00
|
|
|
fields = PASS1_node_psql(dsqlScratch, fields, false);
|
2005-10-06 08:08:10 +02:00
|
|
|
// We do not allow cases like INSERT INTO T(f1, f2, f1)...
|
2006-09-03 03:09:23 +02:00
|
|
|
field_appears_once(fields, old_fields, true, "INSERT");
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2008-04-09 15:47:15 +02:00
|
|
|
// begin IBO hack
|
2004-10-13 20:37:53 +02:00
|
|
|
// 02-May-2004, Nickolay Samofatov. Do not constify ptr further e.g. to
|
2004-05-03 06:25:06 +02:00
|
|
|
// const dsql_nod* const* .... etc. It chokes GCC 3.4.0
|
2008-04-09 15:47:15 +02:00
|
|
|
dsql_nod** ptr = fields->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + fields->nod_count; ptr < end; ptr++)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-04-09 15:47:15 +02:00
|
|
|
DEV_BLKCHK (*ptr, dsql_type_nod);
|
|
|
|
const dsql_nod* temp2 = *ptr;
|
2008-05-23 03:37:19 +02:00
|
|
|
|
|
|
|
const dsql_ctx* tmp_ctx = NULL;
|
|
|
|
const TEXT* tmp_name = NULL;
|
2010-12-19 22:42:32 +01:00
|
|
|
const FieldNode* fieldNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
const DerivedFieldNode* derivedField;
|
2008-05-23 03:37:19 +02:00
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
if ((fieldNode = ExprNode::as<FieldNode>(temp2)))
|
2008-05-23 03:37:19 +02:00
|
|
|
{
|
2010-12-19 22:42:32 +01:00
|
|
|
tmp_ctx = fieldNode->dsqlContext;
|
|
|
|
if (fieldNode->dsqlField)
|
|
|
|
tmp_name = fieldNode->dsqlField->fld_name.c_str();
|
2008-05-23 03:37:19 +02:00
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(temp2)))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
tmp_ctx = derivedField->context;
|
|
|
|
tmp_name = derivedField->name.nullStr();
|
2008-05-23 03:37:19 +02:00
|
|
|
}
|
2002-07-06 07:32:02 +02:00
|
|
|
|
2008-05-23 03:37:19 +02:00
|
|
|
if (tmp_ctx &&
|
|
|
|
((tmp_ctx->ctx_relation && relation->rel_name != tmp_ctx->ctx_relation->rel_name) ||
|
|
|
|
tmp_ctx->ctx_context != context->ctx_context))
|
|
|
|
{
|
2008-04-09 15:47:15 +02:00
|
|
|
const dsql_rel* bad_rel = tmp_ctx->ctx_relation;
|
|
|
|
// At this time, "fields" has been replaced by the processed list in
|
|
|
|
// the same variable, so we refer again to input->nod_arg[e_ins_fields].
|
|
|
|
// CVC: After three years, made old_fields for that purpose.
|
2002-07-06 07:32:02 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
PASS1_field_unknown((bad_rel ? bad_rel->rel_name.c_str() : NULL),
|
|
|
|
tmp_name, old_fields->nod_arg[ptr - fields->nod_arg]);
|
2008-04-09 15:47:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// end IBO hack
|
|
|
|
}
|
2006-09-03 03:09:23 +02:00
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
fields = PASS1_node_psql(dsqlScratch, explode_fields(relation), false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
// Match field fields and values
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack stack;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
if (values)
|
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (fields->nod_count != values->nod_count)
|
|
|
|
{
|
2006-10-24 18:45:43 +02:00
|
|
|
// count of column list and value list don't match
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
|
|
|
|
Arg::Gds(isc_dsql_var_count_err));
|
2006-10-24 18:45:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod** ptr = fields->nod_arg;
|
|
|
|
dsql_nod** ptr2 = values->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + fields->nod_count; ptr < end; ptr++, ptr2++)
|
2006-10-24 18:45:43 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(*ptr2, dsql_type_nod);
|
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
temp->nod_arg[e_asgn_value] = *ptr2;
|
|
|
|
temp->nod_arg[e_asgn_field] = *ptr;
|
|
|
|
stack.push(temp);
|
|
|
|
temp = *ptr2;
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, temp, *ptr, false);
|
2006-10-24 18:45:43 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
node->nod_arg[e_sto_statement] = MAKE_list(stack);
|
2007-04-24 17:22:05 +02:00
|
|
|
|
|
|
|
if (insert_or_update)
|
|
|
|
{
|
2007-04-25 04:26:33 +02:00
|
|
|
// Clone the insert context, push with name "OLD" in the same scope level and
|
|
|
|
// marks it with CTX_null so all fields be resolved to NULL constant.
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_ctx* old_context = FB_NEW(dsqlScratch->getPool()) dsql_ctx(dsqlScratch->getPool());
|
2007-04-24 17:22:05 +02:00
|
|
|
*old_context = *context;
|
2009-01-06 06:53:34 +01:00
|
|
|
old_context->ctx_alias = old_context->ctx_internal_alias = MAKE_cstring(OLD_CONTEXT)->str_data;
|
2007-05-13 03:37:54 +02:00
|
|
|
old_context->ctx_flags |= CTX_system | CTX_null | CTX_returning;
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(old_context);
|
2007-04-24 17:22:05 +02:00
|
|
|
|
|
|
|
// clone the insert context and push with name "NEW" in a greater scope level
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_ctx* new_context = FB_NEW(dsqlScratch->getPool()) dsql_ctx(dsqlScratch->getPool());
|
2007-04-24 17:22:05 +02:00
|
|
|
*new_context = *context;
|
2009-12-20 22:01:10 +01:00
|
|
|
new_context->ctx_scope_level = ++dsqlScratch->scopeLevel;
|
2009-01-06 06:53:34 +01:00
|
|
|
new_context->ctx_alias = new_context->ctx_internal_alias = MAKE_cstring(NEW_CONTEXT)->str_data;
|
2007-05-13 03:37:54 +02:00
|
|
|
new_context->ctx_flags |= CTX_system | CTX_returning;
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(new_context);
|
2007-04-24 17:22:05 +02:00
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
node->nod_arg[e_sto_return] = process_returning(dsqlScratch, input->nod_arg[e_ins_return]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-04-24 17:22:05 +02:00
|
|
|
if (insert_or_update)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->scopeLevel;
|
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->context->pop();
|
2007-04-24 17:22:05 +02:00
|
|
|
}
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
set_parameters_name(node->nod_arg[e_sto_statement], node->nod_arg[e_sto_relation]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-04-24 17:22:05 +02:00
|
|
|
if (!insert_or_update)
|
2010-06-10 04:03:03 +02:00
|
|
|
nullify_returning(dsqlScratch, node, &node);
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->pop();
|
2006-09-16 17:53:51 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-22 17:00:49 +01:00
|
|
|
// Process loop interruption.
|
2011-01-22 22:32:29 +01:00
|
|
|
dsql_nod* PASS1_label(DsqlCompilerScratch* dsqlScratch, bool breakContinue, dsql_nod* label)
|
2003-08-26 09:13:33 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2010-02-22 17:00:49 +01:00
|
|
|
DEV_BLKCHK(label, dsql_type_nod);
|
|
|
|
|
2003-08-26 09:13:33 +02:00
|
|
|
// look for a label, if specified
|
|
|
|
|
2008-07-03 09:12:36 +02:00
|
|
|
const dsql_str* string = NULL;
|
2003-11-18 08:58:35 +01:00
|
|
|
USHORT position = 0;
|
2003-08-26 09:13:33 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (label)
|
|
|
|
{
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(label->nod_type == nod_label);
|
2003-11-10 10:16:38 +01:00
|
|
|
string = (dsql_str*) label->nod_arg[e_label_name];
|
2008-07-03 09:12:36 +02:00
|
|
|
const TEXT* label_string = string->str_data;
|
2009-12-20 22:01:10 +01:00
|
|
|
int index = dsqlScratch->loopLevel;
|
|
|
|
for (DsqlStrStack::iterator stack(dsqlScratch->labels); stack.hasData(); ++stack)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
const dsql_str* obj = stack.object();
|
2009-11-19 10:37:10 +01:00
|
|
|
if (obj)
|
|
|
|
{
|
2008-07-03 09:12:36 +02:00
|
|
|
const TEXT* obj_string = obj->str_data;
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!strcmp(label_string, obj_string))
|
|
|
|
{
|
2003-11-02 13:28:30 +01:00
|
|
|
position = index;
|
|
|
|
break;
|
|
|
|
}
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
USHORT number = 0;
|
2011-01-22 22:32:29 +01:00
|
|
|
|
|
|
|
if (breakContinue)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (position > 0)
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// break/continue the specified loop
|
2003-08-26 09:13:33 +02:00
|
|
|
number = position;
|
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else if (label)
|
|
|
|
{
|
2003-08-26 09:13:33 +02:00
|
|
|
// ERROR: Label %s is not found in the current scope
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
2008-12-05 02:20:14 +01:00
|
|
|
Arg::Gds(isc_dsql_invalid_label) << Arg::Str(string->str_data) <<
|
2008-08-15 13:21:47 +02:00
|
|
|
Arg::Str("is not found"));
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// break/continue the current loop
|
2009-12-20 22:01:10 +01:00
|
|
|
number = dsqlScratch->loopLevel;
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
2009-04-19 12:06:07 +02:00
|
|
|
else
|
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (position > 0)
|
|
|
|
{
|
2003-08-26 09:13:33 +02:00
|
|
|
// ERROR: Label %s already exists in the current scope
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
2008-12-05 02:20:14 +01:00
|
|
|
Arg::Gds(isc_dsql_invalid_label) << Arg::Str(string->str_data) <<
|
2008-08-15 13:21:47 +02:00
|
|
|
Arg::Str("already exists"));
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else
|
|
|
|
{
|
2003-08-26 09:13:33 +02:00
|
|
|
// store label name, if specified
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->labels.push(string);
|
|
|
|
number = dsqlScratch->loopLevel;
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
fb_assert(number > 0 && number <= dsqlScratch->loopLevel);
|
2003-08-26 09:13:33 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!label)
|
|
|
|
{
|
2003-09-28 13:12:03 +02:00
|
|
|
label = MAKE_node(nod_label, e_label_count);
|
|
|
|
// this label is unnamed, i.e. its nod_arg[e_label_name] is NULL
|
|
|
|
}
|
2010-02-22 17:00:49 +01:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
label->nod_arg[e_label_number] = (dsql_nod*) (IPTR) number;
|
2003-09-28 13:12:03 +02:00
|
|
|
|
|
|
|
return label;
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-20 05:41:10 +01:00
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_limit
|
|
|
|
|
|
|
|
@brief Process the limit clause (FIRST/SKIP/ROWS)
|
|
|
|
|
|
|
|
@param dsqlScratch
|
|
|
|
@param firstNode
|
|
|
|
@param skipNode
|
|
|
|
@param rse
|
|
|
|
|
|
|
|
**/
|
|
|
|
static void pass1_limit(DsqlCompilerScratch* dsqlScratch,
|
|
|
|
dsql_nod* firstNode, dsql_nod* skipNode,
|
|
|
|
RseNode* rse)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
|
|
|
DEV_BLKCHK(firstNode, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(skipNode, dsql_type_nod);
|
|
|
|
|
|
|
|
// Initialize this stack variable, and make it look like a node
|
|
|
|
AutoPtr<dsql_nod> descNode(FB_NEW_RPT(*getDefaultMemoryPool(), 0) dsql_nod);
|
|
|
|
|
|
|
|
if (dsqlScratch->clientDialect <= SQL_DIALECT_V5)
|
|
|
|
descNode->nod_desc.makeLong(0);
|
|
|
|
else
|
|
|
|
descNode->nod_desc.makeInt64(0);
|
|
|
|
|
|
|
|
rse->dsqlFirst = PASS1_node_psql(dsqlScratch, firstNode, false);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, rse->dsqlFirst, descNode, false);
|
|
|
|
|
|
|
|
rse->dsqlSkip = PASS1_node_psql(dsqlScratch, skipNode, false);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, rse->dsqlSkip, descNode, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Lookup a matching item in the select list. Return node if found else return NULL.
|
|
|
|
// If more matches are found we raise ambiguity error.
|
|
|
|
dsql_nod* PASS1_lookup_alias(DsqlCompilerScratch* dsqlScratch, const dsql_str* name,
|
|
|
|
dsql_nod* selectList, bool process)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
dsql_nod* returnNode = NULL;
|
2005-02-10 22:14:52 +01:00
|
|
|
dsql_nod** ptr = selectList->nod_arg;
|
|
|
|
const dsql_nod* const* const end = ptr + selectList->nod_count;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (; ptr < end; ptr++)
|
|
|
|
{
|
2005-02-10 22:14:52 +01:00
|
|
|
dsql_nod* matchingNode = NULL;
|
|
|
|
dsql_nod* node = *ptr;
|
2010-12-19 22:42:32 +01:00
|
|
|
FieldNode* fieldNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
case nod_alias:
|
|
|
|
{
|
2008-07-03 09:12:36 +02:00
|
|
|
const dsql_str* alias = (dsql_str*) node->nod_arg[e_alias_alias];
|
2005-05-22 05:11:41 +02:00
|
|
|
if (!strcmp(alias->str_data, name->str_data)) {
|
2007-04-29 21:04:26 +02:00
|
|
|
matchingNode = node;
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
default:
|
|
|
|
if ((fieldNode = ExprNode::as<FieldNode>(node)))
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2010-12-19 22:42:32 +01:00
|
|
|
if (fieldNode->dsqlField->fld_name == name->str_data)
|
2007-04-29 21:04:26 +02:00
|
|
|
matchingNode = node;
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
2010-12-19 22:42:32 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(node)))
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
if (strcmp(derivedField->name.c_str(), name->str_data) == 0)
|
2007-04-29 21:04:26 +02:00
|
|
|
matchingNode = node;
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (matchingNode)
|
|
|
|
{
|
2007-04-29 21:04:26 +02:00
|
|
|
if (process)
|
2009-12-20 22:01:10 +01:00
|
|
|
matchingNode = PASS1_node_psql(dsqlScratch, matchingNode, false);
|
2007-04-29 21:04:26 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (returnNode)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
// There was already a node matched, thus raise ambiguous field name error.
|
2005-02-10 22:14:52 +01:00
|
|
|
TEXT buffer1[256];
|
|
|
|
buffer1[0] = 0;
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (returnNode->nod_type)
|
|
|
|
{
|
2005-02-10 22:14:52 +01:00
|
|
|
case nod_alias:
|
|
|
|
strcat(buffer1, "an alias");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2010-12-19 22:42:32 +01:00
|
|
|
if (ExprNode::is<FieldNode>(returnNode))
|
|
|
|
strcat(buffer1, "a field");
|
|
|
|
else if (ExprNode::is<DerivedFieldNode>(returnNode))
|
2010-11-14 18:25:48 +01:00
|
|
|
strcat(buffer1, "a derived field");
|
|
|
|
else
|
|
|
|
strcat(buffer1, "an item");
|
2005-02-10 22:14:52 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEXT buffer2[256];
|
|
|
|
buffer2[0] = 0;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (matchingNode->nod_type)
|
|
|
|
{
|
2005-02-10 22:14:52 +01:00
|
|
|
case nod_alias:
|
|
|
|
strcat(buffer2, "an alias");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2010-12-19 22:42:32 +01:00
|
|
|
if (ExprNode::is<FieldNode>(matchingNode))
|
|
|
|
strcat(buffer2, "a field");
|
|
|
|
else if (ExprNode::is<DerivedFieldNode>(matchingNode))
|
2010-11-21 04:47:29 +01:00
|
|
|
strcat(buffer2, "a derived field");
|
2010-11-14 18:25:48 +01:00
|
|
|
else
|
2010-11-21 04:47:29 +01:00
|
|
|
strcat(buffer2, "an item");
|
2005-02-10 22:14:52 +01:00
|
|
|
break;
|
|
|
|
}
|
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) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str(name->str_data));
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
returnNode = matchingNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-03 09:12:36 +02:00
|
|
|
return returnNode;
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
|
2003-08-24 04:36:46 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
pass1_make_derived_field
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
@brief Create a derived field based on underlying expressions
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-24 04:36:46 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2008-02-28 14:48:16 +01:00
|
|
|
@param tdbb
|
2005-01-25 00:02:08 +01:00
|
|
|
@param select_item
|
2003-08-24 04:36:46 +02:00
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_make_derived_field(DsqlCompilerScratch* dsqlScratch, thread_db* tdbb,
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_nod* select_item)
|
2003-08-24 04:36:46 +02:00
|
|
|
{
|
2003-09-04 17:02:22 +02:00
|
|
|
DEV_BLKCHK(select_item, dsql_type_nod);
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (select_item->nod_type)
|
|
|
|
{
|
2003-09-04 17:02:22 +02:00
|
|
|
case nod_alias:
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* alias_alias = (dsql_str*) select_item->nod_arg[e_alias_alias];
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
// Create a derived field and ignore alias node.
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
alias_alias->str_data, dsqlScratch->scopeLevel, select_item->nod_arg[e_alias_value]);
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(newField);
|
|
|
|
nod->nod_desc = select_item->nod_desc;
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
return nod;
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2010-11-07 03:18:58 +01:00
|
|
|
case nod_class_exprnode:
|
2005-01-25 00:02:08 +01:00
|
|
|
{
|
2010-11-07 03:18:58 +01:00
|
|
|
SubQueryNode* subQueryNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
DsqlMapNode* mapNode;
|
2010-12-19 22:42:32 +01:00
|
|
|
FieldNode* fieldNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
2010-02-26 02:03:54 +01:00
|
|
|
|
2010-11-07 03:18:58 +01:00
|
|
|
if ((subQueryNode = ExprNode::as<SubQueryNode>(select_item)))
|
2010-02-26 02:03:54 +01:00
|
|
|
{
|
2010-11-07 03:18:58 +01:00
|
|
|
// Try to generate derived field from sub-select
|
|
|
|
dsql_nod* derived_field = pass1_make_derived_field(dsqlScratch, tdbb,
|
|
|
|
subQueryNode->dsqlValue1);
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(derived_field)))
|
2010-11-07 03:18:58 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
derivedField->dsqlValue = select_item;
|
2010-11-07 03:18:58 +01:00
|
|
|
return derived_field;
|
|
|
|
}
|
2010-02-26 02:03:54 +01:00
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((mapNode = ExprNode::as<DsqlMapNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Aggregate's have map on top.
|
|
|
|
dsql_nod* derived_field = pass1_make_derived_field(dsqlScratch, tdbb, mapNode->map->map_node);
|
|
|
|
|
|
|
|
// If we had succesfully made a derived field node change it with orginal map.
|
|
|
|
if ((derivedField = ExprNode::as<DerivedFieldNode>(derived_field)))
|
|
|
|
{
|
|
|
|
derivedField->dsqlValue = select_item;
|
|
|
|
derivedField->scope = dsqlScratch->scopeLevel;
|
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
}
|
2010-12-19 22:42:32 +01:00
|
|
|
else if ((fieldNode = ExprNode::as<FieldNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Create a derived field and hook in.
|
|
|
|
|
|
|
|
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
fieldNode->dsqlField->fld_name, dsqlScratch->scopeLevel, select_item);
|
|
|
|
|
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(newField);
|
|
|
|
nod->nod_desc = fieldNode->dsqlDesc;
|
|
|
|
|
|
|
|
return nod;
|
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
{
|
|
|
|
// Create a derived field that points to a derived field.
|
|
|
|
|
|
|
|
DerivedFieldNode* newField = FB_NEW(pool) DerivedFieldNode(pool,
|
|
|
|
derivedField->name, dsqlScratch->scopeLevel, select_item);
|
|
|
|
|
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(newField);
|
|
|
|
nod->nod_desc = select_item->nod_desc;
|
|
|
|
|
|
|
|
return nod;
|
|
|
|
}
|
2010-02-26 02:03:54 +01:00
|
|
|
|
2010-11-07 03:18:58 +01:00
|
|
|
break;
|
2005-01-25 00:02:08 +01:00
|
|
|
}
|
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return select_item;
|
2003-08-24 04:36:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
/**
|
|
|
|
pass1_merge
|
|
|
|
|
2009-12-22 01:08:49 +01:00
|
|
|
@brief Process MERGE statement.
|
2006-09-14 04:05:32 +02:00
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2006-09-14 04:05:32 +02:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2006-09-14 04:05:32 +02:00
|
|
|
{
|
2010-06-10 04:03:03 +02:00
|
|
|
// Puts a blr_send before blr_for in DSQL statements.
|
Refactored the support for blr_handler, blr_loop, blr_exec_sql, blr_exec_into, blr_exec_stmt,
blr_start_savepoint, blr_end_savepoint, blr_store, blr_store2, blr_erase, blr_modify,
blr_modify2, blr_exec_proc, blr_exec_proc2, blr_exec_pid, blr_dcl_cursor, blr_cursor_stmt,
blr_set_generator, blr_receive, blr_stall, blr_select, blr_block, blr_error_handler,
blr_label, blr_leave, blr_continue and the source info node.
2010-11-29 03:17:04 +01:00
|
|
|
class MergeSendNode : public TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_MERGE>
|
2010-06-10 04:03:03 +02:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit MergeSendNode(MemoryPool& pool, dsql_nod* aStmt)
|
Refactored the support for blr_handler, blr_loop, blr_exec_sql, blr_exec_into, blr_exec_stmt,
blr_start_savepoint, blr_end_savepoint, blr_store, blr_store2, blr_erase, blr_modify,
blr_modify2, blr_exec_proc, blr_exec_proc2, blr_exec_pid, blr_dcl_cursor, blr_cursor_stmt,
blr_set_generator, blr_receive, blr_stall, blr_select, blr_block, blr_error_handler,
blr_label, blr_leave, blr_continue and the source info node.
2010-11-29 03:17:04 +01:00
|
|
|
: TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_MERGE>(pool),
|
2010-06-10 04:03:03 +02:00
|
|
|
stmt(aStmt)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual void print(string& text, Array<dsql_nod*>& nodes) const
|
|
|
|
{
|
|
|
|
text = "MergeSendNode";
|
|
|
|
nodes.add(stmt);
|
|
|
|
}
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
// Do not make dsqlPass to process 'stmt'. It's already processed.
|
2010-06-10 04:03:03 +02:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
virtual void genBlr(DsqlCompilerScratch* dsqlScratch)
|
2010-06-10 04:03:03 +02:00
|
|
|
{
|
|
|
|
dsql_msg* message = dsqlScratch->getStatement()->getReceiveMsg();
|
|
|
|
|
|
|
|
if (!dsqlScratch->isPsql() && message)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_send);
|
|
|
|
dsqlScratch->appendUChar(message->msg_number);
|
2010-06-10 04:03:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
GEN_statement(dsqlScratch, stmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
dsql_nod* stmt;
|
|
|
|
};
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2006-09-14 04:05:32 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
dsql_nod* source = input->nod_arg[e_mrg_using]; // USING
|
|
|
|
dsql_nod* target = input->nod_arg[e_mrg_relation]; // INTO
|
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
fb_assert(input->nod_arg[e_mrg_when]->nod_type == nod_merge_when);
|
|
|
|
dsql_nod* whenNode = input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched];
|
|
|
|
|
|
|
|
dsql_nod* updDelCondition = NULL;
|
|
|
|
if (whenNode)
|
|
|
|
{
|
|
|
|
if (whenNode->nod_type == nod_merge_update)
|
|
|
|
updDelCondition = whenNode->nod_arg[e_mrg_update_condition];
|
|
|
|
else if (whenNode->nod_type == nod_merge_delete)
|
|
|
|
updDelCondition = whenNode->nod_arg[e_mrg_delete_condition];
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* insCondition = input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched] ?
|
|
|
|
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]->nod_arg[e_mrg_insert_condition] :
|
|
|
|
NULL;
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Build a join between USING and INTO tables.
|
|
|
|
RseNode* join = FB_NEW(pool) RseNode(pool);
|
|
|
|
join->dsqlExplicitJoin = true;
|
|
|
|
join->dsqlFrom = MAKE_node(nod_list, 2);
|
|
|
|
|
|
|
|
join->dsqlFrom->nod_arg[0] = source;
|
2007-01-26 11:16:31 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Left join if WHEN NOT MATCHED is present.
|
2007-01-26 11:16:31 +01:00
|
|
|
if (input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched])
|
2011-01-22 21:40:04 +01:00
|
|
|
join->rse_jointype = blr_left;
|
2007-01-26 11:16:31 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
join->dsqlFrom->nod_arg[1] = target;
|
|
|
|
join->dsqlWhere = input->nod_arg[e_mrg_condition];
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* querySpec = FB_NEW(pool) RseNode(pool);
|
|
|
|
querySpec->dsqlFrom = MAKE_node(nod_list, 1);
|
2011-01-22 21:40:04 +01:00
|
|
|
querySpec->dsqlFrom->nod_arg[0] = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
querySpec->dsqlFrom->nod_arg[0]->nod_arg[0] = reinterpret_cast<dsql_nod*>(join);
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
dsql_nod* querySpecNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
querySpecNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(querySpec);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (updDelCondition || insCondition)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
const char* targetName = ExprNode::as<RelationSourceNode>(target)->alias.nullStr();
|
2010-06-10 04:03:03 +02:00
|
|
|
if (!targetName)
|
2011-01-30 01:25:46 +01:00
|
|
|
targetName = ExprNode::as<RelationSourceNode>(target)->dsqlName.c_str();
|
2010-06-10 04:03:03 +02:00
|
|
|
|
|
|
|
if (whenNode) // WHEN MATCHED
|
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
MissingBoolNode* missingNode = FB_NEW(pool) MissingBoolNode(
|
2010-11-14 18:25:48 +01:00
|
|
|
pool, MAKE_node(nod_class_exprnode, 1));
|
|
|
|
missingNode->dsqlArg->nod_arg[0] = reinterpret_cast<dsql_nod*>(
|
2011-01-30 01:25:46 +01:00
|
|
|
FB_NEW(pool) RecordKeyNode(pool, blr_dbkey, targetName));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
NotBoolNode* notNode = FB_NEW(pool) NotBoolNode(
|
|
|
|
pool, MAKE_node(nod_class_exprnode, 1));
|
|
|
|
notNode->dsqlArg->nod_arg[0] = reinterpret_cast<dsql_nod*>(missingNode);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
querySpec->dsqlWhere = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
querySpec->dsqlWhere->nod_arg[0] = reinterpret_cast<dsql_nod*>(notNode);
|
2010-06-10 04:03:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (updDelCondition)
|
2011-01-09 22:58:56 +01:00
|
|
|
querySpec->dsqlWhere = PASS1_compose(querySpec->dsqlWhere, updDelCondition, blr_and);
|
2010-06-10 04:03:03 +02:00
|
|
|
|
|
|
|
dsql_nod* temp = NULL;
|
|
|
|
|
|
|
|
if (input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]) // WHEN NOT MATCHED
|
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
MissingBoolNode* missingNode = FB_NEW(pool) MissingBoolNode(
|
2010-11-14 18:25:48 +01:00
|
|
|
pool, MAKE_node(nod_class_exprnode, 1));
|
|
|
|
missingNode->dsqlArg->nod_arg[0] = reinterpret_cast<dsql_nod*>(
|
2011-01-30 01:25:46 +01:00
|
|
|
FB_NEW(pool) RecordKeyNode(pool, blr_dbkey, targetName));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
temp = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
temp->nod_arg[0] = reinterpret_cast<dsql_nod*>(missingNode);
|
2010-06-10 04:03:03 +02:00
|
|
|
|
|
|
|
if (insCondition)
|
2010-09-17 05:15:32 +02:00
|
|
|
temp = PASS1_compose(temp, insCondition, blr_and);
|
2010-06-10 04:03:03 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
querySpec->dsqlWhere = PASS1_compose(querySpec->dsqlWhere, temp, blr_or);
|
2010-06-10 04:03:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
dsql_nod* select_expr = MAKE_node(nod_select_expr, e_sel_count);
|
2011-01-09 22:58:56 +01:00
|
|
|
select_expr->nod_arg[e_sel_query_spec] = querySpecNod;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
|
|
|
dsql_nod* select = MAKE_node(nod_select, e_select_count);
|
|
|
|
select->nod_arg[e_select_expr] = select_expr;
|
|
|
|
|
|
|
|
// build a FOR SELECT node
|
2010-09-17 05:15:32 +02:00
|
|
|
ForNode* forNode = FB_NEW(pool) ForNode(pool);
|
2010-02-22 17:00:49 +01:00
|
|
|
forNode->dsqlSelect = select;
|
|
|
|
forNode->dsqlAction = MAKE_node(nod_list, 0);
|
|
|
|
|
|
|
|
dsql_nod* for_select = MAKE_node(nod_class_stmtnode, 1);
|
2010-02-24 01:53:10 +01:00
|
|
|
for_select->nod_arg[0] = (dsql_nod*) forNode;
|
2009-12-20 22:01:10 +01:00
|
|
|
for_select = PASS1_statement(dsqlScratch, for_select);
|
2010-02-24 01:53:10 +01:00
|
|
|
forNode = (ForNode*) for_select->nod_arg[0];
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (input->nod_arg[e_mrg_return])
|
|
|
|
forNode->dsqlForceSingular = true;
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Get the already processed relations.
|
|
|
|
source = ExprNode::as<RseNode>(ExprNode::as<RseNode>(
|
|
|
|
forNode->dsqlSelect)->dsqlStreams->nod_arg[0])->dsqlStreams->nod_arg[0];
|
|
|
|
target = ExprNode::as<RseNode>(ExprNode::as<RseNode>(
|
|
|
|
forNode->dsqlSelect)->dsqlStreams->nod_arg[0])->dsqlStreams->nod_arg[1];
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2011-01-30 02:17:41 +01:00
|
|
|
DsqlContextStack usingCtxs;
|
|
|
|
get_contexts(usingCtxs, source);
|
|
|
|
|
2010-03-25 03:10:57 +01:00
|
|
|
dsql_nod* update = NULL;
|
2010-06-10 04:03:03 +02:00
|
|
|
dsql_nod* matchedRet = NULL;
|
|
|
|
dsql_nod* nullRet = NULL;
|
2010-03-23 17:06:16 +01:00
|
|
|
|
|
|
|
if (whenNode && whenNode->nod_type == nod_merge_update)
|
2006-09-14 04:05:32 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
// get the assignments of the UPDATE dsqlScratch
|
2010-03-23 17:06:16 +01:00
|
|
|
dsql_nod* list = whenNode->nod_arg[e_mrg_update_statement];
|
2006-09-17 03:41:24 +02:00
|
|
|
fb_assert(list->nod_type == nod_list);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
Array<dsql_nod*> org_values, new_values;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
// separate the new and org values to process in correct contexts
|
2009-06-27 08:23:36 +02:00
|
|
|
for (int i = 0; i < list->nod_count; ++i)
|
2006-09-17 03:41:24 +02:00
|
|
|
{
|
|
|
|
const dsql_nod* const assign = list->nod_arg[i];
|
|
|
|
fb_assert(assign->nod_type == nod_assign);
|
2006-10-07 11:40:59 +02:00
|
|
|
org_values.add(assign->nod_arg[e_asgn_value]);
|
|
|
|
new_values.add(assign->nod_arg[e_asgn_field]);
|
2006-09-17 03:41:24 +02:00
|
|
|
}
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
// build the MODIFY node
|
2010-03-23 17:06:16 +01:00
|
|
|
update = MAKE_node(nod_modify_current, e_mdc_count);
|
2010-06-10 04:03:03 +02:00
|
|
|
dsql_ctx* old_context = get_context(target);
|
2006-09-17 03:41:24 +02:00
|
|
|
dsql_nod** ptr;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
update->nod_arg[e_mdc_context] = (dsql_nod*) old_context;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++; // go to the same level of source and target contexts
|
2011-01-30 02:17:41 +01:00
|
|
|
|
|
|
|
for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr)
|
|
|
|
dsqlScratch->context->push(itr.object()); // push the USING contexts
|
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
dsqlScratch->context->push(old_context); // process old context values
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2008-05-23 02:38:06 +02:00
|
|
|
// and pop the contexts
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->scopeLevel--;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
// process relation
|
2010-03-23 17:06:16 +01:00
|
|
|
update->nod_arg[e_mdc_update] = pass1_relation(dsqlScratch, input->nod_arg[e_mrg_relation]);
|
2010-06-10 04:03:03 +02:00
|
|
|
dsql_ctx* mod_context = get_context(update->nod_arg[e_mdc_update]);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
// process new context values
|
|
|
|
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr)
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->pop();
|
2006-09-17 03:41:24 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (input->nod_arg[e_mrg_return])
|
|
|
|
{
|
|
|
|
// Repush the source contexts.
|
|
|
|
dsqlScratch->scopeLevel++; // go to the same level of source and target contexts
|
2011-01-30 02:17:41 +01:00
|
|
|
|
|
|
|
for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr)
|
|
|
|
dsqlScratch->context->push(itr.object()); // push the USING contexts
|
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
dsqlScratch->context->push(old_context); // process old context values
|
|
|
|
|
|
|
|
mod_context->ctx_scope_level = old_context->ctx_scope_level;
|
|
|
|
|
|
|
|
matchedRet = update->nod_arg[e_mdc_return] = ReturningProcessor(
|
|
|
|
dsqlScratch, old_context, mod_context).process(input->nod_arg[e_mrg_return]);
|
|
|
|
|
|
|
|
nullRet = nullify_returning(dsqlScratch, update);
|
|
|
|
|
|
|
|
// And pop them.
|
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->scopeLevel--;
|
|
|
|
}
|
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
// recreate list of assignments
|
2010-03-23 17:06:16 +01:00
|
|
|
update->nod_arg[e_mdc_statement] = list = MAKE_node(nod_list, list->nod_count);
|
2006-09-17 03:41:24 +02:00
|
|
|
|
2009-06-27 08:23:36 +02:00
|
|
|
for (int i = 0; i < list->nod_count; ++i)
|
2006-09-17 03:41:24 +02:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (!PASS1_set_parameter_type(dsqlScratch, org_values[i], new_values[i], false))
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, new_values[i], org_values[i], false);
|
2007-11-29 16:15:23 +01:00
|
|
|
|
2006-10-07 11:40:59 +02:00
|
|
|
dsql_nod* assign = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
assign->nod_arg[e_asgn_value] = org_values[i];
|
|
|
|
assign->nod_arg[e_asgn_field] = new_values[i];
|
2006-09-17 03:41:24 +02:00
|
|
|
list->nod_arg[i] = assign;
|
|
|
|
}
|
|
|
|
|
2007-11-29 16:15:23 +01:00
|
|
|
// We do not allow cases like UPDATE SET f1 = v1, f2 = v2, f1 = v3...
|
2010-03-23 17:06:16 +01:00
|
|
|
field_appears_once(update->nod_arg[e_mdc_statement],
|
|
|
|
whenNode->nod_arg[e_mrg_update_statement], false, "MERGE");
|
|
|
|
}
|
|
|
|
else if (whenNode && whenNode->nod_type == nod_merge_delete)
|
|
|
|
{
|
|
|
|
// build the DELETE node
|
|
|
|
update = MAKE_node(nod_erase_current, e_erc_count);
|
|
|
|
dsql_ctx* context = get_context(target);
|
|
|
|
update->nod_arg[e_erc_context] = (dsql_nod*) context;
|
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (input->nod_arg[e_mrg_return])
|
2010-03-23 17:06:16 +01:00
|
|
|
{
|
|
|
|
dsqlScratch->scopeLevel++; // go to the same level of source and target contexts
|
2011-01-30 02:17:41 +01:00
|
|
|
|
|
|
|
for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr)
|
|
|
|
dsqlScratch->context->push(itr.object()); // push the USING contexts
|
|
|
|
|
2010-03-23 17:06:16 +01:00
|
|
|
dsqlScratch->context->push(context); // process old context values
|
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
matchedRet = update->nod_arg[e_erc_return] = ReturningProcessor(
|
|
|
|
dsqlScratch, context, NULL).process(input->nod_arg[e_mrg_return]);
|
|
|
|
|
|
|
|
nullRet = nullify_returning(dsqlScratch, update);
|
2010-03-23 17:06:16 +01:00
|
|
|
|
|
|
|
// and pop the contexts
|
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->scopeLevel--;
|
|
|
|
}
|
2006-09-14 04:05:32 +02:00
|
|
|
}
|
|
|
|
|
2010-03-23 17:06:16 +01:00
|
|
|
whenNode = input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched];
|
2006-09-17 03:41:24 +02:00
|
|
|
dsql_nod* insert = NULL;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-03-23 17:06:16 +01:00
|
|
|
if (whenNode)
|
2006-09-17 03:41:24 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++; // go to the same level of the source context
|
2011-01-30 02:17:41 +01:00
|
|
|
|
|
|
|
for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr)
|
|
|
|
dsqlScratch->context->push(itr.object()); // push the USING contexts
|
2010-03-23 17:06:16 +01:00
|
|
|
|
2008-05-23 02:38:06 +02:00
|
|
|
// the INSERT relation should be processed in a higher level than the source context
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
// build the INSERT node
|
|
|
|
insert = MAKE_node(nod_insert, e_ins_count);
|
|
|
|
insert->nod_arg[e_ins_relation] = input->nod_arg[e_mrg_relation];
|
2010-03-23 17:06:16 +01:00
|
|
|
insert->nod_arg[e_ins_fields] = whenNode->nod_arg[e_mrg_insert_fields];
|
|
|
|
insert->nod_arg[e_ins_values] = whenNode->nod_arg[e_mrg_insert_values];
|
2010-06-10 04:03:03 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
insert = pass1_insert(dsqlScratch, insert, false);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2006-09-17 03:41:24 +02:00
|
|
|
// restore the scope level
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel--;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
dsql_nod* insRet = ReturningProcessor::clone(input->nod_arg[e_mrg_return], matchedRet);
|
2010-03-23 17:06:16 +01:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (insRet)
|
2010-03-23 17:06:16 +01:00
|
|
|
{
|
2010-06-10 04:03:03 +02:00
|
|
|
dsql_ctx* old_context = get_context(target);
|
|
|
|
dsqlScratch->context->push(old_context);
|
|
|
|
|
|
|
|
dsql_ctx* context = get_context(insert->nod_arg[e_sto_relation]);
|
|
|
|
context->ctx_scope_level = old_context->ctx_scope_level;
|
|
|
|
|
|
|
|
insert->nod_arg[e_sto_return] = ReturningProcessor(
|
|
|
|
dsqlScratch, old_context, context).process(insRet);
|
2010-03-23 17:06:16 +01:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (!matchedRet)
|
|
|
|
nullRet = nullify_returning(dsqlScratch, insert);
|
2010-03-23 17:06:16 +01:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
dsqlScratch->context->pop();
|
2010-03-23 17:06:16 +01:00
|
|
|
}
|
2010-06-10 04:03:03 +02:00
|
|
|
|
|
|
|
// pop the USING context
|
|
|
|
dsqlScratch->context->pop();
|
|
|
|
dsqlScratch->scopeLevel--;
|
2006-09-17 03:41:24 +02:00
|
|
|
}
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
MissingBoolNode* missingNode = FB_NEW(pool) MissingBoolNode(
|
|
|
|
pool, MAKE_node(nod_class_exprnode, 1));
|
|
|
|
|
|
|
|
RecordKeyNode* dbKeyNode = FB_NEW(pool) RecordKeyNode(pool, blr_dbkey);
|
|
|
|
dbKeyNode->dsqlRelation = target;
|
|
|
|
missingNode->dsqlArg->nod_arg[0] = reinterpret_cast<dsql_nod*>(dbKeyNode);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2006-09-14 13:44:11 +02:00
|
|
|
// build a IF (target.RDB$DB_KEY IS NULL)
|
2010-09-17 05:15:32 +02:00
|
|
|
IfNode* action = FB_NEW(pool) IfNode(pool);
|
2009-10-24 19:45:33 +02:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
action->dsqlCondition = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
action->dsqlCondition->nod_arg[0] = reinterpret_cast<dsql_nod*>(missingNode);
|
2006-09-17 03:41:24 +02:00
|
|
|
|
|
|
|
if (insert)
|
|
|
|
{
|
2009-10-24 19:45:33 +02:00
|
|
|
action->dsqlTrueAction = insert; // then INSERT
|
2010-03-23 17:06:16 +01:00
|
|
|
action->dsqlFalseAction = update; // else UPDATE/DELETE
|
2006-09-17 03:41:24 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// negate the condition -> IF (target.RDB$DB_KEY IS NOT NULL)
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
NotBoolNode* notNode = FB_NEW(pool) NotBoolNode(pool, action->dsqlCondition);
|
|
|
|
|
|
|
|
action->dsqlCondition = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
action->dsqlCondition->nod_arg[0] = reinterpret_cast<dsql_nod*>(notNode);
|
2006-09-17 03:41:24 +02:00
|
|
|
|
2010-03-23 17:06:16 +01:00
|
|
|
action->dsqlTrueAction = update; // then UPDATE/DELETE
|
2006-09-17 03:41:24 +02:00
|
|
|
}
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (!dsqlScratch->isPsql())
|
|
|
|
{
|
|
|
|
// describe it as EXECUTE_PROCEDURE if RETURNING is present or as INSERT otherwise
|
|
|
|
if (input->nod_arg[e_mrg_return])
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_EXEC_PROCEDURE);
|
|
|
|
else
|
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT);
|
|
|
|
|
|
|
|
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_MERGE;
|
|
|
|
}
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
// insert the IF inside the FOR SELECT
|
2010-02-22 17:00:49 +01:00
|
|
|
forNode->dsqlAction = MAKE_node(nod_class_stmtnode, 1);
|
|
|
|
forNode->dsqlAction->nod_arg[0] = (dsql_nod*) action;
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
// setup the main node
|
|
|
|
|
|
|
|
if (nullRet)
|
|
|
|
{
|
|
|
|
dsql_nod* temp = MAKE_node(nod_list, 2);
|
|
|
|
temp->nod_arg[0] = nullRet;
|
|
|
|
temp->nod_arg[1] = for_select;
|
|
|
|
for_select = temp;
|
|
|
|
}
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
Node* sendNode = (FB_NEW(pool) MergeSendNode(pool, for_select))->dsqlPass(dsqlScratch);
|
2006-09-14 04:05:32 +02:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
dsql_nod* sendNod = MAKE_node(nod_class_stmtnode, 1);
|
|
|
|
sendNod->nod_arg[0] = (dsql_nod*) sendNode;
|
|
|
|
|
|
|
|
return sendNod;
|
2006-09-14 04:05:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
// Changes dsqlScratch->isPsql() value, calls PASS1_node and restore dsqlScratch->isPsql().
|
|
|
|
dsql_nod* PASS1_node_psql(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, bool psql)
|
2008-05-17 18:51:07 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
PsqlChanger changer(dsqlScratch, psql);
|
|
|
|
return PASS1_node(dsqlScratch, input);
|
2008-05-17 18:51:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
// Put recursive non list nodes on the stack.
|
|
|
|
void PASS1_put_args_on_stack( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, DsqlNodStack& stack)
|
2002-08-03 17:27:20 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2002-08-03 17:27:20 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (input->nod_type != nod_list)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
stack.push(PASS1_node(dsqlScratch, input));
|
2002-08-03 17:27:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2011-01-22 21:40:04 +01:00
|
|
|
PASS1_put_args_on_stack(dsqlScratch, *ptr, stack);
|
2002-08-03 17:27:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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_relation
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Prepare a relation name for processing.
|
2003-02-15 04:01:51 +01:00
|
|
|
Allocate a new relation node.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2011-01-30 01:25:46 +01:00
|
|
|
static dsql_nod* pass1_relation(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
dsql_ctx* context = PASS1_make_context(dsqlScratch, input);
|
|
|
|
RecordSourceNode* node = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_relation)
|
|
|
|
{
|
|
|
|
RelationSourceNode* relNode = FB_NEW(*tdbb->getDefaultPool()) RelationSourceNode(
|
|
|
|
*tdbb->getDefaultPool(), context->ctx_relation->rel_name);
|
|
|
|
relNode->dsqlContext = context;
|
|
|
|
node = relNode;
|
|
|
|
}
|
|
|
|
else if (context->ctx_procedure)
|
|
|
|
{
|
|
|
|
ProcedureSourceNode* procNode = FB_NEW(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
|
|
|
*tdbb->getDefaultPool(), context->ctx_procedure->prc_name);
|
|
|
|
procNode->dsqlContext = context;
|
|
|
|
node = procNode;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
fb_assert(node);
|
|
|
|
|
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(node);
|
|
|
|
|
|
|
|
return nod;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_alias_list
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief The passed alias list fully specifies a relation.
|
|
|
|
The first alias represents a relation specified in
|
|
|
|
the from list at this scope levels. Subsequent
|
|
|
|
contexts, if there are any, represent base relations
|
2005-05-28 00:45:31 +02:00
|
|
|
in a view stack. They are used to fully specify a
|
|
|
|
base relation of a view. The aliases used in the
|
2003-02-15 04:01:51 +01:00
|
|
|
view stack are those used in the view definition.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param alias_list
|
|
|
|
|
|
|
|
**/
|
2011-01-09 22:58:56 +01:00
|
|
|
static dsql_ctx* pass1_alias_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* alias_list)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(alias_list, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** arg = alias_list->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* const* const end = arg + alias_list->nod_count;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
// Loop through every alias and find the context for that alias.
|
2005-08-04 06:03:01 +02:00
|
|
|
// All aliases should have a corresponding context.
|
2005-07-26 13:54:37 +02:00
|
|
|
int aliasCount = alias_list->nod_count;
|
2009-12-20 22:01:10 +01:00
|
|
|
USHORT savedScopeLevel = dsqlScratch->scopeLevel;
|
2005-07-26 13:54:37 +02:00
|
|
|
dsql_ctx* context = NULL;
|
|
|
|
while (aliasCount > 0)
|
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
if (context)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
if (context->ctx_rse && !context->ctx_relation && !context->ctx_procedure)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
|
|
|
// Derived table
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
|
|
|
context = pass1_alias(dsqlScratch, context->ctx_childs_derived_table, (dsql_str*) *arg);
|
2005-07-26 13:54:37 +02:00
|
|
|
}
|
2010-07-02 12:43:33 +02:00
|
|
|
else if (context->ctx_relation)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
|
|
|
// This must be a VIEW
|
|
|
|
dsql_nod** startArg = arg;
|
|
|
|
dsql_rel* relation = context->ctx_relation;
|
|
|
|
// find the base table using the specified alias list, skipping the first one
|
|
|
|
// since we already matched it to the context.
|
|
|
|
for (; arg < end; arg++, aliasCount--)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
if (!(relation = pass1_base_table(dsqlScratch, relation, (dsql_str*) *arg)))
|
2005-07-26 13:54:37 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
// Found base relation
|
|
|
|
if ((aliasCount == 0) && relation)
|
|
|
|
{
|
|
|
|
// AB: Pretty ugly huh?
|
|
|
|
// make up a dummy context to hold the resultant relation.
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2009-01-07 10:30:57 +01:00
|
|
|
dsql_ctx* new_context =
|
|
|
|
FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
|
2005-07-26 13:54:37 +02:00
|
|
|
new_context->ctx_context = context->ctx_context;
|
|
|
|
new_context->ctx_relation = relation;
|
|
|
|
|
|
|
|
// concatenate all the contexts to form the alias name;
|
|
|
|
// calculate the length leaving room for spaces and a null
|
|
|
|
USHORT alias_length = alias_list->nod_count;
|
|
|
|
dsql_nod** aliasArg = startArg;
|
2008-12-05 02:20:14 +01:00
|
|
|
for (; aliasArg < end; aliasArg++)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(*aliasArg, dsql_type_str);
|
|
|
|
alias_length += static_cast<USHORT>(((dsql_str*) *aliasArg)->str_length);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
dsql_str* alias = FB_NEW_RPT(*tdbb->getDefaultPool(), alias_length) dsql_str;
|
2005-07-26 13:54:37 +02:00
|
|
|
alias->str_length = alias_length;
|
2009-01-07 10:30:57 +01:00
|
|
|
new_context->ctx_alias = alias->str_data;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
TEXT* p = alias->str_data;
|
2008-12-05 02:20:14 +01:00
|
|
|
for (aliasArg = startArg; aliasArg < end; aliasArg++)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const TEXT* q = ((dsql_str*) *aliasArg)->str_data; *q;)
|
2005-07-26 13:54:37 +02:00
|
|
|
*p++ = *q++;
|
|
|
|
*p++ = ' ';
|
|
|
|
}
|
|
|
|
p[-1] = 0;
|
|
|
|
|
|
|
|
context = new_context;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2008-12-05 02:20:14 +01:00
|
|
|
else
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
context = NULL;
|
|
|
|
}
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2010-07-02 12:43:33 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
context = NULL;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
else
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
context = pass1_alias(dsqlScratch, *dsqlScratch->context, (dsql_str*) *arg);
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
if (!context) {
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
|
|
|
|
arg++;
|
|
|
|
aliasCount--;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel = savedScopeLevel;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!context)
|
|
|
|
{
|
2003-08-20 01:34:23 +02:00
|
|
|
// there is no alias or table named %s at this scope level.
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_no_relation_alias) << Arg::Str(((dsql_str*) *arg)->str_data));
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
return context;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_alias
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief The passed relation or alias represents
|
2003-02-15 04:01:51 +01:00
|
|
|
a context which was previously specified
|
2005-05-28 00:45:31 +02:00
|
|
|
in the from list. Find and return the
|
2003-02-15 04:01:51 +01:00
|
|
|
proper context.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param alias
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_ctx* pass1_alias(DsqlCompilerScratch* dsqlScratch, DsqlContextStack& stack, dsql_str* alias)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_ctx* relation_context = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(alias, dsql_type_str);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
// CVC: Getting rid of trailing spaces.
|
|
|
|
if (alias && alias->str_data) {
|
2009-01-07 10:30:57 +01:00
|
|
|
fb_utils::exact_name(alias->str_data);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
// look through all contexts at this scope level
|
|
|
|
// to find one that has a relation name or alias
|
2005-05-28 00:45:31 +02:00
|
|
|
// name which matches the identifier passed.
|
2009-01-07 10:30:57 +01:00
|
|
|
for (DsqlContextStack::iterator itr(stack); itr.hasData(); ++itr)
|
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
dsql_ctx* context = itr.object();
|
2009-12-20 22:01:10 +01:00
|
|
|
if (context->ctx_scope_level != dsqlScratch->scopeLevel) {
|
2001-05-23 15:26:42 +02:00
|
|
|
continue;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
// check for matching alias.
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_internal_alias.hasData())
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_internal_alias == alias->str_data)
|
2005-01-21 15:09:02 +01:00
|
|
|
return context;
|
|
|
|
continue;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2008-06-03 08:19:21 +02:00
|
|
|
|
|
|
|
// If an unnamed derived table and empty alias
|
2009-01-07 10:30:57 +01:00
|
|
|
if (context->ctx_rse && !context->ctx_relation && !context->ctx_procedure &&
|
2011-01-30 01:25:46 +01:00
|
|
|
alias->str_length == 0)
|
2005-07-26 13:54:37 +02:00
|
|
|
{
|
2008-06-03 08:19:21 +02:00
|
|
|
relation_context = context;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
// check for matching relation name; aliases take priority so
|
|
|
|
// save the context in case there is an alias of the same name;
|
|
|
|
// also to check that there is no self-join in the query.
|
2009-01-07 10:30:57 +01:00
|
|
|
if (context->ctx_relation && context->ctx_relation->rel_name == alias->str_data)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (relation_context)
|
|
|
|
{
|
2009-04-19 12:06:07 +02:00
|
|
|
// the table %s is referenced twice; use aliases to differentiate
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_self_join) << Arg::Str(alias->str_data));
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
relation_context = context;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return relation_context;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
pass1_alias_concat
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
@brief Concatenate 2 input strings together for
|
2005-05-28 00:45:31 +02:00
|
|
|
a new alias string.
|
2003-08-20 01:34:23 +02:00
|
|
|
Note: Both input params can be empty.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
|
|
|
|
@param input1
|
|
|
|
@param input2
|
|
|
|
|
|
|
|
**/
|
2011-01-30 01:25:46 +01:00
|
|
|
static dsql_str* pass1_alias_concat(const dsql_str* input1, const char* input2)
|
2003-08-20 01:34:23 +02:00
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2003-08-20 01:34:23 +02:00
|
|
|
|
|
|
|
DEV_BLKCHK(input1, dsql_type_str);
|
|
|
|
DEV_BLKCHK(input2, dsql_type_str);
|
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
int length = (input1 ? input1->str_length : 0);
|
|
|
|
|
|
|
|
if (input1 && input1->str_length && input2)
|
|
|
|
++length; // Room for space character.
|
|
|
|
|
|
|
|
if (input2)
|
|
|
|
length += strlen(input2);
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
dsql_str* output = FB_NEW_RPT(*tdbb->getDefaultPool(), length) dsql_str;
|
2003-08-20 01:34:23 +02:00
|
|
|
output->str_length = length;
|
2003-10-05 08:37:26 +02:00
|
|
|
TEXT* ptr = output->str_data;
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (input1)
|
2003-08-20 01:34:23 +02:00
|
|
|
strcat(ptr, input1->str_data);
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (input1 && input1->str_length && input2)
|
2003-08-20 01:34:23 +02:00
|
|
|
strcat(ptr, " ");
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (input2)
|
|
|
|
strcat(ptr, input2);
|
|
|
|
|
2003-08-20 01:34:23 +02:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_base_table
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Check if the relation in the passed context
|
|
|
|
has a base table which matches the passed alias.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param relation
|
|
|
|
@param alias
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_rel* pass1_base_table( DsqlCompilerScratch* dsqlScratch, const dsql_rel* relation,
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* alias)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(relation, dsql_type_dsql_rel);
|
|
|
|
DEV_BLKCHK(alias, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
return METD_get_view_relation(dsqlScratch->getTransaction(), dsqlScratch,
|
|
|
|
relation->rel_name.c_str(), alias->str_data);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-06-13 14:45:42 +02:00
|
|
|
pass1_returning
|
|
|
|
|
|
|
|
@brief Compile a RETURNING clause.
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2005-06-13 14:45:42 +02:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_returning(DsqlCompilerScratch* dsqlScratch, const dsql_nod* input)
|
2005-06-13 14:45:42 +02:00
|
|
|
{
|
2010-10-09 03:57:37 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2005-06-13 14:45:42 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_nod* const source = PASS1_node_psql(dsqlScratch, input->nod_arg[e_ret_source], false);
|
2007-05-13 03:37:54 +02:00
|
|
|
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_RETURNING_INTO;
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_nod* const target = PASS1_node(dsqlScratch, input->nod_arg[e_ret_target]);
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags &= ~DsqlCompilerScratch::FLAG_RETURNING_INTO;
|
2005-06-13 14:45:42 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
if (!dsqlScratch->isPsql() && target)
|
2005-06-13 14:45:42 +02:00
|
|
|
{
|
|
|
|
// RETURNING INTO is not allowed syntax for DSQL
|
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("INTO"));
|
2005-06-13 14:45:42 +02:00
|
|
|
}
|
2009-12-20 22:01:10 +01:00
|
|
|
else if (dsqlScratch->isPsql() && !target)
|
2005-06-13 14:45:42 +02:00
|
|
|
{
|
2005-06-16 07:11:08 +02:00
|
|
|
// This trick because we don't copy lexer positions when copying lists.
|
|
|
|
const dsql_nod* errSrc = input->nod_arg[e_ret_source];
|
|
|
|
fb_assert(errSrc->nod_type == nod_list);
|
2005-06-13 14:45:42 +02:00
|
|
|
// RETURNING without INTO is not allowed for PSQL
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Unexpected end of command
|
2008-12-05 02:20:14 +01:00
|
|
|
Arg::Gds(isc_command_end_err2) << Arg::Num(errSrc->nod_line) <<
|
2008-08-15 13:21:47 +02:00
|
|
|
Arg::Num(errSrc->nod_column));
|
2005-06-13 14:45:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const int count = source->nod_count;
|
|
|
|
fb_assert(count);
|
|
|
|
dsql_nod* node = MAKE_node(nod_list, count);
|
|
|
|
|
2008-12-05 02:20:14 +01:00
|
|
|
if (target)
|
2005-06-13 14:45:42 +02:00
|
|
|
{
|
|
|
|
// PSQL case
|
2009-12-20 22:01:10 +01:00
|
|
|
fb_assert(dsqlScratch->isPsql());
|
2009-11-19 10:37:10 +01:00
|
|
|
if (count != target->nod_count)
|
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
// count of column list and value list don't match
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
|
|
|
|
Arg::Gds(isc_dsql_var_count_err));
|
2005-06-13 14:45:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod** src = source->nod_arg;
|
|
|
|
dsql_nod** dst = target->nod_arg;
|
|
|
|
dsql_nod** ptr = node->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count; ptr < end; src++, dst++, ptr++)
|
2005-06-13 14:45:42 +02:00
|
|
|
{
|
2006-10-07 11:40:59 +02:00
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
temp->nod_arg[e_asgn_value] = *src;
|
|
|
|
temp->nod_arg[e_asgn_field] = *dst;
|
2005-06-13 14:45:42 +02:00
|
|
|
*ptr = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// DSQL case
|
2009-12-20 22:01:10 +01:00
|
|
|
fb_assert(!dsqlScratch->isPsql());
|
2005-09-02 07:30:16 +02:00
|
|
|
|
2005-06-13 14:45:42 +02:00
|
|
|
dsql_nod** src = source->nod_arg;
|
|
|
|
dsql_nod** ptr = node->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count; ptr < end; src++, ptr++)
|
2005-06-13 14:45:42 +02:00
|
|
|
{
|
2009-12-22 16:36:10 +01:00
|
|
|
dsql_par* parameter = MAKE_parameter(dsqlScratch->getStatement()->getReceiveMsg(),
|
|
|
|
true, true, 0, *src);
|
2005-09-02 07:30:16 +02:00
|
|
|
parameter->par_node = *src;
|
2010-11-01 01:42:12 +01:00
|
|
|
MAKE_desc(dsqlScratch, ¶meter->par_desc, *src);
|
2007-04-24 17:22:05 +02:00
|
|
|
parameter->par_desc.dsc_flags |= DSC_nullable;
|
2005-06-13 14:45:42 +02:00
|
|
|
|
2010-10-09 03:57:37 +02:00
|
|
|
ParameterNode* paramNode = FB_NEW(*tdbb->getDefaultPool()) ParameterNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
paramNode->dsqlParameterIndex = parameter->par_index;
|
|
|
|
paramNode->dsqlParameter = parameter;
|
|
|
|
|
|
|
|
dsql_nod* p_node = MAKE_node(nod_class_exprnode, 1);
|
2005-06-13 14:45:42 +02:00
|
|
|
p_node->nod_count = 0;
|
2010-10-09 03:57:37 +02:00
|
|
|
p_node->nod_arg[0] = reinterpret_cast<dsql_nod*>(paramNode);
|
2005-06-13 14:45:42 +02:00
|
|
|
|
2006-10-07 11:40:59 +02:00
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
temp->nod_arg[e_asgn_value] = *src;
|
|
|
|
temp->nod_arg[e_asgn_field] = p_node;
|
2005-06-13 14:45:42 +02:00
|
|
|
*ptr = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
if (!dsqlScratch->isPsql())
|
2009-12-23 01:57:08 +01:00
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_EXEC_PROCEDURE);
|
2005-06-13 14:45:42 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_rse
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
@brief wrapper for pass1_rse_impl
|
2008-12-05 02:20:14 +01:00
|
|
|
substitute recursive CTE alias (if needed)
|
2006-08-01 22:37:58 +02:00
|
|
|
and call pass1_rse_impl
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2006-08-01 22:37:58 +02:00
|
|
|
@param input
|
|
|
|
@param order
|
|
|
|
@param rows
|
|
|
|
@param update_lock
|
|
|
|
@param flags
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_rse( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* order,
|
2006-08-01 22:37:58 +02:00
|
|
|
dsql_nod* rows, dsql_nod* update_lock, USHORT flags)
|
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
string save_alias;
|
2006-08-01 22:37:58 +02:00
|
|
|
const bool isRecursive = (input->nod_flags & NOD_SELECT_EXPR_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
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_nod* ret = pass1_rse_impl(dsqlScratch, input, order, rows, update_lock, 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
pass1_rse_impl
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
@brief Compile a record selection expression.
|
|
|
|
The input node may either be a "select_expression"
|
2004-10-13 20:37:53 +02:00
|
|
|
or a "list" (an implicit union) or a "query specification".
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
@param order
|
2003-11-10 10:16:38 +01:00
|
|
|
@param rows
|
2003-02-15 04:01:51 +01:00
|
|
|
@param update_lock
|
2004-10-13 20:37:53 +02:00
|
|
|
@param flags
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_rse_impl( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* order,
|
2004-10-13 20:37:53 +02:00
|
|
|
dsql_nod* rows, dsql_nod* update_lock, USHORT flags)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(order, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2011-01-09 22:58:56 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-09-01 15:18:02 +02:00
|
|
|
// Verify if we're processing view fields and reset flag to not pass to
|
|
|
|
// more than required inner nodes.
|
2011-01-09 22:58:56 +01:00
|
|
|
const USHORT viewFlags = input->nod_flags | (flags & NOD_SELECT_EXPR_VIEW_FIELDS);
|
|
|
|
flags &= ~NOD_SELECT_EXPR_VIEW_FIELDS;
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2004-10-13 20:37:53 +02:00
|
|
|
if (input->nod_type == nod_select_expr)
|
|
|
|
{
|
2006-08-01 22:37:58 +02:00
|
|
|
dsql_nod* node_with = input->nod_arg[e_sel_with_list];
|
2008-12-05 02:20:14 +01:00
|
|
|
try
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
|
|
|
if (node_with)
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->addCTEs(node_with);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2008-12-05 02:20:14 +01:00
|
|
|
dsql_nod* ret =
|
2009-12-20 22:01:10 +01:00
|
|
|
pass1_rse(dsqlScratch, input->nod_arg[e_sel_query_spec],
|
2006-08-01 22:37:58 +02:00
|
|
|
input->nod_arg[e_sel_order], input->nod_arg[e_sel_rows],
|
2008-09-01 15:18:02 +02:00
|
|
|
update_lock, viewFlags);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
if (node_with)
|
2008-03-17 11:27:01 +01:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->checkUnusedCTEs();
|
|
|
|
dsqlScratch->clearCTEs();
|
2008-03-17 11:27:01 +01:00
|
|
|
}
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
return ret;
|
2008-12-05 02:20:14 +01:00
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{
|
2006-08-01 22:37:58 +02:00
|
|
|
if (node_with)
|
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
|
|
|
}
|
|
|
|
else if (input->nod_type == nod_list)
|
|
|
|
{
|
2007-05-18 03:17:27 +02:00
|
|
|
fb_assert(input->nod_count > 1);
|
2010-02-16 17:28:54 +01:00
|
|
|
return pass1_union(dsqlScratch, input, order, rows, update_lock, flags);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* inputRse = ExprNode::as<RseNode>(input);
|
|
|
|
fb_assert(inputRse);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Save the original base of the context stack and process relations
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* targetRse = FB_NEW(pool) RseNode(pool);
|
|
|
|
RseNode* rse = targetRse;
|
2004-03-21 02:48:29 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* rseNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
rseNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(rse);
|
|
|
|
|
|
|
|
if (update_lock)
|
|
|
|
rse->flags |= RseNode::FLAG_WRITELOCK;
|
|
|
|
|
|
|
|
dsql_nod* list = rse->dsqlStreams = PASS1_node_psql(dsqlScratch, inputRse->dsqlFrom, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2011-01-09 22:58:56 +01:00
|
|
|
RelationSourceNode* relNode;
|
2005-05-20 01:41:17 +02:00
|
|
|
const dsql_rel* relation;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (update_lock &&
|
2011-01-09 22:58:56 +01:00
|
|
|
(list->nod_count != 1 || !(relNode = ExprNode::as<RelationSourceNode>(list->nod_arg[0])) ||
|
|
|
|
!(relation = relNode->dsqlContext->ctx_relation) ||
|
2009-01-09 02:50:54 +01:00
|
|
|
(relation->rel_flags & REL_view) || (relation->rel_flags & REL_external)))
|
2005-05-20 01:41:17 +02:00
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Token unknown
|
|
|
|
Arg::Gds(isc_token_err) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str("WITH LOCK"));
|
2005-05-20 01:41:17 +02:00
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
// Process LIMIT and/or ROWS, if any
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((inputRse->dsqlFirst || inputRse->dsqlSkip) && rows)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Token unknown
|
|
|
|
Arg::Gds(isc_token_err) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str("ROWS"));
|
2003-11-07 15:10:16 +01:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else if (rows)
|
|
|
|
{
|
2011-01-20 05:41:10 +01:00
|
|
|
pass1_limit(dsqlScratch, rows->nod_arg[e_rows_length], rows->nod_arg[e_rows_skip], rse);
|
2011-01-09 22:58:56 +01:00
|
|
|
}
|
|
|
|
else if (inputRse->dsqlFirst || inputRse->dsqlSkip)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2011-01-20 05:41:10 +01:00
|
|
|
pass1_limit(dsqlScratch, inputRse->dsqlFirst, inputRse->dsqlSkip, rse);
|
2008-04-09 15:47:15 +02:00
|
|
|
}
|
2003-11-07 15:10:16 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process boolean, if any
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlWhere)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inWhereClause;
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlWhere = PASS1_node_psql(dsqlScratch, inputRse->dsqlWhere, false);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inWhereClause;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
// AB: An aggregate pointing to it's own parent_context isn't
|
|
|
|
// allowed, HAVING should be used instead
|
2010-01-29 02:16:34 +01:00
|
|
|
if (Aggregate2Finder::find(dsqlScratch->scopeLevel, FIELD_MATCH_TYPE_EQUAL, false,
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlWhere))
|
2008-07-01 03:12:02 +02:00
|
|
|
{
|
|
|
|
// Cannot use an aggregate in a WHERE clause, use HAVING instead
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_where_err));
|
2008-07-01 03:12:02 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
2003-09-28 23:36:05 +02:00
|
|
|
#ifdef DSQL_DEBUG
|
2009-11-19 10:37:10 +01:00
|
|
|
if (DSQL_debug & 16)
|
|
|
|
{
|
2003-09-28 23:36:05 +02:00
|
|
|
dsql_trace("PASS1_rse input tree:");
|
2003-01-11 03:49:13 +01:00
|
|
|
DSQL_pretty(input, 0);
|
2003-09-28 23:36:05 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
#endif
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process select list, if any. If not, generate one
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* selectList = inputRse->dsqlSelectList;
|
2005-02-10 22:14:52 +01:00
|
|
|
// First expand select list, this will expand nodes with asterisk.
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inSelectList;
|
2011-01-09 22:58:56 +01:00
|
|
|
selectList = pass1_expand_select_list(dsqlScratch, selectList, rse->dsqlStreams);
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
if ((flags & NOD_SELECT_EXPR_VALUE) && (!selectList || selectList->nod_count > 1))
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
|
|
|
// More than one column (or asterisk) is specified in column_singleton
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_count_mismatch));
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// Pass select list
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlSelectList = pass1_sel_list(dsqlScratch, selectList,
|
|
|
|
(viewFlags & NOD_SELECT_EXPR_VIEW_FIELDS));
|
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-01-09 22:58:56 +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
|
2009-11-19 10:37:10 +01:00
|
|
|
if (update_lock)
|
|
|
|
{
|
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;
|
|
|
|
aggregate->dsqlRse = rseNod;
|
|
|
|
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
|
|
|
|
parentRse->dsqlStreams = list = MAKE_node(nod_list, 1);
|
|
|
|
|
|
|
|
dsql_nod* aggregateNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
aggregateNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(aggregate);
|
|
|
|
list->nod_arg[0] = aggregateNod;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlFirst)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlFirst = rse->dsqlFirst;
|
|
|
|
rse->dsqlFirst = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
if (rse->dsqlSkip)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSkip = rse->dsqlSkip;
|
|
|
|
rse->dsqlSkip = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(parent_context);
|
2003-05-05 00:02:42 +02:00
|
|
|
// replace original contexts with parent context
|
2011-01-09 22:58:56 +01:00
|
|
|
remap_streams_to_parent_context(rse->dsqlStreams, parent_context);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-11-17 15:50:33 +01:00
|
|
|
// Process GROUP BY clause, if any
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlGroup)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
// if there are positions in the group by clause then replace them
|
2005-02-10 22:14:52 +01:00
|
|
|
// by the (newly pass) items from the select_list
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inGroupByClause;
|
2011-01-09 22:58:56 +01:00
|
|
|
aggregate->dsqlGroup = pass1_group_by_list(dsqlScratch, inputRse->dsqlGroup, selectList);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inGroupByClause;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// AB: An field pointing to another parent_context isn't
|
|
|
|
// allowed and GROUP BY items can't contain aggregates
|
2011-01-09 22:58:56 +01:00
|
|
|
if (FieldFinder::find(dsqlScratch->scopeLevel, FIELD_MATCH_TYPE_LOWER, aggregate->dsqlGroup) ||
|
2010-01-29 02:16:34 +01:00
|
|
|
Aggregate2Finder::find(dsqlScratch->scopeLevel, FIELD_MATCH_TYPE_LOWER_EQUAL,
|
2011-01-09 22:58:56 +01:00
|
|
|
false, aggregate->dsqlGroup))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-07-01 03:12:02 +02:00
|
|
|
// Cannot use an aggregate in a GROUP BY clause
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_group_err));
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2008-04-09 15:47:15 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// Parse a user-specified access PLAN
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlPlan = PASS1_node_psql(dsqlScratch, inputRse->dsqlPlan, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// AB: Pass select-items for distinct operation again, because for
|
2008-05-21 18:02:25 +02:00
|
|
|
// sub-selects a new context number should be generated
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlDistinct)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2009-11-19 10:37:10 +01:00
|
|
|
if (update_lock)
|
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Token unknown
|
|
|
|
Arg::Gds(isc_token_err) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str("WITH LOCK"));
|
2003-06-30 16:31:00 +02:00
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inSelectList;
|
2008-09-01 15:18:02 +02:00
|
|
|
// ASF: We pass false to viewFields parameter here because these expressions are
|
|
|
|
// generated inside the view body, and not in view fields.
|
2011-01-09 22:58:56 +01:00
|
|
|
targetRse->dsqlDistinct = pass1_sel_list(dsqlScratch, selectList, false);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inSelectList;
|
2009-06-06 04:21:23 +02:00
|
|
|
|
|
|
|
// sort, group and distinct have the same limit for now
|
|
|
|
if (selectList->nod_count > MAX_SORT_ITEMS)
|
|
|
|
{
|
|
|
|
// Cannot have more than 255 items in DISTINCT list.
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_max_distinct_items));
|
|
|
|
}
|
2003-02-23 02:36:22 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
if (parent_context)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Reset context of select items to point to the parent stream
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSelectList = FieldRemapper::remap(dsqlScratch, parent_context, false,
|
|
|
|
rse->dsqlSelectList);
|
|
|
|
rse->dsqlSelectList = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
// AB: Check for invalid constructions inside selected-items list
|
2011-01-09 22:58:56 +01:00
|
|
|
list = parentRse->dsqlSelectList;
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
{ // scope block
|
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// Invalid expression in the select list
|
|
|
|
// (not contained in either an aggregate or the GROUP BY clause)
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("select list"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // end scope block
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// Reset context of order items to point to the parent stream
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (order)
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlOrder = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
false, rse->dsqlOrder);
|
|
|
|
rse->dsqlOrder = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// AB: Check for invalid contructions inside the ORDER BY clause
|
2011-01-09 22:58:56 +01:00
|
|
|
list = targetRse->dsqlOrder;
|
2009-10-21 02:42:38 +02:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2005-05-20 01:41:17 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// Invalid expression in the ORDER BY clause
|
|
|
|
// (not contained in either an aggregate or the GROUP BY clause)
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("ORDER BY clause"));
|
|
|
|
}
|
2005-05-20 01:41:17 +02:00
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// And, of course, reduction clauses must also apply to the parent
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlDistinct)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlDistinct = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
false, parentRse->dsqlDistinct);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// Process HAVING clause, if any
|
2004-10-14 18:35:13 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (inputRse->dsqlHaving)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
++dsqlScratch->inHavingClause;
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlWhere = PASS1_node_psql(dsqlScratch, inputRse->dsqlHaving, false);
|
2009-12-20 22:01:10 +01:00
|
|
|
--dsqlScratch->inHavingClause;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlWhere = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
false, parentRse->dsqlWhere);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// AB: Check for invalid contructions inside the HAVING clause
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup,
|
|
|
|
parentRse->dsqlWhere))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
// Invalid expression in the HAVING clause
|
|
|
|
// (neither an aggregate nor contained in the GROUP BY clause)
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_having_err) << Arg::Str("HAVING clause"));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (AggregateFinder::find(dsqlScratch, true, parentRse->dsqlWhere))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Cannot use an aggregate in a WHERE clause, use HAVING instead
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
2009-10-21 02:42:38 +02:00
|
|
|
Arg::Gds(isc_dsql_agg_where_err));
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse = parentRse;
|
|
|
|
rseNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
rseNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(rse);
|
2010-01-19 19:26:28 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
parent_context->ctx_context = dsqlScratch->contextNumber++;
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
const bool sortWindow = rse->dsqlOrder && AggregateFinder::find(dsqlScratch, true, rse->dsqlOrder);
|
2009-10-23 02:42:40 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// WINDOW functions
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((rse->dsqlSelectList && AggregateFinder::find(dsqlScratch, true, rse->dsqlSelectList)) ||
|
2009-10-21 02:42:38 +02:00
|
|
|
sortWindow)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
AutoSetRestore<bool> autoProcessingWindow(&dsqlScratch->processingWindow, true);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
parent_context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
|
2009-12-20 22:01:10 +01:00
|
|
|
parent_context->ctx_scope_level = dsqlScratch->scopeLevel;
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
AggregateSourceNode* window = FB_NEW(pool) AggregateSourceNode(pool);
|
|
|
|
window->dsqlContext = parent_context;
|
|
|
|
window->dsqlRse = rseNod;
|
|
|
|
window->dsqlWindow = true;
|
|
|
|
|
|
|
|
dsql_nod* windowNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
windowNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(window);
|
|
|
|
|
|
|
|
parentRse = targetRse = FB_NEW(pool) RseNode(pool);
|
|
|
|
parentRse->dsqlStreams = list = MAKE_node(nod_list, 1);
|
|
|
|
list->nod_arg[0] = windowNod;
|
|
|
|
|
|
|
|
if (rse->flags & RseNode::FLAG_WRITELOCK)
|
2009-12-11 15:47:41 +01:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->flags |= RseNode::FLAG_WRITELOCK;
|
|
|
|
rse->flags &= ~RseNode::FLAG_WRITELOCK;
|
2009-12-11 15:47:41 +01:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlFirst)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlFirst = rse->dsqlFirst;
|
|
|
|
rse->dsqlFirst = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlSkip)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSkip = rse->dsqlSkip;
|
|
|
|
rse->dsqlSkip = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(parent_context);
|
2009-10-21 02:42:38 +02:00
|
|
|
// replace original contexts with parent context
|
2011-01-09 22:58:56 +01:00
|
|
|
remap_streams_to_parent_context(rse->dsqlStreams, parent_context);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (aggregate)
|
2004-10-14 18:35:13 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Check for invalid contructions inside selected-items list
|
2011-01-09 22:58:56 +01:00
|
|
|
list = rse->dsqlSelectList;
|
2009-10-21 02:42:38 +02:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// Invalid expression in the select list
|
|
|
|
// (not contained in either an aggregate or the GROUP BY clause)
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("select list"));
|
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlSelectList = FieldRemapper::remap(dsqlScratch, parent_context, true,
|
|
|
|
rse->dsqlSelectList);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
// Remap the nodes to the partition context.
|
2010-01-21 19:33:18 +01:00
|
|
|
for (size_t i = 0, mapCount = parent_context->ctx_win_maps.getCount(); i < mapCount; ++i)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
PartitionMap* partitionMap = parent_context->ctx_win_maps[i];
|
|
|
|
if (partitionMap->partition)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
partitionMap->partitionRemapped = PASS1_node(dsqlScratch, partitionMap->partition);
|
2010-01-28 16:18:11 +01:00
|
|
|
partitionMap->partitionRemapped = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
true, partitionMap->partitionRemapped, partitionMap->partition,
|
2010-01-23 04:02:53 +01:00
|
|
|
partitionMap->order);
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlSelectList = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (order)
|
2004-10-14 18:35:13 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
if (aggregate)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Check for invalid contructions inside the order-by list
|
2011-01-09 22:58:56 +01:00
|
|
|
list = rse->dsqlOrder;
|
2009-10-21 02:42:38 +02:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
if (InvalidReferenceFinder::find(parent_context, aggregate->dsqlGroup, *ptr))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// Invalid expression in the ORDER BY list
|
|
|
|
// (not contained in either an aggregate or the GROUP BY clause)
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("ORDER BY list"));
|
|
|
|
}
|
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlOrder = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
true, rse->dsqlOrder);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlOrder = NULL;
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// And, of course, reduction clauses must also apply to the parent
|
2011-01-09 22:58:56 +01:00
|
|
|
if (rse->dsqlDistinct)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
parentRse->dsqlDistinct = FieldRemapper::remap(dsqlScratch, parent_context,
|
|
|
|
true, rse->dsqlDistinct);
|
|
|
|
rse->dsqlDistinct = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rse = parentRse;
|
|
|
|
rseNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
rseNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(rse);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
rseNod->nod_flags = flags;
|
|
|
|
|
|
|
|
return rseNod;
|
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
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_sel_list(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, bool viewFields)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack stack;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-09-01 15:18:02 +02:00
|
|
|
if (viewFields)
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->hiddenVarsNumber = 0;
|
2008-09-01 15:18:02 +02:00
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2009-12-20 22:01:10 +01:00
|
|
|
stack.push(PASS1_node_psql(dsqlScratch, *ptr, false));
|
2008-09-01 15:18:02 +02:00
|
|
|
|
|
|
|
if (viewFields)
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->hiddenVarsNumber = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_list(stack);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_sort
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
@brief Process ORDER BY list, which may contain
|
2005-02-14 06:54:45 +01:00
|
|
|
an ordinal or alias which references the
|
2005-02-10 22:14:52 +01:00
|
|
|
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
|
2005-02-10 22:14:52 +01:00
|
|
|
@param selectList
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_sort( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* selectList)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2005-02-10 22:14:52 +01:00
|
|
|
DEV_BLKCHK(selectList, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (input->nod_type != nod_list)
|
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
// invalid ORDER BY clause
|
|
|
|
Arg::Gds(isc_order_by_err));
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-30 11:10:28 +02:00
|
|
|
if (input->nod_count > MAX_SORT_ITEMS)
|
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_order_by_err) <<
|
|
|
|
// invalid ORDER BY clause, cannot sort on more than 255 items
|
|
|
|
Arg::Gds(isc_dsql_max_sort_items));
|
2006-09-30 11:10:28 +02:00
|
|
|
}
|
|
|
|
|
2005-05-20 01:41:17 +02:00
|
|
|
// Node is simply to be rebuilt -- just recurse merrily
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
dsql_nod** ptr2 = node->nod_arg;
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2006-10-07 11:40:59 +02:00
|
|
|
for (int sortloop = 0; sortloop < input->nod_count; sortloop++)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2006-10-07 11:40:59 +02:00
|
|
|
DEV_BLKCHK(input->nod_arg[sortloop], dsql_type_nod);
|
|
|
|
dsql_nod* node1 = input->nod_arg[sortloop];
|
2009-11-19 10:37:10 +01:00
|
|
|
if (node1->nod_type != nod_order)
|
|
|
|
{
|
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-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node2 = MAKE_node(nod_order, e_order_count);
|
2005-05-28 00:45:31 +02:00
|
|
|
node2->nod_arg[e_order_flag] = node1->nod_arg[e_order_flag]; // asc/desc flag
|
|
|
|
node2->nod_arg[e_order_nulls] = node1->nod_arg[e_order_nulls]; // nulls first/last flag
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2006-10-20 20:49:23 +02:00
|
|
|
const dsql_str* collate = NULL;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-04-06 13:20:24 +02:00
|
|
|
// get node of value to be ordered by
|
2002-11-22 16:27:59 +01:00
|
|
|
node1 = node1->nod_arg[e_order_field];
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (node1->nod_type == nod_collate)
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
collate = (dsql_str*) node1->nod_arg[e_coll_target];
|
2003-04-06 13:20:24 +02:00
|
|
|
// substitute nod_collate with its argument (real value)
|
|
|
|
node1 = node1->nod_arg[e_coll_source];
|
|
|
|
}
|
|
|
|
|
2010-10-24 02:26:00 +02:00
|
|
|
LiteralNode* literal;
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (node1->nod_type == nod_field_name)
|
|
|
|
{
|
2005-02-10 22:14:52 +01:00
|
|
|
// check for alias or field node
|
2009-12-20 22:01:10 +01:00
|
|
|
node1 = pass1_field(dsqlScratch, node1, false, selectList);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
else if ((literal = ExprNode::as<LiteralNode>(node1)) && literal->litDesc.dsc_dtype == dtype_long)
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
2010-10-24 02:26:00 +02:00
|
|
|
const ULONG position = literal->getSlong();
|
|
|
|
|
2009-01-09 02:50:54 +01:00
|
|
|
if (position < 1 || !selectList || position > (ULONG) selectList->nod_count)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Invalid column position used in the ORDER BY clause
|
|
|
|
Arg::Gds(isc_dsql_column_pos_err) << Arg::Str("ORDER BY"));
|
2003-01-09 17:50:24 +01:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
|
2003-04-06 13:20:24 +02:00
|
|
|
// substitute ordinal with appropriate field
|
2009-12-20 22:01:10 +01:00
|
|
|
node1 = PASS1_node_psql(dsqlScratch, selectList->nod_arg[position - 1], false);
|
2008-04-09 15:47:15 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
node1 = PASS1_node_psql(dsqlScratch, node1, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (collate)
|
|
|
|
{
|
2003-04-06 13:20:24 +02:00
|
|
|
// finally apply collation order, if necessary
|
2009-12-20 22:01:10 +01:00
|
|
|
node1 = pass1_collate(dsqlScratch, node1, collate);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-04-06 13:20:24 +02:00
|
|
|
|
|
|
|
// store actual value to be ordered by
|
|
|
|
node2->nod_arg[e_order_field] = node1;
|
2001-05-23 15:26:42 +02:00
|
|
|
*ptr2++ = node2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_union
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Handle a UNION of substreams, generating
|
|
|
|
a mapping of all the fields and adding an
|
2005-05-28 00:45:31 +02:00
|
|
|
implicit PROJECT clause to ensure that all
|
2003-02-15 04:01:51 +01:00
|
|
|
the records returned are unique.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
@param order_list
|
2003-11-07 15:10:16 +01:00
|
|
|
@param rows
|
2010-02-16 17:28:54 +01:00
|
|
|
@param update_lock
|
|
|
|
@param flags
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_union( DsqlCompilerScratch* dsqlScratch, dsql_nod* input, dsql_nod* order_list,
|
2010-02-16 17:28:54 +01:00
|
|
|
dsql_nod* rows, dsql_nod* update_lock, USHORT flags)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(order_list, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2011-01-09 22:58:56 +01:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// set up the rse node for the union.
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* unionRse = FB_NEW(pool) RseNode(pool);
|
2011-01-22 21:40:04 +01:00
|
|
|
UnionSourceNode* unionSource = FB_NEW(pool) UnionSourceNode(pool);
|
|
|
|
|
|
|
|
dsql_nod* unionNod = unionRse->dsqlStreams = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
unionNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(unionSource);
|
|
|
|
unionNod->nod_flags = input->nod_flags;
|
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
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (input->nod_flags & NOD_UNION_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-01-22 21:40:04 +01:00
|
|
|
unionSource->dsqlClauses = MAKE_node(nod_list, input->nod_count);
|
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// process all the sub-rse's.
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod** uptr = unionSource->dsqlClauses->nod_arg;
|
2009-12-20 22:01:10 +01:00
|
|
|
const DsqlContextStack::const_iterator base(*dsqlScratch->context);
|
2005-05-20 01:41:17 +02:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ++ptr, ++uptr)
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
|
|
|
*uptr = pass1_rse(dsqlScratch, *ptr, NULL, NULL, NULL, 0);
|
|
|
|
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
|
|
|
|
if ((input->nod_flags & NOD_UNION_RECURSIVE) && (ptr == input->nod_arg))
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(dsqlScratch->recursiveCtx);
|
2005-05-20 01:41:17 +02:00
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// generate the list of fields to select.
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* items = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[0])->dsqlSelectList;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// loop through the list nodes, checking to be sure that they have the
|
2003-08-15 02:02:18 +02:00
|
|
|
// same number of items
|
2003-10-05 08:37:26 +02:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
for (int i = 1; i < unionSource->dsqlClauses->nod_count; ++i)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
const dsql_nod* nod1 = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[i])->dsqlSelectList;
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (items->nod_count != nod1->nod_count)
|
|
|
|
{
|
2009-01-07 10:30:57 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
// overload of msg
|
|
|
|
Arg::Gds(isc_dsql_count_mismatch));
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2009-01-07 10:30:57 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
// Comment below belongs to the old code (way a union was handled).
|
2003-08-18 23:37:47 +02:00
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
// SQL II, section 9.3, pg 195 governs which data types
|
|
|
|
// are considered equivalent for a UNION
|
|
|
|
// The following restriction is in some ways more restrictive
|
|
|
|
// (cannot UNION CHAR with VARCHAR for instance)
|
|
|
|
// (or cannot union CHAR of different lengths)
|
|
|
|
// and in someways less restrictive
|
|
|
|
// (SCALE is not looked at)
|
2009-12-20 22:01:10 +01:00
|
|
|
// Workaround: use a direct CAST() dsqlScratch in the SQL
|
|
|
|
// dsqlScratch to force desired datatype.
|
2003-08-18 23:37:47 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
// loop through the list nodes and cast whenever possible.
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* tmp_list = MAKE_node(nod_list, unionSource->dsqlClauses->nod_count);
|
|
|
|
|
|
|
|
for (int j = 0; j < items->nod_count; ++j)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
for (int i = 0; i < unionSource->dsqlClauses->nod_count; ++i)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
dsql_nod* nod1 = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[i])->dsqlSelectList;
|
2010-11-01 01:42:12 +01:00
|
|
|
MAKE_desc(dsqlScratch, &nod1->nod_arg[j]->nod_desc, nod1->nod_arg[j]);
|
2003-08-15 02:02:18 +02:00
|
|
|
tmp_list->nod_arg[i] = nod1->nod_arg[j];
|
2003-08-18 23:37:47 +02:00
|
|
|
|
|
|
|
// We look only at the items->nod_arg[] when creating the
|
|
|
|
// output descriptors. Make sure that the sub-rses
|
|
|
|
// descriptor flags are copied onto items->nod_arg[]->nod_desc.
|
|
|
|
// Bug 65584.
|
|
|
|
// -Sudesh 07/28/1999.
|
2009-11-19 10:37:10 +01:00
|
|
|
if (i > 0)
|
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
if (nod1->nod_arg[j]->nod_desc.dsc_flags & DSC_nullable)
|
2003-08-25 00:15:23 +02:00
|
|
|
items->nod_arg[j]->nod_desc.dsc_flags |= DSC_nullable;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
dsc desc;
|
2010-11-01 01:42:12 +01:00
|
|
|
MAKE_desc_from_list(dsqlScratch, &desc, tmp_list, "UNION");
|
2005-04-20 15:06:42 +02:00
|
|
|
// Only mark upper node as a NULL node when all sub-nodes are NULL
|
|
|
|
items->nod_arg[j]->nod_desc.dsc_flags &= ~DSC_null;
|
|
|
|
items->nod_arg[j]->nod_desc.dsc_flags |= (desc.dsc_flags & DSC_null);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < unionSource->dsqlClauses->nod_count; ++i)
|
|
|
|
pass1_union_auto_cast(dsqlScratch, unionSource->dsqlClauses->nod_arg[i], desc, j);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
items = ExprNode::as<RseNode>(unionSource->dsqlClauses->nod_arg[0])->dsqlSelectList;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// Create mappings for union.
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* union_items = MAKE_node(nod_list, items->nod_count);
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2011-01-28 18:22:44 +01:00
|
|
|
USHORT count = 0;
|
2005-05-20 01:41:17 +02:00
|
|
|
dsql_nod** uptr = items->nod_arg;
|
|
|
|
dsql_nod** ptr = union_items->nod_arg;
|
2011-01-22 21:40:04 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + union_items->nod_count; ptr < end; ptr++)
|
2005-05-20 01:41:17 +02:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
// Set up the dsql_map* between the sub-rses and the union context.
|
|
|
|
dsql_map* map = union_context->ctx_map = FB_NEW(*tdbb->getDefaultPool()) dsql_map;
|
2005-05-20 01:41:17 +02:00
|
|
|
map->map_position = count++;
|
|
|
|
map->map_node = *uptr++;
|
2010-01-21 19:33:18 +01:00
|
|
|
map->map_next = union_context->ctx_map;
|
|
|
|
map->map_partition = NULL;
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
DsqlMapNode* mapNode = FB_NEW(*tdbb->getDefaultPool()) DsqlMapNode(*tdbb->getDefaultPool(),
|
|
|
|
union_context, map);
|
|
|
|
|
|
|
|
*ptr = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
(*ptr)->nod_arg[0] = reinterpret_cast<dsql_nod*>(mapNode);
|
2005-05-20 01:41:17 +02:00
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
|
|
|
|
unionRse->dsqlSelectList = union_items;
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// Process ORDER clause, if any.
|
2009-01-07 10:30:57 +01:00
|
|
|
if (order_list)
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sort = MAKE_node(nod_list, order_list->nod_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod** uptr = sort->nod_arg;
|
|
|
|
dsql_nod** ptr = order_list->nod_arg;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + order_list->nod_count; ptr < end; ptr++, uptr++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* order1 = *ptr;
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* collate = 0;
|
|
|
|
const dsql_nod* position = order1->nod_arg[e_order_field];
|
2003-04-06 13:20:24 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (position->nod_type == nod_collate)
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
collate = (dsql_str*) position->nod_arg[e_coll_target];
|
2003-04-06 13:20:24 +02:00
|
|
|
position = position->nod_arg[e_coll_source];
|
|
|
|
}
|
|
|
|
|
2010-10-24 02:26:00 +02:00
|
|
|
const LiteralNode* literal = ExprNode::as<LiteralNode>(position);
|
|
|
|
|
|
|
|
if (!literal || literal->litDesc.dsc_dtype != dtype_long)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
// invalid ORDER BY clause.
|
|
|
|
Arg::Gds(isc_order_by_err));
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
|
|
|
|
const SLONG number = literal->getSlong();
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (number < 1 || number > union_items->nod_count)
|
|
|
|
{
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
// invalid ORDER BY clause.
|
|
|
|
Arg::Gds(isc_order_by_err));
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// make a new order node pointing at the Nth item in the select list.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* order2 = MAKE_node(nod_order, e_order_count);
|
2003-10-05 08:37:26 +02:00
|
|
|
*uptr = order2;
|
2002-11-22 16:27:59 +01:00
|
|
|
order2->nod_arg[e_order_field] = union_items->nod_arg[number - 1];
|
|
|
|
order2->nod_arg[e_order_flag] = order1->nod_arg[e_order_flag];
|
2010-10-24 02:26:00 +02:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (collate)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
order2->nod_arg[e_order_field] =
|
2009-12-20 22:01:10 +01:00
|
|
|
pass1_collate(dsqlScratch, order2->nod_arg[e_order_field], collate);
|
2003-04-06 13:20:24 +02:00
|
|
|
}
|
2010-10-24 02:26:00 +02:00
|
|
|
|
2002-11-22 16:27:59 +01:00
|
|
|
order2->nod_arg[e_order_nulls] = order1->nod_arg[e_order_nulls];
|
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)
|
|
|
|
{
|
2011-01-20 05:41:10 +01:00
|
|
|
pass1_limit(dsqlScratch, rows->nod_arg[e_rows_length], rows->nod_arg[e_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.
|
2010-02-16 17:28:54 +01:00
|
|
|
if (!(input->nod_flags & NOD_UNION_ALL))
|
|
|
|
{
|
|
|
|
if (update_lock)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if (update_lock)
|
|
|
|
unionRse->flags |= RseNode::FLAG_WRITELOCK;
|
|
|
|
|
|
|
|
dsql_nod* unionRseNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
unionRseNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(unionRse);
|
|
|
|
unionRseNod->nod_flags = flags;
|
|
|
|
|
|
|
|
return unionRseNod;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
pass1_union_auto_cast
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Auto cast types to the same type by the rules from
|
2003-08-15 02:02:18 +02:00
|
|
|
MAKE_desc_from_list. SELECT X1 FROM .. UNION SELECT X2 FROM ..
|
|
|
|
Items X1..Xn are collected together to make the cast-descriptor, this
|
|
|
|
was done by the caller (param desc and input is the collection).
|
|
|
|
Then is a cast generated (or reused) for every X item if it has
|
|
|
|
another descriptor than the param desc.
|
|
|
|
Position tells us which column-nr we are processing.
|
|
|
|
|
|
|
|
@param input
|
|
|
|
@param desc
|
|
|
|
@param position
|
|
|
|
@param in_select_list
|
|
|
|
|
|
|
|
**/
|
2010-11-01 14:45:52 +01:00
|
|
|
static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, dsql_nod* input,
|
|
|
|
const dsc& desc, SSHORT position, bool in_select_list)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
case nod_list:
|
2005-05-22 05:11:41 +02:00
|
|
|
if (in_select_list)
|
|
|
|
{
|
2009-01-09 02:50:54 +01:00
|
|
|
if (position < 0 || position >= input->nod_count)
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2006-10-17 08:03:21 +02:00
|
|
|
// Internal dsql error: column position out of range in pass1_union_auto_cast
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_auto_field_bad_pos));
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* select_item = input->nod_arg[position];
|
2010-11-01 14:45:52 +01:00
|
|
|
MAKE_desc(dsqlScratch, &select_item->nod_desc, select_item);
|
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
if (select_item->nod_desc.dsc_dtype != desc.dsc_dtype ||
|
|
|
|
select_item->nod_desc.dsc_length != desc.dsc_length ||
|
|
|
|
select_item->nod_desc.dsc_scale != desc.dsc_scale ||
|
|
|
|
select_item->nod_desc.dsc_sub_type != desc.dsc_sub_type)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
// Because this select item has a different descriptor then
|
|
|
|
// our finally descriptor CAST it.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* cast_node = NULL;
|
2004-06-30 00:15:10 +02:00
|
|
|
dsql_nod* alias_node = NULL;
|
2010-11-14 18:25:48 +01:00
|
|
|
DerivedFieldNode* derivedField;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
// Pick a existing cast if available else make a new one.
|
2010-11-02 00:57:31 +01:00
|
|
|
if (select_item->nod_type == nod_alias &&
|
|
|
|
select_item->nod_arg[e_alias_value] &&
|
|
|
|
ExprNode::is<CastNode>(select_item->nod_arg[e_alias_value]))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
cast_node = select_item->nod_arg[e_alias_value];
|
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)) &&
|
|
|
|
ExprNode::is<CastNode>(derivedField->dsqlValue))
|
2005-11-29 00:06:53 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
cast_node = derivedField->dsqlValue;
|
2005-11-29 00:06:53 +01:00
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
else if (ExprNode::is<CastNode>(select_item))
|
2003-08-15 02:02:18 +02:00
|
|
|
cast_node = select_item;
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
|
|
|
{
|
2010-11-02 00:57:31 +01:00
|
|
|
CastNode* castNode = FB_NEW(*tdbb->getDefaultPool()) CastNode(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
|
|
|
|
castNode->dsqlField = FB_NEW(*tdbb->getDefaultPool()) dsql_fld(
|
|
|
|
*tdbb->getDefaultPool());
|
|
|
|
|
|
|
|
cast_node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
cast_node->nod_arg[0] = reinterpret_cast<dsql_nod*>(castNode);
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
// We want to leave the ALIAS node on his place, because a UNION
|
|
|
|
// uses the select_items from the first sub-rse to determine the
|
|
|
|
// columnname.
|
2010-11-02 00:57:31 +01:00
|
|
|
if (select_item->nod_type == nod_alias)
|
|
|
|
castNode->dsqlSource = select_item->nod_arg[e_alias_value];
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
|
|
|
castNode->dsqlSource = derivedField->dsqlValue;
|
2010-11-02 00:57:31 +01:00
|
|
|
else
|
|
|
|
castNode->dsqlSource = select_item;
|
|
|
|
|
2004-06-30 00:15:10 +02:00
|
|
|
// When a cast is created we're losing our fieldname, thus
|
2007-03-31 08:50:01 +02:00
|
|
|
// create an alias to keep it.
|
2007-03-28 11:27:54 +02:00
|
|
|
const dsql_nod* name_node = select_item;
|
2010-11-14 18:25:48 +01:00
|
|
|
const DsqlMapNode* mapNode;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
while ((mapNode = ExprNode::as<DsqlMapNode>(name_node)))
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
// Skip all the DsqlMapNodes.
|
|
|
|
name_node = mapNode->map->map_node;
|
2007-03-28 11:27:54 +02:00
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
const FieldNode* fieldNode;
|
|
|
|
|
|
|
|
if ((fieldNode = ExprNode::as<FieldNode>(name_node)))
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2004-06-30 00:15:10 +02:00
|
|
|
// Create new node for alias and copy fieldname
|
|
|
|
alias_node = MAKE_node(nod_alias, e_alias_count);
|
|
|
|
// Copy fieldname to a new string.
|
2008-02-28 14:48:16 +01:00
|
|
|
dsql_str* str_alias = FB_NEW_RPT(*tdbb->getDefaultPool(),
|
2010-12-19 22:42:32 +01:00
|
|
|
fieldNode->dsqlField->fld_name.length()) dsql_str;
|
|
|
|
strcpy(str_alias->str_data, fieldNode->dsqlField->fld_name.c_str());
|
|
|
|
str_alias->str_length = fieldNode->dsqlField->fld_name.length();
|
2004-06-30 00:15:10 +02:00
|
|
|
alias_node->nod_arg[e_alias_alias] = (dsql_nod*) str_alias;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-11-02 00:57:31 +01:00
|
|
|
dsql_fld* field = ExprNode::as<CastNode>(cast_node)->dsqlField;
|
2003-08-15 02:02:18 +02:00
|
|
|
// Copy the descriptor to a field, because the gen_cast
|
|
|
|
// uses a dsql field type.
|
|
|
|
field->fld_dtype = desc.dsc_dtype;
|
|
|
|
field->fld_scale = desc.dsc_scale;
|
|
|
|
field->fld_sub_type = desc.dsc_sub_type;
|
|
|
|
field->fld_length = desc.dsc_length;
|
|
|
|
field->fld_flags = (desc.dsc_flags & DSC_nullable) ? FLD_nullable : 0;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (desc.dsc_dtype <= dtype_any_text)
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
field->fld_ttype = desc.dsc_sub_type;
|
|
|
|
field->fld_character_set_id = INTL_GET_CHARSET(&desc);
|
|
|
|
field->fld_collation_id = INTL_GET_COLLATE(&desc);
|
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else if (desc.dsc_dtype == dtype_blob)
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
field->fld_character_set_id = desc.dsc_scale;
|
2005-05-28 00:45:31 +02:00
|
|
|
field->fld_collation_id = desc.dsc_flags >> 8;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finally copy the descriptors to the root nodes and swap
|
|
|
|
// the necessary nodes.
|
|
|
|
cast_node->nod_desc = desc;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
|
|
|
if (select_item->nod_desc.dsc_flags & DSC_nullable)
|
2003-08-15 02:02:18 +02:00
|
|
|
cast_node->nod_desc.dsc_flags |= DSC_nullable;
|
2010-11-02 00:57:31 +01:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (select_item->nod_type == nod_alias)
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
select_item->nod_arg[e_alias_value] = cast_node;
|
|
|
|
select_item->nod_desc = desc;
|
|
|
|
}
|
2010-11-14 18:25:48 +01:00
|
|
|
else if ((derivedField = ExprNode::as<DerivedFieldNode>(select_item)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2010-11-14 18:25:48 +01:00
|
|
|
derivedField->dsqlValue = cast_node;
|
2005-11-29 00:06:53 +01:00
|
|
|
select_item->nod_desc = desc;
|
|
|
|
}
|
2009-11-19 10:37:10 +01:00
|
|
|
else
|
|
|
|
{
|
2004-06-30 00:15:10 +02:00
|
|
|
// If a new alias was created for keeping original field-name
|
|
|
|
// make the alias the "top" node.
|
2009-11-19 10:37:10 +01:00
|
|
|
if (alias_node)
|
|
|
|
{
|
2004-06-30 00:15:10 +02:00
|
|
|
alias_node->nod_arg[e_alias_value] = cast_node;
|
|
|
|
alias_node->nod_desc = cast_node->nod_desc;
|
|
|
|
input->nod_arg[position] = alias_node;
|
|
|
|
}
|
2010-11-02 00:57:31 +01:00
|
|
|
else
|
2004-06-30 00:15:10 +02:00
|
|
|
input->nod_arg[position] = cast_node;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2010-11-01 14:45:52 +01:00
|
|
|
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
case nod_class_exprnode:
|
|
|
|
{
|
2011-01-22 21:40:04 +01:00
|
|
|
RseNode* rseNode;
|
|
|
|
UnionSourceNode* unionNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if ((rseNode = ExprNode::as<RseNode>(input)) && !rseNode->dsqlExplicitJoin)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
pass1_union_auto_cast(dsqlScratch, rseNode->dsqlStreams, desc, position);
|
2010-11-01 14:45:52 +01:00
|
|
|
|
2011-01-22 21:40:04 +01:00
|
|
|
if ((unionNode = ExprNode::as<UnionSourceNode>(rseNode->dsqlStreams)))
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
// We're now in a UNION under a UNION so don't change the existing mappings.
|
|
|
|
// Only replace the node where the map points to, because they could be changed.
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* sub_rse_items = ExprNode::as<RseNode>(
|
2011-01-22 21:40:04 +01:00
|
|
|
unionNode->dsqlClauses->nod_arg[0])->dsqlSelectList;
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_map* map = ExprNode::as<DsqlMapNode>(rseNode->dsqlSelectList->nod_arg[position])->map;
|
2003-08-30 18:49:15 +02:00
|
|
|
map->map_node = sub_rse_items->nod_arg[position];
|
2011-01-09 22:58:56 +01:00
|
|
|
rseNode->dsqlSelectList->nod_arg[position]->nod_desc = desc;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2010-11-01 14:45:52 +01:00
|
|
|
else
|
2011-01-09 22:58:56 +01:00
|
|
|
pass1_union_auto_cast(dsqlScratch, rseNode->dsqlSelectList, desc, position, true);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2011-01-22 21:40:04 +01:00
|
|
|
else if ((unionNode = ExprNode::as<UnionSourceNode>(input)))
|
|
|
|
{
|
|
|
|
dsql_nod** ptr = unionNode->dsqlClauses->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const end = ptr + unionNode->dsqlClauses->nod_count;
|
|
|
|
ptr != end;
|
|
|
|
++ptr)
|
|
|
|
{
|
|
|
|
pass1_union_auto_cast(dsqlScratch, *ptr, desc, position);
|
|
|
|
}
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
break;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_update
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2009-12-22 01:08:49 +01:00
|
|
|
@brief Process UPDATE statement.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_update(DsqlCompilerScratch* dsqlScratch, dsql_nod* input, bool insert_or_update)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2011-01-09 22:58:56 +01: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-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-08-25 09:58:45 +02:00
|
|
|
const bool isUpdateSqlCompliant = !Config::getOldSetClauseSemantics();
|
2006-05-16 11:09:18 +02:00
|
|
|
|
|
|
|
// Separate old and new context references
|
|
|
|
|
|
|
|
Firebird::Array<dsql_nod*> org_values, new_values;
|
|
|
|
|
|
|
|
dsql_nod* list = input->nod_arg[e_upd_statement];
|
|
|
|
fb_assert(list->nod_type == nod_list);
|
|
|
|
|
|
|
|
for (int i = 0; i < list->nod_count; ++i)
|
|
|
|
{
|
|
|
|
const dsql_nod* const assign = list->nod_arg[i];
|
|
|
|
fb_assert(assign->nod_type == nod_assign);
|
2006-10-07 11:40:59 +02:00
|
|
|
org_values.add(assign->nod_arg[e_asgn_value]);
|
|
|
|
new_values.add(assign->nod_arg[e_asgn_field]);
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod** ptr;
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* cursor = input->nod_arg[e_upd_cursor];
|
|
|
|
dsql_nod* relation = input->nod_arg[e_upd_relation];
|
2006-05-16 11:09:18 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
if (cursor && dsqlScratch->isPsql())
|
2008-08-25 09:58:45 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* anode = MAKE_node(nod_modify_current, e_mdc_count);
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_ctx* context = pass1_cursor_context(dsqlScratch, cursor, relation);
|
2006-05-16 11:09:18 +02:00
|
|
|
anode->nod_arg[e_mdc_context] = (dsql_nod*) context;
|
2008-08-25 09:58:45 +02:00
|
|
|
|
|
|
|
if (isUpdateSqlCompliant)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
2008-08-25 09:58:45 +02:00
|
|
|
// Process old context values
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(context);
|
|
|
|
dsqlScratch->scopeLevel++;
|
2008-08-25 09:58:45 +02:00
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2008-08-25 09:58:45 +02:00
|
|
|
}
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel--;
|
|
|
|
dsqlScratch->context->pop();
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
2008-08-25 09:58:45 +02:00
|
|
|
|
2006-05-16 11:09:18 +02:00
|
|
|
// Process relation
|
2009-12-20 22:01:10 +01:00
|
|
|
anode->nod_arg[e_mdc_update] = PASS1_node_psql(dsqlScratch, relation, false);
|
2008-08-25 09:58:45 +02:00
|
|
|
|
|
|
|
if (!isUpdateSqlCompliant)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
2008-08-25 09:58:45 +02:00
|
|
|
// Process old context values
|
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2008-08-25 09:58:45 +02:00
|
|
|
}
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
2008-08-25 09:58:45 +02:00
|
|
|
|
2006-05-16 11:09:18 +02:00
|
|
|
// Process new context values
|
2006-05-17 04:43:12 +02:00
|
|
|
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
2006-09-03 03:09:23 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
anode->nod_arg[e_mdc_return] = process_returning(dsqlScratch, input->nod_arg[e_upd_return]);
|
2006-09-03 03:09:23 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->pop();
|
2006-05-16 11:09:18 +02:00
|
|
|
// Recreate list of assignments
|
2009-01-06 06:53:34 +01:00
|
|
|
anode->nod_arg[e_mdc_statement] = list = MAKE_node(nod_list, list->nod_count);
|
2006-05-16 11:09:18 +02:00
|
|
|
for (int i = 0; i < list->nod_count; ++i)
|
|
|
|
{
|
2006-10-07 11:40:59 +02:00
|
|
|
dsql_nod* assign = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
assign->nod_arg[e_asgn_value] = org_values[i];
|
|
|
|
assign->nod_arg[e_asgn_field] = new_values[i];
|
2006-05-16 11:09:18 +02:00
|
|
|
list->nod_arg[i] = assign;
|
|
|
|
}
|
|
|
|
// We do not allow cases like UPDATE T SET f1 = v1, f2 = v2, f1 = v3...
|
|
|
|
field_appears_once(anode->nod_arg[e_mdc_statement],
|
|
|
|
input->nod_arg[e_upd_statement],
|
2006-09-03 03:09:23 +02:00
|
|
|
false, "UPDATE");
|
2003-11-10 10:16:38 +01:00
|
|
|
return anode;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
dsqlScratch->getStatement()->setType(
|
|
|
|
cursor ? DsqlCompiledStatement::TYPE_UPDATE_CURSOR : DsqlCompiledStatement::TYPE_UPDATE);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_modify, e_mod_count);
|
2009-12-20 22:01:10 +01:00
|
|
|
node->nod_arg[e_mod_update] = PASS1_node_psql(dsqlScratch, relation, false);
|
2007-04-24 17:22:05 +02:00
|
|
|
dsql_ctx* mod_context = get_context(node->nod_arg[e_mod_update]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-08-25 09:58:45 +02:00
|
|
|
if (!isUpdateSqlCompliant)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
2008-08-25 09:58:45 +02:00
|
|
|
// Process old context values
|
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
2008-08-25 09:58:45 +02:00
|
|
|
|
2006-05-16 11:09:18 +02:00
|
|
|
// Process new context values
|
2006-05-17 04:43:12 +02:00
|
|
|
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr)
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2006-05-16 11:09:18 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->pop();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
// Generate record selection expression
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_nod* rseNod;
|
|
|
|
|
|
|
|
if (cursor)
|
|
|
|
rseNod = pass1_cursor_reference(dsqlScratch, cursor, relation);
|
2009-01-07 10:30:57 +01:00
|
|
|
else
|
|
|
|
{
|
2011-01-09 22:58:56 +01:00
|
|
|
RseNode* rse = FB_NEW(pool) RseNode(pool);
|
|
|
|
|
|
|
|
rseNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
rseNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(rse);
|
|
|
|
rseNod->nod_flags = (USHORT)(IPTR) input->nod_arg[e_upd_rse_flags];
|
|
|
|
|
2007-04-24 17:22:05 +02:00
|
|
|
if (input->nod_arg[e_upd_return])
|
2011-01-09 22:58:56 +01:00
|
|
|
rseNod->nod_flags |= NOD_SELECT_EXPR_SINGLETON;
|
2006-09-03 03:09:23 +02:00
|
|
|
|
2004-10-27 11:33:08 +02:00
|
|
|
dsql_nod* temp = MAKE_node(nod_list, 1);
|
2011-01-09 22:58:56 +01:00
|
|
|
rse->dsqlStreams = temp;
|
2009-12-20 22:01:10 +01:00
|
|
|
temp->nod_arg[0] = PASS1_node_psql(dsqlScratch, relation, false);
|
2007-04-24 17:22:05 +02:00
|
|
|
dsql_ctx* old_context = get_context(temp->nod_arg[0]);
|
2004-10-27 11:33:08 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_upd_boolean]))
|
|
|
|
rse->dsqlWhere = PASS1_node_psql(dsqlScratch, temp, false);
|
2004-10-27 11:33:08 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_upd_plan]))
|
|
|
|
rse->dsqlPlan = PASS1_node_psql(dsqlScratch, temp, false);
|
2004-10-27 11:33:08 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_upd_sort]))
|
|
|
|
rse->dsqlOrder = pass1_sort(dsqlScratch, temp, NULL);
|
2004-10-27 11:33:08 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
if ((temp = input->nod_arg[e_upd_rows]))
|
2011-01-20 05:41:10 +01:00
|
|
|
pass1_limit(dsqlScratch, temp->nod_arg[e_rows_length], temp->nod_arg[e_rows_skip], rse);
|
2007-04-24 17:22:05 +02:00
|
|
|
|
|
|
|
if (input->nod_arg[e_upd_return])
|
|
|
|
{
|
2010-06-10 04:03:03 +02:00
|
|
|
node->nod_arg[e_mod_return] = ReturningProcessor(
|
|
|
|
dsqlScratch, old_context, mod_context).process(input->nod_arg[e_upd_return]);
|
2007-04-24 17:22:05 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
node->nod_arg[e_mod_source] = ExprNode::as<RseNode>(rseNod)->dsqlStreams->nod_arg[0];
|
|
|
|
node->nod_arg[e_mod_rse] = rseNod;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-08-25 09:58:45 +02:00
|
|
|
if (isUpdateSqlCompliant)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
2008-08-25 09:58:45 +02:00
|
|
|
// Process old context values
|
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
|
2008-08-25 09:58:45 +02:00
|
|
|
}
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
2008-08-25 09:58:45 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->pop();
|
2006-05-16 11:09:18 +02:00
|
|
|
|
|
|
|
// Recreate list of assignments
|
2008-08-25 09:58:45 +02:00
|
|
|
node->nod_arg[e_mod_statement] = list = MAKE_node(nod_list, list->nod_count);
|
|
|
|
|
2006-05-18 08:26:15 +02:00
|
|
|
for (int j = 0; j < list->nod_count; ++j)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
2006-05-18 08:26:15 +02:00
|
|
|
dsql_nod* const sub1 = org_values[j];
|
|
|
|
dsql_nod* const sub2 = new_values[j];
|
2010-02-13 21:29:29 +01:00
|
|
|
if (!PASS1_set_parameter_type(dsqlScratch, sub1, sub2, false))
|
2006-05-17 06:33:44 +02:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, sub2, sub1, false);
|
2006-05-17 06:33:44 +02:00
|
|
|
}
|
2006-10-07 11:40:59 +02:00
|
|
|
dsql_nod* assign = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
assign->nod_arg[e_asgn_value] = sub1;
|
|
|
|
assign->nod_arg[e_asgn_field] = sub2;
|
2006-05-18 08:26:15 +02:00
|
|
|
list->nod_arg[j] = assign;
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// We do not allow cases like UPDATE T SET f1 = v1, f2 = v2, f1 = v3...
|
|
|
|
field_appears_once(node->nod_arg[e_mod_statement],
|
|
|
|
input->nod_arg[e_upd_statement],
|
2006-09-03 03:09:23 +02:00
|
|
|
false, "UPDATE");
|
2006-05-16 11:09:18 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
set_parameters_name(node->nod_arg[e_mod_statement], node->nod_arg[e_mod_update]);
|
2006-05-16 11:09:18 +02:00
|
|
|
|
2007-04-24 17:22:05 +02:00
|
|
|
if (!insert_or_update)
|
2010-06-10 04:03:03 +02:00
|
|
|
nullify_returning(dsqlScratch, node, &node);
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
|
2006-11-26 16:40:10 +01:00
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_update_or_insert
|
|
|
|
|
2009-12-22 01:08:49 +01:00
|
|
|
@brief Process UPDATE OR INSERT statement.
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2006-11-26 16:40:10 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_update_or_insert(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2006-11-26 16:40:10 +01:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2006-11-26 16:40:10 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
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
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2010-09-17 05:15:32 +02:00
|
|
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
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
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
if (!dsqlScratch->isPsql())
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT;
|
2007-04-24 17:22:05 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
const MetaName& relation_name = ExprNode::as<RelationSourceNode>(input->nod_arg[e_upi_relation])->dsqlName;
|
|
|
|
MetaName base_name = relation_name;
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
dsql_nod* values = input->nod_arg[e_upi_values];
|
|
|
|
|
|
|
|
// build the INSERT node
|
|
|
|
dsql_nod* insert = MAKE_node(nod_insert, e_ins_count);
|
|
|
|
insert->nod_arg[e_ins_relation] = input->nod_arg[e_upi_relation];
|
|
|
|
insert->nod_arg[e_ins_fields] = input->nod_arg[e_upi_fields];
|
|
|
|
insert->nod_arg[e_ins_values] = values;
|
|
|
|
insert->nod_arg[e_ins_return] = input->nod_arg[e_upi_return];
|
2009-12-20 22:01:10 +01:00
|
|
|
insert = pass1_insert(dsqlScratch, insert, true);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
// PASS1_statement will transform nod_insert to nod_store
|
|
|
|
fb_assert(insert->nod_type == nod_store);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
dsql_ctx* context = ExprNode::as<RelationSourceNode>(insert->nod_arg[e_sto_relation])->dsqlContext;
|
2006-11-26 16:40:10 +01:00
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
|
|
|
|
dsql_rel* relation = context->ctx_relation;
|
|
|
|
dsql_nod* fields = input->nod_arg[e_upi_fields];
|
|
|
|
|
|
|
|
// if a field list isn't present, build one using the same
|
|
|
|
// rules of INSERT INTO table VALUES ...
|
|
|
|
if (!fields)
|
|
|
|
fields = explode_fields(relation);
|
|
|
|
|
|
|
|
// maintain a pair of view's field name / base field name
|
|
|
|
MetaNamePairMap view_fields;
|
|
|
|
|
|
|
|
if ((relation->rel_flags & REL_view) && !input->nod_arg[e_upi_matching])
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_rel* base_rel = METD_get_view_base(dsqlScratch->getTransaction(), dsqlScratch,
|
2011-01-30 01:25:46 +01:00
|
|
|
relation_name.c_str(), view_fields);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
// get the base table name if there is only one
|
|
|
|
if (base_rel)
|
2011-01-30 01:25:46 +01:00
|
|
|
base_name = base_rel->rel_name;
|
2006-11-26 16:40:10 +01:00
|
|
|
else
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_upd_ins_with_complex_view));
|
2006-11-26 16:40:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* matching = input->nod_arg[e_upi_matching];
|
2010-09-17 05:15:32 +02:00
|
|
|
UCHAR equality_type;
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
if (matching)
|
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
equality_type = blr_equiv;
|
2007-05-13 16:06:41 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->push(context);
|
|
|
|
dsqlScratch->scopeLevel++;
|
2006-11-26 16:40:10 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
const dsql_nod* matching_fields = PASS1_node_psql(dsqlScratch, matching, false);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel--;
|
|
|
|
dsqlScratch->context->pop();
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
field_appears_once(matching_fields, matching, true, "UPDATE OR INSERT");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
equality_type = blr_eql;
|
2007-05-13 16:06:41 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
matching = METD_get_primary_key(dsqlScratch->getTransaction(), base_name.c_str());
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
if (!matching)
|
2011-01-30 01:25:46 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_primary_key_required) << base_name);
|
2006-11-26 16:40:10 +01:00
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
// build a boolean to use in the UPDATE dsqlScratch
|
2006-11-26 16:40:10 +01:00
|
|
|
dsql_nod* match = NULL;
|
|
|
|
USHORT match_count = 0;
|
|
|
|
|
2008-08-15 18:32:42 +02:00
|
|
|
DsqlNodStack varStack;
|
|
|
|
|
2006-11-26 16:40:10 +01:00
|
|
|
DsqlNodStack stack;
|
|
|
|
dsql_nod** field_ptr = fields->nod_arg;
|
|
|
|
dsql_nod** value_ptr = values->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const field_end = field_ptr + fields->nod_count;
|
|
|
|
field_ptr < field_end; field_ptr++, value_ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*field_ptr, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(*value_ptr, dsql_type_nod);
|
|
|
|
|
2008-08-15 18:32:42 +02:00
|
|
|
dsql_nod* assign = MAKE_node(nod_assign, e_asgn_count);
|
|
|
|
assign->nod_arg[e_asgn_value] = *value_ptr;
|
|
|
|
assign->nod_arg[e_asgn_field] = *field_ptr;
|
|
|
|
stack.push(assign);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
2008-08-15 18:32:42 +02:00
|
|
|
dsql_nod* temp = *value_ptr;
|
2006-11-26 16:40:10 +01:00
|
|
|
dsql_nod* temp2 = insert->nod_arg[e_sto_statement]->nod_arg[field_ptr - fields->nod_arg]->nod_arg[1];
|
2010-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, temp, temp2, false);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
fb_assert((*field_ptr)->nod_type == nod_field_name);
|
|
|
|
|
|
|
|
// When relation is a view and MATCHING was not specified, field_name
|
|
|
|
// stores the base field name that is what we should find in the primary
|
|
|
|
// key of base table.
|
|
|
|
Firebird::MetaName field_name;
|
|
|
|
|
|
|
|
if ((relation->rel_flags & REL_view) && !input->nod_arg[e_upi_matching])
|
|
|
|
{
|
|
|
|
view_fields.get(
|
|
|
|
Firebird::MetaName(((dsql_str*) (*field_ptr)->nod_arg[e_fln_name])->str_data),
|
|
|
|
field_name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
field_name = ((dsql_str*) (*field_ptr)->nod_arg[e_fln_name])->str_data;
|
|
|
|
|
|
|
|
if (field_name.hasData())
|
|
|
|
{
|
|
|
|
dsql_nod** matching_ptr = matching->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const matching_end = matching_ptr + matching->nod_count;
|
|
|
|
matching_ptr < matching_end; matching_ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*matching_ptr, dsql_type_nod);
|
|
|
|
fb_assert((*matching_ptr)->nod_type == nod_field_name);
|
|
|
|
|
2009-01-06 06:53:34 +01:00
|
|
|
const Firebird::MetaName
|
2009-01-06 18:46:08 +01:00
|
|
|
testField(((dsql_str*)(*matching_ptr)->nod_arg[e_fln_name])->str_data);
|
2009-01-06 06:53:34 +01:00
|
|
|
|
2009-01-06 18:46:08 +01:00
|
|
|
if (testField == field_name)
|
2006-11-26 16:40:10 +01:00
|
|
|
{
|
|
|
|
++match_count;
|
|
|
|
|
2009-01-06 18:46:08 +01:00
|
|
|
const size_t fieldPos = field_ptr - fields->nod_arg;
|
|
|
|
dsql_nod*& expr = insert->nod_arg[e_sto_statement]->nod_arg[fieldPos]->nod_arg[0];
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_nod* var = pass1_hidden_variable(dsqlScratch, expr);
|
2008-08-15 18:32:42 +02:00
|
|
|
|
|
|
|
if (var)
|
|
|
|
{
|
2008-09-01 15:18:02 +02:00
|
|
|
dsql_nod* varAssign = MAKE_node(nod_assign, e_asgn_count);
|
2011-02-06 19:13:12 +01:00
|
|
|
varAssign->nod_arg[e_asgn_value] = expr;
|
|
|
|
varAssign->nod_arg[e_asgn_field] = var;
|
2008-09-01 15:18:02 +02:00
|
|
|
varStack.push(varAssign);
|
2008-08-15 18:32:42 +02:00
|
|
|
|
|
|
|
assign->nod_arg[e_asgn_value] = expr = var;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
var = *value_ptr;
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
ComparativeBoolNode* eqlNode = FB_NEW(pool) ComparativeBoolNode(pool,
|
|
|
|
equality_type, *field_ptr, var);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
dsql_nod* eqlNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
eqlNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(eqlNode);
|
|
|
|
|
|
|
|
match = PASS1_compose(match, eqlNod, blr_and);
|
2006-11-26 16:40:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if implicit or explicit MATCHING is valid
|
|
|
|
if (match_count != matching->nod_count)
|
|
|
|
{
|
|
|
|
if (input->nod_arg[e_upi_matching])
|
2008-08-15 13:21:47 +02:00
|
|
|
ERRD_post(Arg::Gds(isc_upd_ins_doesnt_match_matching));
|
2006-11-26 16:40:10 +01:00
|
|
|
else
|
2011-01-30 01:25:46 +01:00
|
|
|
ERRD_post(Arg::Gds(isc_upd_ins_doesnt_match_pk) << base_name);
|
2006-11-26 16:40:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// build the UPDATE node
|
|
|
|
dsql_nod* update = MAKE_node(nod_update, e_upd_count);
|
|
|
|
update->nod_arg[e_upd_relation] = input->nod_arg[e_upi_relation];
|
|
|
|
update->nod_arg[e_upd_statement] = MAKE_list(stack);
|
|
|
|
update->nod_arg[e_upd_boolean] = match;
|
|
|
|
|
|
|
|
if (input->nod_arg[e_upi_return])
|
|
|
|
{
|
2007-10-17 16:35:31 +02:00
|
|
|
update->nod_arg[e_upd_rse_flags] = (dsql_nod*)(IPTR) NOD_SELECT_EXPR_SINGLETON;
|
2010-06-10 04:03:03 +02:00
|
|
|
update->nod_arg[e_upd_return] = ReturningProcessor::clone(
|
|
|
|
input->nod_arg[e_upi_return], insert->nod_arg[e_sto_return]);
|
2006-11-26 16:40:10 +01:00
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
update = pass1_update(dsqlScratch, update, true);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
// PASS1_statement will transform nod_update to nod_modify
|
|
|
|
fb_assert(update->nod_type == nod_modify);
|
|
|
|
|
|
|
|
// test if ROW_COUNT = 0
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
ComparativeBoolNode* eqlNode = FB_NEW(pool) ComparativeBoolNode(pool,
|
|
|
|
blr_eql, MAKE_node(nod_class_exprnode, 1), MAKE_const_slong(0));
|
|
|
|
|
|
|
|
eqlNode->dsqlArg1->nod_arg[0] = reinterpret_cast<dsql_nod*>(FB_NEW(pool) InternalInfoNode(pool,
|
|
|
|
MAKE_const_slong(SLONG(InternalInfoNode::INFO_TYPE_ROWS_AFFECTED))));
|
|
|
|
|
|
|
|
dsql_nod* eqlNod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
eqlNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(eqlNode);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
2009-12-20 23:42:43 +01:00
|
|
|
const ULONG save_flags = dsqlScratch->flags;
|
|
|
|
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_BLOCK; // to compile ROW_COUNT
|
2010-09-17 05:15:32 +02:00
|
|
|
eqlNod = PASS1_node(dsqlScratch, eqlNod);
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags = save_flags;
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
// if (ROW_COUNT = 0) then INSERT
|
2010-09-17 05:15:32 +02:00
|
|
|
IfNode* ifNode = FB_NEW(pool) IfNode(pool);
|
|
|
|
ifNode->dsqlCondition = eqlNod;
|
|
|
|
ifNode->dsqlTrueAction = insert;
|
2006-11-26 16:40:10 +01:00
|
|
|
|
2008-08-16 17:42:38 +02:00
|
|
|
// build the temporary vars / UPDATE / IF nodes
|
2008-08-15 18:32:42 +02:00
|
|
|
dsql_nod* list = MAKE_node(nod_list, 3);
|
|
|
|
list->nod_arg[0] = MAKE_list(varStack);
|
|
|
|
list->nod_arg[0]->nod_flags |= NOD_SIMPLE_LIST;
|
|
|
|
list->nod_arg[1] = update;
|
2010-02-13 21:29:29 +01:00
|
|
|
list->nod_arg[2] = MAKE_node(nod_class_stmtnode, 1);
|
2010-09-17 05:15:32 +02:00
|
|
|
list->nod_arg[2]->nod_arg[0] = (dsql_nod*) ifNode;
|
2006-11-26 16:40:10 +01:00
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
// if RETURNING is present, type is already DsqlCompiledStatement::TYPE_EXEC_PROCEDURE
|
2006-11-26 16:40:10 +01:00
|
|
|
if (!input->nod_arg[e_upi_return])
|
2009-12-23 01:57:08 +01:00
|
|
|
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT);
|
2006-11-26 16:40:10 +01:00
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_variable
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Resolve a variable name to an available variable.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2010-11-02 18:05:01 +01:00
|
|
|
static dsql_nod* pass1_variable(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2011-01-31 01:13:15 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
// CVC: I commented this variable and its usage because it wasn't useful for
|
|
|
|
// anything. I didn't delete it in case it's an implementation in progress
|
|
|
|
// by someone.
|
|
|
|
//SSHORT position;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-11-02 18:05:01 +01:00
|
|
|
const dsql_str* var_name = NULL;
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (input->nod_type == nod_field_name)
|
|
|
|
{
|
2008-01-16 07:52:43 +01:00
|
|
|
if (input->nod_arg[e_fln_context])
|
|
|
|
{
|
2009-12-20 23:42:43 +01:00
|
|
|
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_TRIGGER) // triggers only
|
2009-12-20 22:01:10 +01:00
|
|
|
return pass1_field(dsqlScratch, input, false, NULL);
|
2008-01-16 07:52:43 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
PASS1_field_unknown(NULL, NULL, input);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2010-11-02 18:05:01 +01:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
var_name = (dsql_str*) input->nod_arg[e_fln_name];
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
2003-11-10 10:16:38 +01:00
|
|
|
var_name = (dsql_str*) input->nod_arg[e_vrn_name];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
DEV_BLKCHK(var_name, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-31 01:13:15 +01:00
|
|
|
dsql_var* variable = dsqlScratch->resolveVariable(var_name);
|
2010-11-02 18:05:01 +01:00
|
|
|
|
2011-01-31 01:13:15 +01:00
|
|
|
if (variable)
|
2010-11-02 18:05:01 +01:00
|
|
|
{
|
2011-01-31 01:13:15 +01:00
|
|
|
VariableNode* varNode = FB_NEW(*tdbb->getDefaultPool()) VariableNode(*tdbb->getDefaultPool());
|
|
|
|
varNode->dsqlVar = variable;
|
|
|
|
|
2010-11-02 18:05:01 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(varNode);
|
|
|
|
return node;
|
|
|
|
}
|
2004-01-16 11:43:21 +01:00
|
|
|
|
2008-04-09 15:47:15 +02:00
|
|
|
// field unresolved
|
|
|
|
// CVC: That's all [the fix], folks!
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2008-04-09 15:47:15 +02:00
|
|
|
if (var_name)
|
2010-11-14 18:25:48 +01:00
|
|
|
PASS1_field_unknown(NULL, var_name->str_data, input);
|
2008-04-09 15:47:15 +02:00
|
|
|
else
|
2010-11-14 18:25:48 +01:00
|
|
|
PASS1_field_unknown(NULL, NULL, input);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Post an item to a map for a context.
|
|
|
|
dsql_nod* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node, dsql_ctx* context,
|
|
|
|
dsql_nod* partitionNode, dsql_nod* orderNode)
|
|
|
|
{
|
|
|
|
dsql_nod* nod = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
nod->nod_arg[0] = reinterpret_cast<dsql_nod*>(node);
|
|
|
|
return PASS1_post_map(dsqlScratch, nod, context, partitionNode, orderNode);
|
|
|
|
}
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
// Post an item to a map for a context.
|
2010-02-13 21:29:29 +01:00
|
|
|
dsql_nod* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, dsql_nod* node, dsql_ctx* context,
|
2010-01-23 04:02:53 +01:00
|
|
|
dsql_nod* partitionNode, dsql_nod* orderNode)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
PartitionMap* partitionMap = NULL;
|
2010-01-18 22:37:47 +01:00
|
|
|
dsql_map* map = NULL;
|
2010-01-21 19:33:18 +01:00
|
|
|
|
|
|
|
if (dsqlScratch->processingWindow)
|
|
|
|
{
|
2010-01-23 04:02:53 +01:00
|
|
|
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, orderNode);
|
2010-01-21 19:33:18 +01:00
|
|
|
map = partitionMap->map;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
map = context->ctx_map;
|
|
|
|
|
2011-01-28 18:22:44 +01:00
|
|
|
USHORT count = 0;
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
while (map)
|
2008-05-21 18:02:25 +02:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (PASS1_node_match(node, map->map_node, false))
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2010-01-21 19:33:18 +01:00
|
|
|
|
2010-01-18 22:37:47 +01:00
|
|
|
++count;
|
2010-01-21 19:33:18 +01:00
|
|
|
map = map->map_next;
|
2008-05-21 18:02:25 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-05-21 18:02:25 +02:00
|
|
|
if (!map)
|
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
dsql_map** next = partitionMap ? &partitionMap->map : &context->ctx_map;
|
2010-01-18 22:37:47 +01:00
|
|
|
|
|
|
|
if (*next)
|
|
|
|
{
|
|
|
|
while (*(next = &(*next)->map_next))
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
map = *next = FB_NEW(*tdbb->getDefaultPool()) dsql_map;
|
2011-01-28 18:22:44 +01:00
|
|
|
map->map_position = count;
|
2003-08-30 18:49:15 +02:00
|
|
|
map->map_node = node;
|
2010-01-21 19:33:18 +01:00
|
|
|
map->map_partition = partitionMap;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-11-01 14:45:52 +01:00
|
|
|
MAKE_desc(dsqlScratch, &node->nod_desc, node);
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
DsqlMapNode* mapNode = FB_NEW(*tdbb->getDefaultPool()) DsqlMapNode(*tdbb->getDefaultPool(),
|
|
|
|
context, map);
|
|
|
|
|
|
|
|
dsql_nod* new_node = MAKE_node(nod_class_exprnode, 1);
|
|
|
|
new_node->nod_arg[0] = reinterpret_cast<dsql_nod*>(mapNode);
|
2001-05-23 15:26:42 +02:00
|
|
|
new_node->nod_desc = node->nod_desc;
|
|
|
|
|
|
|
|
return new_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
remap_streams_to_parent_context
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief For each relation in the list, flag the relation's context
|
|
|
|
as having a parent context. Be sure to handle joins
|
|
|
|
(Bug 6674).
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param input
|
|
|
|
@param parent_context
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static void remap_streams_to_parent_context( dsql_nod* input, dsql_ctx* parent_context)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(parent_context, dsql_type_ctx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_list:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2009-03-13 16:30:33 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
|
2003-11-18 08:58:35 +01:00
|
|
|
remap_streams_to_parent_context(*ptr, parent_context);
|
2009-03-13 16:26:50 +01:00
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2011-01-09 22:58:56 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
ProcedureSourceNode* procNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
RelationSourceNode* relNode;
|
|
|
|
RseNode* rseNode;
|
2011-01-22 21:40:04 +01:00
|
|
|
UnionSourceNode* unionNode;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
if ((procNode = ExprNode::as<ProcedureSourceNode>(input)))
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(procNode->dsqlContext, dsql_type_ctx);
|
|
|
|
procNode->dsqlContext->ctx_parent = parent_context;
|
|
|
|
}
|
|
|
|
else if ((relNode = ExprNode::as<RelationSourceNode>(input)))
|
2011-01-09 22:58:56 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(relNode->dsqlContext, dsql_type_ctx);
|
|
|
|
relNode->dsqlContext->ctx_parent = parent_context;
|
|
|
|
}
|
|
|
|
else if ((rseNode = ExprNode::as<RseNode>(input)))
|
|
|
|
remap_streams_to_parent_context(rseNode->dsqlStreams, parent_context);
|
2011-01-22 21:40:04 +01:00
|
|
|
else if ((unionNode = ExprNode::as<UnionSourceNode>(input)))
|
|
|
|
{
|
|
|
|
dsql_nod** rse = unionNode->dsqlClauses->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = rse + unionNode->dsqlClauses->nod_count; rse != end; ++rse)
|
|
|
|
remap_streams_to_parent_context(ExprNode::as<RseNode>(*rse)->dsqlStreams, parent_context);
|
|
|
|
}
|
2011-01-09 22:58:56 +01:00
|
|
|
else if (!ExprNode::as<AggregateSourceNode>(input))
|
|
|
|
{
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
resolve_context
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Attempt to resolve field against context.
|
|
|
|
Return first field in context if successful,
|
2003-02-15 04:01:51 +01:00
|
|
|
NULL if not.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-02-15 04:01:51 +01:00
|
|
|
@param name
|
|
|
|
@param qualifier
|
|
|
|
@param context
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_fld* resolve_context( DsqlCompilerScratch* dsqlScratch, const dsql_str* qualifier,
|
2010-08-02 04:22:26 +02:00
|
|
|
dsql_ctx* context, bool resolveByAlias)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
// CVC: Warning: the second param, "name" was is not used anymore and
|
|
|
|
// therefore it was removed. Thus, the local variable "table_name"
|
|
|
|
// is being stripped here to avoid mismatches due to trailing blanks.
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(qualifier, dsql_type_str);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-20 23:42:43 +01:00
|
|
|
if ((dsqlScratch->flags & DsqlCompilerScratch::FLAG_RETURNING_INTO) &&
|
|
|
|
(context->ctx_flags & CTX_returning))
|
|
|
|
{
|
2007-05-13 03:37:54 +02:00
|
|
|
return NULL;
|
2009-12-20 23:42:43 +01:00
|
|
|
}
|
2007-05-13 03:37:54 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_rel* relation = context->ctx_relation;
|
|
|
|
dsql_prc* procedure = context->ctx_procedure;
|
2003-08-15 02:02:18 +02:00
|
|
|
if (!relation && !procedure) {
|
2001-05-23 15:26:42 +02:00
|
|
|
return NULL;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-04-09 15:47:15 +02:00
|
|
|
// if there is no qualifier, then we cannot match against
|
|
|
|
// a context of a different scoping level
|
|
|
|
// AB: Yes we can, but the scope level where the field is has priority.
|
2009-12-20 22:01:10 +01:00
|
|
|
// if (!qualifier && context->ctx_scope_level != dsqlScratch->scopeLevel) {
|
2004-01-09 03:23:46 +01:00
|
|
|
// return NULL;
|
|
|
|
// }
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-05 02:20:14 +01:00
|
|
|
// AB: If this context is a system generated context as in NEW/OLD inside
|
|
|
|
// triggers, the qualifier by the field is mandatory. While we can't
|
|
|
|
// fall back from a higher scope-level to the NEW/OLD contexts without
|
2005-10-25 22:42:29 +02:00
|
|
|
// the qualifier present.
|
|
|
|
// An exception is a check-constraint that is allowed to reference fields
|
|
|
|
// without the qualifier.
|
2010-08-02 04:22:26 +02:00
|
|
|
if (!dsqlScratch->checkConstraintTrigger && (context->ctx_flags & CTX_system) && !qualifier) {
|
2005-10-25 22:42:29 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-01-16 07:52:43 +01:00
|
|
|
const TEXT* table_name = NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
if (context->ctx_internal_alias.hasData() && resolveByAlias)
|
|
|
|
table_name = context->ctx_internal_alias.c_str();
|
|
|
|
|
2008-04-09 15:47:15 +02:00
|
|
|
// AB: For a check constraint we should ignore the alias if the alias
|
|
|
|
// contains the "NEW" alias. This is because it is possible
|
|
|
|
// to reference a field by the complete table-name as alias
|
|
|
|
// (see EMPLOYEE table in examples for a example).
|
2010-08-02 04:22:26 +02:00
|
|
|
if (dsqlScratch->checkConstraintTrigger && table_name)
|
2004-11-03 00:07:09 +01:00
|
|
|
{
|
2004-06-30 00:15:10 +02:00
|
|
|
// If a qualifier is present and it's equal to the alias then we've already the right table-name
|
2009-01-07 10:30:57 +01:00
|
|
|
if (!(qualifier && !strcmp(qualifier->str_data, table_name)))
|
2004-11-03 00:07:09 +01:00
|
|
|
{
|
2011-01-30 01:25:46 +01:00
|
|
|
if (strcmp(table_name, NEW_CONTEXT) == 0)
|
2004-06-30 00:15:10 +02:00
|
|
|
table_name = NULL;
|
2011-01-30 01:25:46 +01:00
|
|
|
else if (strcmp(table_name, OLD_CONTEXT) == 0)
|
2004-12-02 19:57:01 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
// Only use the OLD context if it is explicit used. That means the
|
2004-12-02 19:57:01 +01:00
|
|
|
// qualifer should hold the "OLD" alias.
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-01-10 19:04:40 +01:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 01:25:46 +01:00
|
|
|
|
|
|
|
if (!table_name)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2008-01-16 07:52:43 +01:00
|
|
|
if (relation)
|
|
|
|
table_name = relation->rel_name.c_str();
|
|
|
|
else
|
2009-10-21 02:42:38 +02:00
|
|
|
table_name = procedure->prc_name.identifier.c_str();
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
// If a context qualifier is present, make sure this is the proper context
|
2011-01-30 01:25:46 +01:00
|
|
|
if (qualifier && strcmp(qualifier->str_data, table_name) != 0)
|
2003-10-05 08:37:26 +02:00
|
|
|
return NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:07 +02:00
|
|
|
// Lookup field in relation or procedure
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-30 01:25:46 +01:00
|
|
|
return relation ? relation->rel_fields : procedure->prc_outputs;
|
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-02-13 21:29:29 +01:00
|
|
|
PASS1_set_parameter_type
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Setup the datatype of a parameter.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param in_node
|
|
|
|
@param node
|
|
|
|
@param force_varchar
|
|
|
|
|
|
|
|
**/
|
2010-02-13 21:29:29 +01:00
|
|
|
bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, dsql_nod* in_node,
|
2008-07-03 09:12:36 +02:00
|
|
|
dsql_nod* node, bool force_varchar)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2008-03-01 20:32:16 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(in_node, dsql_type_nod);
|
2002-04-04 18:41:41 +02:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-02-06 19:13:12 +01:00
|
|
|
if (!in_node || in_node->nod_type != nod_class_exprnode)
|
2003-11-07 15:10:16 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-02-06 19:13:12 +01:00
|
|
|
ValueExprNode* exprNode = reinterpret_cast<ValueExprNode*>(in_node->nod_arg[0]);
|
|
|
|
if (exprNode->kind == DmlNode::KIND_VALUE)
|
|
|
|
return exprNode->setParameterType(dsqlScratch, node, force_varchar);
|
|
|
|
else
|
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
set_parameters_name
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Setup parameter parameters name.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param list_node
|
|
|
|
@param rel_node
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static void set_parameters_name( dsql_nod* list_node, const dsql_nod* rel_node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(list_node, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(rel_node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
const dsql_ctx* context = ExprNode::as<RelationSourceNode>(rel_node)->dsqlContext;
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_rel* relation = context->ctx_relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-04-09 15:47:15 +02:00
|
|
|
dsql_nod** ptr = list_node->nod_arg;
|
2009-01-07 10:30:57 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list_node->nod_count; ptr < end; ptr++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
if ((*ptr)->nod_type == nod_assign)
|
2009-01-07 10:30:57 +01:00
|
|
|
set_parameter_name((*ptr)->nod_arg[e_asgn_value], (*ptr)->nod_arg[e_asgn_field], relation);
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(FALSE);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
set_parameter_name
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@brief Setup parameter parameter name.
|
|
|
|
This function was added as a part of array data type
|
|
|
|
support for InterClient. It is called when either
|
2009-12-22 01:08:49 +01:00
|
|
|
"insert" or "update" statements are parsed. If the
|
|
|
|
statements have input parameters, than the parameter
|
2009-12-20 22:01:10 +01:00
|
|
|
is assigned the name of the field it is being inserted
|
|
|
|
(or updated). The same goes to the name of a relation.
|
|
|
|
The names are assigned to the parameter only if the
|
|
|
|
field is of array data type.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param par_node
|
|
|
|
@param fld_node
|
|
|
|
@param relation
|
|
|
|
|
|
|
|
**/
|
2009-01-07 10:30:57 +01:00
|
|
|
static void set_parameter_name( dsql_nod* par_node, const dsql_nod* fld_node, const dsql_rel* relation)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(par_node, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(fld_node, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(relation, dsql_type_dsql_rel);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-30 10:03:53 +01:00
|
|
|
if (!par_node)
|
|
|
|
return;
|
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
const FieldNode* fieldNode = ExprNode::as<FieldNode>(fld_node);
|
|
|
|
fb_assert(fieldNode); // Could it be something else ???
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-12-19 22:42:32 +01:00
|
|
|
if (fieldNode->dsqlDesc.dsc_dtype != dtype_array)
|
2001-05-23 15:26:42 +02:00
|
|
|
return;
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (par_node->nod_type)
|
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
case nod_class_exprnode:
|
|
|
|
{
|
|
|
|
ExprNode* exprNode = reinterpret_cast<ExprNode*>(par_node->nod_arg[0]);
|
|
|
|
|
|
|
|
switch (exprNode->type)
|
|
|
|
{
|
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
|
|
|
case ExprNode::TYPE_ARITHMETIC:
|
2010-02-13 21:29:29 +01:00
|
|
|
case ExprNode::TYPE_CONCATENATE:
|
2010-10-16 20:53:25 +02:00
|
|
|
case ExprNode::TYPE_EXTRACT:
|
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
|
|
|
case ExprNode::TYPE_NEGATE:
|
2010-10-09 20:39:45 +02:00
|
|
|
case ExprNode::TYPE_STR_CASE:
|
2010-10-16 20:17:00 +02:00
|
|
|
case ExprNode::TYPE_STR_LEN:
|
2010-10-16 19:42:04 +02:00
|
|
|
case ExprNode::TYPE_SUBSTRING:
|
2010-02-21 02:47:54 +01:00
|
|
|
case ExprNode::TYPE_SUBSTRING_SIMILAR:
|
2010-10-09 20:39:45 +02:00
|
|
|
case ExprNode::TYPE_TRIM:
|
2010-02-13 21:29:29 +01:00
|
|
|
for (dsql_nod*** i = exprNode->dsqlChildNodes.begin();
|
|
|
|
i != exprNode->dsqlChildNodes.end(); ++i)
|
|
|
|
{
|
|
|
|
set_parameter_name(**i, fld_node, relation);
|
|
|
|
}
|
|
|
|
break;
|
2010-10-09 03:57:37 +02:00
|
|
|
|
|
|
|
case ExprNode::TYPE_PARAMETER:
|
|
|
|
{
|
|
|
|
ParameterNode* paramNode = exprNode->as<ParameterNode>();
|
|
|
|
dsql_par* parameter = paramNode->dsqlParameter;
|
2010-12-19 22:42:32 +01:00
|
|
|
parameter->par_name = fieldNode->dsqlField->fld_name.c_str();
|
2010-10-09 03:57:37 +02:00
|
|
|
parameter->par_rel_name = relation->rel_name.c_str();
|
|
|
|
break;
|
|
|
|
}
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2003-05-02 17:20:17 +02:00
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_savepoint
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
@brief Add savepoint pair of nodes
|
2009-12-22 01:08:49 +01:00
|
|
|
to statement having error handlers.
|
2003-05-02 17:20:17 +02:00
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
@param dsqlScratch
|
2003-05-02 17:20:17 +02:00
|
|
|
@param node
|
|
|
|
|
|
|
|
**/
|
2009-12-20 22:01:10 +01:00
|
|
|
static dsql_nod* pass1_savepoint(const DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
|
2005-08-22 12:12:13 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
if (dsqlScratch->errorHandlers)
|
2009-11-19 10:37:10 +01:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* temp = MAKE_node(nod_list, 3);
|
2003-05-02 17:20:17 +02:00
|
|
|
temp->nod_arg[0] = MAKE_node(nod_start_savepoint, 0);
|
|
|
|
temp->nod_arg[1] = node;
|
|
|
|
temp->nod_arg[2] = MAKE_node(nod_end_savepoint, 0);
|
|
|
|
node = temp;
|
|
|
|
}
|
2005-08-22 12:12:13 +02:00
|
|
|
|
2003-05-02 17:20:17 +02:00
|
|
|
return node;
|
|
|
|
}
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
|
2007-04-29 21:04:26 +02:00
|
|
|
// Returns false for hidden fields and true for non-hidden.
|
|
|
|
// For non-hidden, change "node" if the field is part of an
|
|
|
|
// implicit join.
|
2008-01-16 07:52:43 +01:00
|
|
|
bool dsql_ctx::getImplicitJoinField(const Firebird::MetaName& name, dsql_nod*& node)
|
2007-04-29 21:04:26 +02:00
|
|
|
{
|
|
|
|
ImplicitJoin* impJoin;
|
|
|
|
if (ctx_imp_join.get(name, impJoin))
|
|
|
|
{
|
|
|
|
if (impJoin->visibleInContext == this)
|
|
|
|
{
|
|
|
|
node = impJoin->value;
|
|
|
|
return true;
|
|
|
|
}
|
2008-01-16 07:52:43 +01:00
|
|
|
|
|
|
|
return false;
|
2007-04-29 21:04:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
// Returns (creating, if necessary) the PartitionMap of a given partition (that may be NULL).
|
2010-01-23 04:02:53 +01:00
|
|
|
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, dsql_nod* partitionNode,
|
|
|
|
dsql_nod* orderNode)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
PartitionMap* partitionMap = NULL;
|
2010-01-18 22:37:47 +01:00
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
for (Array<PartitionMap*>::iterator i = ctx_win_maps.begin();
|
|
|
|
!partitionMap && i != ctx_win_maps.end();
|
|
|
|
++i)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
if (PASS1_node_match((*i)->partition, partitionNode, false) &&
|
|
|
|
PASS1_node_match((*i)->order, orderNode, false))
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-21 19:33:18 +01:00
|
|
|
partitionMap = *i;
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
if (!partitionMap)
|
2010-01-18 22:37:47 +01:00
|
|
|
{
|
2010-01-23 04:02:53 +01:00
|
|
|
partitionMap = FB_NEW(*tdbb->getDefaultPool()) PartitionMap(partitionNode, orderNode);
|
2010-01-21 19:33:18 +01:00
|
|
|
ctx_win_maps.add(partitionMap);
|
|
|
|
partitionMap->context = dsqlScratch->contextNumber++;
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
|
2010-01-21 19:33:18 +01:00
|
|
|
return partitionMap;
|
2010-01-18 22:37:47 +01:00
|
|
|
}
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
#ifdef DSQL_DEBUG
|
|
|
|
|
2008-08-27 14:20:47 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
static void trace_line(const char* message, ...)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
char buffer[1024];
|
|
|
|
va_list params;
|
|
|
|
va_start(params, message);
|
|
|
|
VSNPRINTF(buffer, sizeof(buffer), message, params);
|
|
|
|
va_end(params);
|
|
|
|
buffer[sizeof(buffer) - 1] = 0;
|
|
|
|
gds__trace_raw(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
DSQL_pretty
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
@brief Pretty print a node tree.
|
|
|
|
|
|
|
|
|
|
|
|
@param node
|
|
|
|
@param column
|
|
|
|
|
|
|
|
**/
|
|
|
|
void DSQL_pretty(const dsql_nod* node, int column)
|
|
|
|
{
|
|
|
|
TEXT buffer[1024];
|
|
|
|
|
|
|
|
TEXT* p = buffer;
|
|
|
|
p += sprintf(p, "%p ", (void*) node);
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (column)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
USHORT l = column * 3;
|
|
|
|
do {
|
|
|
|
*p++ = ' ';
|
|
|
|
} while (--l);
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = 0;
|
|
|
|
|
2009-11-19 10:37:10 +01:00
|
|
|
if (!node)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%s *** null ***\n", buffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (node->getType())
|
|
|
|
{
|
2009-01-10 12:11:56 +01:00
|
|
|
case dsql_type_str:
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%sSTRING: \"%s\"\n", buffer, ((dsql_str*) node)->str_data);
|
|
|
|
return;
|
|
|
|
|
2009-01-10 12:11:56 +01:00
|
|
|
case dsql_type_fld:
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%sFIELD: %s\n", buffer, ((dsql_fld*) node)->fld_name.c_str());
|
|
|
|
return;
|
|
|
|
|
2009-01-10 12:11:56 +01:00
|
|
|
case dsql_type_sym:
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%sSYMBOL: %s\n", buffer, ((dsql_sym*) node)->sym_string);
|
|
|
|
return;
|
|
|
|
|
2009-01-10 12:11:56 +01:00
|
|
|
case dsql_type_nod:
|
2008-02-28 14:48:16 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
trace_line("%sUNKNOWN BLOCK TYPE\n", buffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEXT s[64];
|
|
|
|
const dsql_str* string;
|
2008-05-21 17:22:22 +02:00
|
|
|
Firebird::string verb;
|
2008-02-28 14:48:16 +01:00
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
2008-05-19 15:47:48 +02:00
|
|
|
const dsql_nod* const* end = ptr + node->nod_count;
|
|
|
|
Firebird::Array<dsql_nod*> subNodes;
|
2008-02-28 14:48:16 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_alias:
|
|
|
|
verb = "alias";
|
|
|
|
break;
|
|
|
|
case nod_all:
|
|
|
|
verb = "all";
|
|
|
|
break;
|
|
|
|
case nod_array:
|
|
|
|
verb = "array element";
|
|
|
|
break;
|
|
|
|
case nod_assign:
|
|
|
|
verb = "assign";
|
|
|
|
break;
|
|
|
|
case nod_collate:
|
|
|
|
verb = "collate";
|
|
|
|
break;
|
|
|
|
case nod_cursor:
|
|
|
|
verb = "cursor";
|
|
|
|
break;
|
|
|
|
case nod_def_database:
|
|
|
|
verb = "define database";
|
|
|
|
break;
|
|
|
|
case nod_def_field:
|
|
|
|
verb = "define field";
|
|
|
|
break;
|
|
|
|
case nod_def_filter:
|
|
|
|
verb = "define filter";
|
|
|
|
break;
|
|
|
|
case nod_def_index:
|
|
|
|
verb = "define index";
|
|
|
|
break;
|
|
|
|
case nod_delete:
|
|
|
|
verb = "delete";
|
|
|
|
break;
|
|
|
|
case nod_del_field:
|
|
|
|
verb = "delete field";
|
|
|
|
break;
|
|
|
|
case nod_del_filter:
|
|
|
|
verb = "delete filter";
|
|
|
|
break;
|
|
|
|
case nod_del_generator:
|
|
|
|
verb = "delete generator";
|
|
|
|
break;
|
|
|
|
case nod_del_index:
|
|
|
|
verb = "delete index";
|
|
|
|
break;
|
|
|
|
case nod_erase:
|
|
|
|
verb = "erase";
|
|
|
|
break;
|
|
|
|
case nod_execute:
|
|
|
|
verb = "execute";
|
|
|
|
break;
|
|
|
|
case nod_flag:
|
|
|
|
verb = "flag";
|
|
|
|
break;
|
|
|
|
case nod_foreign:
|
|
|
|
verb = "foreign key";
|
|
|
|
break;
|
|
|
|
case nod_get_segment:
|
|
|
|
verb = "get segment";
|
|
|
|
break;
|
|
|
|
case nod_grant:
|
|
|
|
verb = "grant";
|
|
|
|
break;
|
|
|
|
case nod_insert:
|
|
|
|
verb = "insert";
|
|
|
|
break;
|
|
|
|
case nod_list:
|
|
|
|
verb = "list";
|
|
|
|
break;
|
|
|
|
case nod_modify:
|
|
|
|
verb = "modify";
|
|
|
|
break;
|
|
|
|
case nod_mod_database:
|
|
|
|
verb = "modify database";
|
|
|
|
break;
|
|
|
|
case nod_mod_field:
|
|
|
|
verb = "modify field";
|
|
|
|
break;
|
|
|
|
case nod_order:
|
|
|
|
verb = "order";
|
|
|
|
break;
|
|
|
|
case nod_primary:
|
|
|
|
verb = "primary key";
|
|
|
|
break;
|
|
|
|
case nod_procedure_name:
|
|
|
|
verb = "procedure name";
|
|
|
|
break;
|
2009-10-24 21:07:35 +02:00
|
|
|
case nod_package_name:
|
|
|
|
verb = "package name";
|
|
|
|
break;
|
2009-12-24 11:42:32 +01:00
|
|
|
case nod_function_name:
|
|
|
|
verb = "function_name";
|
|
|
|
break;
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_put_segment:
|
|
|
|
verb = "put segment";
|
|
|
|
break;
|
|
|
|
case nod_revoke:
|
|
|
|
verb = "revoke";
|
|
|
|
break;
|
|
|
|
case nod_select:
|
|
|
|
verb = "select";
|
|
|
|
break;
|
|
|
|
case nod_select_expr:
|
|
|
|
verb = "select expr";
|
|
|
|
break;
|
|
|
|
case nod_store:
|
|
|
|
verb = "store";
|
|
|
|
break;
|
|
|
|
case nod_update:
|
|
|
|
verb = "update";
|
|
|
|
break;
|
|
|
|
case nod_unique:
|
|
|
|
verb = "unique";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_rows:
|
|
|
|
verb = "rows";
|
|
|
|
break;
|
2009-04-19 12:06:07 +02:00
|
|
|
// IOL: missing node types
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_on_error:
|
|
|
|
verb = "on error";
|
|
|
|
break;
|
|
|
|
case nod_block:
|
|
|
|
verb = "block";
|
|
|
|
break;
|
|
|
|
case nod_default:
|
|
|
|
verb = "default";
|
|
|
|
break;
|
|
|
|
case nod_plan_expr:
|
|
|
|
verb = "plan";
|
|
|
|
break;
|
|
|
|
case nod_index:
|
|
|
|
verb = "index";
|
|
|
|
break;
|
|
|
|
case nod_index_order:
|
|
|
|
verb = "order";
|
|
|
|
break;
|
|
|
|
case nod_plan_item:
|
|
|
|
verb = "item";
|
|
|
|
break;
|
|
|
|
case nod_natural:
|
|
|
|
verb = "natural";
|
|
|
|
break;
|
2008-12-05 02:20:14 +01:00
|
|
|
// SKIDDER: some more missing node types
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_commit:
|
|
|
|
verb = "commit";
|
|
|
|
break;
|
|
|
|
case nod_rollback:
|
|
|
|
verb = "rollback";
|
|
|
|
break;
|
|
|
|
case nod_trans:
|
|
|
|
verb = "trans";
|
|
|
|
break;
|
|
|
|
case nod_def_default:
|
|
|
|
verb = "def_default";
|
|
|
|
break;
|
|
|
|
case nod_del_default:
|
|
|
|
verb = "del_default";
|
|
|
|
break;
|
|
|
|
case nod_def_domain:
|
|
|
|
verb = "def_domain";
|
|
|
|
break;
|
|
|
|
case nod_def_constraint:
|
|
|
|
verb = "def_constraint";
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
case nod_def_trigger_msg:
|
|
|
|
verb = "def_trigger_msg";
|
|
|
|
break;
|
|
|
|
case nod_mod_trigger_msg:
|
|
|
|
verb = "mod_trigger_msg";
|
|
|
|
break;
|
|
|
|
case nod_del_trigger_msg:
|
|
|
|
verb = "del_trigger_msg";
|
|
|
|
break;
|
|
|
|
*/
|
|
|
|
case nod_def_shadow:
|
|
|
|
verb = "def_shadow";
|
|
|
|
break;
|
|
|
|
case nod_del_shadow:
|
|
|
|
verb = "del_shadow";
|
|
|
|
break;
|
|
|
|
case nod_def_udf:
|
|
|
|
verb = "def_udf";
|
|
|
|
break;
|
|
|
|
case nod_del_udf:
|
|
|
|
verb = "del_udf";
|
|
|
|
break;
|
|
|
|
case nod_rel_constraint:
|
|
|
|
verb = "rel_constraint";
|
|
|
|
break;
|
|
|
|
case nod_delete_rel_constraint:
|
|
|
|
verb = "delete_rel_constraint";
|
|
|
|
break;
|
|
|
|
case nod_references:
|
|
|
|
verb = "references";
|
|
|
|
break;
|
|
|
|
case nod_proc_obj:
|
|
|
|
verb = "proc_obj";
|
|
|
|
break;
|
2009-12-21 18:23:07 +01:00
|
|
|
case nod_func_obj:
|
|
|
|
verb = "func_obj";
|
|
|
|
break;
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_trig_obj:
|
|
|
|
verb = "trig_obj";
|
|
|
|
break;
|
|
|
|
case nod_view_obj:
|
|
|
|
verb = "view_obj";
|
|
|
|
break;
|
|
|
|
case nod_erase_current:
|
|
|
|
verb = "erase_current";
|
|
|
|
break;
|
|
|
|
case nod_modify_current:
|
|
|
|
verb = "modify_current";
|
|
|
|
break;
|
|
|
|
case nod_sqlcode:
|
|
|
|
verb = "sqlcode";
|
|
|
|
break;
|
|
|
|
case nod_gdscode:
|
|
|
|
verb = "gdscode";
|
|
|
|
break;
|
|
|
|
case nod_exception:
|
|
|
|
verb = "exception";
|
|
|
|
break;
|
|
|
|
case nod_start_savepoint:
|
|
|
|
verb = "start_savepoint";
|
|
|
|
break;
|
|
|
|
case nod_end_savepoint:
|
|
|
|
verb = "end_savepoint";
|
|
|
|
break;
|
|
|
|
case nod_user_group:
|
|
|
|
verb = "user_group";
|
|
|
|
break;
|
|
|
|
case nod_access:
|
|
|
|
verb = "access";
|
|
|
|
break;
|
|
|
|
case nod_wait:
|
|
|
|
verb = "wait";
|
|
|
|
break;
|
|
|
|
case nod_isolation:
|
|
|
|
verb = "isolation";
|
|
|
|
break;
|
|
|
|
case nod_version:
|
|
|
|
verb = "version";
|
|
|
|
break;
|
|
|
|
case nod_table_lock:
|
|
|
|
verb = "table_lock";
|
|
|
|
break;
|
|
|
|
case nod_lock_mode:
|
|
|
|
verb = "lock_mode";
|
|
|
|
break;
|
|
|
|
case nod_reserve:
|
|
|
|
verb = "reserve";
|
|
|
|
break;
|
|
|
|
case nod_retain:
|
|
|
|
verb = "retain";
|
|
|
|
break;
|
|
|
|
case nod_page_size:
|
|
|
|
verb = "page_size";
|
|
|
|
break;
|
|
|
|
case nod_file_length:
|
|
|
|
verb = "file_length";
|
|
|
|
break;
|
|
|
|
case nod_file_desc:
|
|
|
|
verb = "file_desc";
|
|
|
|
break;
|
|
|
|
case nod_dfl_charset:
|
|
|
|
verb = "dfl_charset";
|
|
|
|
break;
|
|
|
|
case nod_password:
|
|
|
|
verb = "password";
|
|
|
|
break;
|
|
|
|
case nod_lc_ctype:
|
|
|
|
verb = "lc_ctype";
|
|
|
|
break;
|
|
|
|
case nod_udf_return_value:
|
|
|
|
verb = "udf_return_value";
|
|
|
|
break;
|
|
|
|
case nod_def_computed:
|
|
|
|
verb = "def_computed";
|
|
|
|
break;
|
|
|
|
case nod_set_generator:
|
|
|
|
verb = "set_generator";
|
|
|
|
break;
|
|
|
|
case nod_set_generator2:
|
|
|
|
verb = "set_generator2";
|
|
|
|
break;
|
|
|
|
case nod_mod_index:
|
|
|
|
verb = "mod_index";
|
|
|
|
break;
|
|
|
|
case nod_idx_active:
|
|
|
|
verb = "idx_active";
|
|
|
|
break;
|
|
|
|
case nod_idx_inactive:
|
|
|
|
verb = "idx_inactive";
|
|
|
|
break;
|
|
|
|
case nod_restrict:
|
|
|
|
verb = "restrict";
|
|
|
|
break;
|
|
|
|
case nod_cascade:
|
|
|
|
verb = "cascade";
|
|
|
|
break;
|
|
|
|
case nod_set_statistics:
|
|
|
|
verb = "set_statistics";
|
|
|
|
break;
|
|
|
|
case nod_ref_upd_del:
|
|
|
|
verb = "ref_upd_del";
|
|
|
|
break;
|
|
|
|
case nod_ref_trig_action:
|
|
|
|
verb = "ref_trig_action";
|
|
|
|
break;
|
|
|
|
case nod_def_role:
|
|
|
|
verb = "def_role";
|
|
|
|
break;
|
|
|
|
case nod_role_name:
|
|
|
|
verb = "role_name";
|
|
|
|
break;
|
|
|
|
case nod_grant_admin:
|
|
|
|
verb = "grant_admin";
|
|
|
|
break;
|
|
|
|
case nod_del_role:
|
|
|
|
verb = "del_role";
|
|
|
|
break;
|
|
|
|
case nod_mod_field_name:
|
|
|
|
verb = "mod_field_name";
|
|
|
|
break;
|
|
|
|
case nod_mod_field_type:
|
|
|
|
verb = "mod_field_type";
|
|
|
|
break;
|
|
|
|
case nod_mod_field_pos:
|
|
|
|
verb = "mod_field_pos";
|
|
|
|
break;
|
|
|
|
case nod_udf_param:
|
|
|
|
verb = "udf_param";
|
|
|
|
break;
|
|
|
|
case nod_for_update:
|
|
|
|
verb = "for_update";
|
|
|
|
break;
|
|
|
|
case nod_difference_file:
|
|
|
|
verb = "difference_file";
|
|
|
|
break;
|
|
|
|
case nod_drop_difference:
|
|
|
|
verb = "drop_difference";
|
|
|
|
break;
|
|
|
|
case nod_begin_backup:
|
|
|
|
verb = "begin_backup";
|
|
|
|
break;
|
|
|
|
case nod_end_backup:
|
|
|
|
verb = "end_backup";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_while:
|
|
|
|
verb = "while";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_label:
|
|
|
|
verb = "label";
|
|
|
|
DSQL_pretty(node->nod_arg[e_label_name], column + 1);
|
|
|
|
trace_line("%s number %d\n", buffer,
|
|
|
|
(int)(IPTR)node->nod_arg[e_label_number]);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case nod_field_name:
|
|
|
|
trace_line("%sfield name: \"", buffer);
|
|
|
|
string = (dsql_str*) node->nod_arg[e_fln_context];
|
|
|
|
if (string)
|
|
|
|
trace_line("%s.", string->str_data);
|
|
|
|
string = (dsql_str*) node->nod_arg[e_fln_name];
|
|
|
|
if (string != 0) {
|
|
|
|
trace_line("%s\"\n", string->str_data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
trace_line("%s\"\n", "*");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case nod_var_name:
|
|
|
|
trace_line("%svariable name: \"", buffer);
|
|
|
|
string = (dsql_str*) node->nod_arg[e_vrn_name];
|
|
|
|
trace_line("%s\"\n", string->str_data);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case nod_mod_udf:
|
|
|
|
verb = "mod_udf";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_returning:
|
|
|
|
verb = "returning";
|
|
|
|
break;
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_tra_misc:
|
|
|
|
verb = "tra_misc";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_lock_timeout:
|
|
|
|
verb = "lock_timeout"; // maybe show the timeout value?
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_src_info:
|
2008-05-01 09:26:53 +02:00
|
|
|
{
|
|
|
|
const int line = (int) (IPTR) (*ptr++);
|
|
|
|
const int col = (int) (IPTR) (*ptr++);
|
|
|
|
sprintf(s, "src_info: line %d, col %d", line, col);
|
|
|
|
}
|
2008-04-30 22:03:40 +02:00
|
|
|
verb = s;
|
2008-02-28 14:48:16 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_with:
|
|
|
|
verb = "with";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_update_or_insert:
|
|
|
|
verb = "update_or_insert";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_merge:
|
|
|
|
verb = "merge";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_merge_when:
|
|
|
|
verb = "merge_when";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_merge_update:
|
|
|
|
verb = "merge_update";
|
|
|
|
break;
|
|
|
|
|
2010-03-23 17:06:16 +01:00
|
|
|
case nod_merge_delete:
|
|
|
|
verb = "merge_delete";
|
|
|
|
break;
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
case nod_merge_insert:
|
|
|
|
verb = "merge_insert";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_mod_role:
|
|
|
|
verb = "mod_role";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_add_user:
|
|
|
|
verb = "add_user";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_mod_user:
|
|
|
|
verb = "mod_user";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_del_user:
|
|
|
|
verb = "del_user";
|
|
|
|
break;
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
case nod_class_exprnode:
|
|
|
|
case nod_class_stmtnode:
|
2008-05-21 17:22:22 +02:00
|
|
|
reinterpret_cast<Node*>(node->nod_arg[0])->print(verb, subNodes);
|
|
|
|
ptr = subNodes.begin();
|
|
|
|
end = subNodes.end();
|
|
|
|
break;
|
2008-04-15 04:18:38 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
case nod_mod_field_null_flag:
|
2009-12-24 11:42:32 +01:00
|
|
|
verb = "mod_field_null_flag";
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
default:
|
|
|
|
sprintf(s, "unknown type %d", node->nod_type);
|
|
|
|
verb = s;
|
|
|
|
}
|
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
if (node->nod_desc.dsc_dtype)
|
|
|
|
{
|
2008-02-28 14:48:16 +01:00
|
|
|
trace_line("%s%s (%d,%d,%p)\n",
|
2008-05-21 17:22:22 +02:00
|
|
|
buffer, verb.c_str(),
|
2009-01-07 10:30:57 +01:00
|
|
|
node->nod_desc.dsc_dtype, node->nod_desc.dsc_length, node->nod_desc.dsc_address);
|
2008-02-28 14:48:16 +01:00
|
|
|
}
|
2008-05-21 17:22:22 +02:00
|
|
|
else
|
|
|
|
trace_line("%s%s\n", buffer, verb.c_str());
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
++column;
|
|
|
|
|
2008-05-21 17:22:22 +02:00
|
|
|
while (ptr < end)
|
2008-02-28 14:48:16 +01:00
|
|
|
DSQL_pretty(*ptr++, column);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|