/* * PROGRAM: Dynamic SQL runtime support * MODULE: pass1.cpp * DESCRIPTION: First-pass compiler for statement trees. * * 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): ______________________________________. * * 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 syntax. * * 2001.09.10 John Bellardo: fixed gen_rse to attribute skip/first nodes to the parent_rse * if present instead of the child rse. BUG #451798 * * 2001.09.26 Claudio Valderrama: ambiguous field names are rejected from now. * * 2001.10.01 Claudio Valderrama: check constraints are allowed to have ambiguous field * names because they use OLD and NEW as aliases of the same table. However, if the * check constraint has an embedded ambiguous SELECT statement, it won't be detected. * The code should be revisited if check constraints' before delete triggers are used * for whatever reason. Currently they are never generated. The code can be improved * to not report errors for fields between NEW and OLD contexts but complain otherwise. * * 2001.10.05 Neil McCalden: validate udf and parameters when comparing select list and * group by list, to detect invalid SQL statements when grouping by UDFs. * * 2001.10.23 Ann Harrison: allow reasonable checking of ambiguous names in unions. * Remembering, later, that LLS_PUSH expects an object, not an LLS block. Also * stuck in the code for handling variables in pass1 - it apparently doesn't happen * because the code returned an uninitialized pointer. * * 2001.11.17 Neil McCalden: Add aggregate_in_list procedure to handle cases * where select statement has aggregate as a parameter to a udf which does * not have to be in a group by clause. * * 2001.11.21 Claudio Valderrama: don't try to detect ambiguity in pass1_field() * if the field or output procedure parameter has been fully qualified!!! * * 2001.11.27 Ann Harrison: Redo the amiguity checking so as to give better * error messages, return warnings for dialect 1, and simplify. * * 2001.11.28 Claudio Valderrama: allow udf arguments to be query parameters. * Honor the code in the parser that already accepts those parameters. * This closes SF Bug# 409769. * * 2001.11.29 Claudio Valderrama: make the nice new ambiguity checking code do the * right thing instead of crashing the engine and restore fix from 2001.11.21. * * 2001.12.21 Claudio Valderrama: Fix SF Bug #494832 - pass1_variable() should work * with def_proc, mod_proc, redef_proc, def_trig and mod_trig node types. * * 2002.07.30 Arno Brinkman: Added pass1_coalesce, pass1_simple_case, pass1_searched_case * and pass1_put_args_on_stack * * 2002.08.04 Arno Brinkman: Added ignore_cast as parameter to node_match, * Changed invalid_reference procedure for allow EXTRACT, SUBSTRING, CASE, * COALESCE and NULLIF functions in GROUP BY and as select_items. * Removed aggregate_in_list procedure. * * 2002.08.07 Dmitry Yemanov: Disabled BREAK statement in triggers * * 2002.08.10 Dmitry Yemanov: ALTER VIEW * * 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced * exception handling in SPs/triggers, * implemented ROWS_AFFECTED system variable * * 2002.09.29 Arno Brinkman: Adding more checking for aggregate functions * and adding support for 'linking' from sub-selects to aggregate functions * which are in an lower level. * Modified functions pass1_field, pass1_rse, copy_field, pass1_sort. * Functions pass1_found_aggregate and pass1_found_field added. * * 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks * * 2002.10.25 Dmitry Yemanov: Re-allowed plans in triggers * * 2002.10.29 Nickolay Samofatov: Added support for savepoints * * 2002.12.03 Dmitry Yemanov: Implemented ORDER BY clause in subqueries * * 2002.12.18 Dmitry Yemanov: Fixed bug with BREAK and partially implemented * SQL-compliant labels and LEAVE statement * * 2003.01.11 Arno Brinkman: Reworked a lot of functions for bringing back backwards compatibilty * with sub-selects and aggregates. * * 2003.01.14 Dmitry Yemanov: Fixed bug with cursors in triggers * * 2003.01.15 Dmitry Yemanov: Added support for parametrized events * * 2003.04.05 Dmitry Yemanov: Changed logic of ORDER BY with collations * (because of the parser change) * * 2003.08.14 Arno Brinkman: Added derived table support. * * 2003.08.16 Arno Brinkman: Changed ambiguous column name checking. * * 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL. * * 2004.01.16 Vlad Horsun: added support for default parameters and * EXECUTE BLOCK statement * * Adriano dos Santos Fernandes * */ #include "firebird.h" #include #include #include "../jrd/ibase.h" #include "../dsql/dsql.h" #include "../dsql/node.h" #include "../dsql/Nodes.h" #include "../jrd/intl.h" #include "../jrd/blr.h" #include "../jrd/jrd.h" #include "../jrd/constants.h" #include "../jrd/intl_classes.h" #include "../dsql/DdlNodes.h" #include "../dsql/StmtNodes.h" #include "../dsql/ddl_proto.h" #include "../dsql/errd_proto.h" #include "../dsql/hsh_proto.h" #include "../dsql/make_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/misc_func.h" #include "../dsql/pass1_proto.h" #include "../dsql/utld_proto.h" #include "../jrd/dsc_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/jrd_proto.h" #include "../jrd/thread_proto.h" #include "../jrd/why_proto.h" #include "../jrd/SysFunction.h" #include "../common/classes/array.h" #include "../common/classes/auto.h" #include "../common/utils_proto.h" #include "../common/config/config.h" #include "../common/StatusArg.h" using namespace Jrd; using namespace Dsql; using namespace Firebird; #ifdef DEV_BUILD static void DSQL_pretty(const dsql_nod*, int); #endif static dsql_nod* ambiguity_check(DsqlCompilerScratch*, dsql_nod*, const dsql_str*, const DsqlContextStack&); static void assign_fld_dtype_from_dsc(dsql_fld*, const dsc*); static dsql_nod* compose(dsql_nod*, dsql_nod*, NOD_TYPE); static dsql_nod* explode_fields(dsql_rel*); static dsql_nod* explode_outputs(DsqlCompilerScratch*, const dsql_prc*); static void field_appears_once(const dsql_nod*, const dsql_nod*, const bool, const char*); static void field_duplication(const TEXT*, const TEXT*, const dsql_nod*, const char*); static void field_unknown(const TEXT*, const TEXT*, const dsql_nod*); static dsql_par* find_dbkey(const dsql_req*, const dsql_nod*); static dsql_par* find_record_version(const dsql_req*, const dsql_nod*); static dsql_ctx* get_context(const dsql_nod* node); static bool node_match(const dsql_nod*, const dsql_nod*, bool); static dsql_nod* nullify_returning(DsqlCompilerScratch*, dsql_nod* input); static dsql_nod* pass1_alias_list(DsqlCompilerScratch*, dsql_nod*); static dsql_ctx* pass1_alias(DsqlCompilerScratch*, DsqlContextStack&, dsql_str*); static dsql_str* pass1_alias_concat(const dsql_str*, const dsql_str*); static dsql_nod* pass1_any(DsqlCompilerScratch*, dsql_nod*, NOD_TYPE); static dsql_rel* pass1_base_table(DsqlCompilerScratch*, const dsql_rel*, const dsql_str*); static void pass1_blob(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_coalesce(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_collate(DsqlCompilerScratch*, dsql_nod*, const dsql_str*); static dsql_nod* pass1_constant(DsqlCompilerScratch*, dsql_nod*); static dsql_ctx* pass1_cursor_context(DsqlCompilerScratch*, const dsql_nod*, const dsql_nod*); static dsql_nod* pass1_cursor_name(DsqlCompilerScratch*, const dsql_str*, USHORT, bool); static dsql_nod* pass1_cursor_reference(DsqlCompilerScratch*, const dsql_nod*, dsql_nod*); static dsql_nod* pass1_dbkey(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_delete(DsqlCompilerScratch*, dsql_nod*); static void pass1_expand_contexts(DsqlContextStack& contexts, dsql_ctx* context); static dsql_nod* pass1_derived_table(DsqlCompilerScratch*, dsql_nod*, dsql_str*); static dsql_nod* pass1_expand_select_list(DsqlCompilerScratch*, dsql_nod*, dsql_nod*); static void pass1_expand_select_node(DsqlCompilerScratch*, dsql_nod*, DsqlNodStack&, bool); static dsql_nod* pass1_field(DsqlCompilerScratch*, dsql_nod*, const bool, dsql_nod*); static dsql_nod* pass1_group_by_list(DsqlCompilerScratch*, dsql_nod*, dsql_nod*); static dsql_nod* pass1_hidden_variable(DsqlCompilerScratch* dsqlScratch, dsql_nod*& expr); static dsql_nod* pass1_insert(DsqlCompilerScratch*, dsql_nod*, bool); static dsql_nod* pass1_join(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_label(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_lookup_alias(DsqlCompilerScratch*, const dsql_str*, dsql_nod*, bool); static dsql_nod* pass1_make_derived_field(DsqlCompilerScratch*, thread_db*, dsql_nod*); static dsql_nod* pass1_merge(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_not(DsqlCompilerScratch*, const dsql_nod*, bool); static void pass1_put_args_on_stack(DsqlCompilerScratch*, dsql_nod*, DsqlNodStack&); 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_searched_case(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_sel_list(DsqlCompilerScratch*, dsql_nod*, bool); static dsql_nod* pass1_simple_case(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_sort(DsqlCompilerScratch*, dsql_nod*, dsql_nod*); static dsql_nod* pass1_sys_function(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_udf(DsqlCompilerScratch*, dsql_nod*); static void pass1_udf_args(DsqlCompilerScratch*, dsql_nod*, dsql_udf*, USHORT&, DsqlNodStack&); static dsql_nod* pass1_union(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT); static void pass1_union_auto_cast(dsql_nod*, const dsc&, SSHORT, bool in_select_list = false); 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*); static dsql_nod* post_map(DsqlCompilerScratch*, dsql_nod*, dsql_ctx*, dsql_nod*, dsql_nod*); static void remap_streams_to_parent_context(dsql_nod*, dsql_ctx*); static dsql_fld* resolve_context(DsqlCompilerScratch*, const dsql_str*, dsql_ctx*, bool, bool); static dsql_nod* resolve_using_field(DsqlCompilerScratch* dsqlScratch, dsql_str* name, DsqlNodStack& stack, const dsql_nod* flawedNode, const TEXT* side, dsql_ctx*& ctx); static bool set_parameter_type(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, bool); static void set_parameters_name(dsql_nod*, const dsql_nod*); static void set_parameter_name(dsql_nod*, const dsql_nod*, const dsql_rel*); static dsql_nod* pass1_savepoint(const DsqlCompilerScratch*, dsql_nod*); static bool pass1_relproc_is_recursive(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_join_is_recursive(DsqlCompilerScratch*, dsql_nod*&); static dsql_nod* pass1_rse_is_recursive(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* pass1_recursive_cte(DsqlCompilerScratch*, dsql_nod*); static dsql_nod* process_returning(DsqlCompilerScratch*, dsql_nod*); // CVC: more global variables??? static const dsql_str* global_temp_collation_name = NULL; const char* const DB_KEY_STRING = "DB_KEY"; // NTX: pseudo field name const int MAX_MEMBER_LIST = 1500; // Maximum members in "IN" list. // For eg. SELECT * FROM T WHERE // F IN (1, 2, 3, ...) // // Bug 10061, bsriram - 19-Apr-1999 const int LIKE_PARAM_LEN = 30; // CVC: This is a guess for the length of the // parameter for LIKE and others, when the // original dtype isn't string and force_varchar // is true. enum FieldMatchType { FIELD_MATCH_TYPE_EQUAL = 0, FIELD_MATCH_TYPE_LOWER = 1, FIELD_MATCH_TYPE_LOWER_EQUAL = 2 /*** , FIELD_MATCH_TYPE_HIGHER = 3, FIELD_MATCH_TYPE_HIGHER_EQUAL = 4 ***/ }; namespace { template class NodeVisitor { public: NodeVisitor(bool aAssertOnOthers, bool aReturnOnOthers) : assertOnOthers(aAssertOnOthers), returnOnOthers(aReturnOnOthers) { } public: bool visitChildren(T node) { bool ret = false; if (!node) return ret; switch (node->nod_type) { case nod_constant: case nod_parameter: case nod_variable: case nod_null: case nod_current_date: case nod_current_time: case nod_current_timestamp: case nod_user_name: case nod_current_role: case nod_dom_value: break; case nod_alias: ret |= visit(node->nod_arg[e_alias_value]); break; case nod_hidden_var: ret |= visit(node->nod_arg[e_hidden_var_expr]); break; case nod_order: ret |= visit(node->nod_arg[e_order_field]); break; case nod_gen_id: case nod_gen_id2: case nod_cast: case nod_sys_function: if (node->nod_count == 2) ret |= visit(node->nod_arg[1]); break; case nod_udf: if (node->nod_count == 3) ret |= visit(node->nod_arg[2]); break; case nod_or: case nod_and: case nod_not: case nod_equiv: case nod_eql: case nod_neq: case nod_gtr: case nod_geq: case nod_lss: case nod_leq: case nod_between: case nod_like: case nod_containing: case nod_similar: case nod_starting: case nod_missing: case nod_add: case nod_add2: case nod_concatenate: case nod_divide: case nod_divide2: case nod_multiply: case nod_multiply2: case nod_negate: case nod_substr: case nod_subtract: case nod_subtract2: case nod_trim: case nod_upcase: case nod_lowcase: case nod_extract: case nod_strlen: case nod_simple_case: case nod_searched_case: case nod_any: case nod_ansi_any: case nod_ansi_all: case nod_list: case nod_join: case nod_join_inner: case nod_join_left: case nod_join_right: case nod_join_full: { T2 ptr = node->nod_arg; for (T2 end = ptr + node->nod_count; ptr < end; ++ptr) ret |= visit(*ptr); break; } default: if (assertOnOthers) fb_assert(false); return returnOnOthers; } return ret; } virtual bool visit(T node) = 0; private: const bool assertOnOthers; const bool returnOnOthers; }; // Check for an aggregate expression in an expression. It could be buried in an expression // tree and therefore call itselfs again. The level parameters (currentLevel & deepestLevel) // are used to see how deep we are with passing sub-queries (= scope_level). class AggregateFinder : public NodeVisitor { public: AggregateFinder(const DsqlCompilerScratch* aDsqlScratch, bool aWindow) : NodeVisitor(false, false), dsqlScratch(aDsqlScratch), window(aWindow), currentLevel(dsqlScratch->scopeLevel), deepestLevel(0), ignoreSubSelects(false) { } static bool find(const DsqlCompilerScratch* dsqlScratch, bool window, const dsql_nod* node) { return AggregateFinder(dsqlScratch, window).visit(node); } virtual bool visit(const dsql_nod* node); public: const DsqlCompilerScratch* const dsqlScratch; bool window; USHORT currentLevel; USHORT deepestLevel; bool ignoreSubSelects; }; // Check the fields inside an aggregate and check if the field scope_level meets the specified // conditions. // // The SQL 2008 standard says: // ::= // WHERE // Syntax Rules // 1) If a directly contained in the is a // , then the shall be contained in a // or