2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: Dynamic SQL runtime support
|
2003-09-29 14:43:14 +02:00
|
|
|
* MODULE: pass1.cpp
|
2001-05-23 15:26:42 +02:00
|
|
|
* DESCRIPTION: First-pass compiler for request 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): ______________________________________.
|
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
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* and pass1_put_args_on_stack
|
2002-08-11 10:04:54 +02:00
|
|
|
*
|
|
|
|
* 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 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
|
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"
|
|
|
|
#include "../jrd/thd.h"
|
|
|
|
#include "../jrd/intl.h"
|
|
|
|
#include "../jrd/blr.h"
|
2005-05-28 00:45:31 +02:00
|
|
|
#include "../jrd/constants.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/alld_proto.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"
|
2002-09-28 16:04:35 +02:00
|
|
|
#include "../dsql/misc_func.h"
|
2005-06-10 04:03:08 +02:00
|
|
|
#include "../dsql/pass1_proto.h"
|
|
|
|
#include "../dsql/utld_proto.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/dsc_proto.h"
|
2005-05-28 00:45:31 +02:00
|
|
|
#include "../jrd/thread_proto.h"
|
2005-08-21 17:52:30 +02:00
|
|
|
#include "../jrd/why_proto.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"
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
#ifdef DEV_BUILD
|
2003-11-07 14:25:53 +01:00
|
|
|
void DSQL_pretty(const dsql_nod*, int);
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
|
|
|
|
class CStrCmp
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static int greaterThan(const char* s1, const char* s2)
|
|
|
|
{
|
|
|
|
return strcmp(s1, s2) > 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
typedef Firebird::SortedArray<const char*,
|
|
|
|
Firebird::EmptyStorage<const char*>, const char*,
|
2004-01-28 08:50:41 +01:00
|
|
|
Firebird::DefaultKeyValue<const char*>,
|
|
|
|
CStrCmp>
|
|
|
|
StrArray;
|
|
|
|
|
|
|
|
|
2003-11-05 10:02:33 +01:00
|
|
|
static bool aggregate_found(const dsql_req*, const dsql_nod*);
|
|
|
|
static bool aggregate_found2(const dsql_req*, const dsql_nod*, USHORT*,
|
|
|
|
USHORT*, bool);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* ambiguity_check(dsql_req*, dsql_nod*, const dsql_str*,
|
2004-04-18 16:22:27 +02:00
|
|
|
const DsqlContextStack&);
|
2003-11-10 10:16:38 +01:00
|
|
|
static void assign_fld_dtype_from_dsc(dsql_fld*, const dsc*);
|
2004-01-28 08:50:41 +01:00
|
|
|
static void check_unique_fields_names(StrArray& names, const dsql_nod* fields);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* compose(dsql_nod*, dsql_nod*, NOD_TYPE);
|
2006-09-03 03:09:23 +02:00
|
|
|
static dsql_nod* explode_fields(dsql_rel*);
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* explode_outputs(dsql_req*, const dsql_prc*);
|
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*);
|
2005-10-06 08:08:10 +02:00
|
|
|
static void field_unknown(const TEXT*, const TEXT*, const dsql_nod*);
|
2004-02-02 12:02:12 +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);
|
2005-05-28 00:45:31 +02:00
|
|
|
static bool invalid_reference(const dsql_ctx*, const dsql_nod*,
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_nod*, bool, bool);
|
|
|
|
static bool node_match(const dsql_nod*, const dsql_nod*, bool);
|
|
|
|
static dsql_nod* pass1_alias_list(dsql_req*, dsql_nod*);
|
2004-05-09 07:48:33 +02:00
|
|
|
static dsql_ctx* pass1_alias(dsql_req*, DsqlContextStack&, dsql_str*);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_str* pass1_alias_concat(const dsql_str*, const dsql_str*);
|
|
|
|
static dsql_nod* pass1_any(dsql_req*, dsql_nod*, NOD_TYPE);
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_rel* pass1_base_table(dsql_req*, const dsql_rel*, const dsql_str*);
|
2003-11-10 10:16:38 +01:00
|
|
|
static void pass1_blob(dsql_req*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_coalesce(dsql_req*, dsql_nod*, bool);
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* pass1_collate(dsql_req*, dsql_nod*, const dsql_str*);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_constant(dsql_req*, dsql_nod*);
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_ctx* pass1_cursor_context(dsql_req*, const dsql_nod*, const dsql_nod*);
|
2004-11-17 15:50:33 +01:00
|
|
|
static dsql_nod* pass1_cursor_name(dsql_req*, const dsql_str*, USHORT, bool);
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* pass1_cursor_reference(dsql_req*, const dsql_nod*, dsql_nod*);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_dbkey(dsql_req*, dsql_nod*);
|
2005-06-13 14:45:42 +02:00
|
|
|
static dsql_nod* pass1_delete(dsql_req*, dsql_nod*, bool);
|
2006-08-01 22:37:58 +02:00
|
|
|
static dsql_nod* pass1_derived_table(dsql_req*, dsql_nod*, bool, dsql_str*);
|
2005-02-10 22:14:52 +01:00
|
|
|
static dsql_nod* pass1_expand_select_list(dsql_req*, dsql_nod*, dsql_nod*);
|
|
|
|
static void pass1_expand_select_node(dsql_req*, dsql_nod*, DsqlNodStack&);
|
|
|
|
static dsql_nod* pass1_field(dsql_req*, dsql_nod*, const bool, dsql_nod*);
|
2003-11-05 10:02:33 +01:00
|
|
|
static bool pass1_found_aggregate(const dsql_nod*, USHORT, USHORT, bool);
|
|
|
|
static bool pass1_found_field(const dsql_nod*, USHORT, USHORT, bool*);
|
2005-01-06 14:14:38 +01:00
|
|
|
static bool pass1_found_sub_select(const dsql_nod*);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_group_by_list(dsql_req*, dsql_nod*, dsql_nod*);
|
2005-06-13 14:45:42 +02:00
|
|
|
static dsql_nod* pass1_insert(dsql_req*, dsql_nod*, bool);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_join(dsql_req*, dsql_nod*, bool);
|
|
|
|
static dsql_nod* pass1_label(dsql_req*, dsql_nod*);
|
2005-02-10 22:14:52 +01:00
|
|
|
static dsql_nod* pass1_lookup_alias(dsql_req*, const dsql_str*, dsql_nod*);
|
2004-02-02 12:02:12 +01:00
|
|
|
static dsql_nod* pass1_make_derived_field(dsql_req*, tsql*, dsql_nod*);
|
2006-09-14 04:05:32 +02:00
|
|
|
static dsql_nod* pass1_merge(dsql_req*, dsql_nod*, bool);
|
2005-05-02 11:47:27 +02:00
|
|
|
static dsql_nod* pass1_not(dsql_req*, const dsql_nod*, bool, bool);
|
2004-04-18 16:22:27 +02:00
|
|
|
static void pass1_put_args_on_stack(dsql_req*, dsql_nod*, DsqlNodStack&, bool);
|
2006-08-01 22:37:58 +02:00
|
|
|
static dsql_nod* pass1_recursive_cte(dsql_req* request, dsql_nod* input, bool proc_flag);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_relation(dsql_req*, dsql_nod*);
|
2006-09-03 03:09:23 +02:00
|
|
|
static dsql_nod* pass1_replace(dsql_req*, dsql_nod*, bool);
|
2005-06-13 14:45:42 +02:00
|
|
|
static dsql_nod* pass1_returning(dsql_req*, const dsql_nod*, bool);
|
2004-10-13 20:37:53 +02:00
|
|
|
static dsql_nod* pass1_rse(dsql_req*, dsql_nod*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
2006-08-01 22:37:58 +02:00
|
|
|
static dsql_nod* pass1_rse_impl(dsql_req*, dsql_nod*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_searched_case(dsql_req*, dsql_nod*, bool);
|
|
|
|
static dsql_nod* pass1_sel_list(dsql_req*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_simple_case(dsql_req*, dsql_nod*, bool);
|
|
|
|
static dsql_nod* pass1_sort(dsql_req*, dsql_nod*, dsql_nod*);
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* pass1_udf(dsql_req*, dsql_nod*, bool);
|
2004-05-24 14:09:12 +02:00
|
|
|
static void pass1_udf_args(dsql_req*, dsql_nod*, dsql_udf*, USHORT&, DsqlNodStack&,
|
2003-11-10 10:16:38 +01:00
|
|
|
bool);
|
2004-10-13 20:37:53 +02:00
|
|
|
static dsql_nod* pass1_union(dsql_req*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
2003-11-10 10:16:38 +01:00
|
|
|
static void pass1_union_auto_cast(dsql_nod*, const dsc&, SSHORT,
|
2003-10-05 08:37:26 +02:00
|
|
|
bool in_select_list = false);
|
2005-06-13 14:45:42 +02:00
|
|
|
static dsql_nod* pass1_update(dsql_req*, dsql_nod*, bool);
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_variable(dsql_req*, dsql_nod*);
|
|
|
|
static dsql_nod* post_map(dsql_nod*, dsql_ctx*);
|
|
|
|
static dsql_nod* remap_field(dsql_req*, dsql_nod*, dsql_ctx*, USHORT);
|
|
|
|
static dsql_nod* remap_fields(dsql_req*, dsql_nod*, dsql_ctx*);
|
|
|
|
static void remap_streams_to_parent_context(dsql_nod*, dsql_ctx*);
|
2004-01-10 19:04:40 +01:00
|
|
|
static dsql_fld* resolve_context(dsql_req*, const dsql_str*, dsql_ctx*, bool);
|
2004-01-28 08:50:41 +01:00
|
|
|
static dsql_nod* resolve_variable_name(const dsql_nod* var_nodes, const dsql_str* var_name);
|
2005-05-28 00:45:31 +02:00
|
|
|
static bool set_parameter_type(dsql_req*, dsql_nod*, dsql_nod*, 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*);
|
|
|
|
static dsql_nod* pass1_savepoint(const dsql_req*, dsql_nod*);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
static bool pass1_relproc_is_recursive(dsql_req*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_join_is_recursive(dsql_req*, dsql_nod*&);
|
|
|
|
static bool pass1_rse_is_recursive(dsql_req*, dsql_nod*);
|
|
|
|
static dsql_nod* pass1_recursive_cte(dsql_req*, dsql_nod*, bool);
|
2006-09-03 03:09:23 +02:00
|
|
|
static dsql_nod* process_returning(dsql_req*, dsql_nod*, bool);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
// CVC: more global variables???
|
2003-11-18 08:58:35 +01:00
|
|
|
static const dsql_str* global_temp_collation_name = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-28 02:36:28 +02:00
|
|
|
const char* 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, ...)
|
2005-05-28 00:45:31 +02:00
|
|
|
//
|
2003-09-28 02:36:28 +02:00
|
|
|
// Bug 10061, bsriram - 19-Apr-1999
|
2003-11-18 08:58:35 +01:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2003-09-28 02:36:28 +02:00
|
|
|
enum field_match_val {
|
|
|
|
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
|
|
|
|
};
|
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_make_context
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Generate a context for a request.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param relation_node
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_ctx* PASS1_make_context(dsql_req* request, dsql_nod* relation_node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(relation_node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_rel* relation = NULL;
|
|
|
|
dsql_prc* procedure = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* figure out whether this is a relation or a procedure
|
|
|
|
and give an error if it is neither */
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_str* relation_name;
|
2004-02-02 12:02:12 +01:00
|
|
|
switch (relation_node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_rel_proc_name:
|
2003-11-10 10:16:38 +01:00
|
|
|
relation_name = (dsql_str*) relation_node->nod_arg[e_rpn_name];
|
2004-02-02 12:02:12 +01:00
|
|
|
break;
|
|
|
|
case nod_derived_table:
|
2003-11-10 10:16:38 +01:00
|
|
|
relation_name = (dsql_str*) relation_node->nod_arg[e_derived_table_alias];
|
2004-02-02 12:02:12 +01:00
|
|
|
break;
|
|
|
|
default:
|
2003-11-10 10:16:38 +01:00
|
|
|
relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name];
|
2004-02-02 12:02:12 +01:00
|
|
|
break;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// CVC: Let's skim the context, too.
|
|
|
|
if (relation_name && relation_name->str_data) {
|
2004-09-26 03:49:52 +02:00
|
|
|
fb_utils::exact_name((TEXT*) relation_name->str_data);
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(relation_name, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-08-02 03:22:11 +02:00
|
|
|
dsql_nod* cte = NULL;
|
2003-08-15 02:02:18 +02:00
|
|
|
if (relation_node->nod_type == nod_derived_table) {
|
2003-08-18 23:37:47 +02:00
|
|
|
// No processing needed here for derived tables.
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else if ((relation_node->nod_type == nod_rel_proc_name) &&
|
2001-05-23 15:26:42 +02:00
|
|
|
relation_node->nod_arg[e_rpn_inputs])
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
procedure = METD_get_procedure(request, relation_name);
|
|
|
|
if (!procedure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2005-10-06 08:08:10 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 204,
|
|
|
|
isc_arg_gds, isc_dsql_procedure_err,
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, relation_name->str_data,
|
|
|
|
isc_arg_gds, isc_dsql_line_col_error,
|
|
|
|
isc_arg_number, (int) relation_node->nod_line,
|
|
|
|
isc_arg_number, (int) relation_node->nod_column,
|
|
|
|
0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
2006-08-02 03:22:11 +02:00
|
|
|
else if ((cte = request->findCTE(relation_name)))
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
|
|
|
relation_node = cte;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
relation = METD_get_relation(request, relation_name);
|
|
|
|
if (!relation && (relation_node->nod_type == nod_rel_proc_name))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
procedure = METD_get_procedure(request, relation_name);
|
|
|
|
}
|
|
|
|
if (!relation && !procedure)
|
|
|
|
{
|
2005-10-06 08:08:10 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 204,
|
|
|
|
isc_arg_gds, isc_dsql_relation_err,
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, relation_name->str_data,
|
|
|
|
isc_arg_gds, isc_dsql_line_col_error,
|
|
|
|
isc_arg_number, (int) relation_node->nod_line,
|
|
|
|
isc_arg_number, (int) relation_node->nod_column,
|
|
|
|
0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-06-29 08:56:51 +02:00
|
|
|
if (procedure && !procedure->prc_out_count) {
|
2005-10-06 08:08:10 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 84,
|
|
|
|
isc_arg_gds, isc_dsql_procedure_use_err,
|
|
|
|
isc_arg_string, relation_name->str_data,
|
|
|
|
isc_arg_gds, isc_dsql_line_col_error,
|
|
|
|
isc_arg_number, (int) relation_node->nod_line,
|
|
|
|
isc_arg_number, (int) relation_node->nod_column,
|
|
|
|
0);
|
2003-11-11 13:19:20 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// Set up context block.
|
2005-05-28 00:45:31 +02:00
|
|
|
dsql_ctx* context = FB_NEW(*tdsql->getDefaultPool())
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_ctx(*tdsql->getDefaultPool());
|
2001-05-23 15:26:42 +02:00
|
|
|
context->ctx_relation = relation;
|
|
|
|
context->ctx_procedure = procedure;
|
|
|
|
context->ctx_request = request;
|
|
|
|
context->ctx_context = request->req_context_number++;
|
|
|
|
context->ctx_scope_level = request->req_scope_level;
|
2003-08-18 23:37:47 +02:00
|
|
|
// When we're in a outer-join part mark context for it.
|
2003-08-15 02:02:18 +02:00
|
|
|
if (request->req_in_outer_join) {
|
|
|
|
context->ctx_flags |= CTX_outer_join;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// find the context alias name, if it exists.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_str* string;
|
2003-08-15 02:02:18 +02:00
|
|
|
if (relation_node->nod_type == nod_rel_proc_name) {
|
2003-11-10 10:16:38 +01:00
|
|
|
string = (dsql_str*) relation_node->nod_arg[e_rpn_alias];
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else if (relation_node->nod_type == nod_derived_table) {
|
2003-11-10 10:16:38 +01:00
|
|
|
string = (dsql_str*) relation_node->nod_arg[e_derived_table_alias];
|
2003-08-15 02:02:18 +02:00
|
|
|
context->ctx_rse = relation_node->nod_arg[e_derived_table_rse];
|
|
|
|
}
|
|
|
|
else {
|
2003-11-10 10:16:38 +01:00
|
|
|
string = (dsql_str*) relation_node->nod_arg[e_rln_alias];
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-25 16:43:28 +02:00
|
|
|
if (string)
|
|
|
|
{
|
|
|
|
context->ctx_internal_alias = (TEXT*) string->str_data;
|
|
|
|
}
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
2003-11-18 08:58:35 +01:00
|
|
|
if (request->req_alias_relation_prefix && !(relation_node->nod_type == nod_derived_table))
|
2005-07-25 16:43:28 +02:00
|
|
|
{
|
2003-08-20 01:47:07 +02:00
|
|
|
if (string) {
|
|
|
|
string = pass1_alias_concat(request->req_alias_relation_prefix, string);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
string = pass1_alias_concat(request->req_alias_relation_prefix, relation_name);
|
|
|
|
}
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (string) {
|
2003-10-05 08:37:26 +02:00
|
|
|
context->ctx_alias = (TEXT*) string->str_data;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// check to make sure the context is not already used at this same
|
2003-08-18 23:37:47 +02:00
|
|
|
// query level (if there are no subqueries, this checks that the
|
|
|
|
// alias is not used twice in the request).
|
2005-05-28 00:45:31 +02:00
|
|
|
for (DsqlContextStack::iterator stack(*request->req_context);
|
2004-05-27 18:26:52 +02:00
|
|
|
stack.hasData(); ++stack)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
const dsql_ctx* conflict = stack.object();
|
2003-08-18 23:37:47 +02:00
|
|
|
|
|
|
|
if (conflict->ctx_scope_level != context->ctx_scope_level) {
|
2001-05-23 15:26:42 +02:00
|
|
|
continue;
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
const TEXT* conflict_name;
|
2003-10-05 08:37:26 +02:00
|
|
|
ISC_STATUS error_code;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (conflict->ctx_alias) {
|
|
|
|
conflict_name = conflict->ctx_alias;
|
2003-11-11 13:19:20 +01:00
|
|
|
error_code = isc_alias_conflict_err;
|
2003-08-18 23:37:47 +02:00
|
|
|
// alias %s conflicts with an alias in the same statement.
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else if (conflict->ctx_procedure) {
|
|
|
|
conflict_name = conflict->ctx_procedure->prc_name;
|
2003-11-11 13:19:20 +01:00
|
|
|
error_code = isc_procedure_conflict_error;
|
2003-08-18 23:37:47 +02:00
|
|
|
// alias %s conflicts with a procedure in the same statement.
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else if (conflict->ctx_relation) {
|
|
|
|
conflict_name = conflict->ctx_relation->rel_name;
|
2003-11-11 13:19:20 +01:00
|
|
|
error_code = isc_relation_conflict_err;
|
2003-08-18 23:37:47 +02:00
|
|
|
// alias %s conflicts with a relation in the same statement.
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-08-18 23:37:47 +02:00
|
|
|
else {
|
2001-05-23 15:26:42 +02:00
|
|
|
continue;
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
if (!strcmp(conflict_name, context->ctx_alias)) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 204,
|
|
|
|
isc_arg_gds, error_code,
|
|
|
|
isc_arg_string, conflict_name, 0);
|
2003-11-10 10:16:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
if (procedure) {
|
2003-10-05 08:37:26 +02:00
|
|
|
USHORT count = 0;
|
2003-08-18 23:37:47 +02:00
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
if (relation_node->nod_arg[e_rpn_inputs])
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
context->ctx_proc_inputs =
|
|
|
|
PASS1_node(request, relation_node->nod_arg[e_rpn_inputs], false);
|
2001-05-23 15:26:42 +02:00
|
|
|
count = context->ctx_proc_inputs->nod_count;
|
|
|
|
}
|
|
|
|
|
2004-11-17 19:27:48 +01:00
|
|
|
if (!(request->req_flags & REQ_procedure))
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
if (count > procedure->prc_in_count ||
|
2004-01-16 11:43:21 +01:00
|
|
|
count < procedure->prc_in_count - procedure->prc_def_count)
|
2003-11-11 13:19:20 +01:00
|
|
|
{
|
2004-01-16 11:43:21 +01:00
|
|
|
ERRD_post(isc_prcmismat, isc_arg_string,
|
|
|
|
relation_name->str_data, 0);
|
2001-12-24 03:51:06 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
if (count)
|
|
|
|
{
|
|
|
|
// Initialize this stack variable, and make it look like a node
|
2006-05-19 17:17:02 +02:00
|
|
|
Firebird::AutoPtr<dsql_nod> desc_node(FB_NEW_RPT(*tdsql->getDefaultPool(), 0) dsql_nod);
|
2002-01-04 12:34:22 +01:00
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
dsql_nod* const* input = context->ctx_proc_inputs->nod_arg;
|
|
|
|
for (dsql_fld* field = procedure->prc_inputs;
|
2004-01-16 11:43:21 +01:00
|
|
|
*input; input++, field = field->fld_next)
|
2001-12-24 03:51:06 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
DEV_BLKCHK(*input, dsql_type_nod);
|
2005-09-03 09:47:32 +02:00
|
|
|
MAKE_desc_from_field(&desc_node->nod_desc, field);
|
2005-05-28 00:45:31 +02:00
|
|
|
// set_parameter_type(request, *input, &desc_node, false);
|
2006-05-19 17:17:02 +02:00
|
|
|
set_parameter_type(request, *input, desc_node, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// push the context onto the request context stack
|
2003-11-11 13:19:20 +01:00
|
|
|
// for matching fields against
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
request->req_context->push(context);
|
2003-11-11 13:19:20 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return context;
|
2003-11-11 13:19:20 +01:00
|
|
|
}
|
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_node
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Compile a parsed request into something more interesting.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* PASS1_node(dsql_req* request, dsql_nod* input, bool proc_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
if (input == NULL) {
|
2001-05-23 15:26:42 +02:00
|
|
|
return NULL;
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* node;
|
|
|
|
dsql_fld* field;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub1;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Dispatch on node type. Fall thru on easy ones
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_alias:
|
|
|
|
node = MAKE_node(input->nod_type, e_alias_count);
|
|
|
|
node->nod_arg[e_alias_value] = sub1 =
|
|
|
|
PASS1_node(request, input->nod_arg[e_alias_value], proc_flag);
|
|
|
|
node->nod_arg[e_alias_alias] = input->nod_arg[e_alias_alias];
|
|
|
|
node->nod_desc = sub1->nod_desc;
|
|
|
|
return node;
|
|
|
|
|
|
|
|
case nod_cast:
|
|
|
|
node = MAKE_node(input->nod_type, e_cast_count);
|
|
|
|
node->nod_arg[e_cast_source] = sub1 =
|
|
|
|
PASS1_node(request, input->nod_arg[e_cast_source], proc_flag);
|
|
|
|
node->nod_arg[e_cast_target] = input->nod_arg[e_cast_target];
|
2003-11-10 10:16:38 +01:00
|
|
|
field = (dsql_fld*) node->nod_arg[e_cast_target];
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2001-05-23 15:26:42 +02:00
|
|
|
DDL_resolve_intl_type(request, field, NULL);
|
|
|
|
MAKE_desc_from_field(&node->nod_desc, field);
|
2005-12-12 18:27:10 +01:00
|
|
|
set_parameter_type(request, node, NULL, false);
|
2005-05-28 00:45:31 +02:00
|
|
|
// If the source is nullable, so is the target
|
|
|
|
MAKE_desc(request, &sub1->nod_desc, sub1, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (sub1->nod_desc.dsc_flags & DSC_nullable)
|
|
|
|
node->nod_desc.dsc_flags |= DSC_nullable;
|
|
|
|
return node;
|
|
|
|
|
2002-08-03 17:27:20 +02:00
|
|
|
case nod_coalesce:
|
|
|
|
return pass1_coalesce(request, input, proc_flag);
|
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
case nod_derived_field:
|
|
|
|
return input;
|
|
|
|
|
2002-08-03 17:27:20 +02:00
|
|
|
case nod_simple_case:
|
|
|
|
return pass1_simple_case(request, input, proc_flag);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2002-08-03 17:27:20 +02:00
|
|
|
case nod_searched_case:
|
|
|
|
return pass1_searched_case(request, input, proc_flag);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_gen_id:
|
|
|
|
case nod_gen_id2:
|
|
|
|
node = MAKE_node(input->nod_type, e_gen_id_count);
|
|
|
|
node->nod_arg[e_gen_id_value] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_gen_id_value], proc_flag);
|
|
|
|
node->nod_arg[e_gen_id_name] = input->nod_arg[e_gen_id_name];
|
|
|
|
return node;
|
|
|
|
|
|
|
|
case nod_collate:
|
2003-11-18 08:58:35 +01:00
|
|
|
global_temp_collation_name = (dsql_str*) input->nod_arg[e_coll_target];
|
2001-05-23 15:26:42 +02:00
|
|
|
sub1 = PASS1_node(request, input->nod_arg[e_coll_source], proc_flag);
|
2003-11-18 08:58:35 +01:00
|
|
|
global_temp_collation_name = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
node =
|
2003-11-10 10:16:38 +01:00
|
|
|
pass1_collate(request, sub1, (dsql_str*) input->nod_arg[e_coll_target]);
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
|
|
|
|
case nod_extract:
|
|
|
|
|
|
|
|
/* Figure out the data type of the sub parameter, and make
|
|
|
|
sure the requested type of information can be extracted */
|
|
|
|
|
|
|
|
sub1 = PASS1_node(request, input->nod_arg[e_extract_value], proc_flag);
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc(request, &sub1->nod_desc, sub1, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
switch (*(SLONG*)input->nod_arg[e_extract_part]->nod_desc.dsc_address)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
case blr_extract_year:
|
|
|
|
case blr_extract_month:
|
|
|
|
case blr_extract_day:
|
|
|
|
case blr_extract_weekday:
|
|
|
|
case blr_extract_yearday:
|
2004-09-04 20:43:11 +02:00
|
|
|
if (sub1->nod_type != nod_null &&
|
|
|
|
sub1->nod_desc.dsc_dtype != dtype_sql_date &&
|
2001-05-23 15:26:42 +02:00
|
|
|
sub1->nod_desc.dsc_dtype != dtype_timestamp)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 105,
|
|
|
|
isc_arg_gds, isc_extract_input_mismatch, 0);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
case blr_extract_hour:
|
|
|
|
case blr_extract_minute:
|
|
|
|
case blr_extract_second:
|
2004-09-04 20:43:11 +02:00
|
|
|
if (sub1->nod_type != nod_null &&
|
|
|
|
sub1->nod_desc.dsc_dtype != dtype_sql_time &&
|
2001-05-23 15:26:42 +02:00
|
|
|
sub1->nod_desc.dsc_dtype != dtype_timestamp)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 105,
|
|
|
|
isc_arg_gds, isc_extract_input_mismatch, 0);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
default:
|
2004-09-04 20:43:11 +02:00
|
|
|
fb_assert(false);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = MAKE_node(input->nod_type, e_extract_count);
|
|
|
|
node->nod_arg[e_extract_part] = input->nod_arg[e_extract_part];
|
|
|
|
node->nod_arg[e_extract_value] = sub1;
|
|
|
|
if (sub1->nod_desc.dsc_flags & DSC_nullable) {
|
|
|
|
node->nod_desc.dsc_flags |= DSC_nullable;
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
|
|
|
node = MAKE_node(input->nod_type, e_strlen_count);
|
|
|
|
node->nod_arg[e_strlen_type] = input->nod_arg[e_strlen_type];
|
|
|
|
node->nod_arg[e_strlen_value] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_strlen_value], proc_flag);
|
|
|
|
if (node->nod_arg[e_strlen_value]->nod_desc.dsc_flags & DSC_nullable)
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_desc.dsc_flags |= DSC_nullable;
|
|
|
|
return node;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_delete:
|
|
|
|
case nod_insert:
|
2006-09-14 04:05:32 +02:00
|
|
|
case nod_merge:
|
2006-09-03 03:09:23 +02:00
|
|
|
case nod_replace:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_order:
|
|
|
|
case nod_select:
|
2006-08-01 22:37:58 +02:00
|
|
|
case nod_with:
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
case nod_derived_table:
|
2006-08-02 03:22:11 +02:00
|
|
|
return pass1_derived_table(request, input, proc_flag, NULL);
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_select_expr:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
if (proc_flag)
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 206,
|
|
|
|
isc_arg_gds, isc_dsql_subselect_err, 0);
|
|
|
|
|
|
|
|
const DsqlContextStack::iterator base(*request->req_context);
|
|
|
|
node = MAKE_node(nod_via, e_via_count);
|
|
|
|
dsql_nod* rse = PASS1_rse(request, input, NULL);
|
|
|
|
node->nod_arg[e_via_rse] = rse;
|
|
|
|
node->nod_arg[e_via_value_1] = rse->nod_arg[e_rse_items]->nod_arg[0];
|
|
|
|
node->nod_arg[e_via_value_2] = MAKE_node(nod_null, (int) 0);
|
|
|
|
// Finish off by cleaning up contexts
|
|
|
|
request->req_context->clear(base);
|
|
|
|
return node;
|
2003-11-10 10:16:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_exists:
|
|
|
|
case nod_singular:
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
const DsqlContextStack::iterator base(*request->req_context);
|
|
|
|
node = MAKE_node(input->nod_type, 1);
|
|
|
|
input = input->nod_arg[0];
|
|
|
|
node->nod_arg[0] = PASS1_rse(request, input, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
// Finish off by cleaning up contexts
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
request->req_context->clear(base);
|
|
|
|
return node;
|
2004-04-18 16:22:27 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_field_name:
|
|
|
|
if (proc_flag)
|
|
|
|
return pass1_variable(request, input);
|
|
|
|
else
|
2005-02-10 22:14:52 +01:00
|
|
|
return pass1_field(request, input, false, NULL);
|
|
|
|
|
|
|
|
case nod_field:
|
|
|
|
// AB: nod_field is an already passed node.
|
|
|
|
// This could be done in expand_select_list.
|
|
|
|
return input;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_array:
|
|
|
|
if (proc_flag)
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_invalid_array, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
2005-02-10 22:14:52 +01:00
|
|
|
return pass1_field(request, input, false, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_variable:
|
2002-06-29 08:56:51 +02:00
|
|
|
node = MAKE_node (input->nod_type, e_var_count);
|
2003-11-10 10:16:38 +01:00
|
|
|
node->nod_arg[0] = input->nod_arg[0];
|
2002-06-29 08:56:51 +02:00
|
|
|
node->nod_desc = input->nod_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
|
|
|
|
case nod_var_name:
|
|
|
|
return pass1_variable(request, input);
|
|
|
|
|
|
|
|
case nod_dbkey:
|
|
|
|
return pass1_dbkey(request, input);
|
|
|
|
|
|
|
|
case nod_relation_name:
|
|
|
|
case nod_rel_proc_name:
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2006-08-02 03:22:11 +02:00
|
|
|
dsql_str* rel_name;
|
|
|
|
dsql_str* rel_alias;
|
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
if (input->nod_type == nod_rel_proc_name) {
|
|
|
|
rel_name = (dsql_str*) input->nod_arg[e_rpn_name];
|
|
|
|
rel_alias = (dsql_str*) input->nod_arg[e_rpn_alias];
|
|
|
|
}
|
2006-08-02 03:22:11 +02:00
|
|
|
else { // nod_relation_name
|
2006-08-01 22:37:58 +02:00
|
|
|
rel_name = (dsql_str*) input->nod_arg[e_rln_name];
|
|
|
|
rel_alias = (dsql_str*) input->nod_arg[e_rln_alias];
|
|
|
|
}
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
if (!rel_alias) {
|
|
|
|
rel_alias = rel_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* cte = request->findCTE(rel_name);
|
|
|
|
if (cte)
|
|
|
|
{
|
|
|
|
if ((request->req_flags & REQ_CTE_recursive) &&
|
|
|
|
request->req_curr_ctes.hasData() &&
|
|
|
|
(request->req_curr_ctes.object() == cte))
|
|
|
|
{
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "Recursive CTE member can refer itself only in FROM clause",
|
|
|
|
0);
|
|
|
|
}
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
for (DsqlNodStack::iterator stack(request->req_curr_ctes); stack.hasData(); ++stack)
|
|
|
|
{
|
|
|
|
dsql_nod* cte1 = stack.object();
|
|
|
|
if (cte1 == cte) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "Cyclic CTEs dependencies found",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
}
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
dsql_nod* const select_expr = cte->nod_arg[e_derived_table_rse];
|
|
|
|
dsql_nod* const query = select_expr->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_derived_table_alias];
|
|
|
|
if (!isRecursive) {
|
|
|
|
cte->nod_arg[e_derived_table_alias] = (dsql_nod*) rel_alias;
|
|
|
|
}
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
request->req_curr_ctes.push(cte);
|
|
|
|
|
2006-08-17 14:08:49 +02:00
|
|
|
dsql_nod* derived_node = pass1_derived_table(request, cte, proc_flag,
|
|
|
|
isRecursive ? rel_alias : NULL);
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
if (!isRecursive) {
|
|
|
|
cte->nod_arg[e_derived_table_alias] = (dsql_nod*) cte_name;
|
|
|
|
}
|
|
|
|
request->req_curr_ctes.pop();
|
|
|
|
|
2006-08-17 14:08:49 +02:00
|
|
|
return derived_node;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return pass1_relation(request, input);
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_constant:
|
|
|
|
return pass1_constant(request, input);
|
|
|
|
|
|
|
|
case nod_parameter:
|
|
|
|
if (proc_flag)
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
node = MAKE_node(input->nod_type, e_par_count);
|
|
|
|
node->nod_count = 0;
|
2006-09-03 03:09:23 +02:00
|
|
|
node->nod_arg[e_par_parameter] = (dsql_nod*)
|
|
|
|
MAKE_parameter(
|
|
|
|
(input->nod_arg[e_par_parameter] ?
|
|
|
|
(dsql_msg*) ((dsql_par*) input->nod_arg[e_par_parameter])->par_message :
|
|
|
|
request->req_send),
|
|
|
|
true, true,
|
|
|
|
// Pass 0 here to restore older parameter
|
|
|
|
// ordering behavior unconditionally.
|
|
|
|
(USHORT)(IPTR) input->nod_arg[e_par_index],
|
|
|
|
NULL);
|
|
|
|
node->nod_arg[e_par_index] = (dsql_nod*) ((dsql_par*) node->nod_arg[e_par_parameter])->par_index;
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
|
2004-01-16 11:43:21 +01:00
|
|
|
case nod_param_val:
|
|
|
|
node = MAKE_node(input->nod_type, e_prm_val_count);
|
|
|
|
node->nod_arg[e_prm_val_fld] = input->nod_arg[e_prm_val_fld];
|
|
|
|
node->nod_arg[e_prm_val_val] = PASS1_node(request, input->nod_arg[e_prm_val_val], proc_flag);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-11-30 10:03:53 +01:00
|
|
|
field = (dsql_fld*) node->nod_arg[e_prm_val_fld]->nod_arg[e_dfl_field];
|
2004-01-16 11:43:21 +01:00
|
|
|
DDL_resolve_intl_type(request, field, NULL);
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
{ // scope
|
2004-01-16 11:43:21 +01:00
|
|
|
dsql_nod *temp = node->nod_arg[e_prm_val_val];
|
|
|
|
// Initialize this stack variable, and make it look like a node
|
2006-05-19 17:17:02 +02:00
|
|
|
Firebird::AutoPtr<dsql_nod> desc_node(FB_NEW_RPT(*getDefaultMemoryPool(), 0) dsql_nod);
|
2004-01-16 11:43:21 +01:00
|
|
|
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
DEV_BLKCHK(temp, dsql_type_nod);
|
2006-03-29 11:41:48 +02:00
|
|
|
|
|
|
|
field->fld_flags |= FLD_nullable;
|
2004-01-16 11:43:21 +01:00
|
|
|
MAKE_desc_from_field(&(desc_node->nod_desc), field);
|
2006-05-19 17:17:02 +02:00
|
|
|
set_parameter_type(request, temp, desc_node, false);
|
2005-05-22 05:11:41 +02:00
|
|
|
} // end scope
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2004-01-16 11:43:21 +01:00
|
|
|
return node;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_udf:
|
|
|
|
return pass1_udf(request, input, proc_flag);
|
|
|
|
|
2004-10-14 20:54:54 +02:00
|
|
|
case nod_equiv:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_leq_all:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
dsql_nod* sub2 = input->nod_arg[1];
|
|
|
|
if (sub2->nod_type == nod_list) {
|
2005-10-06 08:08:10 +02:00
|
|
|
int list_item_count = 0;
|
2005-05-22 05:11:41 +02:00
|
|
|
|
|
|
|
node = NULL;
|
|
|
|
dsql_nod** ptr = sub2->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + sub2->nod_count;
|
|
|
|
ptr < end && list_item_count < MAX_MEMBER_LIST;
|
|
|
|
list_item_count++, ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
dsql_nod* temp = MAKE_node(input->nod_type, 2);
|
|
|
|
temp->nod_arg[0] = input->nod_arg[0];
|
|
|
|
temp->nod_arg[1] = *ptr;
|
|
|
|
node = compose(node, temp, nod_or);
|
|
|
|
}
|
|
|
|
if (list_item_count >= MAX_MEMBER_LIST)
|
2005-10-06 08:08:10 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -901,
|
2005-05-22 05:11:41 +02:00
|
|
|
isc_arg_gds, isc_imp_exc,
|
2005-10-06 08:08:10 +02:00
|
|
|
isc_arg_gds, isc_dsql_too_many_values,
|
|
|
|
isc_arg_number, MAX_MEMBER_LIST,
|
2005-05-22 05:11:41 +02:00
|
|
|
0);
|
|
|
|
return PASS1_node(request, node, proc_flag);
|
|
|
|
}
|
|
|
|
if (sub2->nod_type == nod_select_expr)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
if (proc_flag)
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 206,
|
|
|
|
isc_arg_gds, isc_dsql_subselect_err, 0);
|
|
|
|
|
|
|
|
if (sub2->nod_flags & NOD_SELECT_EXPR_SINGLETON) {
|
|
|
|
const DsqlContextStack::iterator base(*request->req_context);
|
|
|
|
node = MAKE_node(input->nod_type, 2);
|
|
|
|
node->nod_arg[0] = PASS1_node(request, input->nod_arg[0], false);
|
|
|
|
dsql_nod* temp = MAKE_node(nod_via, e_via_count);
|
|
|
|
node->nod_arg[1] = temp;
|
|
|
|
dsql_nod* rse = PASS1_rse(request, sub2, NULL);
|
|
|
|
temp->nod_arg[e_via_rse] = rse;
|
|
|
|
temp->nod_arg[e_via_value_1] =
|
|
|
|
rse->nod_arg[e_rse_items]->nod_arg[0];
|
|
|
|
temp->nod_arg[e_via_value_2] = MAKE_node(nod_null, (int) 0);
|
|
|
|
|
|
|
|
// Finish off by cleaning up contexts
|
|
|
|
|
|
|
|
request->req_context->clear(base);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
|
|
|
case nod_equiv:
|
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_leq:
|
|
|
|
return pass1_any(request, input, nod_any);
|
|
|
|
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
return pass1_any(request, input, nod_ansi_any);
|
|
|
|
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_leq_all:
|
|
|
|
return pass1_any(request, input, nod_ansi_all);
|
|
|
|
default: // make compiler happy
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_agg_count:
|
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_average:
|
|
|
|
case nod_agg_total:
|
|
|
|
case nod_agg_average2:
|
|
|
|
case nod_agg_total2:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2003-01-11 03:49:13 +01:00
|
|
|
if (proc_flag) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err, 0);
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2003-06-13 09:56:08 +02:00
|
|
|
if (!(request->req_in_select_list || request->req_in_where_clause ||
|
|
|
|
request->req_in_group_by_clause || request->req_in_having_clause ||
|
2003-11-18 08:58:35 +01:00
|
|
|
request->req_in_order_by_clause))
|
|
|
|
{
|
2003-06-13 09:56:08 +02:00
|
|
|
/* not part of a select list, where clause, group by clause,
|
|
|
|
having clause, or order by clause */
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_agg_ref_err, 0);
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2004-04-03 01:20:29 +02:00
|
|
|
node = MAKE_node(input->nod_type, e_agg_function_count);
|
|
|
|
node->nod_count = input->nod_count; // Copy count, because this must be exactly the same as input.
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_flags = input->nod_flags;
|
2003-01-11 03:49:13 +01:00
|
|
|
if (input->nod_count) {
|
2006-04-24 19:24:26 +02:00
|
|
|
for (int i = 0; i < input->nod_count; i++) {
|
|
|
|
node->nod_arg[i] =
|
|
|
|
PASS1_node(request, input->nod_arg[i], proc_flag);
|
|
|
|
|
2006-05-19 17:17:02 +02:00
|
|
|
Firebird::AutoPtr<dsql_nod> desc_node(FB_NEW_RPT(*getDefaultMemoryPool(), 0) dsql_nod);
|
2006-04-24 19:24:26 +02:00
|
|
|
desc_node->nod_desc.dsc_dtype = dtype_text;
|
|
|
|
desc_node->nod_desc.dsc_length = 1;
|
|
|
|
desc_node->nod_desc.dsc_sub_type = 0;
|
|
|
|
desc_node->nod_desc.dsc_scale = 0;
|
2006-05-19 17:17:02 +02:00
|
|
|
set_parameter_type(request, node->nod_arg[i], desc_node, false);
|
2006-04-24 19:24:26 +02:00
|
|
|
}
|
2004-04-03 01:20:29 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Scope level is needed to determine to which context COUNT(*) belongs.
|
2006-04-24 19:24:26 +02:00
|
|
|
node->nod_arg[e_agg_function_scope_level] =
|
|
|
|
(dsql_nod*)(IPTR) request->req_scope_level;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
return node;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// access plan node types
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_plan_item:
|
|
|
|
node = MAKE_node(input->nod_type, 2);
|
|
|
|
node->nod_arg[0] = sub1 = MAKE_node(nod_relation, e_rel_count);
|
|
|
|
sub1->nod_arg[e_rel_context] =
|
|
|
|
pass1_alias_list(request, input->nod_arg[0]);
|
|
|
|
node->nod_arg[1] = PASS1_node(request, input->nod_arg[1], proc_flag);
|
|
|
|
return node;
|
|
|
|
|
|
|
|
case nod_index:
|
|
|
|
node = MAKE_node(input->nod_type, 1);
|
|
|
|
node->nod_arg[0] = input->nod_arg[0];
|
|
|
|
return node;
|
|
|
|
|
2003-09-14 17:37:05 +02: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
|
|
|
case nod_dom_value:
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_desc = input->nod_desc;
|
|
|
|
return node;
|
|
|
|
|
2003-01-15 13:00:33 +01:00
|
|
|
case nod_internal_info:
|
2002-09-28 16:04:35 +02:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
const internal_info_id id =
|
|
|
|
*reinterpret_cast<internal_info_id*>(input->nod_arg[0]->nod_desc.dsc_address);
|
|
|
|
USHORT req_mask = InternalInfo::getMask(id);
|
|
|
|
if (req_mask && !(request->req_flags & req_mask))
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, InternalInfo::getAlias(id), 0);
|
2002-09-28 16:04:35 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-08-24 11:42:14 +02:00
|
|
|
case nod_current_time:
|
|
|
|
case nod_current_timestamp:
|
|
|
|
{
|
|
|
|
dsql_nod* const_node = input->nod_arg[0];
|
2005-08-25 14:37:26 +02:00
|
|
|
if (const_node) {
|
|
|
|
fb_assert(const_node->nod_type == nod_constant);
|
|
|
|
const int precision = (int)(IPTR) const_node->nod_arg[0];
|
|
|
|
fb_assert(precision >= 0);
|
2005-10-21 11:43:26 +02:00
|
|
|
if (precision > MAX_TIME_PRECISION) {
|
|
|
|
ERRD_post(isc_invalid_time_precision,
|
|
|
|
isc_arg_number, MAX_TIME_PRECISION, 0);
|
2005-08-25 14:37:26 +02:00
|
|
|
}
|
2005-08-24 11:42:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_join:
|
2003-08-15 02:02:18 +02:00
|
|
|
return pass1_join(request, input, proc_flag);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-06-13 14:45:42 +02:00
|
|
|
case nod_returning:
|
|
|
|
return pass1_returning(request, input, proc_flag);
|
|
|
|
|
2005-05-02 11:47:27 +02:00
|
|
|
case nod_not:
|
|
|
|
return pass1_not(request, input, proc_flag, true);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Node is simply to be rebuilt -- just recurse merrily
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_nod** ptr2 = const_cast<const dsql_nod**>(node->nod_arg);
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr < end; ptr++)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
*ptr2++ = PASS1_node(request, *ptr, proc_flag);
|
|
|
|
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
// Try to match parameters against things of known data type.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub2 = NULL;
|
|
|
|
dsql_nod* sub3 = NULL;
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_between:
|
|
|
|
sub3 = node->nod_arg[2];
|
2005-05-28 00:45:31 +02:00
|
|
|
// FALLINTO
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_assign:
|
2004-10-14 20:54:54 +02:00
|
|
|
case nod_equiv:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_eql:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_leq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
sub1 = node->nod_arg[0];
|
|
|
|
sub2 = node->nod_arg[1];
|
|
|
|
|
2006-09-13 14:59:53 +02:00
|
|
|
// Try to force sub1 to be same type as sub2 eg: ? = FIELD case
|
|
|
|
set_parameter_type(request, sub1, sub2, false);
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
set_parameter_type(request, sub2, sub1, false);
|
2005-11-30 10:03:53 +01:00
|
|
|
|
2006-09-13 14:59:53 +02:00
|
|
|
// X BETWEEN Y AND ? case
|
2005-11-30 10:03:53 +01:00
|
|
|
if (!set_parameter_type(request, sub3, sub1, false))
|
2005-02-06 14:15:22 +01:00
|
|
|
{
|
2006-09-13 14:59:53 +02:00
|
|
|
// ? BETWEEN Y AND ? case
|
2005-11-30 10:03:53 +01:00
|
|
|
set_parameter_type(request, sub3, sub2, false);
|
2005-02-06 14:15:22 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_like:
|
|
|
|
if (node->nod_count == 3) {
|
|
|
|
sub3 = node->nod_arg[2];
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
// FALLINTO
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_containing:
|
|
|
|
case nod_starting:
|
|
|
|
sub1 = node->nod_arg[0];
|
|
|
|
sub2 = node->nod_arg[1];
|
|
|
|
|
2006-09-13 14:59:53 +02:00
|
|
|
// Try to force sub1 to be same type as sub2 eg: ? LIKE FIELD case
|
|
|
|
set_parameter_type(request, sub1, sub2, true);
|
|
|
|
|
|
|
|
// Try to force sub2 same type as sub 1 eg: FIELD LIKE ? case
|
|
|
|
// Try even when the above call succeeded, because "sub2" may
|
|
|
|
// have sub-expressions that should be resolved.
|
|
|
|
set_parameter_type(request, sub2, sub1, true);
|
2005-11-30 10:03:53 +01:00
|
|
|
|
2006-09-13 14:59:53 +02:00
|
|
|
// X LIKE Y ESCAPE ? case
|
2005-11-30 10:03:53 +01:00
|
|
|
set_parameter_type(request, sub3, sub2, true);
|
2005-05-28 00:45:31 +02:00
|
|
|
break;
|
|
|
|
|
2005-12-12 18:27:10 +01:00
|
|
|
case nod_missing:
|
|
|
|
set_parameter_type(request, node, NULL, false);
|
|
|
|
break;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
|
|
|
sub1 = node->nod_arg[e_trim_characters];
|
|
|
|
sub2 = node->nod_arg[e_trim_value];
|
|
|
|
|
2006-09-13 14:59:53 +02:00
|
|
|
// Try to force sub1 to be same type as sub2 eg: TRIM(? FROM FIELD) case
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, sub1, sub2, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_rse
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Compile a record selection expression,
|
|
|
|
bumping up the request scope level
|
2003-02-15 04:01:51 +01:00
|
|
|
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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param update_lock
|
|
|
|
|
|
|
|
**/
|
2004-10-13 20:37:53 +02:00
|
|
|
dsql_nod* PASS1_rse(dsql_req* request, dsql_nod* input, dsql_nod* update_lock)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2004-10-13 20:37:53 +02:00
|
|
|
DEV_BLKCHK(update_lock, dsql_type_nod);
|
|
|
|
|
|
|
|
fb_assert(input->nod_type == nod_select_expr);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
request->req_scope_level++;
|
2004-10-13 20:37:53 +02:00
|
|
|
dsql_nod* node = pass1_rse(request, input, NULL, NULL, update_lock, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_scope_level--;
|
|
|
|
|
|
|
|
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_statement
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Compile a parsed request into something more interesting.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* PASS1_statement(dsql_req* request, dsql_nod* input, bool proc_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-28 23:36:05 +02:00
|
|
|
#ifdef DSQL_DEBUG
|
|
|
|
if (DSQL_debug & 2) {
|
|
|
|
dsql_trace("Node tree at DSQL pass1 entry:");
|
2001-05-23 15:26:42 +02:00
|
|
|
DSQL_pretty(input, 0);
|
2003-09-28 23:36:05 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* node;
|
2004-05-09 07:48:33 +02:00
|
|
|
const DsqlContextStack::iterator base(*request->req_context);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Dispatch on node type. Fall thru on easy ones
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_def_relation:
|
2002-06-29 08:56:51 +02:00
|
|
|
case nod_redef_relation:
|
2005-03-26 17:11:52 +01:00
|
|
|
case nod_mod_relation:
|
|
|
|
case nod_del_relation:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_def_index:
|
2005-03-26 17:11:52 +01:00
|
|
|
case nod_mod_index:
|
|
|
|
case nod_del_index:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_def_view:
|
2002-09-01 17:49:03 +02:00
|
|
|
case nod_redef_view:
|
2002-08-11 10:04:54 +02:00
|
|
|
case nod_mod_view:
|
2002-08-27 09:48:34 +02:00
|
|
|
case nod_replace_view:
|
2002-06-29 08:56:51 +02:00
|
|
|
case nod_del_view:
|
2005-03-26 17:11:52 +01:00
|
|
|
case nod_def_constraint:
|
|
|
|
case nod_def_exception:
|
|
|
|
case nod_redef_exception:
|
|
|
|
case nod_mod_exception:
|
|
|
|
case nod_replace_exception:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_del_exception:
|
|
|
|
case nod_grant:
|
|
|
|
case nod_revoke:
|
|
|
|
case nod_def_database:
|
|
|
|
case nod_mod_database:
|
|
|
|
case nod_def_generator:
|
2005-03-26 17:11:52 +01:00
|
|
|
case nod_del_generator:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_def_role:
|
|
|
|
case nod_del_role:
|
|
|
|
case nod_def_filter:
|
|
|
|
case nod_del_filter:
|
|
|
|
case nod_def_domain:
|
|
|
|
case nod_mod_domain:
|
2005-03-26 17:11:52 +01:00
|
|
|
case nod_del_domain:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_def_udf:
|
|
|
|
case nod_del_udf:
|
|
|
|
case nod_def_shadow:
|
|
|
|
case nod_del_shadow:
|
|
|
|
case nod_set_statistics:
|
2005-05-17 09:17:25 +02:00
|
|
|
case nod_comment:
|
2005-05-22 04:42:17 +02:00
|
|
|
case nod_mod_udf:
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_type = REQ_DDL;
|
|
|
|
return input;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_def_collation:
|
2006-08-07 18:39:21 +02:00
|
|
|
case nod_del_collation:
|
2005-05-28 00:45:31 +02:00
|
|
|
request->req_type = REQ_DDL;
|
|
|
|
return input;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_def_trigger:
|
2005-10-29 11:15:37 +02:00
|
|
|
case nod_redef_trigger:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_mod_trigger:
|
2002-08-27 09:48:34 +02:00
|
|
|
case nod_replace_trigger:
|
2005-03-26 17:11:52 +01:00
|
|
|
case nod_del_trigger:
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_type = REQ_DDL;
|
2004-11-17 19:27:48 +01:00
|
|
|
request->req_flags |= (REQ_block | REQ_procedure | REQ_trigger);
|
2001-05-23 15:26:42 +02:00
|
|
|
return input;
|
|
|
|
|
|
|
|
case nod_del_procedure:
|
|
|
|
request->req_type = REQ_DDL;
|
2004-11-17 19:27:48 +01:00
|
|
|
request->req_flags |= (REQ_block | REQ_procedure);
|
2001-05-23 15:26:42 +02:00
|
|
|
return input;
|
|
|
|
|
|
|
|
case nod_def_procedure:
|
2002-06-29 08:56:51 +02:00
|
|
|
case nod_redef_procedure:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_mod_procedure:
|
2002-08-27 09:48:34 +02:00
|
|
|
case nod_replace_procedure:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
request->req_type = REQ_DDL;
|
|
|
|
request->req_flags |= (REQ_block | REQ_procedure);
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
const dsql_nod* variables = input->nod_arg[e_prc_dcls];
|
|
|
|
if (variables) {
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
// insure that variable names do not duplicate parameter names
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
const dsql_nod* const* ptr = variables->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + variables->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
if ((*ptr)->nod_type == nod_def_field) {
|
|
|
|
|
|
|
|
const dsql_fld* field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
|
|
|
|
const dsql_nod* parameters = input->nod_arg[e_prc_inputs];
|
|
|
|
if (parameters) {
|
|
|
|
|
|
|
|
const dsql_nod* const* ptr2 = parameters->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end2 =
|
|
|
|
ptr2 + parameters->nod_count; ptr2 < end2; ptr2++)
|
|
|
|
{
|
|
|
|
const dsql_fld* field2 =
|
|
|
|
(dsql_fld*) (*ptr2)->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field2, dsql_type_fld);
|
|
|
|
if (!strcmp(field->fld_name, field2->fld_name))
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number,
|
|
|
|
(SLONG) - 901, isc_arg_gds,
|
|
|
|
isc_dsql_var_conflict, isc_arg_string,
|
|
|
|
field->fld_name, 0);
|
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
}
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
parameters = input->nod_arg[e_prc_outputs];
|
|
|
|
if (parameters) {
|
|
|
|
|
|
|
|
const dsql_nod* const* ptr2 = parameters->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end2 =
|
|
|
|
ptr2 + parameters->nod_count; ptr2 < end2; ptr2++)
|
|
|
|
{
|
|
|
|
const dsql_fld* field2 =
|
|
|
|
(dsql_fld*) (*ptr2)->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field2, dsql_type_fld);
|
|
|
|
if (!strcmp(field->fld_name, field2->fld_name))
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number,
|
|
|
|
(SLONG) - 901, isc_arg_gds,
|
|
|
|
isc_dsql_var_conflict, isc_arg_string,
|
|
|
|
field->fld_name, 0);
|
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
return input;
|
2003-11-10 10:16:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_assign:
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_arg[0] = PASS1_node(request, input->nod_arg[0], proc_flag);
|
|
|
|
node->nod_arg[1] = PASS1_node(request, input->nod_arg[1], proc_flag);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_commit:
|
|
|
|
if ((input->nod_arg[e_commit_retain]) &&
|
2005-07-20 12:05:57 +02:00
|
|
|
(input->nod_arg[e_commit_retain]->nod_type == nod_retain))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_type = REQ_COMMIT_RETAIN;
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
|
|
|
request->req_type = REQ_COMMIT;
|
|
|
|
return input;
|
|
|
|
|
2005-07-20 12:05:57 +02:00
|
|
|
case nod_rollback:
|
|
|
|
if ((input->nod_arg[e_rollback_retain]) &&
|
|
|
|
(input->nod_arg[e_rollback_retain]->nod_type == nod_retain))
|
|
|
|
{
|
|
|
|
request->req_type = REQ_ROLLBACK_RETAIN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
request->req_type = REQ_ROLLBACK;
|
|
|
|
return input;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_delete:
|
2005-08-24 08:21:47 +02:00
|
|
|
node = pass1_savepoint(request, pass1_delete(request, input, proc_flag));
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_exec_procedure:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
const dsql_str* name = (dsql_str*) input->nod_arg[e_exe_procedure];
|
|
|
|
DEV_BLKCHK(name, dsql_type_str);
|
|
|
|
dsql_prc* procedure = METD_get_procedure(request, name);
|
|
|
|
if (!procedure) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 204,
|
|
|
|
isc_arg_gds, isc_dsql_procedure_err,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, name->str_data, 0);
|
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
if (!proc_flag) {
|
|
|
|
request->req_procedure = procedure;
|
|
|
|
request->req_type = REQ_EXEC_PROCEDURE;
|
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_arg[e_exe_procedure] = input->nod_arg[e_exe_procedure];
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
// handle input parameters
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
USHORT count = input->nod_arg[e_exe_inputs] ?
|
|
|
|
input->nod_arg[e_exe_inputs]->nod_count : 0;
|
|
|
|
if (count > procedure->prc_in_count ||
|
|
|
|
count < procedure->prc_in_count - procedure->prc_def_count)
|
|
|
|
{
|
|
|
|
ERRD_post(isc_prcmismat, isc_arg_string, name->str_data, 0);
|
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
node->nod_arg[e_exe_inputs] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_exe_inputs], proc_flag);
|
2004-01-16 11:43:21 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
if (count) {
|
|
|
|
// Initialize this stack variable, and make it look like a node
|
2006-05-19 17:17:02 +02:00
|
|
|
Firebird::AutoPtr<dsql_nod> desc_node(FB_NEW_RPT(*getDefaultMemoryPool(), 0) dsql_nod);
|
2002-04-04 18:41:41 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
dsql_nod* const* ptr = node->nod_arg[e_exe_inputs]->nod_arg;
|
|
|
|
for (const dsql_fld* field = procedure->prc_inputs;
|
|
|
|
*ptr; ptr++, field = field->fld_next)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
MAKE_desc_from_field(&desc_node->nod_desc, field);
|
2005-09-03 09:47:32 +02:00
|
|
|
// set_parameter_type(*ptr, &desc_node, false);
|
2006-05-19 17:17:02 +02:00
|
|
|
set_parameter_type(request, *ptr, desc_node, false);
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
// handle output parameters
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
dsql_nod* temp = input->nod_arg[e_exe_outputs];
|
|
|
|
if (proc_flag) {
|
|
|
|
count = temp ? temp->nod_count : 0;
|
|
|
|
if (count != procedure->prc_out_count)
|
|
|
|
{
|
2005-06-06 15:37:31 +02:00
|
|
|
ERRD_post(isc_prc_out_param_mismatch,
|
|
|
|
isc_arg_string, name->str_data, 0);
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2005-03-26 15:05:44 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
node->nod_arg[e_exe_outputs] =
|
|
|
|
PASS1_node(request, temp, proc_flag);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (temp) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds,
|
|
|
|
isc_token_err, // Token unknown
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "RETURNING_VALUES", 0);
|
|
|
|
}
|
|
|
|
node->nod_arg[e_exe_outputs] =
|
|
|
|
explode_outputs(request, request->req_procedure);
|
2005-03-26 14:32:29 +01:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_exec_block:
|
2004-01-16 11:43:21 +01:00
|
|
|
if (input->nod_arg[e_exe_blk_outputs] &&
|
|
|
|
input->nod_arg[e_exe_blk_outputs]->nod_count)
|
2004-01-28 08:50:41 +01:00
|
|
|
{
|
2004-01-16 11:43:21 +01:00
|
|
|
request->req_type = REQ_SELECT_BLOCK;
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2004-01-16 11:43:21 +01:00
|
|
|
else
|
|
|
|
request->req_type = REQ_EXEC_BLOCK;
|
2004-11-17 19:27:48 +01:00
|
|
|
request->req_flags |= REQ_block;
|
2004-01-16 11:43:21 +01:00
|
|
|
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_exe_blk_inputs] =
|
2005-03-26 17:11:52 +01:00
|
|
|
PASS1_node(request, input->nod_arg[e_exe_blk_inputs], false);
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_exe_blk_outputs] =
|
2004-01-16 11:43:21 +01:00
|
|
|
input->nod_arg[e_exe_blk_outputs];
|
|
|
|
|
|
|
|
node->nod_arg[e_exe_blk_dcls] = input->nod_arg[e_exe_blk_dcls];
|
|
|
|
node->nod_arg[e_exe_blk_body] = input->nod_arg[e_exe_blk_body];
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
{ // scope
|
2006-08-13 08:46:16 +02:00
|
|
|
const size_t ncount =
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_exe_blk_inputs] ?
|
2004-01-16 11:43:21 +01:00
|
|
|
node->nod_arg[e_exe_blk_inputs]->nod_count : 0 +
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_exe_blk_outputs] ?
|
2004-01-16 11:43:21 +01:00
|
|
|
node->nod_arg[e_exe_blk_outputs]->nod_count : 0 +
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_exe_blk_dcls] ?
|
2006-08-13 08:46:16 +02:00
|
|
|
node->nod_arg[e_exe_blk_dcls]->nod_count : 0;
|
2004-01-16 11:43:21 +01:00
|
|
|
|
2006-08-13 08:46:16 +02:00
|
|
|
if (ncount)
|
|
|
|
{
|
|
|
|
StrArray names( *getDefaultMemoryPool(), ncount);
|
|
|
|
|
|
|
|
check_unique_fields_names(names, node->nod_arg[e_exe_blk_inputs]);
|
|
|
|
check_unique_fields_names(names, node->nod_arg[e_exe_blk_outputs]);
|
|
|
|
check_unique_fields_names(names, node->nod_arg[e_exe_blk_dcls]);
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
} // end scope
|
2004-01-16 11:43:21 +01:00
|
|
|
return node;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_for_select:
|
2003-01-15 13:00:33 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_flags = input->nod_flags;
|
|
|
|
dsql_nod* cursor = node->nod_arg[e_flp_cursor] = input->nod_arg[e_flp_cursor];
|
|
|
|
|
|
|
|
node->nod_arg[e_flp_select] =
|
|
|
|
PASS1_statement(request, input->nod_arg[e_flp_select], proc_flag);
|
|
|
|
|
|
|
|
if (cursor) {
|
|
|
|
fb_assert(cursor->nod_flags > 0);
|
|
|
|
pass1_cursor_name(request, (dsql_str*) cursor->nod_arg[e_cur_name],
|
|
|
|
NOD_CURSOR_ALL, false);
|
|
|
|
cursor->nod_arg[e_cur_rse] = node->nod_arg[e_flp_select];
|
|
|
|
cursor->nod_arg[e_cur_number] = (dsql_nod*) (IPTR) request->req_cursor_number++;
|
|
|
|
request->req_cursors.push(cursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* into_in = input->nod_arg[e_flp_into];
|
2006-09-14 04:05:32 +02:00
|
|
|
|
|
|
|
if (into_in)
|
2005-05-22 05:11:41 +02:00
|
|
|
{
|
2006-09-14 04:05:32 +02:00
|
|
|
dsql_nod* into_out = MAKE_node(into_in->nod_type, into_in->nod_count);
|
|
|
|
node->nod_arg[e_flp_into] = into_out;
|
|
|
|
const dsql_nod** ptr2 = const_cast<const dsql_nod**>(into_out->nod_arg);
|
|
|
|
dsql_nod** ptr = into_in->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + into_in->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
*ptr2++ = PASS1_node(request, *ptr, proc_flag);
|
|
|
|
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (input->nod_arg[e_flp_action]) {
|
|
|
|
// CVC: Let's add the ability to BREAK the for_select same as the while,
|
|
|
|
// but only if the command is FOR SELECT, otherwise we have singular SELECT
|
|
|
|
request->req_loop_level++;
|
|
|
|
node->nod_arg[e_flp_label] = pass1_label(request, input);
|
|
|
|
node->nod_arg[e_flp_action] =
|
|
|
|
PASS1_statement(request, input->nod_arg[e_flp_action], proc_flag);
|
|
|
|
request->req_loop_level--;
|
|
|
|
request->req_labels.pop();
|
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
if (cursor) {
|
|
|
|
request->req_cursor_number--;
|
|
|
|
request->req_cursors.pop();
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_get_segment:
|
|
|
|
case nod_put_segment:
|
|
|
|
pass1_blob(request, input);
|
|
|
|
return input;
|
|
|
|
|
|
|
|
case nod_if:
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_if_condition] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_if_condition], proc_flag);
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[e_if_true] =
|
|
|
|
PASS1_statement(request, input->nod_arg[e_if_true], proc_flag);
|
2003-11-18 08:58:35 +01:00
|
|
|
if (input->nod_arg[e_if_false]) {
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[e_if_false] =
|
2002-12-18 16:01:50 +01:00
|
|
|
PASS1_statement(request, input->nod_arg[e_if_false], proc_flag);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
|
|
|
node->nod_arg[e_if_false] = NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_exception_stmt:
|
|
|
|
node = input;
|
2002-09-28 16:04:35 +02:00
|
|
|
/* if exception value is defined,
|
|
|
|
pass value node */
|
2003-10-01 20:11:23 +02:00
|
|
|
if (input->nod_arg[e_xcps_msg])
|
2002-09-28 16:04:35 +02:00
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_xcps_msg] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_xcps_msg], proc_flag);
|
2002-09-28 16:04:35 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-10-01 20:11:23 +02:00
|
|
|
node->nod_arg[e_xcps_msg] = 0;
|
2002-09-28 16:04:35 +02:00
|
|
|
}
|
2005-08-24 08:21:47 +02:00
|
|
|
return pass1_savepoint(request, node);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_insert:
|
2005-08-24 08:21:47 +02:00
|
|
|
node = pass1_savepoint(request, pass1_insert(request, input, proc_flag));
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
case nod_merge:
|
|
|
|
node = pass1_savepoint(request, pass1_merge(request, input, proc_flag));
|
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_block:
|
|
|
|
if (input->nod_arg[e_blk_errs])
|
|
|
|
request->req_error_handlers++;
|
2002-06-20 13:42:15 +02:00
|
|
|
else if (input->nod_arg[e_blk_action]) {
|
2001-05-23 15:26:42 +02:00
|
|
|
input->nod_count = 1;
|
|
|
|
if (!request->req_error_handlers)
|
|
|
|
input->nod_type = nod_list;
|
|
|
|
}
|
2002-06-20 13:42:15 +02:00
|
|
|
else {
|
|
|
|
input->nod_count = 0;
|
|
|
|
input->nod_type = nod_list;
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_list:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
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(request, *ptr, proc_flag);
|
|
|
|
else
|
|
|
|
*ptr2++ = PASS1_statement(request, *ptr, proc_flag);
|
|
|
|
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
|
|
|
|
}
|
|
|
|
if (input->nod_type == nod_block && input->nod_arg[e_blk_errs])
|
|
|
|
request->req_error_handlers--;
|
|
|
|
return node;
|
2003-11-10 10:16:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02: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];
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_err_action] =
|
|
|
|
PASS1_statement(request, input->nod_arg[e_err_action], proc_flag);
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
|
|
|
|
case nod_post:
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_pst_event] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_pst_event], proc_flag);
|
|
|
|
node->nod_arg[e_pst_argument] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_pst_argument], proc_flag);
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
|
2002-04-04 18:41:41 +02:00
|
|
|
case nod_exec_sql:
|
2002-04-04 15:53:20 +02:00
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_exec_sql_stmnt] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_exec_sql_stmnt], proc_flag);
|
2005-08-24 08:21:47 +02:00
|
|
|
return pass1_savepoint(request, node);
|
2003-03-01 20:19:23 +01:00
|
|
|
|
|
|
|
case nod_exec_into:
|
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_exec_into_stmnt] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_exec_into_stmnt], proc_flag);
|
2003-08-26 09:13:33 +02:00
|
|
|
if (input->nod_arg[e_exec_into_block]) {
|
|
|
|
request->req_loop_level++;
|
|
|
|
node->nod_arg[e_exec_into_label] = pass1_label(request, input);
|
|
|
|
node->nod_arg[e_exec_into_block] =
|
|
|
|
PASS1_statement(request, input->nod_arg[e_exec_into_block], proc_flag);
|
|
|
|
request->req_loop_level--;
|
2004-04-18 16:22:27 +02:00
|
|
|
request->req_labels.pop();
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
node->nod_arg[e_exec_into_list] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_exec_into_list], proc_flag);
|
2005-08-24 08:21:47 +02:00
|
|
|
return pass1_savepoint(request, node);
|
2002-04-04 15:53:20 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_exit:
|
|
|
|
return input;
|
|
|
|
|
2002-06-29 08:56:51 +02:00
|
|
|
case nod_breakleave:
|
2002-12-18 16:01:50 +01:00
|
|
|
if (!request->req_loop_level)
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "BREAK/LEAVE", 0);
|
2003-08-26 09:13:33 +02:00
|
|
|
input->nod_arg[e_breakleave_label] = pass1_label(request, input);
|
2002-06-29 08:56:51 +02:00
|
|
|
return input;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_return:
|
2004-11-17 19:27:48 +01:00
|
|
|
if (request->req_flags & REQ_trigger) // triggers only
|
2006-07-18 04:45:35 +02:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "SUSPEND", 0);
|
2006-07-18 04:45:35 +02:00
|
|
|
}
|
|
|
|
|
2006-07-17 14:44:18 +02:00
|
|
|
request->req_flags |= REQ_selectable;
|
2004-01-16 11:43:21 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
input->nod_arg[e_rtn_procedure] =
|
2004-01-16 11:43:21 +01:00
|
|
|
request->req_ddl_node ? request->req_ddl_node : request->req_blk_node;
|
2001-05-23 15:26:42 +02:00
|
|
|
return input;
|
|
|
|
|
|
|
|
case nod_select:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
node = PASS1_rse(request, input->nod_arg[e_select_expr],
|
|
|
|
input->nod_arg[e_select_lock]);
|
2003-11-07 15:10:16 +01:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
if (input->nod_arg[e_select_update]) {
|
|
|
|
request->req_type = REQ_SELECT_UPD;
|
|
|
|
request->req_flags |= REQ_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.
|
|
|
|
*/
|
|
|
|
if (node->nod_arg[e_rse_sort] || node->nod_arg[e_rse_reduced])
|
|
|
|
{
|
|
|
|
request->req_flags &= ~REQ_no_batch;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_trans:
|
|
|
|
request->req_type = REQ_START_TRANS;
|
|
|
|
return input;
|
|
|
|
|
|
|
|
case nod_update:
|
2005-08-24 08:21:47 +02:00
|
|
|
node = pass1_savepoint(request, pass1_update(request, input, proc_flag));
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_while:
|
2002-12-18 16:01:50 +01:00
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
node = MAKE_node(input->nod_type, input->nod_count);
|
|
|
|
node->nod_arg[e_while_cond] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_while_cond], proc_flag);
|
|
|
|
|
|
|
|
/* CVC: loop numbers should be incremented before analyzing the body
|
|
|
|
to preserve nesting <==> increasing level number. */
|
|
|
|
request->req_loop_level++;
|
|
|
|
node->nod_arg[e_while_label] = pass1_label(request, input);
|
|
|
|
node->nod_arg[e_while_action] =
|
|
|
|
PASS1_statement(request, input->nod_arg[e_while_action], proc_flag);
|
|
|
|
request->req_loop_level--;
|
|
|
|
request->req_labels.pop();
|
2002-12-18 16:01:50 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_abort:
|
|
|
|
case nod_exception:
|
|
|
|
case nod_sqlcode:
|
|
|
|
case nod_gdscode:
|
|
|
|
return input;
|
2004-11-17 15:50:33 +01:00
|
|
|
|
2002-10-29 21:20:44 +01:00
|
|
|
case nod_user_savepoint:
|
2004-11-17 19:27:48 +01:00
|
|
|
if (request->req_flags & REQ_block) // blocks, procedures and triggers
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "SAVEPOINT", 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
request->req_type = REQ_SAVEPOINT;
|
2002-10-31 13:01:53 +01:00
|
|
|
return input;
|
|
|
|
|
2003-06-10 15:40:19 +02:00
|
|
|
case nod_release_savepoint:
|
2004-11-17 19:27:48 +01:00
|
|
|
if (request->req_flags & REQ_block) // blocks, procedures and triggers
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "RELEASE", 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
request->req_type = REQ_SAVEPOINT;
|
2003-06-10 15:40:19 +02:00
|
|
|
return input;
|
|
|
|
|
2002-10-29 21:20:44 +01:00
|
|
|
case nod_undo_savepoint:
|
2004-11-17 19:27:48 +01:00
|
|
|
if (request->req_flags & REQ_block) // blocks, procedures and triggers
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "ROLLBACK", 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
request->req_type = REQ_SAVEPOINT;
|
2002-10-29 21:20:44 +01:00
|
|
|
return input;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_null:
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
case nod_set_generator:
|
|
|
|
node = MAKE_node(input->nod_type, e_gen_id_count);
|
|
|
|
node->nod_arg[e_gen_id_value] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_gen_id_value], proc_flag);
|
|
|
|
node->nod_arg[e_gen_id_name] = input->nod_arg[e_gen_id_name];
|
|
|
|
request->req_type = REQ_SET_GENERATOR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_set_generator2:
|
|
|
|
node = MAKE_node(input->nod_type, e_gen_id_count);
|
|
|
|
node->nod_arg[e_gen_id_value] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_gen_id_value], proc_flag);
|
|
|
|
node->nod_arg[e_gen_id_name] = input->nod_arg[e_gen_id_name];
|
|
|
|
request->req_type = REQ_SET_GENERATOR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_union:
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 901,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_union_err, // union not supported
|
2001-05-23 15:26:42 +02:00
|
|
|
0);
|
|
|
|
break;
|
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
case nod_cursor:
|
|
|
|
{
|
2005-05-22 05:11:41 +02:00
|
|
|
fb_assert(input->nod_flags > 0);
|
|
|
|
// make sure the cursor doesn't exist
|
|
|
|
pass1_cursor_name(request, (dsql_str*) input->nod_arg[e_cur_name],
|
|
|
|
NOD_CURSOR_ALL, false);
|
|
|
|
// temporarily hide unnecessary contexts and process our RSE
|
|
|
|
DsqlContextStack* const base_context = request->req_context;
|
|
|
|
DsqlContextStack temp;
|
|
|
|
request->req_context = &temp;
|
|
|
|
const dsql_nod* select = input->nod_arg[e_cur_rse];
|
|
|
|
input->nod_arg[e_cur_rse] =
|
|
|
|
PASS1_rse(request, select->nod_arg[e_select_expr],
|
|
|
|
select->nod_arg[e_select_lock]);
|
|
|
|
request->req_context->clear();
|
|
|
|
request->req_context = base_context;
|
|
|
|
// assign number and store in the request stack
|
|
|
|
input->nod_arg[e_cur_number] = (dsql_nod*) (IPTR) request->req_cursor_number++;
|
|
|
|
request->req_cursors.push(input);
|
2003-11-02 13:28:30 +01:00
|
|
|
}
|
|
|
|
return input;
|
|
|
|
|
|
|
|
case nod_cursor_open:
|
|
|
|
case nod_cursor_close:
|
|
|
|
case nod_cursor_fetch:
|
|
|
|
// resolve the cursor
|
|
|
|
input->nod_arg[e_cur_stmt_id] =
|
2004-11-17 15:50:33 +01:00
|
|
|
pass1_cursor_name(request, (dsql_str*) input->nod_arg[e_cur_stmt_id],
|
|
|
|
NOD_CURSOR_EXPLICIT, true);
|
2003-11-02 13:28:30 +01:00
|
|
|
// process a seek node, if exists
|
2003-11-18 08:58:35 +01:00
|
|
|
if (input->nod_arg[e_cur_stmt_seek]) {
|
2003-11-02 13:28:30 +01:00
|
|
|
input->nod_arg[e_cur_stmt_seek] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_cur_stmt_seek], proc_flag);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
// process an assignment node, if exists
|
2003-11-18 08:58:35 +01:00
|
|
|
if (input->nod_arg[e_cur_stmt_into]) {
|
2003-11-02 13:28:30 +01:00
|
|
|
input->nod_arg[e_cur_stmt_into] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_cur_stmt_into], proc_flag);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
return input;
|
|
|
|
|
2006-07-04 16:44:43 +02:00
|
|
|
case nod_src_info:
|
|
|
|
{
|
|
|
|
input->nod_line = (USHORT) (IPTR) input->nod_arg[0];
|
|
|
|
input->nod_column = (USHORT) (IPTR) input->nod_arg[1];
|
|
|
|
return input;
|
|
|
|
}
|
|
|
|
|
2006-09-03 03:09:23 +02:00
|
|
|
case nod_replace:
|
|
|
|
node = pass1_savepoint(request, pass1_replace(request, input, proc_flag));
|
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 901,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_dsql_construct_err, // Unsupported DSQL construct
|
2001-05-23 15:26:42 +02:00
|
|
|
0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Finish off by cleaning up contexts
|
2004-04-21 16:48:23 +02:00
|
|
|
request->req_context->clear(base);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-28 23:36:05 +02:00
|
|
|
#ifdef DSQL_DEBUG
|
|
|
|
if (DSQL_debug & 1) {
|
|
|
|
dsql_trace("Node tree at DSQL pass1 exit:");
|
2001-05-23 15:26:42 +02:00
|
|
|
DSQL_pretty(node, 0);
|
2003-09-28 23:36:05 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
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
|
|
|
aggregate_found
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Check for an aggregate expression in an
|
|
|
|
rse select list. It could be buried in
|
2003-02-15 04:01:51 +01:00
|
|
|
an expression tree.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
@param request
|
|
|
|
@param node
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2003-11-05 10:02:33 +01:00
|
|
|
static bool aggregate_found( const dsql_req* request, const dsql_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
2003-01-11 03:49:13 +01:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
USHORT current_level = request->req_scope_level;
|
|
|
|
USHORT deepest_level = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
return aggregate_found2(request, node, ¤t_level, &deepest_level, 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
|
|
|
aggregate_found2
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Check for an aggregate expression in an
|
|
|
|
expression. It could be buried in an expression
|
2003-05-05 00:02:42 +02:00
|
|
|
tree and therefore call itselfs again. The level
|
|
|
|
parameters (current_level & deepest_level) are used
|
|
|
|
to see how deep we are with passing sub-queries
|
|
|
|
(= scope_level).
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
field is true if a non-aggregate field reference is seen.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param node
|
|
|
|
@param current_level
|
|
|
|
@param deepest_level
|
|
|
|
@param ignore_sub_selects
|
|
|
|
|
|
|
|
**/
|
2003-11-05 10:02:33 +01:00
|
|
|
static bool aggregate_found2(const dsql_req* request, const dsql_nod* node,
|
|
|
|
USHORT* current_level,
|
2003-10-05 08:37:26 +02:00
|
|
|
USHORT* deepest_level, bool ignore_sub_selects)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
2003-01-11 03:49:13 +01:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-30 10:03:53 +01:00
|
|
|
if (!node)
|
|
|
|
return false;
|
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
bool aggregate = false;
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2003-05-05 00:02:42 +02:00
|
|
|
// handle the simple case of a straightforward aggregate
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_agg_average:
|
|
|
|
case nod_agg_average2:
|
|
|
|
case nod_agg_total2:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_total:
|
|
|
|
case nod_agg_count:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2005-05-22 05:11:41 +02:00
|
|
|
if (!ignore_sub_selects)
|
|
|
|
{
|
2003-05-07 03:57:18 +02:00
|
|
|
if (node->nod_count) {
|
2003-09-02 01:22:22 +02:00
|
|
|
USHORT ldeepest_level = 0;
|
2005-05-28 00:45:31 +02:00
|
|
|
// If we are already in a aggregate function don't search inside
|
|
|
|
// sub-selects and other aggregate-functions for the deepest field
|
2003-05-07 03:57:18 +02:00
|
|
|
// used else we would have a wrong deepest_level value.
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate_found2(request, node->nod_arg[e_agg_function_expression],
|
2004-04-03 01:20:29 +02:00
|
|
|
current_level, &ldeepest_level, true);
|
2005-05-28 00:45:31 +02:00
|
|
|
if (ldeepest_level == 0) {
|
2003-05-07 03:57:18 +02:00
|
|
|
*deepest_level = *current_level;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*deepest_level = ldeepest_level;
|
|
|
|
}
|
2003-08-26 01:35:30 +02:00
|
|
|
// If the deepest_value is the same as the current scope_level
|
|
|
|
// this an aggregate that belongs to the current context.
|
|
|
|
if (*deepest_level == request->req_scope_level) {
|
2003-09-02 01:22:22 +02:00
|
|
|
aggregate = true;
|
2003-05-07 03:57:18 +02:00
|
|
|
}
|
2003-07-29 02:29:17 +02:00
|
|
|
else {
|
|
|
|
// Check also for a nested aggregate that could belong to this context
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate |= aggregate_found2(request, node->nod_arg[e_agg_function_expression],
|
2003-09-02 01:22:22 +02:00
|
|
|
current_level, &ldeepest_level, false);
|
2003-07-29 02:29:17 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2003-05-07 03:57:18 +02:00
|
|
|
else {
|
|
|
|
// we have Count(*)
|
2004-04-03 01:20:29 +02:00
|
|
|
if (request->req_scope_level == (USHORT)(U_IPTR)node->nod_arg[e_agg_function_scope_level]) {
|
2003-09-02 01:22:22 +02:00
|
|
|
aggregate = true;
|
2003-05-07 03:57:18 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return aggregate;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_field:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-05 10:02:33 +01:00
|
|
|
const dsql_ctx* lcontext =
|
2003-11-10 10:16:38 +01:00
|
|
|
reinterpret_cast<dsql_ctx*>(node->nod_arg[e_fld_context]);
|
2003-09-02 01:22:22 +02:00
|
|
|
if (*deepest_level < lcontext->ctx_scope_level) {
|
|
|
|
*deepest_level = lcontext->ctx_scope_level;
|
|
|
|
}
|
|
|
|
return false;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_alias:
|
2005-05-22 05:11:41 +02:00
|
|
|
aggregate = aggregate_found2(request, node->nod_arg[e_alias_value],
|
2003-09-04 17:02:22 +02:00
|
|
|
current_level, deepest_level, ignore_sub_selects);
|
|
|
|
return aggregate;
|
|
|
|
|
|
|
|
case nod_derived_field:
|
|
|
|
{
|
|
|
|
// This is an derived table don't look further,
|
2003-08-24 04:36:46 +02:00
|
|
|
// but don't forget to check for deepest scope_level.
|
2003-11-18 08:58:35 +01:00
|
|
|
const USHORT lscope_level = (USHORT)(U_IPTR)node->nod_arg[e_derived_field_scope];
|
2003-08-24 04:36:46 +02:00
|
|
|
if (*deepest_level < lscope_level) {
|
|
|
|
*deepest_level = lscope_level;
|
|
|
|
}
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
return aggregate;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_map:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_ctx* lcontext = reinterpret_cast<dsql_ctx*>(node->nod_arg[e_map_context]);
|
2003-09-02 01:22:22 +02:00
|
|
|
if (lcontext->ctx_scope_level == request->req_scope_level) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_map* lmap = reinterpret_cast<dsql_map*>(node->nod_arg[e_map_map]);
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate = aggregate_found2(request, lmap->map_node, current_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
deepest_level, ignore_sub_selects);
|
|
|
|
return aggregate;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-05-05 00:02:42 +02:00
|
|
|
// for expressions in which an aggregate might
|
|
|
|
// be buried, recursively check for one
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_via:
|
2003-01-30 14:30:15 +01:00
|
|
|
if (!ignore_sub_selects) {
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate = aggregate_found2(request, node->nod_arg[e_via_rse], current_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
deepest_level, ignore_sub_selects);
|
2003-01-30 14:30:15 +01:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
return aggregate;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_exists:
|
|
|
|
case nod_singular:
|
2004-11-17 23:01:44 +01:00
|
|
|
if (!ignore_sub_selects) {
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate = aggregate_found2(request, node->nod_arg[0], current_level,
|
2004-11-17 23:01:44 +01:00
|
|
|
deepest_level, ignore_sub_selects);
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
return aggregate;
|
|
|
|
|
|
|
|
case nod_aggregate:
|
2003-01-30 14:30:15 +01:00
|
|
|
if (!ignore_sub_selects) {
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate = aggregate_found2(request, node->nod_arg[e_agg_rse], current_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
deepest_level, ignore_sub_selects);
|
2003-01-30 14:30:15 +01:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
return aggregate;
|
|
|
|
|
|
|
|
case nod_rse:
|
|
|
|
(*current_level)++;
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate |= aggregate_found2(request, node->nod_arg[e_rse_streams], current_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
deepest_level, ignore_sub_selects);
|
2005-11-30 10:03:53 +01:00
|
|
|
aggregate |= aggregate_found2(request, node->nod_arg[e_rse_boolean],
|
|
|
|
current_level, deepest_level, ignore_sub_selects);
|
|
|
|
aggregate |= aggregate_found2(request, node->nod_arg[e_rse_items],
|
|
|
|
current_level, deepest_level, ignore_sub_selects);
|
2003-01-11 03:49:13 +01:00
|
|
|
(*current_level)--;
|
2005-05-28 00:45:31 +02:00
|
|
|
return aggregate;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_order:
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate = aggregate_found2(request, node->nod_arg[e_order_field], current_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
deepest_level, ignore_sub_selects);
|
2003-01-17 13:47:19 +01:00
|
|
|
return aggregate;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
|
|
|
case nod_or:
|
|
|
|
case nod_and:
|
|
|
|
case nod_not:
|
2004-10-14 20:54:54 +02:00
|
|
|
case nod_equiv:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_leq_all:
|
|
|
|
case nod_between:
|
|
|
|
case nod_like:
|
|
|
|
case nod_containing:
|
|
|
|
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:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_coalesce:
|
|
|
|
case nod_simple_case:
|
|
|
|
case nod_searched_case:
|
|
|
|
case nod_list:
|
2003-05-07 03:57:18 +02:00
|
|
|
case nod_join:
|
|
|
|
case nod_join_inner:
|
|
|
|
case nod_join_left:
|
|
|
|
case nod_join_right:
|
|
|
|
case nod_join_full:
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-11-05 10:02:33 +01:00
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
|
|
|
ptr < end; ++ptr)
|
|
|
|
{
|
2005-11-30 10:03:53 +01:00
|
|
|
aggregate |= aggregate_found2(request, *ptr, current_level,
|
|
|
|
deepest_level, ignore_sub_selects);
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
return aggregate;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case nod_cast:
|
|
|
|
case nod_gen_id:
|
|
|
|
case nod_gen_id2:
|
|
|
|
case nod_udf:
|
2003-09-02 01:22:22 +02:00
|
|
|
if (node->nod_count == 2) {
|
2005-05-28 00:45:31 +02:00
|
|
|
return (aggregate_found2(request, node->nod_arg[1], current_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
deepest_level, ignore_sub_selects));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
|
|
|
|
case nod_constant:
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-04-17 22:58:36 +02:00
|
|
|
case nod_relation:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_ctx* lrelation_context = reinterpret_cast<dsql_ctx*>(node->nod_arg[e_rel_context]);
|
2003-09-02 01:22:22 +02:00
|
|
|
// Check if relation is a procedure
|
|
|
|
if (lrelation_context->ctx_procedure) {
|
2005-11-30 10:03:53 +01:00
|
|
|
// Check if a aggregate is buried inside the input parameters
|
|
|
|
aggregate |= aggregate_found2(request, lrelation_context->ctx_proc_inputs,
|
|
|
|
current_level, deepest_level, ignore_sub_selects);
|
2003-04-17 22:58:36 +02:00
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
return aggregate;
|
2003-04-17 22:58:36 +02:00
|
|
|
}
|
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
default:
|
2003-09-02 01:22:22 +02: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
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
ambiguity
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Check for ambiguity in a field
|
2003-08-18 23:37:47 +02:00
|
|
|
reference. The list with contexts where the
|
2003-08-16 02:36:54 +02:00
|
|
|
field was found is checked and the necessary
|
|
|
|
message is build from it.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
2003-08-16 02:36:54 +02:00
|
|
|
@param node
|
|
|
|
@param name
|
|
|
|
@param ambiguous_contexts
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* ambiguity_check(dsql_req* request, dsql_nod* node,
|
2004-04-18 16:22:27 +02:00
|
|
|
const dsql_str* name, const DsqlContextStack& ambiguous_contexts)
|
2002-06-29 08:56:51 +02:00
|
|
|
{
|
2003-08-16 02:36:54 +02:00
|
|
|
// If there are no relations or only 1 there's no ambiguity, thus return.
|
2004-04-18 16:22:27 +02:00
|
|
|
if (ambiguous_contexts.getCount() < 2) {
|
2003-08-16 02:36:54 +02:00
|
|
|
return node;
|
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
TEXT buffer[1024];
|
2003-08-16 02:36:54 +02:00
|
|
|
USHORT loop = 0;
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2003-08-16 02:36:54 +02:00
|
|
|
buffer[0] = 0;
|
2003-10-05 08:37:26 +02:00
|
|
|
TEXT* b = buffer;
|
|
|
|
TEXT* p = 0;
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2004-05-27 18:26:52 +02:00
|
|
|
for (DsqlContextStack::const_iterator stack(ambiguous_contexts); stack.hasData(); ++stack)
|
2003-08-16 02:36:54 +02:00
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
const dsql_ctx* context = stack.object();
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_rel* relation = context->ctx_relation;
|
|
|
|
const dsql_prc* procedure = context->ctx_procedure;
|
2003-11-02 13:28:30 +01:00
|
|
|
if (strlen(b) > (sizeof(buffer) - 50)) {
|
2003-08-16 02:36:54 +02:00
|
|
|
// Buffer full
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// if this is the second loop add "and " before relation.
|
|
|
|
if (++loop > 2) {
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(buffer, "and ");
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
// Process relation when present.
|
|
|
|
if (relation) {
|
|
|
|
if (!(relation->rel_flags & REL_view)) {
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(buffer, "table ");
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
else {
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(buffer, "view ");
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(buffer, relation->rel_name);
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
else if (procedure) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process procedure when present.
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(b, "procedure ");
|
|
|
|
strcat(b, procedure->prc_name);
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// When there's no relation and no procedure it's a derived table.
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(b, "derived table ");
|
2003-08-16 02:36:54 +02:00
|
|
|
if (context->ctx_alias) {
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(b, context->ctx_alias);
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
}
|
2003-11-02 13:28:30 +01:00
|
|
|
strcat(buffer, " ");
|
2003-08-16 02:36:54 +02:00
|
|
|
if (!p) {
|
2003-11-02 13:28:30 +01:00
|
|
|
p = b + strlen(b);
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2003-08-16 02:36:54 +02:00
|
|
|
if (p) {
|
|
|
|
*--p = 0;
|
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2003-08-16 02:36:54 +02:00
|
|
|
if (request->req_client_dialect >= SQL_DIALECT_V6) {
|
|
|
|
if (node) {
|
|
|
|
delete node;
|
|
|
|
}
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -204,
|
|
|
|
isc_arg_gds, isc_dsql_ambiguous_field_name,
|
|
|
|
isc_arg_string, buffer,
|
|
|
|
isc_arg_string, ++p,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, name->str_data,
|
2003-08-16 02:36:54 +02:00
|
|
|
0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post_warning(isc_sqlwarn, isc_arg_number, (SLONG) 204,
|
|
|
|
isc_arg_warning, isc_dsql_ambiguous_field_name,
|
|
|
|
isc_arg_string, buffer,
|
|
|
|
isc_arg_string, ++p,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, name->str_data,
|
2003-08-16 02:36:54 +02:00
|
|
|
0);
|
|
|
|
|
|
|
|
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
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
assign_fld_dtype_from_dsc
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Set a field's descriptor from a DSC
|
2003-11-10 10:16:38 +01:00
|
|
|
(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
|
|
|
|
|
|
|
@param field
|
|
|
|
@param nod_desc
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static void assign_fld_dtype_from_dsc( dsql_fld* field, const dsc* nod_desc)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2001-05-23 15:26:42 +02: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;
|
2005-07-22 04:08:14 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (nod_desc->dsc_dtype <= dtype_any_text) {
|
|
|
|
field->fld_collation_id = DSC_GET_COLLATE(nod_desc);
|
|
|
|
field->fld_character_set_id = DSC_GET_CHARSET(nod_desc);
|
|
|
|
}
|
|
|
|
else if (nod_desc->dsc_dtype == dtype_blob)
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
field->fld_character_set_id = nod_desc->dsc_scale;
|
2005-05-28 00:45:31 +02:00
|
|
|
field->fld_collation_id = nod_desc->dsc_flags >> 8;
|
2005-06-06 10:30:03 +02:00
|
|
|
}
|
2005-07-22 04:08:14 +02:00
|
|
|
|
|
|
|
if (nod_desc->dsc_flags & DSC_nullable)
|
|
|
|
field->fld_flags |= FLD_nullable;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
/**
|
|
|
|
check_unique_fields_names
|
|
|
|
|
|
|
|
check fields (params, variables, cursors etc) names against
|
2005-05-28 00:45:31 +02:00
|
|
|
sorted array
|
|
|
|
if success, add them into array
|
2004-01-28 08:50:41 +01:00
|
|
|
**/
|
|
|
|
static void check_unique_fields_names(StrArray& names, const dsql_nod* fields)
|
|
|
|
{
|
|
|
|
if (!fields)
|
|
|
|
return;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2004-01-28 08:50:41 +01:00
|
|
|
const dsql_nod* const* ptr = fields->nod_arg;
|
|
|
|
const dsql_nod* const* const end = ptr + fields->nod_count;
|
|
|
|
const dsql_nod* temp;
|
|
|
|
const dsql_fld* field;
|
|
|
|
const dsql_str* str;
|
|
|
|
const char* name = NULL;
|
|
|
|
|
|
|
|
for (; ptr < end; ptr++) {
|
2005-05-22 05:11:41 +02:00
|
|
|
switch ((*ptr)->nod_type)
|
|
|
|
{
|
2004-01-28 08:50:41 +01:00
|
|
|
case nod_def_field:
|
|
|
|
field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
name = field->fld_name;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_param_val:
|
|
|
|
temp = (*ptr)->nod_arg[e_prm_val_fld];
|
|
|
|
field = (dsql_fld*) temp->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
name = field->fld_name;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_cursor:
|
|
|
|
str = (dsql_str*) (*ptr)->nod_arg[e_cur_name];
|
|
|
|
DEV_BLKCHK(str, dsql_type_str);
|
|
|
|
name = str->str_data;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
|
2004-07-17 01:06:31 +02:00
|
|
|
size_t pos;
|
2004-01-28 08:50:41 +01:00
|
|
|
if (!names.find(name, pos))
|
|
|
|
names.add(name);
|
|
|
|
else {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -637,
|
|
|
|
isc_arg_gds, isc_dsql_duplicate_spec,
|
|
|
|
isc_arg_string, name, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
compose
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Compose two booleans.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param expr1
|
|
|
|
@param expr2
|
2004-05-29 06:36:09 +02:00
|
|
|
@param dsql_operator
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* compose( dsql_nod* expr1, dsql_nod* expr2, NOD_TYPE dsql_operator)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(expr1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(expr2, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (!expr1)
|
|
|
|
return expr2;
|
|
|
|
|
|
|
|
if (!expr2)
|
|
|
|
return expr1;
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(dsql_operator, 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[0] = expr1;
|
|
|
|
node->nod_arg[1] = expr2;
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-03 03:09:23 +02: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* nod = MAKE_node(nod_field_name, e_fld_count);
|
|
|
|
nod->nod_arg[e_fln_name] = (dsql_nod*) MAKE_cstring(field->fld_name);
|
|
|
|
stack.push(nod);
|
|
|
|
}
|
|
|
|
|
|
|
|
return MAKE_list(stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
explode_outputs
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Generate a parameter list to correspond to procedure outputs.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param procedure
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* explode_outputs( dsql_req* request, const dsql_prc* procedure)
|
2003-01-11 03:49:13 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(procedure, dsql_type_prc);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
const SSHORT count = procedure->prc_out_count;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_list, count);
|
|
|
|
dsql_nod** ptr = node->nod_arg;
|
|
|
|
for (const dsql_fld* field = procedure->prc_outputs; field;
|
2003-10-05 08:37:26 +02:00
|
|
|
field = field->fld_next, ptr++)
|
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* p_node = MAKE_node(nod_parameter, e_par_count);
|
2003-10-05 08:37:26 +02:00
|
|
|
*ptr = p_node;
|
2001-05-23 15:26:42 +02:00
|
|
|
p_node->nod_count = 0;
|
2005-09-02 07:30:16 +02:00
|
|
|
dsql_par* parameter =
|
|
|
|
MAKE_parameter(request->req_receive, true, true, 0, NULL);
|
2006-09-03 03:09:23 +02:00
|
|
|
p_node->nod_arg[e_par_index] = (dsql_nod*) parameter->par_index;
|
2003-11-10 10:16:38 +01:00
|
|
|
p_node->nod_arg[e_par_parameter] = (dsql_nod*) parameter;
|
2001-05-23 15:26:42 +02:00
|
|
|
MAKE_desc_from_field(¶meter->par_desc, field);
|
|
|
|
parameter->par_name = parameter->par_alias = field->fld_name;
|
|
|
|
parameter->par_rel_name = procedure->prc_name;
|
|
|
|
parameter->par_owner_name = procedure->prc_owner;
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
field_appears_once
|
|
|
|
|
|
|
|
@brief Check that a field is named only once in INSERT or UPDATE statements.
|
|
|
|
|
|
|
|
|
|
|
|
@param fields
|
|
|
|
@param old_fields
|
|
|
|
@param is_insert
|
|
|
|
|
|
|
|
**/
|
|
|
|
static void field_appears_once(const dsql_nod* fields, const dsql_nod* old_fields,
|
2006-09-03 03:09:23 +02:00
|
|
|
const bool is_insert, const char* statement)
|
2005-10-06 08:08:10 +02:00
|
|
|
{
|
|
|
|
for (int i = 0; i < fields->nod_count; ++i)
|
|
|
|
{
|
|
|
|
const dsql_nod* elem1 = fields->nod_arg[i];
|
|
|
|
if (elem1->nod_type == nod_assign && !is_insert)
|
|
|
|
elem1 = elem1->nod_arg[1]; // 0 = value, 1 = field.
|
|
|
|
|
|
|
|
if (elem1->nod_type == nod_field)
|
|
|
|
{
|
|
|
|
const char* n1 = reinterpret_cast<dsql_fld*>(elem1->nod_arg[e_fld_field])->fld_name;
|
|
|
|
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[1]; // 0 = value, 1 = field.
|
|
|
|
|
|
|
|
if (elem2->nod_type == nod_field)
|
|
|
|
{
|
|
|
|
const char* n2 = reinterpret_cast<dsql_fld*>(elem2->nod_arg[e_fld_field])->fld_name;
|
|
|
|
if (strcmp(n1, n2) == 0)
|
|
|
|
{
|
|
|
|
const dsql_ctx* tmp_ctx = (dsql_ctx*) elem2->nod_arg[e_fld_context];
|
|
|
|
const dsql_rel* bad_rel = tmp_ctx ? tmp_ctx->ctx_relation : 0;
|
|
|
|
field_duplication(bad_rel ? bad_rel->rel_name : 0,
|
|
|
|
n2,
|
|
|
|
is_insert ? old_fields->nod_arg[j]: old_fields->nod_arg[j]->nod_arg[1],
|
2006-09-03 03:09:23 +02:00
|
|
|
statement);
|
2005-10-06 08:08:10 +02: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,
|
2006-09-03 03:09:23 +02:00
|
|
|
const dsql_nod* flawed_node, const char* statement)
|
2005-10-06 08:08:10 +02:00
|
|
|
{
|
|
|
|
TEXT field_buffer[MAX_SQL_IDENTIFIER_SIZE * 2];
|
|
|
|
|
|
|
|
if (qualifier_name)
|
|
|
|
{
|
2006-01-16 17:31:15 +01:00
|
|
|
sprintf(field_buffer, "%.*s.%.*s", (int) MAX_SQL_IDENTIFIER_LEN, qualifier_name,
|
|
|
|
(int) MAX_SQL_IDENTIFIER_LEN, field_name);
|
2005-10-06 08:08:10 +02:00
|
|
|
field_name = field_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -206,
|
|
|
|
isc_arg_gds, isc_dsql_no_dup_name,
|
|
|
|
isc_arg_string, field_name,
|
2006-09-03 03:09:23 +02:00
|
|
|
isc_arg_string, statement,
|
2005-10-06 08:08:10 +02:00
|
|
|
isc_arg_gds, isc_dsql_line_col_error,
|
|
|
|
isc_arg_number, (int) flawed_node->nod_line,
|
|
|
|
isc_arg_number, (int) flawed_node->nod_column,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
field_unknown
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Report a field parsing recognition error.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param qualifier_name
|
|
|
|
@param field_name
|
|
|
|
@param flawed_node
|
|
|
|
|
|
|
|
**/
|
2005-10-06 08:08:10 +02:00
|
|
|
static void field_unknown(const TEXT* qualifier_name, const TEXT* field_name,
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_nod* flawed_node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2005-10-06 08:08:10 +02:00
|
|
|
TEXT field_buffer[MAX_SQL_IDENTIFIER_SIZE * 2];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
if (qualifier_name)
|
|
|
|
{
|
2006-01-16 17:31:15 +01:00
|
|
|
sprintf(field_buffer, "%.*s.%.*s", (int) MAX_SQL_IDENTIFIER_LEN, qualifier_name,
|
|
|
|
(int) MAX_SQL_IDENTIFIER_LEN, field_name ? field_name : "*");
|
2005-10-06 08:08:10 +02:00
|
|
|
field_name = field_buffer;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
if (flawed_node)
|
|
|
|
{
|
|
|
|
if (field_name)
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -206,
|
|
|
|
isc_arg_gds, isc_dsql_field_err,
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, field_name,
|
|
|
|
isc_arg_gds, isc_dsql_line_col_error,
|
|
|
|
isc_arg_number, (int) flawed_node->nod_line,
|
|
|
|
isc_arg_number, (int) flawed_node->nod_column,
|
|
|
|
0);
|
|
|
|
else
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -206,
|
|
|
|
isc_arg_gds, isc_dsql_field_err,
|
|
|
|
isc_arg_gds, isc_dsql_line_col_error,
|
|
|
|
isc_arg_number, (int) flawed_node->nod_line,
|
|
|
|
isc_arg_number, (int) flawed_node->nod_column,
|
|
|
|
0);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
2005-10-06 08:08:10 +02:00
|
|
|
{
|
|
|
|
if (field_name)
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -206,
|
|
|
|
isc_arg_gds, isc_dsql_field_err,
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, field_name,
|
|
|
|
isc_arg_gds, isc_dsql_unknown_pos,
|
|
|
|
0);
|
|
|
|
else
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -206,
|
|
|
|
isc_arg_gds, isc_dsql_field_err,
|
|
|
|
isc_arg_gds, isc_dsql_unknown_pos,
|
|
|
|
0);
|
|
|
|
}
|
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
|
|
|
find_dbkey
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Find dbkey for named relation in request's saved dbkeys.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param relation_name
|
|
|
|
|
|
|
|
**/
|
2004-02-02 12:02:12 +01:00
|
|
|
static dsql_par* find_dbkey(const dsql_req* request, const dsql_nod* relation_name)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_msg* message = request->req_receive;
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_par* candidate = NULL;
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* rel_name = (dsql_str*) relation_name->nod_arg[e_rln_name];
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(rel_name, dsql_type_str);
|
2004-02-02 12:02:12 +01:00
|
|
|
for (dsql_par* parameter = message->msg_parameters; parameter;
|
2003-10-05 08:37:26 +02:00
|
|
|
parameter = parameter->par_next)
|
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(parameter, dsql_type_par);
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_ctx* context = parameter->par_dbkey_ctx;
|
2003-10-05 08:37:26 +02:00
|
|
|
if (context) {
|
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;
|
|
|
|
if (!strcmp(reinterpret_cast<const char*>(rel_name->str_data),
|
2003-10-05 08:37:26 +02:00
|
|
|
relation->rel_name))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (candidate)
|
|
|
|
return NULL;
|
|
|
|
else
|
|
|
|
candidate = parameter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
find_record_version
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Find record version for relation in request's saved record version
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param relation_name
|
|
|
|
|
|
|
|
**/
|
2004-02-02 12:02:12 +01:00
|
|
|
static dsql_par* find_record_version(const dsql_req* request, const dsql_nod* relation_name)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_msg* message = request->req_receive;
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_par* candidate = NULL;
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_str* rel_name = (dsql_str*) relation_name->nod_arg[e_rln_name];
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(rel_name, dsql_type_str);
|
2004-02-02 12:02:12 +01:00
|
|
|
for (dsql_par* parameter = message->msg_parameters; parameter;
|
2003-10-05 08:37:26 +02:00
|
|
|
parameter = parameter->par_next)
|
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(parameter, dsql_type_par);
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_ctx* context = parameter->par_rec_version_ctx;
|
2003-10-05 08:37:26 +02:00
|
|
|
if (context) {
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_rel* relation = context->ctx_relation;
|
|
|
|
if (!strcmp(reinterpret_cast<const char*>(rel_name->str_data),
|
2003-10-05 08:37:26 +02:00
|
|
|
relation->rel_name))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (candidate)
|
|
|
|
return NULL;
|
|
|
|
else
|
|
|
|
candidate = parameter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-14 04:05:32 +02:00
|
|
|
/**
|
|
|
|
|
|
|
|
get_context
|
|
|
|
|
|
|
|
@brief Get the context of a relation or derived table.
|
|
|
|
|
|
|
|
|
|
|
|
@param node
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_ctx* get_context(const dsql_nod* node)
|
|
|
|
{
|
|
|
|
fb_assert(node->nod_type == nod_relation || node->nod_type == nod_derived_table);
|
|
|
|
|
|
|
|
if (node->nod_type == nod_relation)
|
|
|
|
return (dsql_ctx*) node->nod_arg[e_rel_context];
|
|
|
|
else // nod_derived_table
|
|
|
|
return (dsql_ctx*) node->nod_arg[e_derived_table_context];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
invalid_reference
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Validate that an expanded field / context
|
|
|
|
pair is in a specified list. Thus is used
|
2005-05-28 00:45:31 +02:00
|
|
|
in one instance to check that a simple field selected
|
|
|
|
through a grouping rse is a grouping field -
|
2003-02-15 04:01:51 +01:00
|
|
|
thus a valid field reference. For the sake of
|
|
|
|
argument, we'll match qualified to unqualified
|
|
|
|
reference, but qualified reference must match
|
2005-05-28 00:45:31 +02:00
|
|
|
completely.
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
A list element containing a simple CAST for collation purposes
|
|
|
|
is allowed.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param context
|
|
|
|
@param node
|
|
|
|
@param list
|
|
|
|
@param inside_map
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static bool invalid_reference(const dsql_ctx* context, const dsql_nod* node,
|
|
|
|
const dsql_nod* list, bool inside_own_map,
|
2003-09-02 01:22:22 +02:00
|
|
|
bool inside_higher_map)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(list, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node == NULL) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
bool invalid = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (list) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// Check if this node (with ignoring of CASTs) appear also
|
|
|
|
// in the list of group by. If yes then it's allowed
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr < end; ptr++)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2003-09-02 01:22:22 +02:00
|
|
|
if (node_match(node, *ptr, true)) {
|
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
switch (node->nod_type) {
|
|
|
|
default:
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2005-05-28 00:45:31 +02:00
|
|
|
// FALLINTO
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_map:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_ctx* lcontext = reinterpret_cast<dsql_ctx*>(node->nod_arg[e_map_context]);
|
|
|
|
const dsql_map* lmap = reinterpret_cast<dsql_map*>(node->nod_arg[e_map_map]);
|
2003-09-02 01:22:22 +02:00
|
|
|
if (lcontext->ctx_scope_level == context->ctx_scope_level) {
|
|
|
|
invalid |= invalid_reference(context, lmap->map_node, list, true, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bool linside_higher_map = lcontext->ctx_scope_level > context->ctx_scope_level;
|
|
|
|
invalid |= invalid_reference(context, lmap->map_node, list, false, linside_higher_map);
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_field:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_ctx* lcontext = reinterpret_cast<dsql_ctx*>(node->nod_arg[e_fld_context]);
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Wouldn't it be better to call a error from this
|
2005-05-20 01:41:17 +02:00
|
|
|
// point where return is true. Then we could give
|
2003-09-02 01:22:22 +02:00
|
|
|
// the fieldname that's making the trouble
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// If we come here then this Field is used inside a
|
|
|
|
// aggregate-function. The ctx_scope_level gives the
|
2003-09-02 01:22:22 +02:00
|
|
|
// info how deep the context is inside the request.
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// If the context-scope-level from this field is
|
|
|
|
// lower or the sameas the scope-level from the
|
|
|
|
// given context then it is an invalid field
|
2003-09-02 01:22:22 +02:00
|
|
|
if (lcontext->ctx_scope_level == context->ctx_scope_level) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// Return TRUE (invalid) if this Field isn't inside
|
|
|
|
// the GROUP BY clause , that should already been
|
|
|
|
// seen in the match_node above
|
2003-09-02 01:22:22 +02:00
|
|
|
invalid = true;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_agg_count:
|
|
|
|
case nod_agg_average:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_total:
|
|
|
|
case nod_agg_average2:
|
|
|
|
case nod_agg_total2:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2003-07-29 02:29:17 +02:00
|
|
|
if (!inside_own_map) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// We are not in an aggregate from the same scope_level so
|
2003-05-07 03:57:18 +02:00
|
|
|
// check for valid fields inside this aggregate
|
2003-01-11 03:49:13 +01:00
|
|
|
if (node->nod_count) {
|
2005-05-28 00:45:31 +02:00
|
|
|
invalid |= invalid_reference(context, node->nod_arg[e_agg_function_expression], list,
|
2003-09-02 01:22:22 +02:00
|
|
|
inside_own_map, inside_higher_map);
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
if (!inside_higher_map)
|
|
|
|
{
|
2003-05-07 03:57:18 +02:00
|
|
|
if (node->nod_count) {
|
|
|
|
// If there's another aggregate with the same scope_level or
|
2003-07-28 21:22:56 +02:00
|
|
|
// an higher one then it's a invalid aggregate, because
|
2005-05-28 00:45:31 +02:00
|
|
|
// aggregate-functions from the same context can't
|
|
|
|
// be part of each other.
|
2004-04-03 01:20:29 +02:00
|
|
|
if (pass1_found_aggregate(node->nod_arg[e_agg_function_expression], context->ctx_scope_level,
|
2005-05-28 00:45:31 +02:00
|
|
|
FIELD_MATCH_TYPE_EQUAL, true))
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_agg_nested_err, 0);
|
2003-06-13 09:56:08 +02:00
|
|
|
// Nested aggregate functions are not allowed
|
|
|
|
}
|
2003-05-07 03:57:18 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
break;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_gen_id:
|
|
|
|
case nod_gen_id2:
|
|
|
|
case nod_cast:
|
|
|
|
case nod_udf:
|
2003-05-07 03:57:18 +02:00
|
|
|
// If there are no arguments given to the UDF then it's always valid
|
2003-01-11 03:49:13 +01:00
|
|
|
if (node->nod_count == 2) {
|
2005-05-28 00:45:31 +02:00
|
|
|
invalid |= invalid_reference(context, node->nod_arg[1], list,
|
2003-09-02 01:22:22 +02:00
|
|
|
inside_own_map, inside_higher_map);
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
break;
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_via:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_exists:
|
|
|
|
case nod_singular:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr < end; ptr++)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
invalid |= invalid_reference(context, *ptr, list,
|
2003-11-10 10:16:38 +01:00
|
|
|
inside_own_map, inside_higher_map);
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_order:
|
2005-05-28 00:45:31 +02:00
|
|
|
invalid |= invalid_reference(context, node->nod_arg[e_order_field], list,
|
2003-09-02 01:22:22 +02:00
|
|
|
inside_own_map, inside_higher_map);
|
2003-01-11 03:49:13 +01:00
|
|
|
break;
|
2002-08-11 10:04:54 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_coalesce:
|
|
|
|
case nod_simple_case:
|
|
|
|
case nod_searched_case:
|
|
|
|
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:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2004-10-14 20:54:54 +02:00
|
|
|
case nod_equiv:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_leq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_between:
|
|
|
|
case nod_like:
|
|
|
|
case nod_missing:
|
|
|
|
case nod_and:
|
|
|
|
case nod_or:
|
|
|
|
case nod_any:
|
|
|
|
case nod_ansi_any:
|
|
|
|
case nod_ansi_all:
|
|
|
|
case nod_not:
|
|
|
|
case nod_unique:
|
|
|
|
case nod_containing:
|
|
|
|
case nod_starting:
|
|
|
|
case nod_rse:
|
2003-05-07 03:57:18 +02:00
|
|
|
case nod_join:
|
|
|
|
case nod_join_inner:
|
|
|
|
case nod_join_left:
|
|
|
|
case nod_join_right:
|
|
|
|
case nod_join_full:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_list:
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr < end; ptr++)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
|
|
|
invalid |= invalid_reference(context, *ptr, list, inside_own_map, inside_higher_map);
|
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_alias:
|
2005-05-28 00:45:31 +02:00
|
|
|
invalid |= invalid_reference(context, node->nod_arg[e_alias_value],
|
2003-09-04 17:02:22 +02:00
|
|
|
list, inside_own_map, inside_higher_map);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_derived_field:
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
const USHORT lscope_level = (USHORT)(U_IPTR)node->nod_arg[e_derived_field_scope];
|
2003-08-24 04:36:46 +02:00
|
|
|
if (lscope_level == context->ctx_scope_level) {
|
2003-09-02 01:22:22 +02:00
|
|
|
invalid |= true;
|
2003-08-24 04:36:46 +02:00
|
|
|
}
|
2005-11-09 00:49:50 +01:00
|
|
|
else if (context->ctx_scope_level < lscope_level) {
|
|
|
|
invalid |= invalid_reference(context, node->nod_arg[e_derived_field_value],
|
|
|
|
list, inside_own_map, inside_higher_map);
|
|
|
|
}
|
2003-08-24 04:36:46 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_aggregate:
|
2005-05-28 00:45:31 +02:00
|
|
|
invalid |= invalid_reference(context, node->nod_arg[e_agg_rse],
|
2003-09-02 01:22:22 +02:00
|
|
|
list, inside_own_map, inside_higher_map);
|
2003-01-11 03:49:13 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_relation:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_ctx* lrelation_context = reinterpret_cast<dsql_ctx*>(node->nod_arg[e_rel_context]);
|
2003-09-02 01:22:22 +02:00
|
|
|
// Check if relation is a procedure
|
|
|
|
if (lrelation_context->ctx_procedure) {
|
2005-11-30 10:03:53 +01:00
|
|
|
// Check if the parameters are valid
|
|
|
|
invalid |= invalid_reference(context, lrelation_context->ctx_proc_inputs,
|
|
|
|
list, inside_own_map, inside_higher_map);
|
2003-04-17 22:58:36 +02:00
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2003-04-17 22:58:36 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_variable:
|
|
|
|
case nod_constant:
|
|
|
|
case nod_parameter:
|
|
|
|
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_internal_info:
|
2005-04-24 20:26:12 +02:00
|
|
|
case nod_dom_value:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_dbkey:
|
2003-08-24 04:36:46 +02:00
|
|
|
case nod_derived_table:
|
2003-10-13 14:56:44 +02:00
|
|
|
case nod_plan_expr:
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
node_match
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Compare two nodes for equality of value.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
[2002-08-04]--- Arno Brinkman
|
2003-10-05 08:37:26 +02:00
|
|
|
If ignore_map_cast is true and the node1 is
|
|
|
|
type nod_cast or nod_map then node_match is
|
2005-05-28 00:45:31 +02:00
|
|
|
calling itselfs again with the node1
|
2003-02-15 04:01:51 +01:00
|
|
|
CASTs source or map->node.
|
|
|
|
This is for allow CAST to other datatypes
|
|
|
|
without complaining that it's an unknown
|
2006-08-01 22:37:58 +02:00
|
|
|
column reference. (Aggregate functions)
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param node1
|
|
|
|
@param node2
|
|
|
|
@param ignore_map_cast
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static bool node_match(const dsql_nod* node1, const dsql_nod* node2,
|
|
|
|
bool ignore_map_cast)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(node1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(node2, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if ((!node1) && (!node2)) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return true;
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if ((!node1) || (!node2)) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
if (ignore_map_cast && node1->nod_type == nod_cast) {
|
2003-09-02 01:22:22 +02:00
|
|
|
// If node2 is also cast and same type continue with both sources.
|
2005-05-28 00:45:31 +02:00
|
|
|
if (node2->nod_type == nod_cast &&
|
2002-08-11 10:04:54 +02:00
|
|
|
node1->nod_desc.dsc_dtype == node2->nod_desc.dsc_dtype &&
|
|
|
|
node1->nod_desc.dsc_scale == node2->nod_desc.dsc_scale &&
|
|
|
|
node1->nod_desc.dsc_length == node2->nod_desc.dsc_length &&
|
2003-11-18 08:58:35 +01:00
|
|
|
node1->nod_desc.dsc_sub_type == node2->nod_desc.dsc_sub_type)
|
|
|
|
{
|
2003-01-11 03:49:13 +01:00
|
|
|
return node_match(node1->nod_arg[e_cast_source], node2->nod_arg[e_cast_source], ignore_map_cast);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2002-10-19 02:32:23 +02:00
|
|
|
else {
|
2003-01-11 03:49:13 +01:00
|
|
|
return node_match(node1->nod_arg[e_cast_source], node2, ignore_map_cast);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ignore_map_cast && node1->nod_type == nod_map) {
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_map* map1 = (dsql_map*)node1->nod_arg[e_map_map];
|
2003-01-11 03:49:13 +01:00
|
|
|
DEV_BLKCHK(map1, dsql_type_map);
|
|
|
|
if (node2->nod_type == nod_map) {
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_map* map2 = (dsql_map*)node2->nod_arg[e_map_map];
|
2003-01-11 03:49:13 +01:00
|
|
|
DEV_BLKCHK(map2, dsql_type_map);
|
|
|
|
if (node1->nod_arg[e_map_context] != node2->nod_arg[e_map_context]) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
return node_match(map1->map_node, map2->map_node, ignore_map_cast);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return node_match(map1->map_node, node2, ignore_map_cast);
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
// We don't care about the alias itself but only about its field.
|
2002-10-19 02:32:23 +02:00
|
|
|
if ((node1->nod_type == nod_alias) || (node2->nod_type == nod_alias)) {
|
|
|
|
if ((node1->nod_type == nod_alias) && (node2->nod_type == nod_alias)) {
|
2002-10-04 19:53:35 +02:00
|
|
|
return node_match(node1->nod_arg[e_alias_value],
|
2003-01-11 03:49:13 +01:00
|
|
|
node2->nod_arg[e_alias_value], ignore_map_cast);
|
2002-10-04 19:53:35 +02:00
|
|
|
}
|
2002-10-19 02:32:23 +02:00
|
|
|
else {
|
|
|
|
if (node1->nod_type == nod_alias) {
|
2003-01-11 03:49:13 +01:00
|
|
|
return node_match(node1->nod_arg[e_alias_value], node2, ignore_map_cast);
|
2002-10-04 19:53:35 +02:00
|
|
|
}
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node2->nod_type == nod_alias) {
|
2003-01-11 03:49:13 +01:00
|
|
|
return node_match(node1, node2->nod_arg[e_alias_value], ignore_map_cast);
|
2002-10-04 19:53:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
// Handle derived fields.
|
2006-03-14 11:57:11 +01:00
|
|
|
if ((node1->nod_type == nod_derived_field) || (node2->nod_type == nod_derived_field))
|
|
|
|
{
|
|
|
|
if ((node1->nod_type == nod_derived_field) && (node2->nod_type == nod_derived_field))
|
|
|
|
{
|
|
|
|
const USHORT scope_level1 = (USHORT)(U_IPTR)node1->nod_arg[e_derived_field_scope];
|
|
|
|
const USHORT scope_level2 = (USHORT)(U_IPTR)node2->nod_arg[e_derived_field_scope];
|
|
|
|
if (scope_level1 != scope_level2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dsql_str* alias1 = (dsql_str*) node1->nod_arg[e_derived_field_name];
|
|
|
|
dsql_str* alias2 = (dsql_str*) node2->nod_arg[e_derived_field_name];
|
|
|
|
if (strcmp(alias1->str_data, alias2->str_data))
|
|
|
|
return false;
|
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
return node_match(node1->nod_arg[e_derived_field_value],
|
|
|
|
node2->nod_arg[e_derived_field_value], ignore_map_cast);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (node1->nod_type == nod_derived_field) {
|
|
|
|
return node_match(node1->nod_arg[e_derived_field_value], node2, ignore_map_cast);
|
|
|
|
}
|
|
|
|
if (node2->nod_type == nod_derived_field) {
|
|
|
|
return node_match(node1, node2->nod_arg[e_derived_field_value], ignore_map_cast);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if ((node1->nod_type != node2->nod_type) || (node1->nod_count != node2->nod_count)) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is to get rid of assertion failures when trying
|
|
|
|
// to node_match nod_aggregate's children. This was happening because not
|
2003-10-05 08:37:26 +02:00
|
|
|
// all of the children are of type "dsql_nod". Pointer to the first child
|
2003-09-02 01:22:22 +02:00
|
|
|
// (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
|
2005-05-28 00:45:31 +02:00
|
|
|
// nod_aggregate is created for an rse that have aggregate expression,
|
2003-09-02 01:22:22 +02:00
|
|
|
// group by or having clause and each rse has its own context). But just in
|
|
|
|
// case we compare two other subtrees.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node1->nod_type == nod_aggregate) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (node1->nod_arg[e_agg_context] != node2->nod_arg[e_agg_context]) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return node_match( node1->nod_arg[e_agg_group],
|
2003-01-11 03:49:13 +01:00
|
|
|
node2->nod_arg[e_agg_group], ignore_map_cast) &&
|
2001-05-23 15:26:42 +02:00
|
|
|
node_match( node1->nod_arg[e_agg_rse],
|
2003-01-11 03:49:13 +01:00
|
|
|
node2->nod_arg[e_agg_rse], ignore_map_cast);
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node1->nod_type == nod_relation) {
|
|
|
|
if (node1->nod_arg[e_rel_context] != node2->nod_arg[e_rel_context]) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node1->nod_type == nod_field) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (node1->nod_arg[e_fld_field] != node2->nod_arg[e_fld_field] ||
|
2004-12-24 09:52:39 +01:00
|
|
|
node1->nod_arg[e_fld_context] != node2->nod_arg[e_fld_context])
|
|
|
|
{
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node1->nod_arg[e_fld_indices] || node2->nod_arg[e_fld_indices]) {
|
2001-05-23 15:26:42 +02:00
|
|
|
return node_match(node1->nod_arg[e_fld_indices],
|
2003-01-11 03:49:13 +01:00
|
|
|
node2->nod_arg[e_fld_indices], ignore_map_cast);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node1->nod_type == nod_constant) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (node1->nod_desc.dsc_dtype != node2->nod_desc.dsc_dtype ||
|
|
|
|
node1->nod_desc.dsc_length != node2->nod_desc.dsc_length ||
|
2004-12-24 09:52:39 +01:00
|
|
|
node1->nod_desc.dsc_scale != node2->nod_desc.dsc_scale)
|
|
|
|
{
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
const UCHAR* p1 = node1->nod_desc.dsc_address;
|
|
|
|
const UCHAR* p2 = node2->nod_desc.dsc_address;
|
|
|
|
for (USHORT l = node1->nod_desc.dsc_length; l > 0; l--) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*p1++ != *p2++) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node1->nod_type == nod_map) {
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_map* map1 = (dsql_map*)node1->nod_arg[e_map_map];
|
|
|
|
const dsql_map* map2 = (dsql_map*)node2->nod_arg[e_map_map];
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(map1, dsql_type_map);
|
|
|
|
DEV_BLKCHK(map2, dsql_type_map);
|
2003-01-11 03:49:13 +01:00
|
|
|
return node_match(map1->map_node, map2->map_node, ignore_map_cast);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((node1->nod_type == nod_gen_id) ||
|
|
|
|
(node1->nod_type == nod_gen_id2) ||
|
|
|
|
(node1->nod_type == nod_udf) ||
|
2005-05-28 00:45:31 +02:00
|
|
|
(node1->nod_type == nod_cast))
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (node1->nod_arg[0] != node2->nod_arg[0]) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (node1->nod_count == 2) {
|
2003-01-11 03:49:13 +01:00
|
|
|
return node_match(node1->nod_arg[1], node2->nod_arg[1], ignore_map_cast);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
else {
|
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((node1->nod_type == nod_agg_count) ||
|
|
|
|
(node1->nod_type == nod_agg_total) ||
|
|
|
|
(node1->nod_type == nod_agg_total2) ||
|
|
|
|
(node1->nod_type == nod_agg_average2) ||
|
2006-08-01 22:37:58 +02:00
|
|
|
(node1->nod_type == nod_agg_average) ||
|
|
|
|
(node1->nod_type == nod_agg_list))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if ((node1->nod_flags & NOD_AGG_DISTINCT) !=
|
2005-05-28 00:45:31 +02:00
|
|
|
(node2->nod_flags & NOD_AGG_DISTINCT))
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-10-19 02:32:23 +02:00
|
|
|
if (node1->nod_type == nod_variable) {
|
2004-02-02 12:02:12 +01:00
|
|
|
const dsql_var* var1 = reinterpret_cast<dsql_var*>(node1->nod_arg[e_var_variable]);
|
|
|
|
const dsql_var* var2 = reinterpret_cast<dsql_var*>(node2->nod_arg[e_var_variable]);
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(var1, dsql_type_var);
|
|
|
|
DEV_BLKCHK(var2, dsql_type_var);
|
2001-05-23 15:26:42 +02:00
|
|
|
if ((strcmp(var1->var_name, var2->var_name)) ||
|
|
|
|
(var1->var_field != var2->var_field) ||
|
|
|
|
(var1->var_variable_number != var2->var_variable_number) ||
|
|
|
|
(var1->var_msg_item != var2->var_msg_item) ||
|
2005-05-28 00:45:31 +02:00
|
|
|
(var1->var_msg_number != var2->var_msg_number))
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2005-05-04 11:53:37 +02:00
|
|
|
if (node1->nod_type == nod_parameter) {
|
|
|
|
// Parameters are equal when there index is the same
|
2005-05-17 09:17:25 +02:00
|
|
|
const dsql_par* parameter1 = (dsql_par*) node1->nod_arg[e_par_parameter];
|
|
|
|
const dsql_par* parameter2 = (dsql_par*) node2->nod_arg[e_par_parameter];
|
2005-05-04 11:53:37 +02:00
|
|
|
return (parameter1->par_index == parameter2->par_index);
|
|
|
|
}
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_nod* const* ptr1 = node1->nod_arg;
|
|
|
|
const dsql_nod* const* ptr2 = node2->nod_arg;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr1 + node1->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr1 < end; ptr1++, ptr2++)
|
2003-11-10 10:16:38 +01:00
|
|
|
{
|
2003-01-11 03:49:13 +01:00
|
|
|
if (!node_match(*ptr1, *ptr2, ignore_map_cast)) {
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2002-10-19 02:32:23 +02:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
return true;
|
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_any
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Compile a parsed request into something more interesting.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param ntype
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_any( dsql_req* request, dsql_nod* input, NOD_TYPE ntype)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-02 14:41:02 +02:00
|
|
|
// create a derived table representing our subquery
|
|
|
|
dsql_nod* dt = MAKE_node(nod_derived_table, e_derived_table_count);
|
2005-05-17 09:17:25 +02:00
|
|
|
// Ignore validation for columnames that must exist for "user" derived tables.
|
2005-08-16 23:19:17 +02:00
|
|
|
dt->nod_flags |= NOD_DT_IGNORE_COLUMN_CHECK;
|
2005-05-02 14:41:02 +02:00
|
|
|
dt->nod_arg[e_derived_table_rse] = input->nod_arg[1];
|
|
|
|
dsql_nod* from = MAKE_node(nod_list, 1);
|
|
|
|
from->nod_arg[0] = dt;
|
|
|
|
dsql_nod* query_spec = MAKE_node(nod_query_spec, e_qry_count);
|
|
|
|
query_spec->nod_arg[e_qry_from] = from;
|
|
|
|
dsql_nod* select_expr = MAKE_node(nod_select_expr, e_sel_count);
|
|
|
|
select_expr->nod_arg[e_sel_query_spec] = query_spec;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-01-22 20:14:27 +01:00
|
|
|
const DsqlContextStack::iterator base(*request->req_context);
|
2005-05-02 14:41:02 +02:00
|
|
|
dsql_nod* rse = PASS1_rse(request, select_expr, NULL);
|
2005-02-21 14:18:49 +01:00
|
|
|
|
2005-05-02 14:41:02 +02:00
|
|
|
// create a conjunct to be injected
|
|
|
|
dsql_nod* temp = MAKE_node(input->nod_type, 2);
|
|
|
|
temp->nod_arg[0] = PASS1_node(request, input->nod_arg[0], false);
|
|
|
|
temp->nod_arg[1] = rse->nod_arg[e_rse_items]->nod_arg[0];
|
2003-01-12 21:27:00 +01:00
|
|
|
|
2005-05-02 14:41:02 +02:00
|
|
|
rse->nod_arg[e_rse_boolean] = temp;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-02 14:41:02 +02:00
|
|
|
// create output node
|
|
|
|
dsql_nod* node = MAKE_node(ntype, 1);
|
|
|
|
node->nod_arg[0] = rse;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-01-22 20:14:27 +01:00
|
|
|
// Finish off by cleaning up contexts
|
|
|
|
request->req_context->clear(base);
|
|
|
|
|
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_blob
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Process a blob get or put segment.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static void pass1_blob( dsql_req* request, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-28 15:07:29 +02:00
|
|
|
PASS1_make_context(request, input->nod_arg[e_blb_relation]);
|
2005-02-10 22:14:52 +01:00
|
|
|
dsql_nod* field = pass1_field(request, input->nod_arg[e_blb_field], false, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (field->nod_desc.dsc_dtype != dtype_blob)
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 206,
|
|
|
|
isc_arg_gds, isc_dsql_blob_err, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
request->req_type = (input->nod_type == nod_get_segment) ?
|
|
|
|
REQ_GET_SEGMENT : REQ_PUT_SEGMENT;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_blb* blob = FB_NEW(*tdsql->getDefaultPool()) dsql_blb;
|
2003-10-05 08:37:26 +02:00
|
|
|
request->req_blob = blob;
|
2001-05-23 15:26:42 +02:00
|
|
|
blob->blb_field = field;
|
|
|
|
blob->blb_open_in_msg = request->req_send;
|
2004-08-16 14:28:43 +02:00
|
|
|
blob->blb_open_out_msg = FB_NEW(*tdsql->getDefaultPool()) dsql_msg;
|
2001-05-23 15:26:42 +02:00
|
|
|
blob->blb_segment_msg = request->req_receive;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Create a parameter for the blob segment
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-09-02 07:30:16 +02:00
|
|
|
dsql_par* parameter =
|
|
|
|
MAKE_parameter(blob->blb_segment_msg, true, true, 0, NULL);
|
2003-10-05 08:37:26 +02:00
|
|
|
blob->blb_segment = parameter;
|
2001-05-23 15:26:42 +02:00
|
|
|
parameter->par_desc.dsc_dtype = dtype_text;
|
2004-05-21 08:16:17 +02:00
|
|
|
parameter->par_desc.dsc_ttype() = ttype_binary;
|
2001-05-23 15:26:42 +02:00
|
|
|
parameter->par_desc.dsc_length =
|
2003-11-10 10:16:38 +01:00
|
|
|
((dsql_fld*) field->nod_arg[e_fld_field])->fld_seg_length;
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(field->nod_arg[e_fld_field], dsql_type_fld);
|
2001-05-23 15:26:42 +02: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.
|
|
|
|
*/
|
|
|
|
if (input->nod_type == nod_get_segment)
|
|
|
|
parameter->par_desc.dsc_flags |= DSC_nullable;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Create a parameter for the blob id
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
dsql_msg* temp_msg = (input->nod_type == nod_get_segment) ?
|
|
|
|
blob->blb_open_in_msg : blob->blb_open_out_msg;
|
2005-09-02 07:30:16 +02:00
|
|
|
blob->blb_blob_id = parameter =
|
|
|
|
MAKE_parameter(temp_msg, true, true, 0, NULL);
|
|
|
|
MAKE_desc(request, ¶meter->par_desc, field, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
parameter->par_desc.dsc_dtype = dtype_quad;
|
|
|
|
parameter->par_desc.dsc_scale = 0;
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* list = input->nod_arg[e_blb_filter];
|
2003-10-05 08:37:26 +02:00
|
|
|
if (list) {
|
2003-09-02 01:22:22 +02:00
|
|
|
if (list->nod_arg[0]) {
|
|
|
|
blob->blb_from = PASS1_node(request, list->nod_arg[0], false);
|
|
|
|
}
|
|
|
|
if (list->nod_arg[1]) {
|
|
|
|
blob->blb_to = PASS1_node(request, list->nod_arg[1], false);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
if (!blob->blb_from) {
|
2003-11-10 10:16:38 +01:00
|
|
|
blob->blb_from = MAKE_constant((dsql_str*) 0, CONSTANT_SLONG);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
|
|
|
if (!blob->blb_to) {
|
2003-11-10 10:16:38 +01:00
|
|
|
blob->blb_to = MAKE_constant((dsql_str*) 0, CONSTANT_SLONG);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
for (parameter = blob->blb_open_in_msg->msg_parameters; parameter;
|
|
|
|
parameter = parameter->par_next)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (parameter->par_index >
|
2005-05-28 00:45:31 +02:00
|
|
|
((input->nod_type == nod_get_segment) ? 1 : 0))
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
parameter->par_desc.dsc_dtype = dtype_short;
|
|
|
|
parameter->par_desc.dsc_scale = 0;
|
|
|
|
parameter->par_desc.dsc_length = sizeof(SSHORT);
|
|
|
|
}
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
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_coalesce
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Handle a reference to a coalesce function.
|
2003-03-31 21:11:55 +02:00
|
|
|
|
|
|
|
COALESCE(expr-1, expr-2 [, expr-n])
|
|
|
|
is the same as :
|
|
|
|
CASE WHEN (expr-1 IS NULL) THEN expr-2 ELSE expr-1 END
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_coalesce( dsql_req* request, dsql_nod* input, bool proc_flag)
|
2002-08-03 17:27:20 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(input->nod_arg[0], dsql_type_nod);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_coalesce, 2);
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-03-31 21:11:55 +02:00
|
|
|
// Pass list of arguments 2..n on stack and make a list from it
|
2006-07-30 04:59:29 +02:00
|
|
|
{ // scope
|
|
|
|
DsqlNodStack stack;
|
|
|
|
pass1_put_args_on_stack(request, input->nod_arg[0], stack, proc_flag);
|
|
|
|
pass1_put_args_on_stack(request, input->nod_arg[1], stack, proc_flag);
|
|
|
|
node->nod_arg[0] = MAKE_list(stack);
|
|
|
|
} // end scope
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-03-31 21:11:55 +02:00
|
|
|
// Parse the items again for the return values.
|
|
|
|
// We can't copy else we get an 'context in use error' with sub-selects.
|
2006-07-30 04:59:29 +02:00
|
|
|
{ // scope
|
|
|
|
DsqlNodStack stack;
|
|
|
|
pass1_put_args_on_stack(request, input->nod_arg[0], stack, proc_flag);
|
|
|
|
pass1_put_args_on_stack(request, input->nod_arg[1], stack, proc_flag);
|
|
|
|
node->nod_arg[1] = MAKE_list(stack);
|
|
|
|
} // end scope
|
2003-03-31 21:11:55 +02:00
|
|
|
|
|
|
|
// Set descriptor for output node
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc(request, &node->nod_desc, node, NULL);
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-03-31 21:11:55 +02:00
|
|
|
// Set parameter-types if parameters are there
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = node->nod_arg[0]->nod_arg;
|
|
|
|
const dsql_nod* const* end = ptr + node->nod_arg[0]->nod_count;
|
2003-03-31 21:11:55 +02:00
|
|
|
for (; ptr < end; ptr++) {
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, *ptr, node, false);
|
2003-03-31 21:11:55 +02:00
|
|
|
}
|
|
|
|
ptr = node->nod_arg[1]->nod_arg;
|
|
|
|
end = ptr + node->nod_arg[1]->nod_count;
|
|
|
|
for (; ptr < end; ptr++) {
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, *ptr, node, false);
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
|
|
|
|
2002-08-03 17:27:20 +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_collate
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +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)
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param sub1
|
|
|
|
@param collation
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* pass1_collate( dsql_req* request, dsql_nod* sub1,
|
|
|
|
const dsql_str* collation)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(sub1, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(collation, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_cast, e_cast_count);
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_fld* field = FB_NEW_RPT(*tdsql->getDefaultPool(), 1) dsql_fld;
|
2001-05-23 15:26:42 +02:00
|
|
|
field->fld_name[0] = 0;
|
2003-11-10 10:16:38 +01:00
|
|
|
node->nod_arg[e_cast_target] = (dsql_nod*) field;
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[e_cast_source] = sub1;
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc(request, &sub1->nod_desc, sub1, NULL);
|
|
|
|
|
|
|
|
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))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
assign_fld_dtype_from_dsc(field, &sub1->nod_desc);
|
|
|
|
field->fld_character_length = 0;
|
|
|
|
}
|
|
|
|
else {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 204,
|
|
|
|
isc_arg_gds, isc_dsql_datatype_err,
|
|
|
|
isc_arg_gds, isc_collation_requires_text, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
DDL_resolve_intl_type(request, field, collation);
|
|
|
|
MAKE_desc_from_field(&node->nod_desc, field);
|
|
|
|
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_constant
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Turn an international string reference into internal
|
|
|
|
subtype ID.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param constant
|
|
|
|
|
|
|
|
**/
|
2006-03-10 01:08:44 +01:00
|
|
|
static dsql_nod* pass1_constant( dsql_req* request, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
2006-03-10 01:08:44 +01:00
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-03-10 01:08:44 +01:00
|
|
|
if (input->nod_desc.dsc_dtype > dtype_any_text) {
|
|
|
|
return input;
|
2003-02-12 20:28:13 +01:00
|
|
|
}
|
|
|
|
|
2006-03-10 01:08:44 +01:00
|
|
|
dsql_nod* constant = MAKE_node(input->nod_type, 1);
|
|
|
|
constant->nod_arg[0] = input->nod_arg[0];
|
|
|
|
constant->nod_desc = input->nod_desc;
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* string = (dsql_str*) constant->nod_arg[0];
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
2003-02-12 20:28:13 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
if (string && string->str_charset)
|
2003-02-12 20:28:13 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
const dsql_intlsym* resolved =
|
|
|
|
METD_get_charset(request, strlen(string->str_charset),
|
|
|
|
string->str_charset);
|
|
|
|
if (!resolved)
|
|
|
|
{
|
|
|
|
// character set name is not defined
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 504, isc_arg_gds,
|
|
|
|
isc_charset_not_found, isc_arg_string, string->str_charset,
|
|
|
|
0);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
if (global_temp_collation_name)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
const dsql_intlsym* resolved_collation =
|
|
|
|
METD_get_collation(request, global_temp_collation_name, resolved->intlsym_charset_id);
|
|
|
|
if (!resolved_collation)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
** Specified collation not found
|
|
|
|
*/
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 204, isc_arg_gds,
|
|
|
|
isc_dsql_datatype_err, isc_arg_gds,
|
2005-06-14 05:16:54 +02:00
|
|
|
isc_collation_not_found,
|
|
|
|
isc_arg_string, global_temp_collation_name->str_data,
|
|
|
|
isc_arg_string, resolved->intlsym_name, 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
|
|
|
resolved = resolved_collation;
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
INTL_ASSIGN_TTYPE(&constant->nod_desc, resolved->intlsym_ttype);
|
|
|
|
}
|
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
USHORT adjust = 0;
|
2005-05-28 00:45:31 +02:00
|
|
|
if (constant->nod_desc.dsc_dtype == dtype_varying)
|
2005-06-06 10:30:03 +02:00
|
|
|
adjust = sizeof(USHORT);
|
2005-05-28 00:45:31 +02:00
|
|
|
else if (constant->nod_desc.dsc_dtype == dtype_cstring)
|
2005-06-06 10:30:03 +02:00
|
|
|
adjust = 1;
|
|
|
|
|
|
|
|
constant->nod_desc.dsc_length -= adjust;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-08-21 17:52:30 +02:00
|
|
|
USHORT length;
|
2005-06-13 04:12:14 +02:00
|
|
|
|
2005-09-14 21:06:28 +02:00
|
|
|
ISC_STATUS_ARRAY status_vector = {0};
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
THREAD_EXIT();
|
2005-06-06 10:30:03 +02:00
|
|
|
const ISC_STATUS s =
|
2005-09-14 21:06:28 +02:00
|
|
|
gds__intl_function(status_vector, &request->req_dbb->dbb_database_handle,
|
2005-08-21 17:52:30 +02:00
|
|
|
INTL_FUNCTION_CHAR_LENGTH, INTL_GET_CHARSET(&constant->nod_desc),
|
|
|
|
constant->nod_desc.dsc_length, constant->nod_desc.dsc_address, &length);
|
2005-05-28 00:45:31 +02:00
|
|
|
THREAD_ENTER();
|
2005-09-14 21:06:28 +02:00
|
|
|
|
|
|
|
if (s) {
|
|
|
|
ERRD_punt(status_vector);
|
|
|
|
}
|
2005-06-06 10:30:03 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
constant->nod_desc.dsc_length = length * METD_get_charset_bpc(request, INTL_GET_CHARSET(&constant->nod_desc));
|
2005-06-06 10:30:03 +02:00
|
|
|
constant->nod_desc.dsc_length += adjust;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return constant;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
pass1_cursor_context
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
@brief Turn a cursor reference into a record selection expression.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param cursor
|
|
|
|
@param relation_name
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_ctx* pass1_cursor_context( dsql_req* request, const dsql_nod* cursor,
|
|
|
|
const dsql_nod* relation_name)
|
2003-11-02 13:28:30 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(cursor, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_str* rname = (dsql_str*) relation_name->nod_arg[e_rln_name];
|
2003-11-02 13:28:30 +01:00
|
|
|
DEV_BLKCHK(rname, dsql_type_str);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_str* string = (dsql_str*) cursor->nod_arg[e_cur_name];
|
2003-11-02 13:28:30 +01:00
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
|
|
|
|
|
|
|
// this function must throw an error if no cursor was found
|
2004-11-28 04:44:24 +01:00
|
|
|
const dsql_nod* node = pass1_cursor_name(request, string, NOD_CURSOR_ALL, true);
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node);
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2004-11-17 15:50:33 +01:00
|
|
|
const dsql_nod* rse = node->nod_arg[e_cur_rse];
|
|
|
|
fb_assert(rse);
|
|
|
|
|
|
|
|
if (rse->nod_arg[e_rse_reduced]) {
|
|
|
|
// cursor with DISTINCT is not updatable
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 510,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_update_err,
|
|
|
|
isc_arg_string, string->str_data, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
const dsql_nod* temp = rse->nod_arg[e_rse_streams];
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_ctx* context = NULL;
|
|
|
|
dsql_nod* const* ptr = temp->nod_arg;
|
2003-11-10 10:16:38 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + temp->nod_count;
|
2003-11-02 13:28:30 +01:00
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* r_node = *ptr;
|
2003-11-02 13:28:30 +01:00
|
|
|
if (r_node->nod_type == nod_relation) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_ctx* candidate = (dsql_ctx*) r_node->nod_arg[e_rel_context];
|
2003-11-02 13:28:30 +01:00
|
|
|
DEV_BLKCHK(candidate, dsql_type_ctx);
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_rel* relation = candidate->ctx_relation;
|
2003-11-02 13:28:30 +01:00
|
|
|
DEV_BLKCHK(rname, dsql_type_str);
|
2004-11-17 15:50:33 +01:00
|
|
|
if (!(relation->rel_flags & REL_view) &&
|
|
|
|
!strcmp(rname->str_data, relation->rel_name))
|
2003-11-02 13:28:30 +01:00
|
|
|
{
|
|
|
|
if (context)
|
2004-11-17 15:50:33 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 504,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_err,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_rel_ambiguous,
|
|
|
|
isc_arg_string, rname->str_data,
|
|
|
|
isc_arg_string, string->str_data, 0);
|
2003-11-02 13:28:30 +01:00
|
|
|
else
|
|
|
|
context = candidate;
|
|
|
|
}
|
|
|
|
}
|
2004-11-17 15:50:33 +01:00
|
|
|
else if (r_node->nod_type == nod_aggregate) {
|
|
|
|
// cursor with aggregation is not updatable
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 510,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_update_err,
|
|
|
|
isc_arg_string, string->str_data, 0);
|
|
|
|
}
|
2004-11-23 05:05:49 +01:00
|
|
|
// note that nod_union and nod_join will cause the error below,
|
2004-11-17 15:50:33 +01:00
|
|
|
// as well as derived tables. Some cases deserve fixing in the future
|
2003-11-02 13:28:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!context)
|
2004-11-17 15:50:33 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 504,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_err,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_rel_not_found,
|
|
|
|
isc_arg_string, rname->str_data,
|
|
|
|
isc_arg_string, string->str_data, 0);
|
2003-11-02 13:28:30 +01:00
|
|
|
|
|
|
|
return context;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
pass1_cursor_name
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
@brief Find a cursor.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param string
|
2004-11-17 15:50:33 +01:00
|
|
|
@param mask
|
|
|
|
@param existence_flag
|
2003-11-02 13:28:30 +01:00
|
|
|
|
|
|
|
**/
|
2004-05-09 07:48:33 +02:00
|
|
|
static dsql_nod* pass1_cursor_name(dsql_req* request, const dsql_str* string,
|
2004-11-17 15:50:33 +01:00
|
|
|
USHORT mask, bool existence_flag)
|
2003-11-02 13:28:30 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
2003-11-05 10:02:33 +01:00
|
|
|
dsql_nod* cursor = NULL;
|
2003-11-02 13:28:30 +01:00
|
|
|
|
2004-11-17 15:50:33 +01:00
|
|
|
if (!strlen(string->str_data)) {
|
|
|
|
if (existence_flag) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 504,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_err,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_invalid, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 502,
|
|
|
|
isc_arg_gds, isc_dsql_decl_err,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_invalid, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-27 18:26:52 +02:00
|
|
|
for (DsqlNodStack::iterator itr(request->req_cursors); itr.hasData(); ++itr) {
|
2004-04-18 16:22:27 +02:00
|
|
|
cursor = itr.object();
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_str* cname = (dsql_str*) cursor->nod_arg[e_cur_name];
|
2004-11-17 15:50:33 +01:00
|
|
|
if (!strcmp(string->str_data, cname->str_data) && (cursor->nod_flags & mask))
|
2003-11-02 13:28:30 +01:00
|
|
|
break;
|
|
|
|
cursor = NULL;
|
|
|
|
}
|
|
|
|
|
2004-11-17 15:50:33 +01:00
|
|
|
if (!cursor && existence_flag) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 504,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_err,
|
2004-11-17 15:50:33 +01:00
|
|
|
isc_arg_gds, isc_dsql_cursor_not_found,
|
|
|
|
isc_arg_string, string->str_data,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
else if (cursor && !existence_flag) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 502,
|
|
|
|
isc_arg_gds, isc_dsql_decl_err,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_exists,
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_string, string->str_data,
|
2003-11-02 13:28:30 +01:00
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-11-02 13:28:30 +01:00
|
|
|
pass1_cursor_reference
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Turn a cursor reference into a record selection expression.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param cursor
|
|
|
|
@param relation_name
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* pass1_cursor_reference( dsql_req* request,
|
|
|
|
const dsql_nod* cursor, dsql_nod* relation_name)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(cursor, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(relation_name, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Lookup parent request
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_str* string = (dsql_str*) cursor->nod_arg[e_cur_name];
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(string, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-05 10:02:33 +01:00
|
|
|
const dsql_sym* symbol =
|
2001-05-23 15:26:42 +02:00
|
|
|
HSHD_lookup(request->req_dbb,
|
2003-11-05 10:02:33 +01:00
|
|
|
reinterpret_cast<const char*>(string->str_data),
|
2003-10-05 08:37:26 +02:00
|
|
|
static_cast<SSHORT>(string->str_length), SYM_cursor,
|
2001-05-23 15:26:42 +02:00
|
|
|
0);
|
|
|
|
|
2003-11-28 07:48:34 +01:00
|
|
|
if (!symbol) {
|
2004-11-17 15:50:33 +01:00
|
|
|
// cursor is not found
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 504,
|
|
|
|
isc_arg_gds, isc_dsql_cursor_err,
|
2004-11-17 15:50:33 +01:00
|
|
|
isc_arg_gds, isc_dsql_cursor_not_found,
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_string, string->str_data,
|
2003-11-02 13:28:30 +01:00
|
|
|
0);
|
2003-11-28 07:48:34 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_req* parent = (dsql_req*) symbol->sym_object;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Verify that the cursor is appropriate and updatable
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_par* rv_source = find_record_version(parent, relation_name);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_par* source;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (parent->req_type != REQ_SELECT_UPD ||
|
|
|
|
!(source = find_dbkey(parent, relation_name)) ||
|
2005-09-10 09:15:24 +02:00
|
|
|
!rv_source)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
// cursor is not updatable
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 510,
|
2004-11-17 15:50:33 +01:00
|
|
|
isc_arg_gds, isc_dsql_cursor_update_err,
|
|
|
|
isc_arg_string, string->str_data, 0);
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
request->req_parent = parent;
|
|
|
|
request->req_parent_dbkey = source;
|
|
|
|
request->req_parent_rec_version = rv_source;
|
|
|
|
request->req_sibling = parent->req_offspring;
|
|
|
|
parent->req_offspring = request;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Build record selection expression
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* rse = MAKE_node(nod_rse, e_rse_count);
|
|
|
|
dsql_nod* temp = MAKE_node(nod_list, 1);
|
2003-10-05 08:37:26 +02:00
|
|
|
rse->nod_arg[e_rse_streams] = temp;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* relation_node = pass1_relation(request, relation_name);
|
2003-10-05 08:37:26 +02:00
|
|
|
temp->nod_arg[0] = relation_node;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_eql, 2);
|
2003-10-05 08:37:26 +02:00
|
|
|
rse->nod_arg[e_rse_boolean] = node;
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[0] = temp = MAKE_node(nod_dbkey, 1);
|
|
|
|
temp->nod_arg[0] = relation_node;
|
|
|
|
|
|
|
|
node->nod_arg[1] = temp = MAKE_node(nod_parameter, e_par_count);
|
|
|
|
temp->nod_count = 0;
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_par* parameter = request->req_dbkey =
|
2005-09-02 07:30:16 +02:00
|
|
|
MAKE_parameter(request->req_send, false, false, 0, NULL);
|
2006-09-03 03:09:23 +02:00
|
|
|
temp->nod_arg[e_par_index] = (dsql_nod*) parameter->par_index;
|
2003-11-10 10:16:38 +01:00
|
|
|
temp->nod_arg[e_par_parameter] = (dsql_nod*) parameter;
|
2001-05-23 15:26:42 +02:00
|
|
|
parameter->par_desc = source->par_desc;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// record version will be set only for V4 - for the parent select cursor
|
2001-05-23 15:26:42 +02:00
|
|
|
if (rv_source) {
|
|
|
|
node = MAKE_node(nod_eql, 2);
|
|
|
|
node->nod_arg[0] = temp = MAKE_node(nod_rec_version, 1);
|
|
|
|
temp->nod_arg[0] = relation_node;
|
|
|
|
node->nod_arg[1] = temp = MAKE_node(nod_parameter, e_par_count);
|
|
|
|
temp->nod_count = 0;
|
|
|
|
parameter = request->req_rec_version =
|
2005-09-02 07:30:16 +02:00
|
|
|
MAKE_parameter(request->req_send, false, false, 0, NULL);
|
2006-09-03 03:09:23 +02:00
|
|
|
temp->nod_arg[e_par_index] = (dsql_nod*) parameter->par_index;
|
2003-11-10 10:16:38 +01:00
|
|
|
temp->nod_arg[e_par_parameter] = (dsql_nod*) parameter;
|
2001-05-23 15:26:42 +02:00
|
|
|
parameter->par_desc = rv_source->par_desc;
|
|
|
|
|
|
|
|
rse->nod_arg[e_rse_boolean] =
|
|
|
|
compose(rse->nod_arg[e_rse_boolean], node, nod_and);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_dbkey
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Resolve a dbkey to an available context.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_dbkey( dsql_req* request, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* qualifier = (dsql_str*) input->nod_arg[0];
|
2003-10-05 08:37:26 +02:00
|
|
|
if (!qualifier) {
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(qualifier, dsql_type_str);
|
2005-05-28 00:45:31 +02:00
|
|
|
// No qualifier, if only one context then use, else error
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
if (request->req_context->getCount() == 1)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
dsql_ctx* context = request->req_context->object();
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_dbkey, 1);
|
|
|
|
dsql_nod* rel_node = MAKE_node(nod_relation, e_rel_count);
|
2003-11-10 10:16:38 +01:00
|
|
|
rel_node->nod_arg[0] = (dsql_nod*) context;
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[0] = rel_node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2004-05-27 18:26:52 +02:00
|
|
|
for (DsqlContextStack::iterator stack(*request->req_context); stack.hasData(); ++stack)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2004-04-18 16:22:27 +02:00
|
|
|
dsql_ctx* context = stack.object();
|
2001-05-23 15:26:42 +02:00
|
|
|
if ((!(context->ctx_relation) ||
|
2003-11-18 08:58:35 +01:00
|
|
|
strcmp(reinterpret_cast<const char*>(qualifier->str_data),
|
2001-05-23 15:26:42 +02:00
|
|
|
context->ctx_relation->rel_name)) &&
|
2005-10-08 08:47:34 +02:00
|
|
|
(!(context->ctx_alias) ||
|
2001-05-23 15:26:42 +02:00
|
|
|
strcmp(reinterpret_cast<const char*>(qualifier->str_data),
|
2003-11-18 08:58:35 +01:00
|
|
|
context->ctx_alias)))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_dbkey, 1);
|
|
|
|
dsql_nod* rel_node = MAKE_node(nod_relation, e_rel_count);
|
2003-11-10 10:16:38 +01:00
|
|
|
rel_node->nod_arg[0] = (dsql_nod*) context;
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[0] = rel_node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// field unresolved
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
field_unknown(reinterpret_cast<const char*>(qualifier ? qualifier->str_data : 0),
|
2003-09-29 14:43:14 +02:00
|
|
|
DB_KEY_STRING, input);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_delete
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
@brief Process DELETE statement.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
2005-06-13 14:45:42 +02:00
|
|
|
@param proc_flag
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2005-06-13 14:45:42 +02:00
|
|
|
static dsql_nod* pass1_delete( dsql_req* request, dsql_nod* input, bool proc_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* cursor = input->nod_arg[e_del_cursor];
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* relation = input->nod_arg[e_del_relation];
|
2005-06-13 14:45:42 +02:00
|
|
|
if (cursor && proc_flag) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* anode = MAKE_node(nod_erase_current, e_erc_count);
|
2003-10-05 08:37:26 +02:00
|
|
|
anode->nod_arg[e_erc_context] =
|
2003-11-10 10:16:38 +01:00
|
|
|
(dsql_nod*) pass1_cursor_context(request, cursor, relation);
|
2006-05-31 20:02:34 +02:00
|
|
|
anode->nod_arg[e_erc_return] =
|
2006-09-03 03:09:23 +02:00
|
|
|
process_returning(request, input->nod_arg[e_del_return], proc_flag);
|
2003-10-05 08:37:26 +02:00
|
|
|
return anode;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
request->req_type = (cursor) ? REQ_DELETE_CURSOR : REQ_DELETE;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_erase, e_era_count);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Generate record selection expression
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* rse;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (cursor)
|
2003-11-02 13:28:30 +01:00
|
|
|
rse = pass1_cursor_reference(request, cursor, relation);
|
2001-05-23 15:26:42 +02:00
|
|
|
else {
|
|
|
|
rse = MAKE_node(nod_rse, e_rse_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* temp = MAKE_node(nod_list, 1);
|
|
|
|
rse->nod_arg[e_rse_streams] = temp;
|
2003-09-02 01:22:22 +02:00
|
|
|
temp->nod_arg[0] = PASS1_node(request, relation, false);
|
2004-10-27 11:33:08 +02:00
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_del_boolean]) ) {
|
2003-09-02 01:22:22 +02:00
|
|
|
rse->nod_arg[e_rse_boolean] = PASS1_node(request, temp, false);
|
2004-10-27 11:33:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_del_plan]) ) {
|
|
|
|
rse->nod_arg[e_rse_plan] = PASS1_node(request, temp, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_del_sort]) ) {
|
|
|
|
rse->nod_arg[e_rse_sort] = pass1_sort(request, temp, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_del_rows]) ) {
|
|
|
|
rse->nod_arg[e_rse_first] =
|
|
|
|
PASS1_node(request, temp->nod_arg[e_rows_length], false);
|
|
|
|
rse->nod_arg[e_rse_skip] =
|
|
|
|
PASS1_node(request, temp->nod_arg[e_rows_skip], false);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
node->nod_arg[e_era_rse] = rse;
|
2004-10-27 11:33:08 +02:00
|
|
|
node->nod_arg[e_era_relation] = rse->nod_arg[e_rse_streams]->nod_arg[0];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-31 20:02:34 +02:00
|
|
|
node->nod_arg[e_era_return] =
|
2006-09-03 03:09:23 +02:00
|
|
|
process_returning(request, input->nod_arg[e_del_return], proc_flag);
|
2006-05-31 20:02:34 +02:00
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
request->req_context->pop();
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_relproc_is_recursive
|
|
|
|
|
|
|
|
@brief check if table reference is recursive i.e. its name is equal
|
|
|
|
to the name of current processing CTE
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
|
|
|
static bool pass1_relproc_is_recursive(dsql_req* request, dsql_nod* input)
|
|
|
|
{
|
2006-08-02 03:22:11 +02:00
|
|
|
dsql_str* rel_name = NULL;
|
|
|
|
dsql_str* rel_alias = NULL;
|
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
switch (input->nod_type)
|
|
|
|
{
|
|
|
|
case nod_rel_proc_name:
|
|
|
|
rel_name = (dsql_str*) input->nod_arg[e_rpn_name];
|
|
|
|
rel_alias = (dsql_str*) input->nod_arg[e_rpn_alias];
|
2006-08-02 03:22:11 +02:00
|
|
|
break;
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
case nod_relation_name:
|
|
|
|
rel_name = (dsql_str*) input->nod_arg[e_rln_name];
|
|
|
|
rel_alias = (dsql_str*) input->nod_arg[e_rln_alias];
|
2006-08-02 03:22:11 +02:00
|
|
|
break;
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(request->req_curr_ctes.hasData());
|
|
|
|
const dsql_nod* curr_cte = request->req_curr_ctes.object();
|
|
|
|
const dsql_str* cte_name = (dsql_str*) curr_cte->nod_arg[e_derived_table_alias];
|
|
|
|
|
|
|
|
const bool recursive = (cte_name->str_length == rel_name->str_length) &&
|
2006-08-02 03:22:11 +02:00
|
|
|
(strncmp(rel_name->str_data, cte_name->str_data, cte_name->str_length) == 0);
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
if (recursive) {
|
|
|
|
request->addCTEAlias(rel_alias ? rel_alias : rel_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return recursive;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_join_is_recursive
|
|
|
|
|
|
|
|
@brief check if join have recursive members. If found remove this member
|
|
|
|
from join and return its boolean (to be added into WHERE clause).
|
2006-08-05 23:56:04 +02:00
|
|
|
We must remove member only if it is a table reference.
|
2006-08-01 22:37:58 +02:00
|
|
|
Punt if recursive reference is found in outer join or more than one
|
|
|
|
recursive reference is found
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_join_is_recursive(dsql_req* request, dsql_nod*& input)
|
|
|
|
{
|
|
|
|
const NOD_TYPE join_type = input->nod_arg[e_join_type]->nod_type;
|
2006-08-05 23:56:04 +02:00
|
|
|
bool remove = false;
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
bool leftRecursive = false;
|
2006-08-06 20:03:22 +02:00
|
|
|
dsql_nod* leftBool = NULL;
|
2006-08-05 23:56:04 +02:00
|
|
|
dsql_nod** join_table = &input->nod_arg[e_join_left_rel];
|
|
|
|
if ((*join_table)->nod_type == nod_join)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2006-08-05 23:56:04 +02:00
|
|
|
leftBool = pass1_join_is_recursive(request, *join_table);
|
2006-08-06 20:03:22 +02:00
|
|
|
leftRecursive = (leftBool != NULL);
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
leftBool = input->nod_arg[e_join_boolean];
|
2006-08-05 23:56:04 +02:00
|
|
|
leftRecursive = pass1_relproc_is_recursive(request, *join_table);
|
|
|
|
if (leftRecursive)
|
|
|
|
remove = true;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (leftRecursive && join_type != nod_join_inner) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive member of cte can''t be member of an outer join",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rightRecursive = false;
|
2006-08-06 20:03:22 +02:00
|
|
|
dsql_nod* rightBool = NULL;
|
2006-08-05 23:56:04 +02:00
|
|
|
join_table = &input->nod_arg[e_join_rght_rel];
|
|
|
|
if ((*join_table)->nod_type == nod_join)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
2006-08-05 23:56:04 +02:00
|
|
|
rightBool = pass1_join_is_recursive(request, *join_table);
|
2006-08-06 20:03:22 +02:00
|
|
|
rightRecursive = (rightBool != NULL);
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rightBool = input->nod_arg[e_join_boolean];
|
2006-08-05 23:56:04 +02:00
|
|
|
rightRecursive = pass1_relproc_is_recursive(request, *join_table);
|
|
|
|
if (rightRecursive)
|
|
|
|
remove = true;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rightRecursive && join_type != nod_join_inner) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive member of cte can''t be member of an outer join",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (leftRecursive && rightRecursive) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive member of cte can''t reference to itself more than once",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (leftRecursive)
|
|
|
|
{
|
2006-08-05 23:56:04 +02:00
|
|
|
if (remove)
|
|
|
|
input = input->nod_arg[e_join_rght_rel];
|
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
return leftBool;
|
|
|
|
}
|
|
|
|
if (rightRecursive)
|
|
|
|
{
|
2006-08-05 23:56:04 +02:00
|
|
|
if (remove)
|
|
|
|
input = input->nod_arg[e_join_left_rel];
|
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
return rightBool;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_rse_is_recursive
|
|
|
|
|
|
|
|
@brief check if rse is recursive. If recursive reference is a table
|
|
|
|
in the FROM list remove it. If recursive reference is a part of
|
|
|
|
join add join boolean (returned by pass1_join_is_recursive) to the
|
|
|
|
WHERE clause. Punt if more than one recursive reference is found
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
|
|
|
static bool pass1_rse_is_recursive(dsql_req* request, dsql_nod* input)
|
|
|
|
{
|
|
|
|
fb_assert(input->nod_type == nod_query_spec);
|
|
|
|
|
|
|
|
dsql_nod* table_list = input->nod_arg[e_qry_from];
|
|
|
|
dsql_nod** table = table_list->nod_arg;
|
|
|
|
dsql_nod** end = table_list->nod_arg + table_list->nod_count;
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
for (dsql_nod** prev = table; table < end; table++)
|
|
|
|
{
|
|
|
|
*prev++ = *table;
|
|
|
|
switch ((*table)->nod_type)
|
|
|
|
{
|
|
|
|
case nod_rel_proc_name:
|
|
|
|
case nod_relation_name:
|
|
|
|
if (pass1_relproc_is_recursive(request, *table))
|
|
|
|
{
|
|
|
|
if (found) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive member of cte can''t reference to itself more than once",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
prev--;
|
|
|
|
table_list->nod_count--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_join:
|
|
|
|
{
|
|
|
|
dsql_nod* joinBool = pass1_join_is_recursive(request, *table);
|
|
|
|
if (joinBool)
|
|
|
|
{
|
|
|
|
if (found) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive member of cte can''t reference to itself more than once",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
input->nod_arg[e_qry_where] =
|
|
|
|
compose(input->nod_arg[e_qry_where], joinBool, nod_and);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_derived_table:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_recursive_cte
|
|
|
|
|
|
|
|
@brief Process derived table which can be recursive CTE
|
|
|
|
If it is non-recursive return input node unchanged
|
|
|
|
If it is recursive return new derived table which is an union of
|
|
|
|
union of anchor (non-recursive) queries and union of recursive
|
|
|
|
queries. Check recursive queries to satisfy various criterias.
|
|
|
|
Note that our parser is right-to-left therefore recursive members
|
|
|
|
will be first in union's list
|
|
|
|
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_recursive_cte(dsql_req* request, dsql_nod* input, bool proc_flag)
|
|
|
|
{
|
|
|
|
dsql_str* const cte_alias = (dsql_str*) input->nod_arg[e_derived_table_alias];
|
|
|
|
dsql_nod* const select_expr = input->nod_arg[e_derived_table_rse];
|
|
|
|
dsql_nod* query = select_expr->nod_arg[e_sel_query_spec];
|
|
|
|
|
|
|
|
if (query->nod_type != nod_list && pass1_rse_is_recursive(request, query))
|
|
|
|
{
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive cte must be union",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// split queries list on two parts: anchor and recursive
|
|
|
|
dsql_nod* anchor_rse = 0, *recursive_rse = 0;
|
|
|
|
dsql_nod* qry = query;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
dsql_nod* rse = 0;
|
|
|
|
if (qry->nod_type == nod_list)
|
|
|
|
rse = qry->nod_arg[1];
|
|
|
|
else
|
|
|
|
rse = qry;
|
|
|
|
|
|
|
|
if (pass1_rse_is_recursive(request, rse)) // rse is recursive
|
|
|
|
{
|
|
|
|
if (anchor_rse)
|
|
|
|
{
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "non-recursive query after recursive",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
if (rse->nod_arg[e_qry_distinct]) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive query have DISTINCT clause",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
if (rse->nod_arg[e_qry_group]) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive query have GROUP BY clause",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
if (rse->nod_arg[e_qry_having]) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive query have HAVING clause",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
// hvlad: we need also forbid any aggregate function here
|
|
|
|
// but for now i have no idea how to do it simple
|
|
|
|
|
|
|
|
if ((qry->nod_type == nod_list) && !(qry->nod_flags & NOD_UNION_ALL)) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "recursive members of CTE must be linked with another members via UNION ALL",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
if (!recursive_rse) {
|
|
|
|
recursive_rse = qry;
|
|
|
|
}
|
|
|
|
rse->nod_flags |= NOD_SELECT_EXPR_RECURSIVE;
|
|
|
|
}
|
|
|
|
else if (!anchor_rse)
|
|
|
|
{
|
|
|
|
anchor_rse = qry;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qry->nod_type == nod_list)
|
|
|
|
qry = qry->nod_arg[0];
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!recursive_rse) {
|
|
|
|
return input;
|
|
|
|
}
|
|
|
|
if (!anchor_rse) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "non-recursive query is missing",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
qry = recursive_rse;
|
|
|
|
dsql_nod* list = 0;
|
|
|
|
while (qry->nod_arg[0] != anchor_rse)
|
|
|
|
{
|
|
|
|
list = qry;
|
|
|
|
qry = qry->nod_arg[0];
|
|
|
|
}
|
|
|
|
qry->nod_arg[0] = 0;
|
|
|
|
if (list) {
|
|
|
|
list->nod_arg[0] = qry->nod_arg[1];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
recursive_rse = qry->nod_arg[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* union_node = MAKE_node(nod_list, 2);
|
|
|
|
union_node->nod_flags = NOD_UNION_ALL | NOD_UNION_RECURSIVE;
|
|
|
|
union_node->nod_arg[0] = anchor_rse;
|
|
|
|
union_node->nod_arg[1] = recursive_rse;
|
|
|
|
|
|
|
|
dsql_nod* select = MAKE_node(nod_select_expr, e_sel_count);
|
|
|
|
select->nod_arg[e_sel_query_spec] = union_node;
|
|
|
|
select->nod_arg[e_sel_order] = select->nod_arg[e_sel_rows] =
|
|
|
|
select->nod_arg[e_sel_with_list] = NULL;
|
|
|
|
|
|
|
|
dsql_nod* node = MAKE_node(nod_derived_table, e_derived_table_count);
|
|
|
|
dsql_str* alias = (dsql_str*) input->nod_arg[e_derived_table_alias];
|
|
|
|
node->nod_arg[e_derived_table_alias] = (dsql_nod*) alias;
|
|
|
|
node->nod_arg[e_derived_table_column_alias] = input->nod_arg[e_derived_table_column_alias];
|
|
|
|
node->nod_arg[e_derived_table_rse] = select;
|
2006-09-14 04:05:32 +02:00
|
|
|
node->nod_arg[e_derived_table_context] = input->nod_arg[e_derived_table_context];
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-03 03:09:23 +02:00
|
|
|
/**
|
|
|
|
|
|
|
|
process_returning
|
|
|
|
|
|
|
|
@brief Compile a RETURNING clause (nod_returning or not).
|
|
|
|
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* process_returning(dsql_req* request,
|
|
|
|
dsql_nod* input,
|
|
|
|
bool proc_flag)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
|
|
|
dsql_nod* node;
|
|
|
|
|
|
|
|
if (!input || input->nod_type == nod_returning)
|
|
|
|
node = PASS1_node(request, input, proc_flag);
|
|
|
|
else
|
|
|
|
node = PASS1_node(request, input, false);
|
|
|
|
|
|
|
|
if (input && !proc_flag)
|
|
|
|
request->req_type = REQ_EXEC_PROCEDURE;
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
pass1_derived_table
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
@brief Process derived table which is part of a from clause.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2006-08-01 22:37:58 +02:00
|
|
|
static dsql_nod* pass1_derived_table(dsql_req* request, dsql_nod* input, bool proc_flag, dsql_str* cte_alias)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node (nod_derived_table, e_derived_table_count);
|
|
|
|
dsql_str* alias = (dsql_str*) input->nod_arg[e_derived_table_alias];
|
|
|
|
node->nod_arg[e_derived_table_alias] = (dsql_nod*) alias;
|
2003-08-20 01:34:23 +02:00
|
|
|
node->nod_arg[e_derived_table_column_alias] = input->nod_arg[e_derived_table_column_alias];
|
|
|
|
|
|
|
|
// Create the context now, because we need to know it for the tables inside.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_ctx* context = PASS1_make_context(request, node);
|
2006-09-14 04:05:32 +02:00
|
|
|
node->nod_arg[e_derived_table_context] = (dsql_nod*) context;
|
2003-08-20 01:34:23 +02:00
|
|
|
|
|
|
|
// Save some values to restore after rse process.
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlContextStack* const req_base = request->req_context;
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_str* const req_alias_relation_prefix = request->req_alias_relation_prefix;
|
2003-08-20 01:34:23 +02:00
|
|
|
|
2004-03-21 02:48:29 +01:00
|
|
|
// Change req_context, because when we are processing the derived table rse
|
|
|
|
// it may not reference to other streams in the same scope_level.
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlContextStack temp;
|
2005-08-01 21:05:57 +02:00
|
|
|
// Put special contexts (NEW/OLD) also on the stack
|
|
|
|
for (DsqlContextStack::iterator stack(*request->req_context); stack.hasData(); ++stack)
|
2005-07-25 16:43:28 +02:00
|
|
|
{
|
2006-08-17 14:08:49 +02:00
|
|
|
dsql_ctx* local_context = stack.object();
|
|
|
|
if ((local_context->ctx_scope_level < request->req_scope_level) ||
|
|
|
|
(local_context->ctx_flags & CTX_system))
|
2005-07-25 16:43:28 +02:00
|
|
|
{
|
2006-08-17 14:08:49 +02:00
|
|
|
temp.push(local_context);
|
2005-07-25 16:43:28 +02:00
|
|
|
}
|
|
|
|
}
|
2005-08-02 10:36:51 +02:00
|
|
|
dsql_ctx* baseContext = NULL;
|
|
|
|
if (temp.hasData()) {
|
|
|
|
baseContext = temp.object();
|
|
|
|
}
|
2005-08-01 21:05:57 +02:00
|
|
|
request->req_context = &temp;
|
2003-08-20 01:56:28 +02:00
|
|
|
request->req_alias_relation_prefix = pass1_alias_concat(req_alias_relation_prefix, alias);
|
2003-09-15 23:21:40 +02:00
|
|
|
|
2004-10-13 20:37:53 +02:00
|
|
|
dsql_nod* const select_expr = input->nod_arg[e_derived_table_rse];
|
2005-02-21 14:18:49 +01:00
|
|
|
dsql_nod* query = select_expr->nod_arg[e_sel_query_spec];
|
2005-01-06 14:14:38 +01:00
|
|
|
dsql_nod* rse = NULL;
|
2006-08-01 22:37:58 +02:00
|
|
|
const bool isRecursive =
|
|
|
|
(query->nod_type == nod_list) && (query->nod_flags & NOD_UNION_RECURSIVE);
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
if (isRecursive)
|
2005-08-01 21:05:57 +02:00
|
|
|
{
|
2006-08-01 22:37:58 +02:00
|
|
|
// Create dummy, non-recursive select statement by doing a union of
|
|
|
|
// one, non-recursive member. The dummy will be replaced at the end
|
|
|
|
// of this function.
|
|
|
|
|
|
|
|
query->nod_count = 1;
|
|
|
|
query->nod_flags &= ~NOD_UNION_RECURSIVE;
|
|
|
|
|
|
|
|
dsql_ctx* baseUnionCtx = request->req_union_context.hasData() ?
|
|
|
|
request->req_union_context.object() : NULL;
|
|
|
|
|
|
|
|
request->req_recursive_ctx_id = request->req_context_number;
|
|
|
|
rse = pass1_union(request, query, NULL, NULL, 0);
|
|
|
|
request->req_context_number = request->req_recursive_ctx_id + 1;
|
|
|
|
|
|
|
|
// recursive union always have exactly 2 members
|
|
|
|
query->nod_count = 2;
|
|
|
|
query->nod_flags |= NOD_UNION_RECURSIVE;
|
|
|
|
|
|
|
|
while (request->req_union_context.hasData() &&
|
2006-08-02 03:22:11 +02:00
|
|
|
request->req_union_context.object() != baseUnionCtx)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
|
|
|
request->req_union_context.pop();
|
|
|
|
}
|
2005-08-01 21:05:57 +02:00
|
|
|
}
|
2006-08-01 22:37:58 +02:00
|
|
|
else
|
2004-04-21 16:48:23 +02:00
|
|
|
{
|
2006-08-01 22:37:58 +02:00
|
|
|
// 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 curently can't be used in
|
|
|
|
// deciding the JOIN order.
|
|
|
|
bool foundSubSelect = false;
|
|
|
|
if (query->nod_type == nod_query_spec) {
|
|
|
|
foundSubSelect = pass1_found_sub_select(query->nod_arg[e_qry_list]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (foundSubSelect) {
|
|
|
|
dsql_nod* union_expr = MAKE_node(nod_list, 1);
|
|
|
|
union_expr->nod_arg[0] = select_expr;
|
|
|
|
union_expr->nod_flags = NOD_UNION_ALL;
|
|
|
|
rse = pass1_union(request, union_expr, NULL, NULL, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rse = PASS1_rse(request, select_expr, NULL);
|
|
|
|
}
|
2003-08-20 01:34:23 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
// Finish off by cleaning up contexts and put them into req_dt_context
|
|
|
|
// so create view (ddl) can deal with it.
|
|
|
|
// Also add the used contexts into the childs stack.
|
|
|
|
while (temp.hasData() && (temp.object() != baseContext))
|
|
|
|
{
|
|
|
|
request->req_dt_context.push(temp.object());
|
|
|
|
context->ctx_childs_derived_table.push(temp.pop());
|
|
|
|
}
|
|
|
|
while (temp.hasData())
|
|
|
|
{
|
|
|
|
temp.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
context->ctx_rse = node->nod_arg[e_derived_table_rse] = rse;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
// If an alias-list is specified process it.
|
2005-05-17 09:17:25 +02:00
|
|
|
const bool ignoreColumnChecks = (input->nod_flags & NOD_DT_IGNORE_COLUMN_CHECK);
|
2005-05-28 00:45:31 +02:00
|
|
|
if (node->nod_arg[e_derived_table_column_alias] &&
|
|
|
|
node->nod_arg[e_derived_table_column_alias]->nod_count)
|
2003-09-04 17:02:22 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* list = node->nod_arg[e_derived_table_column_alias];
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
// Have both lists the same number of items?
|
|
|
|
if (list->nod_count != rse->nod_arg[e_rse_items]->nod_count) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// Column list by derived table %s [alias-name] has %s [more/fewer] columns
|
2003-08-15 02:02:18 +02:00
|
|
|
// than the number of items.
|
2005-05-28 00:45:31 +02:00
|
|
|
//
|
2003-09-04 17:02:22 +02:00
|
|
|
TEXT err_message[200], aliasname[100];
|
2003-08-16 02:36:54 +02:00
|
|
|
aliasname[0] = 0;
|
|
|
|
if (alias) {
|
|
|
|
int length = alias->str_length;
|
|
|
|
if (length > 99) {
|
|
|
|
length = 99;
|
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
const TEXT* src = alias->str_data;
|
|
|
|
TEXT* dest = aliasname;
|
2003-08-16 02:36:54 +02:00
|
|
|
for (; length; length--) {
|
|
|
|
*dest++ = *src++;
|
|
|
|
}
|
|
|
|
*dest = 0;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
if (list->nod_count > rse->nod_arg[e_rse_items]->nod_count) {
|
2005-05-28 00:45:31 +02:00
|
|
|
sprintf (err_message, "list by derived table %s has more columns than the number of items.",
|
2003-08-16 02:36:54 +02:00
|
|
|
aliasname);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else {
|
2005-05-28 00:45:31 +02:00
|
|
|
sprintf (err_message, "list by derived table %s has fewer columns than the number of items.",
|
2003-08-16 02:36:54 +02:00
|
|
|
aliasname);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
//
|
|
|
|
// !!! THIS MESSAGE SHOULD BE CHANGED !!!
|
|
|
|
//
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_field_name,
|
|
|
|
isc_arg_string, err_message, 0);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
// Generate derived fields and assign alias-name to it.
|
2003-10-05 08:37:26 +02:00
|
|
|
for (int count = 0; count < list->nod_count; count++) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* select_item = rse->nod_arg[e_rse_items]->nod_arg[count];
|
2003-09-04 17:02:22 +02:00
|
|
|
// Make new derived field node
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* derived_field = MAKE_node(nod_derived_field, e_derived_field_count);
|
2003-09-04 17:02:22 +02:00
|
|
|
derived_field->nod_arg[e_derived_field_value] = select_item;
|
|
|
|
derived_field->nod_arg[e_derived_field_name] = list->nod_arg[count];
|
2004-01-21 08:18:30 +01:00
|
|
|
derived_field->nod_arg[e_derived_field_scope] = (dsql_nod*)(IPTR) request->req_scope_level;
|
2003-09-04 17:02:22 +02:00
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
|
|
|
|
rse->nod_arg[e_rse_items]->nod_arg[count] = derived_field;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
2005-01-25 00:02:08 +01:00
|
|
|
else {
|
|
|
|
// For those select-items where no alias is specified try
|
|
|
|
// to generate one from the field_name.
|
|
|
|
for (int count = 0; count < rse->nod_arg[e_rse_items]->nod_count; count++) {
|
2005-05-28 00:45:31 +02:00
|
|
|
dsql_nod* select_item =
|
2005-01-25 00:02:08 +01:00
|
|
|
pass1_make_derived_field(request, tdsql, rse->nod_arg[e_rse_items]->nod_arg[count]);
|
2005-05-04 21:38:47 +02:00
|
|
|
|
|
|
|
// Auto-create dummy column name for pass_any()
|
|
|
|
if (ignoreColumnChecks && (select_item->nod_type != nod_derived_field)) {
|
|
|
|
// Make new derived field node
|
2005-05-28 00:45:31 +02:00
|
|
|
dsql_nod* derived_field =
|
2005-05-04 21:38:47 +02:00
|
|
|
MAKE_node(nod_derived_field, e_derived_field_count);
|
|
|
|
derived_field->nod_arg[e_derived_field_value] = select_item;
|
|
|
|
|
|
|
|
// Construct dummy fieldname
|
|
|
|
char fieldname[25];
|
|
|
|
sprintf (fieldname, "f%d", count);
|
2006-08-17 14:08:49 +02:00
|
|
|
dsql_str* field_alias = FB_NEW_RPT(*tdsql->getDefaultPool(),
|
2005-05-04 21:38:47 +02:00
|
|
|
strlen(fieldname)) dsql_str;
|
2006-08-17 14:08:49 +02:00
|
|
|
strcpy(field_alias->str_data, fieldname);
|
|
|
|
field_alias->str_length = strlen(fieldname);
|
2005-05-04 21:38:47 +02:00
|
|
|
|
2006-08-17 14:08:49 +02:00
|
|
|
derived_field->nod_arg[e_derived_field_name] = (dsql_nod*) field_alias;
|
2005-05-28 00:45:31 +02:00
|
|
|
derived_field->nod_arg[e_derived_field_scope] =
|
2005-11-09 00:49:50 +01:00
|
|
|
(dsql_nod*)(IPTR) request->req_scope_level;
|
2005-05-04 21:38:47 +02:00
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
select_item = derived_field;
|
|
|
|
}
|
|
|
|
|
|
|
|
rse->nod_arg[e_rse_items]->nod_arg[count] = select_item;
|
2005-01-25 00:02:08 +01:00
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
2005-01-25 00:02:08 +01:00
|
|
|
int count;
|
2003-09-04 17:02:22 +02:00
|
|
|
// Check if all root select-items have an derived field else show a message.
|
|
|
|
for (count = 0; count < rse->nod_arg[e_rse_items]->nod_count; count++) {
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* select_item = rse->nod_arg[e_rse_items]->nod_arg[count];
|
2003-09-04 17:02:22 +02:00
|
|
|
if (select_item->nod_type != nod_derived_field) {
|
2003-08-15 02:02:18 +02:00
|
|
|
// No columnname specified for column number %d
|
|
|
|
//
|
|
|
|
// !!! THIS MESSAGE SHOULD BE CHANGED !!!
|
|
|
|
//
|
|
|
|
TEXT columnnumber[80];
|
2003-09-04 17:02:22 +02:00
|
|
|
sprintf (columnnumber, "%d is specified without a name", count + 1);
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_field_name,
|
|
|
|
isc_arg_string, columnnumber, 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-09-04 17:02:22 +02:00
|
|
|
}
|
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
// Check for ambiguous columnnames inside this derived table.
|
2003-09-04 17:02:22 +02:00
|
|
|
for (count = 0; count < rse->nod_arg[e_rse_items]->nod_count; count++) {
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* select_item1 = rse->nod_arg[e_rse_items]->nod_arg[count];
|
2003-10-05 08:37:26 +02:00
|
|
|
for (int count2 = (count + 1); count2 < rse->nod_arg[e_rse_items]->nod_count; count2++)
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* select_item2 = rse->nod_arg[e_rse_items]->nod_arg[count2];
|
|
|
|
const dsql_str* name1 = (dsql_str*) select_item1->nod_arg[e_derived_field_name];
|
|
|
|
const dsql_str* name2 = (dsql_str*) select_item2->nod_arg[e_derived_field_name];
|
2003-10-05 08:37:26 +02:00
|
|
|
if (!strcmp(reinterpret_cast<const char*>(name1->str_data),
|
|
|
|
reinterpret_cast<const char*>(name2->str_data)))
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
2003-09-04 17:02:22 +02:00
|
|
|
// The column %s was specified multiple times for derived table %s
|
|
|
|
//
|
|
|
|
// !!! THIS MESSAGE SHOULD BE CHANGED !!!
|
|
|
|
//
|
|
|
|
TEXT columnnumber[80];
|
2003-09-04 23:40:21 +02:00
|
|
|
if (alias) {
|
2005-05-28 00:45:31 +02:00
|
|
|
sprintf (columnnumber,
|
|
|
|
"The column %s was specified multiple times for derived table %s",
|
2003-09-04 23:40:21 +02:00
|
|
|
name1->str_data, alias->str_data);
|
|
|
|
}
|
|
|
|
else {
|
2005-05-28 00:45:31 +02:00
|
|
|
sprintf (columnnumber,
|
|
|
|
"The column %s was specified multiple times for derived table %s",
|
2003-09-04 23:40:21 +02:00
|
|
|
name1->str_data, "unnamed");
|
|
|
|
}
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_field_name,
|
|
|
|
isc_arg_string, columnnumber, 0);
|
2003-09-04 17:02:22 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
2006-08-01 22:37:58 +02: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)
|
|
|
|
{
|
|
|
|
request->req_recursive_ctx = context;
|
|
|
|
request->req_context = &temp;
|
|
|
|
|
|
|
|
request->resetCTEAlias();
|
|
|
|
|
|
|
|
rse = PASS1_rse(request, input->nod_arg[e_derived_table_rse], NULL);
|
|
|
|
|
|
|
|
// Finish off by cleaning up contexts and put them into req_dt_context
|
|
|
|
// so create view (ddl) can deal with it.
|
|
|
|
// Also add the used contexts into the childs stack.
|
|
|
|
while (temp.hasData() && (temp.object() != baseContext))
|
|
|
|
{
|
|
|
|
request->req_dt_context.push(temp.object());
|
|
|
|
context->ctx_childs_derived_table.push(temp.pop());
|
|
|
|
}
|
2006-08-02 03:22:11 +02:00
|
|
|
|
|
|
|
temp.clear();
|
2006-08-01 22:37:58 +02:00
|
|
|
|
|
|
|
rse->nod_arg[e_rse_items] = context->ctx_rse->nod_arg[e_rse_items];
|
|
|
|
|
2006-08-02 03:22:11 +02:00
|
|
|
dsql_nod* node2 = MAKE_node(nod_derived_table, e_derived_table_count);
|
2006-08-01 22:37:58 +02:00
|
|
|
*node2 = *node;
|
|
|
|
node2->nod_arg[e_derived_table_rse] = rse;
|
|
|
|
node = node2;
|
|
|
|
|
|
|
|
context->ctx_rse = rse;
|
|
|
|
if (cte_alias) {
|
|
|
|
context->ctx_alias = cte_alias->str_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->req_context = req_base;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete request->req_alias_relation_prefix;
|
|
|
|
// Restore our original values.
|
|
|
|
request->req_context = req_base;
|
|
|
|
request->req_alias_relation_prefix = req_alias_relation_prefix;
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
pass1_expand_select_list
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
@brief Expand asterisk nodes into fields.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param list
|
|
|
|
@param streams
|
|
|
|
|
|
|
|
**/
|
2005-02-14 06:54:45 +01:00
|
|
|
static dsql_nod* pass1_expand_select_list(dsql_req* request, dsql_nod* list,
|
|
|
|
dsql_nod* streams)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
2005-02-14 06:54:45 +01:00
|
|
|
if (!list)
|
|
|
|
list = streams;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
DsqlNodStack stack;
|
2005-02-14 06:54:45 +01:00
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
pass1_expand_select_node(request, *ptr, stack);
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
dsql_nod* node = MAKE_list(stack);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
pass1_expand_select_node
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
@brief Expand a select item node.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param node
|
|
|
|
@param stack
|
|
|
|
|
|
|
|
**/
|
|
|
|
static void pass1_expand_select_node(dsql_req* request, dsql_nod* node, DsqlNodStack& stack)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
|
|
|
|
if (node->nod_type == nod_join) {
|
|
|
|
pass1_expand_select_node(request, node->nod_arg[e_join_left_rel], stack);
|
|
|
|
pass1_expand_select_node(request, node->nod_arg[e_join_rght_rel], stack);
|
|
|
|
}
|
|
|
|
else if (node->nod_type == nod_derived_table) {
|
|
|
|
// AB: Derived table support
|
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
|
|
|
dsql_nod* sub_items = node->nod_arg[e_derived_table_rse]->nod_arg[e_rse_items];
|
|
|
|
dsql_nod** ptr = sub_items->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + sub_items->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr < end; ++ptr)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
|
|
|
// Create a new alias else mappings would be mangled.
|
|
|
|
dsql_nod* select_item = *ptr;
|
|
|
|
// select-item should always be a derived field!
|
|
|
|
if (select_item->nod_type != nod_derived_field) {
|
|
|
|
// Internal dsql error: alias type expected by exploding.
|
|
|
|
//
|
|
|
|
// !!! THIS MESSAGE SHOULD BE CHANGED !!!
|
|
|
|
//
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err, 0);
|
|
|
|
}
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (node->nod_type == nod_relation) {
|
|
|
|
dsql_ctx* context = (dsql_ctx*) node->nod_arg[e_rel_context];
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
dsql_prc* procedure;
|
|
|
|
dsql_rel* relation = context->ctx_relation;
|
|
|
|
if (relation) {
|
|
|
|
for (dsql_fld* field = relation->rel_fields; field;
|
|
|
|
field = field->fld_next)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
dsql_nod* select_item = MAKE_field(context, field, 0);
|
|
|
|
stack.push(select_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (procedure = context->ctx_procedure) {
|
|
|
|
for (dsql_fld* field = procedure->prc_outputs; field;
|
2005-05-28 00:45:31 +02:00
|
|
|
field = field->fld_next)
|
2005-02-10 22:14:52 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
dsql_nod* 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(request, node, true, NULL);
|
2005-02-14 06:54:45 +01:00
|
|
|
// The node could be a relation so call recursively.
|
2005-02-10 22:14:52 +01:00
|
|
|
pass1_expand_select_node(request, select_item, stack);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
stack.push(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_field
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Resolve a field name to an available context.
|
2003-11-18 08:58:35 +01:00
|
|
|
If list is true, then this function can detect and
|
2003-02-15 04:01:51 +01:00
|
|
|
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
|
2003-11-01 11:26:43 +01:00
|
|
|
select from t1 join t2 on t1.f = t2.f order by common_field.
|
2003-02-15 04:01:51 +01:00
|
|
|
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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param list
|
|
|
|
|
|
|
|
**/
|
2005-05-28 00:45:31 +02:00
|
|
|
static dsql_nod* pass1_field( dsql_req* request, dsql_nod* input,
|
2005-02-10 22:14:52 +01:00
|
|
|
const bool list, dsql_nod* select_list)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-16 02:36:54 +02:00
|
|
|
// CVC: This shameful hack added to allow CHECK constraint implementation via triggers
|
|
|
|
// to be able to work.
|
2003-10-05 08:37:26 +02:00
|
|
|
bool is_check_constraint = false;
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
const dsql_nod* ddl_node = request->req_ddl_node;
|
|
|
|
if (ddl_node && ddl_node->nod_type == nod_def_constraint) {
|
|
|
|
is_check_constraint = true;
|
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-16 02:36:54 +02:00
|
|
|
// handle an array element.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* indices;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (input->nod_type == nod_array) {
|
|
|
|
indices = input->nod_arg[e_ary_indices];
|
|
|
|
input = input->nod_arg[e_ary_array];
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
else {
|
2001-05-23 15:26:42 +02:00
|
|
|
indices = NULL;
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_str* name;
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* qualifier; // We assume the qualifier was stripped elsewhere.
|
2001-05-23 15:26:42 +02:00
|
|
|
if (input->nod_count == 1) {
|
2003-11-10 10:16:38 +01:00
|
|
|
name = (dsql_str*) input->nod_arg[0];
|
2001-05-23 15:26:42 +02:00
|
|
|
qualifier = NULL;
|
|
|
|
}
|
|
|
|
else {
|
2003-11-10 10:16:38 +01:00
|
|
|
name = (dsql_str*) input->nod_arg[1];
|
|
|
|
qualifier = (dsql_str*) input->nod_arg[0];
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(name, dsql_type_str);
|
|
|
|
DEV_BLKCHK(qualifier, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-16 02:36:54 +02:00
|
|
|
// CVC: Let's strip trailing blanks or comparisons may fail in dialect 3.
|
|
|
|
if (name && name->str_data) {
|
2004-09-26 03:49:52 +02:00
|
|
|
fb_utils::exact_name((TEXT*) name->str_data);
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2002-06-29 08:56:51 +02: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
|
2005-10-06 08:08:10 +02:00
|
|
|
to field_unknown() near the end of this function. None of the multiple callers
|
2002-06-29 08:56:51 +02: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.
|
2004-01-09 03:23:46 +01:00
|
|
|
|
2002-06-29 08:56:51 +02: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.
|
2004-01-09 03:23:46 +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
|
2004-01-13 10:52:19 +01:00
|
|
|
from it's own level (of course ambiguity-check on each level is done).
|
2004-01-09 03:23:46 +01:00
|
|
|
|
2002-06-29 08:56:51 +02: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-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
if (select_list && !qualifier && name && name->str_data) {
|
|
|
|
// AB: Check first against the select list for matching column.
|
2005-05-28 00:45:31 +02:00
|
|
|
// When no matches at all are found we go on with our
|
2005-02-10 22:14:52 +01:00
|
|
|
// normal way of field name lookup.
|
|
|
|
dsql_nod* node = pass1_lookup_alias(request, name, select_list);
|
|
|
|
if (node) {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Try to resolve field against various contexts;
|
|
|
|
if there is an alias, check only against the first matching */
|
|
|
|
|
2004-10-13 20:37:53 +02:00
|
|
|
dsql_nod* node = NULL; // This var must be initialized.
|
2005-02-10 22:14:52 +01:00
|
|
|
DsqlContextStack ambiguous_ctx_stack;
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
// AB: Loop through the scope_levels starting by its own.
|
2004-03-21 02:48:29 +01:00
|
|
|
bool done = false;
|
2004-01-09 03:23:46 +01:00
|
|
|
USHORT current_scope_level = request->req_scope_level + 1;
|
2004-03-21 02:48:29 +01:00
|
|
|
for (; (current_scope_level > 0) && !done; current_scope_level--) {
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
// If we've found a node we're done.
|
|
|
|
if (node) {
|
|
|
|
break;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2004-05-27 18:26:52 +02:00
|
|
|
for (DsqlContextStack::iterator stack(*request->req_context); stack.hasData(); ++stack)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2004-01-09 03:23:46 +01:00
|
|
|
// resolve_context() checks the type of the
|
|
|
|
// given context, so the cast to dsql_ctx* is safe.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
dsql_ctx* context = stack.object();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
if (context->ctx_scope_level != (current_scope_level - 1)) {
|
|
|
|
continue;
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
dsql_fld* field = resolve_context(request, qualifier, context, is_check_constraint);
|
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
// AB: When there's no relation and no procedure then we have a derived table.
|
2005-05-28 00:45:31 +02:00
|
|
|
const bool is_derived_table =
|
|
|
|
(!context->ctx_procedure && !context->ctx_relation && context->ctx_rse);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-01-09 03:23:46 +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) {
|
|
|
|
node = MAKE_node(nod_relation, e_rel_count);
|
2004-04-18 16:22:27 +02:00
|
|
|
node->nod_arg[e_rel_context] = reinterpret_cast<dsql_nod*>(stack.object());
|
2004-01-09 03:23:46 +01:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
for (; field; field = field->fld_next) {
|
|
|
|
if (!strcmp(reinterpret_cast<const char*>(name->str_data), field->fld_name)) {
|
2004-11-03 00:07:09 +01:00
|
|
|
ambiguous_ctx_stack.push(context);
|
2005-05-28 00:45:31 +02:00
|
|
|
break;
|
2004-01-09 03:23:46 +01:00
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
if (qualifier && !field) {
|
2004-03-21 02:48:29 +01:00
|
|
|
// 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;
|
2004-01-09 03:23:46 +01:00
|
|
|
break;
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
if (field) {
|
|
|
|
// Intercept any reference to a field with datatype that
|
|
|
|
// did not exist prior to V6 and post an error
|
|
|
|
|
|
|
|
if (request->req_client_dialect <= SQL_DIALECT_V5 &&
|
|
|
|
(field->fld_dtype == dtype_sql_date ||
|
|
|
|
field->fld_dtype == dtype_sql_time ||
|
|
|
|
field->fld_dtype == dtype_int64))
|
|
|
|
{
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 206,
|
|
|
|
isc_arg_gds, isc_dsql_field_err,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, field->fld_name,
|
|
|
|
isc_arg_gds,
|
|
|
|
isc_sql_dialect_datatype_unsupport,
|
2005-06-16 07:11:08 +02:00
|
|
|
isc_arg_number, (SLONG) request->req_client_dialect,
|
2004-01-09 03:23:46 +01:00
|
|
|
isc_arg_string,
|
2004-11-10 05:26:45 +01:00
|
|
|
DSC_dtype_tostring(static_cast<UCHAR>
|
2004-01-09 03:23:46 +01:00
|
|
|
(field->fld_dtype)), 0);
|
|
|
|
return NULL;
|
2004-11-07 11:38:13 +01:00
|
|
|
}
|
2004-01-09 03:23:46 +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.
|
|
|
|
if (node) {
|
2003-08-16 02:36:54 +02:00
|
|
|
continue;
|
|
|
|
}
|
2004-01-09 03:23:46 +01:00
|
|
|
|
|
|
|
if (indices) {
|
|
|
|
indices = PASS1_node(request, indices, false);
|
|
|
|
}
|
|
|
|
node = MAKE_field(context, field, indices);
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2004-01-09 03:23:46 +01:00
|
|
|
else if (is_derived_table) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// if an qualifier is present check if we have the same derived
|
2004-01-09 03:23:46 +01:00
|
|
|
// table else continue;
|
|
|
|
if (qualifier) {
|
|
|
|
if (context->ctx_alias) {
|
|
|
|
if (strcmp(reinterpret_cast<const char*>(qualifier->str_data),
|
2004-11-10 05:26:45 +01:00
|
|
|
reinterpret_cast<const char*>(context->ctx_alias)))
|
|
|
|
{
|
2004-01-09 03:23:46 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
continue;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
|
2004-01-09 03:23:46 +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) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// Node is created so caller pass1_expand_select_node()
|
2005-02-10 22:14:52 +01:00
|
|
|
// can deal with it.
|
2004-01-09 03:23:46 +01:00
|
|
|
node = MAKE_node(nod_derived_table, e_derived_table_count);
|
|
|
|
node->nod_arg[e_derived_table_rse] = context->ctx_rse;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Because every select item has an alias we can just walk
|
2004-01-09 03:23:46 +01:00
|
|
|
// through the list and return the correct node when found.
|
|
|
|
const dsql_nod* rse_items = context->ctx_rse->nod_arg[e_rse_items];
|
|
|
|
dsql_nod* const* ptr = rse_items->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + rse_items->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
dsql_nod* node_select_item = *ptr;
|
|
|
|
// select-item should always be a alias!
|
|
|
|
if (node_select_item->nod_type == nod_derived_field) {
|
2005-05-28 00:45:31 +02:00
|
|
|
const dsql_str* string =
|
2004-01-09 03:23:46 +01:00
|
|
|
(dsql_str*) node_select_item->nod_arg[e_derived_field_name];
|
|
|
|
if (!strcmp(reinterpret_cast<const char*>(name->str_data),
|
|
|
|
reinterpret_cast<const char*>(string->str_data)))
|
|
|
|
{
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
// This is a matching item so add the context to the ambiguous list.
|
2004-11-03 00:07:09 +01:00
|
|
|
ambiguous_ctx_stack.push(context);
|
2004-01-09 03:23:46 +01:00
|
|
|
|
|
|
|
// Stop here if this is our second or more iteration.
|
|
|
|
if (node) {
|
|
|
|
break;
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
node = node_select_item;
|
2003-08-16 02:36:54 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-01-09 03:23:46 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Internal dsql error: alias type expected by field.
|
|
|
|
//
|
|
|
|
// !!! THIS MESSAGE SHOULD BE CHANGED !!!
|
|
|
|
//
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err, 0);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
|
2004-03-21 02:48:29 +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;
|
|
|
|
}
|
2003-08-16 02:36:54 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-16 02:36:54 +02: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.
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2004-11-03 00:07:09 +01:00
|
|
|
if (node && name) {
|
2003-08-16 02:36:54 +02:00
|
|
|
node = ambiguity_check(request, node, name, ambiguous_ctx_stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up stack
|
2004-04-18 16:22:27 +02:00
|
|
|
ambiguous_ctx_stack.clear();
|
2002-06-29 08:56:51 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
if (node)
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
return node;
|
2002-06-29 08:56:51 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
field_unknown(qualifier ? (TEXT*) qualifier->str_data : NULL,
|
2003-10-05 08:37:26 +02:00
|
|
|
name ? (TEXT*) name->str_data : NULL, input);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
// CVC: field_unknown() calls ERRD_post() that never returns, so the next line
|
2003-08-16 02:36:54 +02:00
|
|
|
// is only to make the compiler happy.
|
2001-05-23 15:26:42 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_found_aggregate
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Check the fields inside an aggregate
|
|
|
|
and check if the field scope_level
|
2003-02-15 04:01:51 +01:00
|
|
|
meets the specified conditions.
|
|
|
|
In the first call current_scope_level_equal
|
|
|
|
should always be true, because this is used
|
|
|
|
internally!
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param node
|
|
|
|
@param check_scope_level
|
|
|
|
@param match_type
|
|
|
|
@param current_scope_level_equal
|
|
|
|
|
|
|
|
**/
|
2003-11-05 10:02:33 +01:00
|
|
|
static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
USHORT match_type, bool current_scope_level_equal)
|
2002-09-29 01:52:36 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
bool found = false;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_gen_id:
|
|
|
|
case nod_gen_id2:
|
|
|
|
case nod_cast:
|
|
|
|
case nod_udf:
|
2005-05-28 00:45:31 +02:00
|
|
|
// If arguments are given to the UDF then there's a node list
|
2002-09-29 01:52:36 +02:00
|
|
|
if (node->nod_count == 2) {
|
2003-11-10 10:16:38 +01:00
|
|
|
found |= pass1_found_aggregate(node->nod_arg[1],
|
2002-10-01 02:34:29 +02:00
|
|
|
check_scope_level, match_type, current_scope_level_equal);
|
2002-09-29 01:52:36 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_exists:
|
|
|
|
case nod_singular:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_coalesce:
|
|
|
|
case nod_simple_case:
|
|
|
|
case nod_searched_case:
|
|
|
|
case nod_add:
|
|
|
|
case nod_concatenate:
|
|
|
|
case nod_divide:
|
|
|
|
case nod_multiply:
|
|
|
|
case nod_negate:
|
|
|
|
case nod_substr:
|
|
|
|
case nod_subtract:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_add2:
|
|
|
|
case nod_divide2:
|
|
|
|
case nod_multiply2:
|
|
|
|
case nod_subtract2:
|
2004-10-14 20:54:54 +02:00
|
|
|
case nod_equiv:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_leq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_between:
|
|
|
|
case nod_like:
|
|
|
|
case nod_missing:
|
|
|
|
case nod_and:
|
|
|
|
case nod_or:
|
|
|
|
case nod_any:
|
|
|
|
case nod_ansi_any:
|
|
|
|
case nod_ansi_all:
|
|
|
|
case nod_not:
|
|
|
|
case nod_unique:
|
|
|
|
case nod_containing:
|
|
|
|
case nod_starting:
|
|
|
|
case nod_list:
|
2003-05-07 03:57:18 +02:00
|
|
|
case nod_join:
|
|
|
|
case nod_join_inner:
|
|
|
|
case nod_join_left:
|
|
|
|
case nod_join_right:
|
|
|
|
case nod_join_full:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-05 10:02:33 +01:00
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
|
|
|
ptr < end; ++ptr)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_aggregate(*ptr, check_scope_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
match_type, current_scope_level_equal);
|
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
case nod_via:
|
2005-05-28 00:45:31 +02:00
|
|
|
// Pass only the rse from the nod_via
|
2002-09-29 01:52:36 +02:00
|
|
|
found |= pass1_found_aggregate(node->nod_arg[e_via_rse],
|
2002-10-01 02:34:29 +02:00
|
|
|
check_scope_level, match_type, current_scope_level_equal);
|
2002-09-29 01:52:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_rse:
|
2005-05-28 00:45:31 +02:00
|
|
|
// Pass rse_boolean (where clause) and rse_items (select items)
|
2002-09-29 01:52:36 +02:00
|
|
|
found |= pass1_found_aggregate(node->nod_arg[e_rse_boolean],
|
2003-09-02 01:22:22 +02:00
|
|
|
check_scope_level, match_type, false);
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_aggregate(node->nod_arg[e_rse_items],
|
2003-09-02 01:22:22 +02:00
|
|
|
check_scope_level, match_type, false);
|
2002-09-29 01:52:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_alias:
|
2003-09-04 17:02:22 +02:00
|
|
|
found |= pass1_found_aggregate(node->nod_arg[e_alias_value],
|
|
|
|
check_scope_level, match_type, current_scope_level_equal);
|
2002-09-29 01:52:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_aggregate:
|
2005-05-28 00:45:31 +02:00
|
|
|
// Pass only rse_group (group by clause)
|
2002-09-29 01:52:36 +02:00
|
|
|
found |= pass1_found_aggregate(node->nod_arg[e_agg_group],
|
2002-10-01 02:34:29 +02:00
|
|
|
check_scope_level, match_type, current_scope_level_equal);
|
2002-09-29 01:52:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_agg_average:
|
|
|
|
case nod_agg_count:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_total:
|
|
|
|
case nod_agg_average2:
|
|
|
|
case nod_agg_total2:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
|
|
|
bool field = false;
|
|
|
|
if (node->nod_count) {
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(node->nod_arg[e_agg_function_expression],
|
2004-04-03 01:20:29 +02:00
|
|
|
check_scope_level, match_type, &field);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
|
|
|
if (!field) {
|
2005-05-28 00:45:31 +02:00
|
|
|
/* For example COUNT(*) is always same scope_level (node->nod_count = 0)
|
2003-09-02 01:22:22 +02:00
|
|
|
Normaly COUNT(*) is the only way to come here but something stupid
|
|
|
|
as SUM(5) is also possible.
|
|
|
|
If current_scope_level_equal is FALSE scope_level is always higher */
|
|
|
|
switch (match_type) {
|
|
|
|
case FIELD_MATCH_TYPE_LOWER_EQUAL:
|
|
|
|
case FIELD_MATCH_TYPE_EQUAL:
|
|
|
|
if (current_scope_level_equal) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
2002-10-01 02:34:29 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
case FIELD_MATCH_TYPE_HIGHER_EQUAL:
|
|
|
|
return true;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
case FIELD_MATCH_TYPE_LOWER:
|
|
|
|
case FIELD_MATCH_TYPE_HIGHER:
|
|
|
|
return false;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
default:
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
case nod_map:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-05 10:02:33 +01:00
|
|
|
const dsql_map* map =
|
2003-11-10 10:16:38 +01:00
|
|
|
reinterpret_cast<dsql_map*>(node->nod_arg[e_map_map]);
|
2003-09-02 01:22:22 +02:00
|
|
|
found |= pass1_found_aggregate(map->map_node,
|
|
|
|
check_scope_level, match_type, current_scope_level_equal);
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
case nod_derived_field:
|
2002-10-01 02:34:29 +02:00
|
|
|
case nod_dbkey:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_field:
|
|
|
|
case nod_parameter:
|
|
|
|
case nod_relation:
|
|
|
|
case nod_variable:
|
|
|
|
case nod_constant:
|
|
|
|
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_internal_info:
|
2005-04-24 20:26:12 +02:00
|
|
|
case nod_dom_value:
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
default:
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2005-01-06 14:14:38 +01: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_found_field
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Check the fields inside an aggregate
|
|
|
|
and check if the field scope_level
|
2003-02-15 04:01:51 +01:00
|
|
|
meets the specified conditions.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param node
|
|
|
|
@param check_scope_level
|
|
|
|
@param match_type
|
|
|
|
@param field
|
|
|
|
|
|
|
|
**/
|
2003-11-05 10:02:33 +01:00
|
|
|
static bool pass1_found_field(const dsql_nod* node, USHORT check_scope_level,
|
2003-10-05 08:37:26 +02:00
|
|
|
USHORT match_type, bool* field)
|
2002-09-29 01:52:36 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
2003-09-02 01:22:22 +02:00
|
|
|
|
|
|
|
bool found = false;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_field:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_ctx* field_context = (dsql_ctx*) node->nod_arg[e_fld_context];
|
2003-09-02 01:22:22 +02:00
|
|
|
DEV_BLKCHK(field_context, dsql_type_ctx);
|
|
|
|
*field = true;
|
|
|
|
switch (match_type) {
|
|
|
|
case FIELD_MATCH_TYPE_EQUAL:
|
|
|
|
return (field_context->ctx_scope_level == check_scope_level);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
case FIELD_MATCH_TYPE_LOWER:
|
|
|
|
return (field_context->ctx_scope_level < check_scope_level);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
case FIELD_MATCH_TYPE_LOWER_EQUAL:
|
|
|
|
return (field_context->ctx_scope_level <= check_scope_level);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
case FIELD_MATCH_TYPE_HIGHER:
|
|
|
|
return (field_context->ctx_scope_level > check_scope_level);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
case FIELD_MATCH_TYPE_HIGHER_EQUAL:
|
|
|
|
return (field_context->ctx_scope_level >= check_scope_level);
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
default:
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_gen_id:
|
|
|
|
case nod_gen_id2:
|
|
|
|
case nod_cast:
|
|
|
|
case nod_udf:
|
2005-05-28 00:45:31 +02:00
|
|
|
// If arguments are given to the UDF then there's a node list
|
2002-09-29 01:52:36 +02:00
|
|
|
if (node->nod_count == 2) {
|
2003-11-10 10:16:38 +01:00
|
|
|
found |= pass1_found_field(node->nod_arg[1], check_scope_level,
|
2002-09-29 01:52:36 +02:00
|
|
|
match_type, field);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_exists:
|
|
|
|
case nod_singular:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_coalesce:
|
|
|
|
case nod_simple_case:
|
|
|
|
case nod_searched_case:
|
|
|
|
case nod_add:
|
|
|
|
case nod_concatenate:
|
|
|
|
case nod_divide:
|
|
|
|
case nod_multiply:
|
|
|
|
case nod_negate:
|
|
|
|
case nod_substr:
|
|
|
|
case nod_subtract:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_add2:
|
|
|
|
case nod_divide2:
|
|
|
|
case nod_multiply2:
|
|
|
|
case nod_subtract2:
|
2004-10-14 20:54:54 +02:00
|
|
|
case nod_equiv:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_leq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_between:
|
|
|
|
case nod_like:
|
|
|
|
case nod_missing:
|
|
|
|
case nod_and:
|
|
|
|
case nod_or:
|
|
|
|
case nod_any:
|
|
|
|
case nod_ansi_any:
|
|
|
|
case nod_ansi_all:
|
|
|
|
case nod_not:
|
|
|
|
case nod_unique:
|
|
|
|
case nod_containing:
|
|
|
|
case nod_starting:
|
|
|
|
case nod_list:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-05 10:02:33 +01:00
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(*ptr, check_scope_level,
|
2003-09-02 01:22:22 +02:00
|
|
|
match_type, field);
|
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
case nod_via:
|
2003-09-02 01:22:22 +02:00
|
|
|
// Pass only the rse from the nod_via
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(node->nod_arg[e_via_rse],
|
2002-09-29 01:52:36 +02:00
|
|
|
check_scope_level, match_type, field);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_rse:
|
2003-09-02 01:22:22 +02:00
|
|
|
// Pass rse_boolean (where clause) and rse_items (select items)
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(node->nod_arg[e_rse_boolean],
|
2002-09-29 01:52:36 +02:00
|
|
|
check_scope_level, match_type, field);
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(node->nod_arg[e_rse_items],
|
2002-09-29 01:52:36 +02:00
|
|
|
check_scope_level, match_type, field);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_alias:
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(node->nod_arg[e_alias_value],
|
2003-09-04 17:02:22 +02:00
|
|
|
check_scope_level, match_type, field);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_derived_field:
|
2006-08-04 06:32:27 +02:00
|
|
|
{
|
|
|
|
// This is a "virtual" field
|
|
|
|
*field = true;
|
|
|
|
const USHORT df_scope_level =
|
|
|
|
(USHORT)(U_IPTR) node->nod_arg[e_derived_field_scope];
|
|
|
|
|
|
|
|
switch (match_type) {
|
|
|
|
case FIELD_MATCH_TYPE_EQUAL:
|
|
|
|
return (df_scope_level == check_scope_level);
|
|
|
|
|
|
|
|
case FIELD_MATCH_TYPE_LOWER:
|
|
|
|
return (df_scope_level < check_scope_level);
|
|
|
|
|
|
|
|
case FIELD_MATCH_TYPE_LOWER_EQUAL:
|
|
|
|
return (df_scope_level <= check_scope_level);
|
|
|
|
|
|
|
|
case FIELD_MATCH_TYPE_HIGHER:
|
|
|
|
return (df_scope_level > check_scope_level);
|
|
|
|
|
|
|
|
case FIELD_MATCH_TYPE_HIGHER_EQUAL:
|
|
|
|
return (df_scope_level >= check_scope_level);
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_aggregate:
|
2003-09-02 01:22:22 +02:00
|
|
|
// Pass only rse_group (group by clause)
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(node->nod_arg[e_agg_group],
|
2002-09-29 01:52:36 +02:00
|
|
|
check_scope_level, match_type, field);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_agg_average:
|
|
|
|
case nod_agg_count:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_total:
|
|
|
|
case nod_agg_average2:
|
|
|
|
case nod_agg_total2:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2002-09-29 01:52:36 +02:00
|
|
|
if (node->nod_count) {
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(node->nod_arg[e_agg_function_expression],
|
2004-04-03 01:20:29 +02:00
|
|
|
check_scope_level, match_type, field);
|
2002-09-29 01:52:36 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_map:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2003-11-05 10:02:33 +01:00
|
|
|
const dsql_map* map =
|
2004-11-10 05:26:45 +01:00
|
|
|
reinterpret_cast<dsql_map*>(node->nod_arg[e_map_map]);
|
2005-05-28 00:45:31 +02:00
|
|
|
found |= pass1_found_field(map->map_node, check_scope_level,
|
2002-09-29 01:52:36 +02:00
|
|
|
match_type, field);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2002-10-01 02:34:29 +02:00
|
|
|
case nod_dbkey:
|
2002-09-29 01:52:36 +02:00
|
|
|
case nod_parameter:
|
|
|
|
case nod_relation:
|
|
|
|
case nod_variable:
|
|
|
|
case nod_constant:
|
|
|
|
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_internal_info:
|
2005-04-24 20:26:12 +02:00
|
|
|
case nod_dom_value:
|
2003-09-02 01:22:22 +02:00
|
|
|
return false;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
default:
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-06 14:14:38 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-01-06 14:14:38 +01:00
|
|
|
pass1_found_sub_select
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Search if a sub select is buried inside
|
2005-01-06 14:14:38 +01:00
|
|
|
an select list from an query expression.
|
|
|
|
|
|
|
|
@param node
|
|
|
|
|
|
|
|
**/
|
|
|
|
static bool pass1_found_sub_select(const dsql_nod* node)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
|
|
|
|
|
|
|
if (node == NULL) return false;
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2005-01-06 14:14:38 +01:00
|
|
|
case nod_gen_id:
|
|
|
|
case nod_gen_id2:
|
|
|
|
case nod_cast:
|
|
|
|
case nod_udf:
|
2005-05-28 00:45:31 +02:00
|
|
|
// If arguments are given to the UDF then there's a node list
|
2005-01-06 14:14:38 +01:00
|
|
|
if (node->nod_count == 2) {
|
|
|
|
if (pass1_found_sub_select(node->nod_arg[1])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_exists:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_singular:
|
2005-01-06 14:14:38 +01:00
|
|
|
case nod_coalesce:
|
|
|
|
case nod_simple_case:
|
|
|
|
case nod_searched_case:
|
|
|
|
case nod_add:
|
|
|
|
case nod_concatenate:
|
|
|
|
case nod_divide:
|
|
|
|
case nod_multiply:
|
|
|
|
case nod_negate:
|
|
|
|
case nod_substr:
|
|
|
|
case nod_subtract:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2005-01-06 14:14:38 +01:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2005-01-06 14:14:38 +01:00
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2005-01-06 14:14:38 +01:00
|
|
|
case nod_add2:
|
|
|
|
case nod_divide2:
|
|
|
|
case nod_multiply2:
|
|
|
|
case nod_subtract2:
|
|
|
|
case nod_equiv:
|
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_leq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_between:
|
|
|
|
case nod_like:
|
|
|
|
case nod_missing:
|
|
|
|
case nod_and:
|
|
|
|
case nod_or:
|
|
|
|
case nod_any:
|
|
|
|
case nod_ansi_any:
|
|
|
|
case nod_ansi_all:
|
|
|
|
case nod_not:
|
|
|
|
case nod_unique:
|
|
|
|
case nod_containing:
|
|
|
|
case nod_starting:
|
|
|
|
case nod_list:
|
|
|
|
case nod_join:
|
|
|
|
case nod_join_inner:
|
|
|
|
case nod_join_left:
|
|
|
|
case nod_join_right:
|
|
|
|
case nod_join_full:
|
|
|
|
{
|
|
|
|
const dsql_nod* const* ptr = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
|
|
|
ptr < end; ++ptr)
|
|
|
|
{
|
|
|
|
if (pass1_found_sub_select(*ptr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2005-01-06 14:14:38 +01:00
|
|
|
|
|
|
|
case nod_via:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case nod_alias:
|
|
|
|
if (pass1_found_sub_select(node->nod_arg[e_alias_value])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_aggregate:
|
2005-11-26 17:15:47 +01:00
|
|
|
case nod_agg_average:
|
|
|
|
case nod_agg_count:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_total:
|
|
|
|
case nod_agg_average2:
|
|
|
|
case nod_agg_total2:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2005-11-26 17:15:47 +01:00
|
|
|
case nod_map:
|
|
|
|
|
2005-01-06 14:14:38 +01:00
|
|
|
case nod_derived_field:
|
|
|
|
case nod_dbkey:
|
|
|
|
case nod_field:
|
|
|
|
case nod_parameter:
|
|
|
|
case nod_relation:
|
|
|
|
case nod_variable:
|
|
|
|
case nod_constant:
|
|
|
|
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_internal_info:
|
2005-04-24 20:26:12 +02:00
|
|
|
case nod_dom_value:
|
2005-01-25 00:02:08 +01:00
|
|
|
case nod_field_name:
|
2005-01-06 14:14:38 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return true;
|
2005-05-22 05:11:41 +02:00
|
|
|
}
|
2005-01-06 14:14:38 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param select_list
|
|
|
|
|
|
|
|
**/
|
2005-02-10 22:14:52 +01:00
|
|
|
static dsql_nod* pass1_group_by_list(dsql_req* request, dsql_nod* input, dsql_nod* selectList)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
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
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack stack;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
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);
|
2003-08-15 02:02:18 +02:00
|
|
|
if (sub->nod_type == nod_field_name) {
|
2005-02-10 22:14:52 +01:00
|
|
|
// check for alias or field node
|
|
|
|
dsql_nod* frnode = pass1_field(request, sub, false, selectList);
|
|
|
|
stack.push(frnode);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else if ((sub->nod_type == nod_constant) && (sub->nod_desc.dsc_dtype == dtype_long)) {
|
2004-01-21 08:18:30 +01:00
|
|
|
const ULONG position = (IPTR) (sub->nod_arg[0]);
|
2005-05-28 00:45:31 +02:00
|
|
|
if ((position < 1) || !selectList ||
|
2005-02-10 22:14:52 +01:00
|
|
|
(position > (ULONG) selectList->nod_count))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_dsql_column_pos_err,
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_string, "GROUP BY", 0);
|
2003-08-15 02:02:18 +02:00
|
|
|
// Invalid column position used in the GROUP BY clause
|
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
stack.push(PASS1_node(request, selectList->nod_arg[position - 1], false));
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
|
|
|
stack.push(PASS1_node(request, *ptr, false));
|
|
|
|
}
|
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
|
|
|
|
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
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Process INSERT statement.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
2005-06-13 14:45:42 +02:00
|
|
|
@param proc_flag
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2005-06-13 14:45:42 +02:00
|
|
|
static dsql_nod* pass1_insert( dsql_req* request, dsql_nod* input, bool proc_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
request->req_type = REQ_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];
|
2003-10-05 08:37:26 +02:00
|
|
|
if (rse) {
|
2004-10-13 20:37:53 +02:00
|
|
|
node->nod_arg[e_sto_rse] = rse = PASS1_rse(request, rse, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
values = rse->nod_arg[e_rse_items];
|
|
|
|
}
|
|
|
|
else
|
2003-09-02 01:22:22 +02:00
|
|
|
values = PASS1_node(request, 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
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* temp_rel = pass1_relation(request, input->nod_arg[e_ins_relation]);
|
|
|
|
node->nod_arg[e_sto_relation] = temp_rel;
|
|
|
|
dsql_ctx* context = (dsql_ctx*) temp_rel->nod_arg[0];
|
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];
|
2003-10-05 08:37:26 +02:00
|
|
|
if (fields) {
|
2005-10-06 08:08:10 +02:00
|
|
|
const dsql_nod* old_fields = fields; // for error reporting.
|
2003-09-02 01:22:22 +02:00
|
|
|
fields = PASS1_node(request, 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");
|
2005-10-06 08:08:10 +02:00
|
|
|
|
2005-05-28 00:45:31 +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
|
|
|
|
dsql_nod** ptr = fields->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + fields->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
const dsql_ctx* tmp_ctx = 0;
|
2002-07-06 07:32:02 +02:00
|
|
|
DEV_BLKCHK (*ptr, dsql_type_nod);
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* temp2 = *ptr;
|
|
|
|
if (temp2->nod_type == nod_field &&
|
|
|
|
(tmp_ctx = (dsql_ctx*) temp2->nod_arg[e_fld_context]) != 0 &&
|
2002-07-06 07:32:02 +02:00
|
|
|
tmp_ctx->ctx_relation &&
|
2003-10-05 08:37:26 +02:00
|
|
|
strcmp (tmp_ctx->ctx_relation->rel_name, relation->rel_name))
|
|
|
|
{
|
2002-07-06 07:32:02 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_rel* bad_rel = tmp_ctx->ctx_relation;
|
|
|
|
const dsql_fld* bad_fld = (dsql_fld*) temp2->nod_arg[e_fld_field];
|
2002-07-06 07:32:02 +02:00
|
|
|
// At this time, "fields" has been replaced by the processed list in
|
2003-11-10 10:16:38 +01:00
|
|
|
// the same variable, so we refer again to input->nod_arg[e_ins_fields].
|
2005-10-06 08:08:10 +02:00
|
|
|
// CVC: After three years, made old_fields for that purpose.
|
2002-07-06 07:32:02 +02:00
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
field_unknown(bad_rel ? (TEXT*) bad_rel->rel_name : NULL,
|
2003-10-05 08:37:26 +02:00
|
|
|
bad_fld ? (TEXT*) bad_fld->fld_name : NULL,
|
2005-10-06 08:08:10 +02:00
|
|
|
old_fields->nod_arg[ptr - fields->nod_arg]);
|
2002-07-06 07:32:02 +02:00
|
|
|
}
|
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
// end IBO hack
|
2002-07-06 07:32:02 +02:00
|
|
|
}
|
2006-09-03 03:09:23 +02:00
|
|
|
else
|
|
|
|
fields = PASS1_node(request, 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
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
if (fields->nod_count != values->nod_count) {
|
2005-05-28 00:45:31 +02:00
|
|
|
// count of column list and value list don't match
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 804,
|
|
|
|
isc_arg_gds, isc_dsql_var_count_err, 0);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
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
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod** ptr = fields->nod_arg;
|
|
|
|
dsql_nod** ptr2 = values->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + fields->nod_count;
|
|
|
|
ptr < end; ptr++, ptr2++)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(*ptr2, dsql_type_nod);
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, 2);
|
2001-05-23 15:26:42 +02:00
|
|
|
temp->nod_arg[0] = *ptr2;
|
|
|
|
temp->nod_arg[1] = *ptr;
|
2004-04-18 16:22:27 +02:00
|
|
|
stack.push(temp);
|
2001-05-23 15:26:42 +02:00
|
|
|
temp = *ptr2;
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, temp, *ptr, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
node->nod_arg[e_sto_statement] = MAKE_list(stack);
|
2005-06-13 14:45:42 +02:00
|
|
|
node->nod_arg[e_sto_return] =
|
2006-09-03 03:09:23 +02:00
|
|
|
process_returning(request, input->nod_arg[e_ins_return], proc_flag);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
set_parameters_name(node->nod_arg[e_sto_statement],
|
|
|
|
node->nod_arg[e_sto_relation]);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
pass1_join
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Make up join node and mark relations as "possibly NULL"
|
2003-08-15 02:02:18 +02:00
|
|
|
if they are in outer joins (req_in_outer_join).
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_join(dsql_req* request, dsql_nod* input, bool proc_flag)
|
2003-08-15 02:02:18 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(input->nod_type, input->nod_count);
|
2004-10-13 20:37:53 +02:00
|
|
|
|
|
|
|
// First process type
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_type] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_type], proc_flag);
|
2004-10-13 20:37:53 +02:00
|
|
|
|
|
|
|
// Process relations
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
switch (node->nod_arg[e_join_type]->nod_type) {
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_join_inner:
|
|
|
|
node->nod_arg[e_join_left_rel] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_left_rel], proc_flag);
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_rght_rel] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_rght_rel], proc_flag);
|
|
|
|
break;
|
|
|
|
case nod_join_left:
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_left_rel] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_left_rel], proc_flag);
|
|
|
|
request->req_in_outer_join++;
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_rght_rel] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_rght_rel], proc_flag);
|
|
|
|
request->req_in_outer_join--;
|
|
|
|
break;
|
|
|
|
case nod_join_right:
|
|
|
|
request->req_in_outer_join++;
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_left_rel] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_left_rel], proc_flag);
|
|
|
|
request->req_in_outer_join--;
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_rght_rel] =
|
2003-09-28 23:36:05 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_rght_rel], proc_flag);
|
2003-08-15 02:02:18 +02:00
|
|
|
break;
|
|
|
|
case nod_join_full:
|
|
|
|
request->req_in_outer_join++;
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_left_rel] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_left_rel], proc_flag);
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_rght_rel] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_rght_rel], proc_flag);
|
|
|
|
request->req_in_outer_join--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2004-10-13 20:37:53 +02:00
|
|
|
fb_assert(false); // join type expected
|
2003-08-15 02:02:18 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
|
|
|
|
// Process boolean
|
|
|
|
|
|
|
|
dsql_nod* boolean = input->nod_arg[e_join_boolean];
|
|
|
|
if (boolean && (boolean->nod_type == nod_flag || boolean->nod_type == nod_list))
|
|
|
|
{
|
|
|
|
// Process NATURAL JOIN or USING clause
|
|
|
|
ERRD_post(isc_wish_list, 0);
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_join_boolean] =
|
2003-08-15 02:02:18 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_join_boolean], proc_flag);
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-26 09:13:33 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-26 09:13:33 +02:00
|
|
|
pass1_label
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-26 09:13:33 +02:00
|
|
|
@brief Process loop interruption
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-08-26 09:13:33 +02:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_label(dsql_req* request, dsql_nod* input)
|
2003-08-26 09:13:33 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* label = NULL;
|
2003-08-26 09:13:33 +02:00
|
|
|
|
|
|
|
// retrieve a label
|
|
|
|
|
2003-09-28 13:12:03 +02:00
|
|
|
switch (input->nod_type) {
|
|
|
|
case nod_breakleave:
|
2003-08-26 09:13:33 +02:00
|
|
|
label = input->nod_arg[e_breakleave_label];
|
2003-09-28 13:12:03 +02:00
|
|
|
break;
|
|
|
|
case nod_for_select:
|
2003-08-26 09:13:33 +02:00
|
|
|
label = input->nod_arg[e_flp_label];
|
2003-09-28 13:12:03 +02:00
|
|
|
break;
|
|
|
|
case nod_exec_into:
|
2003-08-26 09:13:33 +02:00
|
|
|
label = input->nod_arg[e_exec_into_label];
|
2003-09-28 13:12:03 +02:00
|
|
|
break;
|
|
|
|
case nod_while:
|
2003-08-26 09:13:33 +02:00
|
|
|
label = input->nod_arg[e_while_label];
|
2003-09-28 13:12:03 +02:00
|
|
|
break;
|
|
|
|
default:
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// look for a label, if specified
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_str* string = NULL;
|
2003-11-18 08:58:35 +01:00
|
|
|
USHORT position = 0;
|
2003-08-26 09:13:33 +02: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];
|
2003-11-18 08:58:35 +01:00
|
|
|
const TEXT* label_string = (TEXT*) string->str_data;
|
2003-08-26 09:13:33 +02:00
|
|
|
int index = request->req_loop_level;
|
2004-05-27 18:26:52 +02:00
|
|
|
for (DsqlStrStack::iterator stack(request->req_labels); stack.hasData(); ++stack) {
|
2004-04-18 16:22:27 +02:00
|
|
|
const dsql_str* obj = stack.object();
|
2003-11-02 13:28:30 +01:00
|
|
|
if (obj) {
|
2003-11-18 08:58:35 +01:00
|
|
|
const TEXT* obj_string = (TEXT*) obj->str_data;
|
2003-11-02 13:28:30 +01:00
|
|
|
if (!strcmp(label_string, obj_string)) {
|
|
|
|
position = index;
|
|
|
|
break;
|
|
|
|
}
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
USHORT number = 0;
|
2003-08-26 09:13:33 +02:00
|
|
|
if (input->nod_type == nod_breakleave) {
|
|
|
|
if (position > 0) {
|
|
|
|
// break the specified loop
|
|
|
|
number = position;
|
|
|
|
}
|
|
|
|
else if (label) {
|
|
|
|
// ERROR: Label %s is not found in the current scope
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_dsql_invalid_label,
|
|
|
|
isc_arg_string, string->str_data,
|
|
|
|
isc_arg_string, "is not found", 0);
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// break the current loop
|
|
|
|
number = request->req_loop_level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (position > 0) {
|
|
|
|
// ERROR: Label %s already exists in the current scope
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_dsql_invalid_label,
|
|
|
|
isc_arg_string, string->str_data,
|
|
|
|
isc_arg_string, "already exists", 0);
|
2003-08-26 09:13:33 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// store label name, if specified
|
2004-04-18 16:22:27 +02:00
|
|
|
request->req_labels.push(string);
|
2003-08-26 09:13:33 +02:00
|
|
|
number = request->req_loop_level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(number > 0 && number <= request->req_loop_level);
|
2003-08-26 09:13:33 +02:00
|
|
|
|
2003-09-28 13:12:03 +02:00
|
|
|
if (!label) {
|
|
|
|
label = MAKE_node(nod_label, e_label_count);
|
|
|
|
// this label is unnamed, i.e. its nod_arg[e_label_name] is NULL
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
pass1_lookup_alias
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-02-14 06:54:45 +01:00
|
|
|
@brief Lookup a matching item in the select list.
|
2005-02-10 22:14:52 +01:00
|
|
|
Return node if found else return NULL.
|
|
|
|
If more matches are found we raise ambiguity error.
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param name
|
|
|
|
@param selectList
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_lookup_alias(dsql_req* request, const dsql_str* name, dsql_nod* selectList)
|
|
|
|
{
|
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;
|
|
|
|
for (; ptr < end; ptr++) {
|
|
|
|
dsql_nod* matchingNode = NULL;
|
|
|
|
dsql_nod* node = *ptr;
|
|
|
|
switch (node->nod_type) {
|
2005-05-22 05:11:41 +02:00
|
|
|
case nod_alias:
|
|
|
|
{
|
|
|
|
dsql_str* alias = (dsql_str*) node->nod_arg[e_alias_alias];
|
|
|
|
if (!strcmp(alias->str_data, name->str_data)) {
|
|
|
|
matchingNode = PASS1_node(request, node, false);
|
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
case nod_field:
|
|
|
|
{
|
|
|
|
dsql_fld* field = (dsql_fld*) node->nod_arg[e_fld_field];
|
|
|
|
if (!strcmp(field->fld_name, name->str_data)) {
|
|
|
|
matchingNode = PASS1_node(request, node, false);
|
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
case nod_derived_field:
|
|
|
|
{
|
|
|
|
dsql_str* alias = (dsql_str*) node->nod_arg[e_derived_field_name];
|
|
|
|
if (!strcmp(alias->str_data, name->str_data)) {
|
|
|
|
matchingNode = PASS1_node(request, node, false);
|
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (matchingNode) {
|
|
|
|
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;
|
|
|
|
switch (returnNode->nod_type) {
|
|
|
|
case nod_field:
|
|
|
|
strcat(buffer1, "a field");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_alias:
|
|
|
|
strcat(buffer1, "an alias");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_derived_field:
|
|
|
|
strcat(buffer1, "a derived field");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
strcat(buffer1, "an item");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEXT buffer2[256];
|
|
|
|
buffer2[0] = 0;
|
|
|
|
switch (matchingNode->nod_type) {
|
|
|
|
case nod_field:
|
|
|
|
strcat(buffer2, "a field");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_alias:
|
|
|
|
strcat(buffer2, "an alias");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_derived_field:
|
|
|
|
strcat(buffer2, "a derived field");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
strcat(buffer2, "an item");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strcat(buffer2, " in the select list with name");
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -204,
|
2005-02-10 22:14:52 +01:00
|
|
|
isc_arg_gds, isc_dsql_ambiguous_field_name,
|
|
|
|
isc_arg_string, buffer1,
|
|
|
|
isc_arg_string, buffer2,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, name->str_data,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
returnNode = matchingNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (returnNode) {
|
|
|
|
return returnNode;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
@param request
|
2003-09-04 17:02:22 +02:00
|
|
|
@param tdsql
|
2005-01-25 00:02:08 +01:00
|
|
|
@param select_item
|
2003-08-24 04:36:46 +02:00
|
|
|
|
|
|
|
**/
|
2004-02-02 12:02:12 +01:00
|
|
|
static dsql_nod* pass1_make_derived_field(dsql_req* request, tsql* tdsql,
|
|
|
|
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);
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (select_item->nod_type)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_derived_field:
|
2004-11-28 23:38:45 +01:00
|
|
|
{
|
|
|
|
// Create a derived field that points to a derived field
|
|
|
|
dsql_nod* derived_field = MAKE_node(nod_derived_field, e_derived_field_count);
|
|
|
|
derived_field->nod_arg[e_derived_field_value] = select_item;
|
|
|
|
derived_field->nod_arg[e_derived_field_name] = select_item->nod_arg[e_derived_field_name];
|
|
|
|
derived_field->nod_arg[e_derived_field_scope] = (dsql_nod*)(IPTR) request->req_scope_level;
|
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
return derived_field;
|
|
|
|
}
|
2004-11-29 10:08:02 +01:00
|
|
|
|
2004-11-28 23:38:45 +01:00
|
|
|
case nod_field:
|
2003-09-04 17:02:22 +02:00
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_fld* field = (dsql_fld*) select_item->nod_arg[e_fld_field];
|
2003-09-04 17:02:22 +02:00
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
2003-08-24 04:36:46 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
// Copy fieldname to a new string.
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_str* alias = FB_NEW_RPT(*tdsql->getDefaultPool(), strlen(field->fld_name)) dsql_str;
|
2003-09-04 17:02:22 +02:00
|
|
|
strcpy(alias->str_data, field->fld_name);
|
|
|
|
alias->str_length = strlen(field->fld_name);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
// Create a derived field and hook in.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* derived_field = MAKE_node(nod_derived_field, e_derived_field_count);
|
2003-09-04 17:02:22 +02:00
|
|
|
derived_field->nod_arg[e_derived_field_value] = select_item;
|
2003-11-10 10:16:38 +01:00
|
|
|
derived_field->nod_arg[e_derived_field_name] = (dsql_nod*) alias;
|
2004-01-21 08:18:30 +01:00
|
|
|
derived_field->nod_arg[e_derived_field_scope] = (dsql_nod*)(IPTR) request->req_scope_level;
|
2003-09-04 17:02:22 +02:00
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
|
|
|
|
case nod_alias:
|
|
|
|
{
|
|
|
|
// Copy aliasname to a new string.
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* alias_alias = (dsql_str*) select_item->nod_arg[e_alias_alias];
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_str* alias = FB_NEW_RPT(*tdsql->getDefaultPool(), strlen(alias_alias->str_data)) dsql_str;
|
2003-09-04 17:02:22 +02:00
|
|
|
strcpy(alias->str_data, alias_alias->str_data);
|
|
|
|
alias->str_length = strlen(alias_alias->str_data);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-09-04 17:02:22 +02:00
|
|
|
// Create a derived field and ignore alias node.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* derived_field = MAKE_node(nod_derived_field, e_derived_field_count);
|
2003-09-04 17:02:22 +02:00
|
|
|
derived_field->nod_arg[e_derived_field_value] = select_item->nod_arg[e_alias_value];
|
2003-11-10 10:16:38 +01:00
|
|
|
derived_field->nod_arg[e_derived_field_name] = (dsql_nod*) alias;
|
2004-01-21 08:18:30 +01:00
|
|
|
derived_field->nod_arg[e_derived_field_scope] = (dsql_nod*)(IPTR) request->req_scope_level;
|
2003-09-04 17:02:22 +02:00
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
|
|
|
|
case nod_map :
|
|
|
|
{
|
|
|
|
// Aggregate's have map on top.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_map* map = (dsql_map*) select_item->nod_arg[e_map_map];
|
|
|
|
dsql_nod* derived_field = pass1_make_derived_field(request, tdsql, map->map_node);
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// If we had succesfully made a derived field node change it
|
2003-09-04 17:02:22 +02:00
|
|
|
// with orginal map.
|
|
|
|
if (derived_field->nod_type == nod_derived_field) {
|
|
|
|
derived_field->nod_arg[e_derived_field_value] = select_item;
|
2004-01-21 08:18:30 +01:00
|
|
|
derived_field->nod_arg[e_derived_field_scope] = (dsql_nod*)(IPTR) request->req_scope_level;
|
2003-09-04 17:02:22 +02:00
|
|
|
derived_field->nod_desc = select_item->nod_desc;
|
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return select_item;
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-09-04 17:02:22 +02:00
|
|
|
|
2005-01-25 00:02:08 +01:00
|
|
|
case nod_via :
|
|
|
|
{
|
|
|
|
// Try to generate derived field from sub-select
|
2005-05-28 00:45:31 +02:00
|
|
|
dsql_nod* derived_field = pass1_make_derived_field(request, tdsql,
|
2005-01-25 00:02:08 +01:00
|
|
|
select_item->nod_arg[e_via_value_1]);
|
|
|
|
derived_field->nod_arg[e_derived_field_value] = select_item;
|
|
|
|
return derived_field;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
|
@brief Process MERGE statement.
|
|
|
|
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_merge(dsql_req* request, dsql_nod* input, bool proc_flag)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
|
|
|
dsql_nod* source = input->nod_arg[e_mrg_using]; // USING
|
|
|
|
dsql_nod* target = input->nod_arg[e_mrg_relation]; // INTO
|
|
|
|
|
|
|
|
// build a left join between USING and INTO tables
|
|
|
|
dsql_nod* join = MAKE_node(nod_join, e_join_count);
|
|
|
|
join->nod_arg[e_join_left_rel] = source;
|
|
|
|
join->nod_arg[e_join_type] = MAKE_node(nod_join_left, 0);
|
|
|
|
join->nod_arg[e_join_rght_rel] = target;
|
|
|
|
join->nod_arg[e_join_boolean] = input->nod_arg[e_mrg_condition];
|
|
|
|
|
|
|
|
dsql_nod* query_spec = MAKE_node(nod_query_spec, e_qry_count);
|
|
|
|
query_spec->nod_arg[e_qry_from] = join;
|
|
|
|
|
|
|
|
dsql_nod* select_expr = MAKE_node(nod_select_expr, e_sel_count);
|
|
|
|
select_expr->nod_arg[e_sel_query_spec] = query_spec;
|
|
|
|
|
|
|
|
dsql_nod* select = MAKE_node(nod_select, e_select_count);
|
|
|
|
select->nod_arg[e_select_expr] = select_expr;
|
|
|
|
|
|
|
|
// build a FOR SELECT node
|
|
|
|
dsql_nod* for_select = MAKE_node(nod_for_select, e_flp_count);
|
|
|
|
for_select->nod_arg[e_flp_select] = select;
|
|
|
|
for_select->nod_arg[e_flp_action] = MAKE_node(nod_list, 0);
|
|
|
|
for_select = PASS1_statement(request, for_select, proc_flag);
|
|
|
|
|
|
|
|
// get the already processed relations
|
|
|
|
source = for_select->nod_arg[e_flp_select]->nod_arg[e_select_expr]->nod_arg[e_join_left_rel];
|
|
|
|
target = for_select->nod_arg[e_flp_select]->nod_arg[e_select_expr]->nod_arg[e_join_rght_rel];
|
|
|
|
|
|
|
|
// get the assignments of the UPDATE statement
|
|
|
|
dsql_nod* list =
|
|
|
|
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement];
|
|
|
|
fb_assert(list->nod_type == nod_list);
|
|
|
|
|
|
|
|
Firebird::Array<dsql_nod*> org_values, new_values;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// separate the new and org values to process in correct contexts
|
|
|
|
for (i = 0; i < list->nod_count; ++i)
|
|
|
|
{
|
|
|
|
const dsql_nod* const assign = list->nod_arg[i];
|
|
|
|
fb_assert(assign->nod_type == nod_assign);
|
|
|
|
org_values.add(assign->nod_arg[0]);
|
|
|
|
new_values.add(assign->nod_arg[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// build the MODIFY node
|
|
|
|
dsql_nod* modify = MAKE_node(nod_modify_current, e_mdc_count);
|
|
|
|
dsql_ctx* context = get_context(target);
|
|
|
|
dsql_nod** ptr;
|
|
|
|
|
|
|
|
modify->nod_arg[e_mdc_context] = (dsql_nod*) context;
|
|
|
|
|
|
|
|
// push the USING context
|
|
|
|
request->req_context->push(get_context(source));
|
|
|
|
request->req_scope_level++;
|
|
|
|
|
|
|
|
// process old context values
|
|
|
|
request->req_context->push(context);
|
|
|
|
request->req_scope_level++;
|
|
|
|
|
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
|
|
|
|
request->req_scope_level--;
|
|
|
|
request->req_context->pop();
|
|
|
|
|
|
|
|
// pop the USING context
|
|
|
|
request->req_scope_level--;
|
|
|
|
request->req_context->pop();
|
|
|
|
|
|
|
|
// process relation
|
|
|
|
modify->nod_arg[e_mdc_update] = pass1_relation(request, input->nod_arg[e_mrg_relation]);
|
|
|
|
|
|
|
|
// process new context values
|
|
|
|
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr)
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
|
|
|
|
request->req_context->pop();
|
|
|
|
|
|
|
|
// recreate list of assignments
|
|
|
|
modify->nod_arg[e_mdc_statement] = list =
|
|
|
|
MAKE_node(nod_list, list->nod_count);
|
|
|
|
|
|
|
|
for (i = 0; i < list->nod_count; ++i)
|
|
|
|
{
|
|
|
|
dsql_nod* assign = MAKE_node(nod_assign, 2);
|
|
|
|
assign->nod_arg[0] = org_values[i];
|
|
|
|
assign->nod_arg[1] = new_values[i];
|
|
|
|
list->nod_arg[i] = assign;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We do not allow cases like UPDATE T SET f1 = v1, f2 = v2, f1 = v3...
|
|
|
|
field_appears_once(modify->nod_arg[e_mdc_statement],
|
|
|
|
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement],
|
|
|
|
false, "MERGE");
|
|
|
|
|
|
|
|
// push the USING context
|
|
|
|
request->req_context->push(get_context(source));
|
|
|
|
request->req_scope_level++;
|
|
|
|
|
|
|
|
// the INSERT relation should be processed in a higher level than the source
|
|
|
|
request->req_scope_level++;
|
|
|
|
|
|
|
|
// build the INSERT node
|
|
|
|
dsql_nod* insert = MAKE_node(nod_insert, e_ins_count);
|
|
|
|
insert->nod_arg[e_ins_relation] = input->nod_arg[e_mrg_relation];
|
|
|
|
insert->nod_arg[e_ins_fields] =
|
|
|
|
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]->nod_arg[e_mrg_insert_fields];
|
|
|
|
insert->nod_arg[e_ins_values] =
|
|
|
|
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]->nod_arg[e_mrg_insert_values];
|
|
|
|
insert = PASS1_statement(request, insert, proc_flag);
|
|
|
|
|
|
|
|
// restore the scope level
|
|
|
|
request->req_scope_level--;
|
|
|
|
|
|
|
|
// pop the USING context
|
|
|
|
request->req_scope_level--;
|
|
|
|
request->req_context->pop();
|
|
|
|
|
|
|
|
// build a IF (target.RDB$DB_KEY IS NOT NULL)
|
|
|
|
dsql_nod* action = MAKE_node(nod_if, e_if_count);
|
|
|
|
action->nod_arg[e_if_condition] = MAKE_node(nod_not, 1);
|
|
|
|
action->nod_arg[e_if_condition]->nod_arg[0] = MAKE_node(nod_missing, 1);
|
|
|
|
action->nod_arg[e_if_condition]->nod_arg[0]->nod_arg[0] = MAKE_node(nod_dbkey, 1);
|
|
|
|
action->nod_arg[e_if_condition]->nod_arg[0]->nod_arg[0]->nod_arg[0] = target;
|
|
|
|
action->nod_arg[e_if_true] = modify; // then UPDATE
|
|
|
|
action->nod_arg[e_if_false] = insert; // else INSERT
|
|
|
|
|
|
|
|
// insert the IF inside the FOR SELECT
|
|
|
|
for_select->nod_arg[e_flp_action] = action;
|
|
|
|
|
|
|
|
// describe it as an INSERT
|
|
|
|
request->req_type = REQ_INSERT;
|
|
|
|
|
|
|
|
// and return the whole FOR SELECT
|
|
|
|
return for_select;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-05-02 11:47:27 +02:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-05-02 11:47:27 +02:00
|
|
|
pass1_not
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-05-02 11:47:27 +02:00
|
|
|
@brief Replace NOT with an appropriately inverted condition, if
|
|
|
|
possible. Get rid of redundant nested NOT predicates.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-05-02 11:47:27 +02:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
@param invert
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_not(dsql_req* request,
|
|
|
|
const dsql_nod* input,
|
|
|
|
bool proc_flag,
|
|
|
|
bool invert)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
|
|
|
fb_assert(input->nod_type == nod_not);
|
|
|
|
dsql_nod* sub = input->nod_arg[0];
|
|
|
|
|
|
|
|
if (sub->nod_type == nod_not) {
|
|
|
|
// recurse until different node is found
|
|
|
|
// (every even call means no inversion required)
|
|
|
|
return pass1_not(request, sub, proc_flag, !invert);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* node;
|
2005-05-04 10:44:35 +02:00
|
|
|
nod_t node_type = input->nod_type;
|
|
|
|
bool is_between = false, invert_args = false, no_op = false;
|
2005-05-02 11:47:27 +02:00
|
|
|
|
|
|
|
if (invert) {
|
|
|
|
// invert the given boolean
|
|
|
|
switch (sub->nod_type) {
|
|
|
|
case nod_eql:
|
|
|
|
node_type = nod_neq;
|
|
|
|
break;
|
|
|
|
case nod_neq:
|
|
|
|
node_type = nod_eql;
|
|
|
|
break;
|
|
|
|
case nod_lss:
|
|
|
|
node_type = nod_geq;
|
|
|
|
break;
|
|
|
|
case nod_gtr:
|
|
|
|
node_type = nod_leq;
|
|
|
|
break;
|
|
|
|
case nod_leq:
|
|
|
|
node_type = nod_gtr;
|
|
|
|
break;
|
|
|
|
case nod_geq:
|
|
|
|
node_type = nod_lss;
|
|
|
|
break;
|
|
|
|
case nod_eql_all:
|
|
|
|
node_type = nod_neq_any;
|
|
|
|
break;
|
|
|
|
case nod_neq_all:
|
|
|
|
node_type = nod_eql_any;
|
|
|
|
break;
|
|
|
|
case nod_lss_all:
|
|
|
|
node_type = nod_geq_any;
|
|
|
|
break;
|
|
|
|
case nod_gtr_all:
|
|
|
|
node_type = nod_leq_any;
|
|
|
|
break;
|
|
|
|
case nod_leq_all:
|
|
|
|
node_type = nod_gtr_any;
|
|
|
|
break;
|
|
|
|
case nod_geq_all:
|
|
|
|
node_type = nod_lss_any;
|
|
|
|
break;
|
|
|
|
case nod_eql_any:
|
2005-05-04 10:44:35 +02:00
|
|
|
if (sub->nod_arg[1]->nod_type == nod_list) {
|
|
|
|
// this is NOT IN (<list>), don't change it
|
|
|
|
no_op = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
node_type = nod_neq_all;
|
|
|
|
}
|
2005-05-02 11:47:27 +02:00
|
|
|
break;
|
|
|
|
case nod_neq_any:
|
|
|
|
node_type = nod_eql_all;
|
|
|
|
break;
|
|
|
|
case nod_lss_any:
|
|
|
|
node_type = nod_geq_all;
|
|
|
|
break;
|
|
|
|
case nod_gtr_any:
|
|
|
|
node_type = nod_leq_all;
|
|
|
|
break;
|
|
|
|
case nod_leq_any:
|
|
|
|
node_type = nod_gtr_all;
|
|
|
|
break;
|
|
|
|
case nod_geq_any:
|
|
|
|
node_type = nod_lss_all;
|
|
|
|
break;
|
|
|
|
case nod_between:
|
|
|
|
node_type = nod_or;
|
|
|
|
is_between = true;
|
|
|
|
break;
|
|
|
|
case nod_and:
|
|
|
|
node_type = nod_or;
|
|
|
|
invert_args = true;
|
|
|
|
break;
|
|
|
|
case nod_or:
|
|
|
|
node_type = nod_and;
|
|
|
|
invert_args = true;
|
|
|
|
break;
|
|
|
|
case nod_not:
|
|
|
|
// this case is handled in the beginning
|
|
|
|
fb_assert(false);
|
|
|
|
default:
|
2005-05-04 10:44:35 +02:00
|
|
|
no_op = true;
|
|
|
|
break;
|
2005-05-02 11:47:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// subnode type hasn't been changed
|
|
|
|
node_type = sub->nod_type;
|
|
|
|
}
|
|
|
|
|
2005-05-04 10:44:35 +02:00
|
|
|
if (no_op) {
|
|
|
|
// no inversion is possible, so just recreate the input node
|
|
|
|
// and return immediately to avoid infinite recursion later
|
|
|
|
fb_assert(node_type == nod_not);
|
|
|
|
node = MAKE_node(input->nod_type, 1);
|
|
|
|
node->nod_arg[0] = PASS1_node(request, sub, proc_flag);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
else if (is_between) {
|
2005-05-02 11:47:27 +02:00
|
|
|
// handle the special BETWEEN case
|
|
|
|
fb_assert(node_type == nod_or);
|
|
|
|
node = MAKE_node(node_type, 2);
|
|
|
|
node->nod_arg[0] = MAKE_node(nod_lss, 2);
|
|
|
|
node->nod_arg[0]->nod_arg[0] = sub->nod_arg[0];
|
|
|
|
node->nod_arg[0]->nod_arg[1] = sub->nod_arg[1];
|
|
|
|
node->nod_arg[1] = MAKE_node(nod_gtr, 2);
|
|
|
|
node->nod_arg[1]->nod_arg[0] = sub->nod_arg[0];
|
|
|
|
node->nod_arg[1]->nod_arg[1] = sub->nod_arg[2];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// create new (possibly inverted) node
|
|
|
|
node = MAKE_node(node_type, sub->nod_count);
|
|
|
|
dsql_nod* const* src = sub->nod_arg;
|
|
|
|
dsql_nod** dst = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* end = src + sub->nod_count;
|
|
|
|
src < end; src++)
|
|
|
|
{
|
|
|
|
if (invert_args) {
|
|
|
|
dsql_nod* temp = MAKE_node(nod_not, 1);
|
|
|
|
temp->nod_arg[0] = *src;
|
|
|
|
*dst++ = temp;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*dst++ = *src;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PASS1_node(request, node, proc_flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_put_args_on_stack
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Put recursive non list nodes on the stack
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param stack
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static void pass1_put_args_on_stack( dsql_req* request, dsql_nod* input,
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack& stack, bool proc_flag)
|
2002-08-03 17:27:20 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
2002-10-19 02:56:25 +02:00
|
|
|
if (input->nod_type != nod_list) {
|
2004-04-18 16:22:27 +02:00
|
|
|
stack.push(PASS1_node(request, input, proc_flag));
|
2002-08-03 17:27:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2002-08-03 17:27:20 +02:00
|
|
|
pass1_put_args_on_stack(request, *ptr, stack, proc_flag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_relation( dsql_req* request, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_relation, e_rel_count);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
node->nod_arg[e_rel_context] = (dsql_nod*) PASS1_make_context(request, input);
|
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_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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param alias_list
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_alias_list(dsql_req* request, dsql_nod* alias_list)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
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;
|
|
|
|
USHORT savedScopeLevel = request->req_scope_level;
|
|
|
|
dsql_ctx* context = NULL;
|
|
|
|
while (aliasCount > 0)
|
|
|
|
{
|
|
|
|
if (context)
|
|
|
|
{
|
|
|
|
if (context->ctx_rse && !context->ctx_relation && !context->ctx_procedure)
|
|
|
|
{
|
|
|
|
// Derived table
|
|
|
|
request->req_scope_level++;
|
|
|
|
context = pass1_alias(request, context->ctx_childs_derived_table, (dsql_str*) *arg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 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--)
|
|
|
|
{
|
|
|
|
if (!(relation = pass1_base_table(request, relation, (dsql_str*) *arg)))
|
|
|
|
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.
|
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
|
|
|
dsql_ctx* new_context = FB_NEW(*tdsql->getDefaultPool())
|
|
|
|
dsql_ctx(*tdsql->getDefaultPool());
|
|
|
|
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;
|
|
|
|
for (; aliasArg < end; aliasArg++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*aliasArg, dsql_type_str);
|
|
|
|
alias_length += static_cast<USHORT>(((dsql_str*) *aliasArg)->str_length);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
dsql_str* alias = FB_NEW_RPT(*tdsql->getDefaultPool(), alias_length) dsql_str;
|
|
|
|
alias->str_length = alias_length;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
TEXT* p = new_context->ctx_alias = (TEXT*) alias->str_data;
|
|
|
|
for (aliasArg = startArg; aliasArg < end; aliasArg++)
|
|
|
|
{
|
|
|
|
for (const TEXT* q = (TEXT*) ((dsql_str*) *aliasArg)->str_data; *q;)
|
|
|
|
*p++ = *q++;
|
|
|
|
*p++ = ' ';
|
|
|
|
}
|
|
|
|
p[-1] = 0;
|
|
|
|
|
|
|
|
context = new_context;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
context = NULL;
|
|
|
|
}
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
context = pass1_alias(request, *request->req_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
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
request->req_scope_level = savedScopeLevel;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
if (!context) {
|
2003-08-20 01:34:23 +02:00
|
|
|
// there is no alias or table named %s at this scope level.
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-07-26 13:54:37 +02:00
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_dsql_no_relation_alias,
|
|
|
|
isc_arg_string, ((dsql_str*) *arg)->str_data, 0);
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-26 13:54:37 +02:00
|
|
|
return (dsql_nod*) 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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param alias
|
|
|
|
|
|
|
|
**/
|
2004-05-09 07:48:33 +02:00
|
|
|
static dsql_ctx* pass1_alias(dsql_req* request, 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
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
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) {
|
2004-09-26 03:49:52 +02:00
|
|
|
fb_utils::exact_name(reinterpret_cast<TEXT*>(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.
|
2004-05-27 18:26:52 +02:00
|
|
|
for (DsqlContextStack::iterator itr(stack); itr.hasData(); ++itr) {
|
2004-04-18 16:22:27 +02:00
|
|
|
dsql_ctx* context = itr.object();
|
2003-08-20 01:34:23 +02:00
|
|
|
if (context->ctx_scope_level != request->req_scope_level) {
|
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.
|
2005-07-26 13:54:37 +02:00
|
|
|
if (context->ctx_internal_alias)
|
|
|
|
{
|
2005-07-25 16:43:28 +02:00
|
|
|
if (!strcmp(context->ctx_internal_alias,
|
2005-01-21 15:09:02 +01:00
|
|
|
reinterpret_cast<const char*>(alias->str_data)))
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
2005-01-21 15:09:02 +01:00
|
|
|
return context;
|
|
|
|
}
|
|
|
|
continue;
|
2003-08-20 01:34:23 +02:00
|
|
|
}
|
2005-07-26 13:54:37 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// If an unnamed derived table and empty alias
|
|
|
|
if (context->ctx_rse && !context->ctx_relation &&
|
|
|
|
!context->ctx_procedure && (alias->str_length == 0))
|
|
|
|
{
|
|
|
|
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.
|
2001-05-23 15:26:42 +02:00
|
|
|
if (context->ctx_relation &&
|
|
|
|
!strcmp(context->ctx_relation->rel_name,
|
2003-10-05 08:37:26 +02:00
|
|
|
reinterpret_cast<const char*>(alias->str_data)))
|
|
|
|
{
|
2003-08-20 01:34:23 +02:00
|
|
|
if (relation_context) {
|
2001-05-23 15:26:42 +02:00
|
|
|
/* the table %s is referenced twice; use aliases to differentiate */
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_dsql_self_join,
|
|
|
|
isc_arg_string, alias->str_data, 0);
|
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
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_str* pass1_alias_concat(const dsql_str* input1, const dsql_str* input2)
|
2003-08-20 01:34:23 +02:00
|
|
|
{
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2003-08-20 01:34:23 +02:00
|
|
|
|
|
|
|
DEV_BLKCHK(input1, dsql_type_str);
|
|
|
|
DEV_BLKCHK(input2, dsql_type_str);
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
int length = 0;
|
2003-08-20 01:34:23 +02:00
|
|
|
if (input1) {
|
|
|
|
length += input1->str_length;
|
|
|
|
}
|
2004-03-21 02:48:29 +01:00
|
|
|
if (input1 && input1->str_length && input2) {
|
2003-08-20 01:34:23 +02:00
|
|
|
length++; // Room for space character.
|
|
|
|
}
|
|
|
|
if (input2) {
|
|
|
|
length += input2->str_length;
|
|
|
|
}
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_str* output = FB_NEW_RPT(*tdsql->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;
|
2003-08-20 01:34:23 +02:00
|
|
|
if (input1) {
|
|
|
|
strcat(ptr, input1->str_data);
|
|
|
|
}
|
2004-03-21 02:48:29 +01:00
|
|
|
if (input1 && input1->str_length && input2) {
|
2003-08-20 01:34:23 +02:00
|
|
|
strcat(ptr, " ");
|
|
|
|
}
|
|
|
|
if (input2) {
|
|
|
|
strcat(ptr, input2->str_data);
|
|
|
|
}
|
|
|
|
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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param relation
|
|
|
|
@param alias
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_rel* pass1_base_table( dsql_req* request, const dsql_rel* relation,
|
|
|
|
const dsql_str* alias)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(relation, dsql_type_dsql_rel);
|
|
|
|
DEV_BLKCHK(alias, dsql_type_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return METD_get_view_relation(request,
|
2003-02-12 20:28:13 +01:00
|
|
|
relation->rel_name,
|
2001-05-23 15:26:42 +02:00
|
|
|
alias->str_data, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-03 03:09:23 +02:00
|
|
|
/**
|
|
|
|
|
|
|
|
pass1_replace
|
|
|
|
|
|
|
|
@brief Process REPLACE statement.
|
|
|
|
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_replace(dsql_req* request, dsql_nod* input, bool proc_flag)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
|
|
|
dsql_str* relation_name =
|
|
|
|
(dsql_str*) input->nod_arg[e_rep_relation]->nod_arg[e_rpn_name];
|
|
|
|
dsql_str* base_name = relation_name;
|
|
|
|
|
|
|
|
dsql_nod* values = input->nod_arg[e_rep_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_rep_relation];
|
|
|
|
insert->nod_arg[e_ins_fields] = input->nod_arg[e_rep_fields];
|
|
|
|
insert->nod_arg[e_ins_values] = values;
|
|
|
|
insert->nod_arg[e_ins_return] = input->nod_arg[e_rep_return];
|
|
|
|
insert = PASS1_statement(request, insert, proc_flag);
|
|
|
|
|
|
|
|
// PASS1_statement will transform nod_insert to nod_store
|
|
|
|
fb_assert(insert->nod_type == nod_store);
|
|
|
|
|
|
|
|
dsql_ctx* context = (dsql_ctx*) insert->nod_arg[e_sto_relation]->nod_arg[e_rel_context];
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
|
|
|
|
dsql_rel* relation = context->ctx_relation;
|
|
|
|
dsql_nod* fields = input->nod_arg[e_rep_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_rep_matching])
|
|
|
|
{
|
|
|
|
dsql_rel* base_rel =
|
|
|
|
METD_get_view_base(request, relation_name->str_data, view_fields);
|
|
|
|
|
|
|
|
// get the base table name if there is only one
|
|
|
|
if (base_rel)
|
|
|
|
base_name = MAKE_cstring(base_rel->rel_name);
|
|
|
|
else
|
|
|
|
ERRD_post(isc_replace_with_complex_view, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* matching = input->nod_arg[e_rep_matching];
|
|
|
|
|
|
|
|
if (matching)
|
|
|
|
{
|
|
|
|
request->req_context->push(context);
|
|
|
|
request->req_scope_level++;
|
|
|
|
|
|
|
|
dsql_nod* matching_fields = PASS1_node(request, matching, false);
|
|
|
|
|
|
|
|
request->req_scope_level--;
|
|
|
|
request->req_context->pop();
|
|
|
|
|
|
|
|
field_appears_once(matching_fields, matching, true, "REPLACE");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
matching = METD_get_primary_key(request, base_name);
|
|
|
|
|
|
|
|
if (!matching)
|
|
|
|
{
|
|
|
|
ERRD_post(isc_primary_key_required,
|
|
|
|
isc_arg_string, base_name->str_data,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// build a boolean to use in the UPDATE statement
|
|
|
|
dsql_nod* match = NULL;
|
|
|
|
USHORT match_count = 0;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, 2);
|
|
|
|
temp->nod_arg[0] = *value_ptr;
|
|
|
|
temp->nod_arg[1] = *field_ptr;
|
|
|
|
stack.push(temp);
|
|
|
|
|
|
|
|
temp = *value_ptr;
|
|
|
|
dsql_nod* temp2 = insert->nod_arg[e_sto_statement]->nod_arg[field_ptr - fields->nod_arg]->nod_arg[1];
|
|
|
|
set_parameter_type(request, temp, temp2, false);
|
|
|
|
|
|
|
|
fb_assert((*field_ptr)->nod_type == nod_field_name);
|
|
|
|
|
|
|
|
// When relation is a view and MATCHING was not specified, field_name
|
2006-09-05 10:17:26 +02:00
|
|
|
// stores the base field name that is what we should find in the primary
|
2006-09-03 03:09:23 +02:00
|
|
|
// key of base table.
|
|
|
|
Firebird::MetaName field_name;
|
|
|
|
|
|
|
|
if ((relation->rel_flags & REL_view) && !input->nod_arg[e_rep_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);
|
|
|
|
|
|
|
|
if (Firebird::MetaName(((dsql_str*)
|
|
|
|
(*matching_ptr)->nod_arg[e_fln_name])->str_data) ==
|
|
|
|
field_name)
|
|
|
|
{
|
|
|
|
++match_count;
|
|
|
|
|
|
|
|
dsql_nod* eql = MAKE_node(nod_eql, 2);
|
|
|
|
eql->nod_arg[0] = *field_ptr;
|
|
|
|
eql->nod_arg[1] = *value_ptr;
|
|
|
|
|
|
|
|
if (match)
|
|
|
|
{
|
|
|
|
// It's a composed MATCHING. Build an AND.
|
2006-09-07 13:46:43 +02:00
|
|
|
dsql_nod* and_node = MAKE_node(nod_and, 2);
|
|
|
|
and_node->nod_arg[0] = match;
|
|
|
|
and_node->nod_arg[1] = eql;
|
|
|
|
match = and_node;
|
2006-09-03 03:09:23 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
match = eql;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if implicit or explicit MATCHING is valid
|
|
|
|
if (match_count != matching->nod_count)
|
|
|
|
{
|
|
|
|
if (input->nod_arg[e_rep_matching])
|
|
|
|
ERRD_post(isc_replace_doesnt_match_matching, 0);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERRD_post(isc_replace_doesnt_match_pk,
|
|
|
|
isc_arg_string, base_name->str_data,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// build the UPDATE node
|
|
|
|
dsql_nod* update = MAKE_node(nod_update, e_upd_count);
|
|
|
|
update->nod_arg[e_upd_relation] = input->nod_arg[e_rep_relation];
|
|
|
|
update->nod_arg[e_upd_statement] = MAKE_list(stack);
|
|
|
|
update->nod_arg[e_upd_boolean] = match;
|
|
|
|
|
|
|
|
if (input->nod_arg[e_rep_return])
|
|
|
|
{
|
2006-09-05 10:17:26 +02:00
|
|
|
update->nod_arg[e_upd_rse_flags] = (dsql_nod*) NOD_SELECT_EXPR_SINGLETON;
|
2006-09-06 04:43:37 +02:00
|
|
|
|
2006-09-03 03:09:23 +02:00
|
|
|
dsql_nod* store_ret = insert->nod_arg[e_sto_return];
|
|
|
|
|
|
|
|
// nod_returning was already processed
|
|
|
|
fb_assert(store_ret->nod_type == nod_list);
|
|
|
|
|
|
|
|
// And we create an already processed RETURNING, because
|
2006-09-05 10:17:26 +02:00
|
|
|
// nod_returning creates parameters and they're already
|
2006-09-03 03:09:23 +02:00
|
|
|
// created by the INSERT statement.
|
|
|
|
dsql_nod* update_ret = update->nod_arg[e_upd_return] =
|
|
|
|
MAKE_node(nod_list, store_ret->nod_count);
|
|
|
|
|
|
|
|
dsql_nod** src_ptr = input->nod_arg[e_rep_return]->nod_arg[e_ret_source]->nod_arg;
|
|
|
|
dsql_nod** dst_ptr = store_ret->nod_arg;
|
|
|
|
dsql_nod** ptr = update_ret->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const end = ptr + update_ret->nod_count;
|
|
|
|
ptr < end; src_ptr++, dst_ptr++, ptr++)
|
|
|
|
{
|
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, 2);
|
|
|
|
temp->nod_arg[0] = *src_ptr;
|
|
|
|
temp->nod_arg[1] = (*dst_ptr)->nod_arg[1];
|
|
|
|
*ptr = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
update = PASS1_statement(request, update, proc_flag);
|
|
|
|
|
|
|
|
// PASS1_statement will transform nod_update to nod_modify
|
|
|
|
fb_assert(update->nod_type == nod_modify);
|
|
|
|
|
|
|
|
// test if ROW_COUNT = 0
|
|
|
|
dsql_nod* eql = MAKE_node(nod_eql, 2);
|
|
|
|
eql->nod_arg[0] = MAKE_node(nod_internal_info, e_internal_info_count);
|
|
|
|
eql->nod_arg[0]->nod_arg[e_internal_info] =
|
|
|
|
MAKE_constant((dsql_str*) internal_rows_affected, CONSTANT_SLONG);
|
|
|
|
eql->nod_arg[1] = MAKE_constant((dsql_str*) 0, CONSTANT_SLONG);
|
|
|
|
|
|
|
|
USHORT req_flags = request->req_flags;
|
|
|
|
request->req_flags |= REQ_block; // to compile ROW_COUNT
|
|
|
|
eql = PASS1_node(request, eql, proc_flag);
|
|
|
|
request->req_flags = req_flags;
|
|
|
|
|
|
|
|
// if (ROW_COUNT = 0) then INSERT
|
|
|
|
dsql_nod* if_nod = MAKE_node(nod_if, e_if_count);
|
|
|
|
if_nod->nod_arg[e_if_condition] = eql;
|
|
|
|
if_nod->nod_arg[e_if_true] = insert;
|
|
|
|
|
|
|
|
// build the UPDATE / IF nodes
|
|
|
|
dsql_nod* list = MAKE_node(nod_list, 2);
|
|
|
|
list->nod_arg[0] = update;
|
|
|
|
list->nod_arg[1] = if_nod;
|
|
|
|
|
|
|
|
// if RETURNING is present, req_type is already REQ_EXEC_PROCEDURE
|
|
|
|
if (!input->nod_arg[e_rep_return])
|
|
|
|
request->req_type = REQ_INSERT;
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_returning(dsql_req* request,
|
|
|
|
const dsql_nod* input,
|
|
|
|
bool proc_flag)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
|
|
|
|
dsql_nod* const source =
|
|
|
|
PASS1_node(request, input->nod_arg[e_ret_source], false);
|
|
|
|
dsql_nod* const target =
|
|
|
|
PASS1_node(request, input->nod_arg[e_ret_target], false);
|
|
|
|
|
|
|
|
if (!proc_flag && target)
|
|
|
|
{
|
|
|
|
// RETURNING INTO is not allowed syntax for DSQL
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -104,
|
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "INTO", 0);
|
|
|
|
}
|
|
|
|
else if (proc_flag && !target)
|
|
|
|
{
|
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
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -104,
|
2005-06-16 07:11:08 +02:00
|
|
|
isc_arg_gds, isc_command_end_err2, // Unexpected end of command
|
|
|
|
isc_arg_number, (SLONG) errSrc->nod_line,
|
|
|
|
isc_arg_number, (SLONG) errSrc->nod_column,
|
2005-06-13 14:45:42 +02:00
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int count = source->nod_count;
|
|
|
|
fb_assert(count);
|
|
|
|
dsql_nod* node = MAKE_node(nod_list, count);
|
|
|
|
|
|
|
|
if (target)
|
|
|
|
{
|
|
|
|
// PSQL case
|
|
|
|
fb_assert(proc_flag);
|
|
|
|
if (count != target->nod_count) {
|
|
|
|
// count of column list and value list don't match
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -804,
|
|
|
|
isc_arg_gds, isc_dsql_var_count_err, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod** src = source->nod_arg;
|
|
|
|
dsql_nod** dst = target->nod_arg;
|
|
|
|
dsql_nod** ptr = node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
2006-03-07 15:42:19 +01:00
|
|
|
ptr < end; src++, dst++, ptr++)
|
2005-06-13 14:45:42 +02:00
|
|
|
{
|
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, 2);
|
|
|
|
temp->nod_arg[0] = *src;
|
|
|
|
temp->nod_arg[1] = *dst;
|
|
|
|
*ptr = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// DSQL case
|
|
|
|
fb_assert(!proc_flag);
|
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;
|
|
|
|
for (const dsql_nod* const* const end = ptr + node->nod_count;
|
|
|
|
ptr < end; src++, ptr++)
|
|
|
|
{
|
2005-09-02 07:30:16 +02:00
|
|
|
dsql_par* parameter =
|
|
|
|
MAKE_parameter(request->req_receive, true, true, 0, *src);
|
|
|
|
parameter->par_node = *src;
|
2005-06-13 14:45:42 +02:00
|
|
|
MAKE_desc(request, ¶meter->par_desc, *src, NULL);
|
|
|
|
|
|
|
|
dsql_nod* p_node = MAKE_node(nod_parameter, e_par_count);
|
|
|
|
p_node->nod_count = 0;
|
2006-09-03 03:09:23 +02:00
|
|
|
p_node->nod_arg[e_par_index] = (dsql_nod*) parameter->par_index;
|
2005-06-13 14:45:42 +02:00
|
|
|
p_node->nod_arg[e_par_parameter] = (dsql_nod*) parameter;
|
|
|
|
|
|
|
|
dsql_nod* temp = MAKE_node(nod_assign, 2);
|
|
|
|
temp->nod_arg[0] = *src;
|
|
|
|
temp->nod_arg[1] = p_node;
|
|
|
|
*ptr = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!proc_flag)
|
|
|
|
{
|
|
|
|
request->req_type = REQ_EXEC_PROCEDURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
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
|
|
|
|
substitute recursive CTE alias (if needed)
|
|
|
|
and call pass1_rse_impl
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param order
|
|
|
|
@param rows
|
|
|
|
@param update_lock
|
|
|
|
@param flags
|
|
|
|
|
|
|
|
**/
|
|
|
|
static dsql_nod* pass1_rse( dsql_req* request, dsql_nod* input, dsql_nod* order,
|
|
|
|
dsql_nod* rows, dsql_nod* update_lock, USHORT flags)
|
|
|
|
{
|
|
|
|
TEXT* save_alias = 0;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
fb_assert(request->req_recursive_ctx);
|
|
|
|
save_alias = request->req_recursive_ctx->ctx_alias;
|
|
|
|
|
|
|
|
request->req_recursive_ctx->ctx_alias = (TEXT*) request->getNextCTEAlias()->str_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* ret = pass1_rse_impl(request, input, order, rows, update_lock, flags);
|
|
|
|
|
|
|
|
if (isRecursive) {
|
|
|
|
request->req_recursive_ctx->ctx_alias = save_alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
@param request
|
|
|
|
@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
|
|
|
|
|
|
|
**/
|
2006-08-01 22:37:58 +02:00
|
|
|
static dsql_nod* pass1_rse_impl( dsql_req* request, 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
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(order, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2001-05-23 15:26:42 +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];
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (node_with)
|
|
|
|
request->addCTEs(node_with);
|
|
|
|
|
|
|
|
dsql_nod* ret =
|
|
|
|
pass1_rse(request, input->nod_arg[e_sel_query_spec],
|
|
|
|
input->nod_arg[e_sel_order], input->nod_arg[e_sel_rows],
|
|
|
|
update_lock, input->nod_flags);
|
|
|
|
|
|
|
|
if (node_with)
|
|
|
|
request->clearCTEs();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
catch (const Firebird::Exception&) {
|
|
|
|
if (node_with)
|
|
|
|
request->clearCTEs();
|
|
|
|
throw;
|
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
|
|
|
else if (input->nod_type == nod_list)
|
|
|
|
{
|
2005-02-21 13:48:39 +01:00
|
|
|
fb_assert(input->nod_count > 1);
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2005-02-21 13:48:39 +01:00
|
|
|
if (update_lock)
|
2005-05-28 00:45:31 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds,
|
|
|
|
isc_token_err, // Token unknown
|
2005-02-21 13:48:39 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "WITH LOCK", 0);
|
|
|
|
|
|
|
|
return pass1_union(request, input, order, rows, flags);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
else
|
2004-10-13 20:37:53 +02:00
|
|
|
{
|
|
|
|
fb_assert(input->nod_type == nod_query_spec);
|
|
|
|
}
|
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
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* target_rse = MAKE_node(nod_rse, e_rse_count);
|
|
|
|
dsql_nod* rse = target_rse;
|
2003-01-11 03:49:13 +01:00
|
|
|
rse->nod_arg[e_rse_lock] = update_lock;
|
2004-03-21 02:48:29 +01:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* list = rse->nod_arg[e_rse_streams] =
|
2004-10-13 20:37:53 +02:00
|
|
|
PASS1_node(request, input->nod_arg[e_qry_from], false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
const dsql_rel* relation;
|
|
|
|
if (update_lock && (list->nod_count != 1 || list->nod_arg[0]->nod_type != nod_relation ||
|
|
|
|
!(relation = ((dsql_ctx*)list->nod_arg[0]->nod_arg[e_rel_context])->ctx_relation) ||
|
|
|
|
(relation->rel_flags & REL_view) || (relation->rel_flags & REL_external) ))
|
|
|
|
{
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "WITH LOCK", 0);
|
|
|
|
}
|
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
|
|
|
|
|
|
|
dsql_nod* node = input->nod_arg[e_qry_limit];
|
2003-11-18 08:58:35 +01:00
|
|
|
if (node && rows) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "ROWS", 0);
|
2003-11-07 15:10:16 +01:00
|
|
|
}
|
|
|
|
else if (node || (node = rows) ) {
|
|
|
|
const int length_index = rows ? e_rows_length : e_limit_length;
|
|
|
|
const int skip_index = rows ? e_rows_skip : e_limit_skip;
|
|
|
|
|
|
|
|
if (node->nod_arg[length_index]) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub = PASS1_node(request, node->nod_arg[length_index], false);
|
2003-04-06 13:20:24 +02:00
|
|
|
rse->nod_arg[e_rse_first] = sub;
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, sub, node, false);
|
2002-06-29 08:56:51 +02:00
|
|
|
}
|
2003-11-07 15:10:16 +01:00
|
|
|
if (node->nod_arg[skip_index]) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub = PASS1_node(request, node->nod_arg[skip_index], false);
|
2003-04-06 13:20:24 +02:00
|
|
|
rse->nod_arg[e_rse_skip] = sub;
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, sub, node, false);
|
2002-06-29 08:56:51 +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
|
|
|
|
2004-10-13 20:37:53 +02:00
|
|
|
if ( (node = input->nod_arg[e_qry_where]) ) {
|
2001-05-23 15:26:42 +02:00
|
|
|
++request->req_in_where_clause;
|
2003-09-02 01:22:22 +02:00
|
|
|
rse->nod_arg[e_rse_boolean] = PASS1_node(request, node, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
--request->req_in_where_clause;
|
2002-09-29 01:52:36 +02:00
|
|
|
|
|
|
|
/* AB: An aggregate pointing to it's own parent_context isn't
|
2003-06-13 09:56:08 +02:00
|
|
|
allowed, HAVING should be used instead */
|
2005-05-28 00:45:31 +02:00
|
|
|
if (pass1_found_aggregate(rse->nod_arg[e_rse_boolean],
|
2003-09-02 01:22:22 +02:00
|
|
|
request->req_scope_level, FIELD_MATCH_TYPE_EQUAL, true))
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_agg_where_err, 0);
|
2003-06-13 09:56:08 +02:00
|
|
|
// Cannot use an aggregate in a WHERE clause, use HAVING instead
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
2003-09-28 23:36:05 +02:00
|
|
|
#ifdef DSQL_DEBUG
|
|
|
|
if (DSQL_debug & 16) {
|
|
|
|
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
|
2005-02-10 22:14:52 +01:00
|
|
|
dsql_nod* selectList = input->nod_arg[e_qry_list];
|
|
|
|
// First expand select list, this will expand nodes with asterisk.
|
|
|
|
++request->req_in_select_list;
|
|
|
|
selectList = pass1_expand_select_list(request, selectList, rse->nod_arg[e_rse_streams]);
|
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
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_dsql_count_mismatch, 0);
|
|
|
|
}
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// Pass select list
|
|
|
|
rse->nod_arg[e_rse_items] = pass1_sel_list(request, selectList);
|
|
|
|
--request->req_in_select_list;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process ORDER clause, if any
|
2003-01-11 03:49:13 +01:00
|
|
|
if (order) {
|
|
|
|
++request->req_in_order_by_clause;
|
2005-02-10 22:14:52 +01:00
|
|
|
rse->nod_arg[e_rse_sort] = pass1_sort(request, order, selectList);
|
2003-01-11 03:49:13 +01:00
|
|
|
--request->req_in_order_by_clause;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
dsql_nod* parent_rse = NULL;
|
|
|
|
dsql_nod* aggregate = NULL;
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
if (input->nod_arg[e_qry_group] ||
|
2004-10-13 20:37:53 +02:00
|
|
|
input->nod_arg[e_qry_having] ||
|
2003-01-12 17:29:59 +01:00
|
|
|
(rse->nod_arg[e_rse_items] && aggregate_found(request, rse->nod_arg[e_rse_items])) ||
|
2003-11-18 08:58:35 +01:00
|
|
|
(rse->nod_arg[e_rse_sort] && aggregate_found(request, rse->nod_arg[e_rse_sort])))
|
|
|
|
{
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2003-06-12 11:38:01 +02:00
|
|
|
// dimitr: don't allow WITH LOCK for aggregates
|
|
|
|
|
|
|
|
if (update_lock) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "WITH LOCK", 0);
|
2003-06-12 11:38:01 +02:00
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
parent_context = FB_NEW(*tdsql->getDefaultPool())
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_ctx(*tdsql->getDefaultPool());
|
2003-01-11 03:49:13 +01:00
|
|
|
parent_context->ctx_context = request->req_context_number++;
|
|
|
|
parent_context->ctx_scope_level = request->req_scope_level;
|
|
|
|
aggregate = MAKE_node(nod_aggregate, e_agg_count);
|
2003-11-10 10:16:38 +01:00
|
|
|
aggregate->nod_arg[e_agg_context] = (dsql_nod*) parent_context;
|
2003-01-11 03:49:13 +01:00
|
|
|
aggregate->nod_arg[e_agg_rse] = rse;
|
|
|
|
parent_rse = target_rse = MAKE_node(nod_rse, e_rse_count);
|
|
|
|
parent_rse->nod_arg[e_rse_streams] = list = MAKE_node(nod_list, 1);
|
|
|
|
list->nod_arg[0] = aggregate;
|
|
|
|
|
|
|
|
if (rse->nod_arg[e_rse_first]) {
|
|
|
|
parent_rse->nod_arg[e_rse_first] = rse->nod_arg[e_rse_first];
|
|
|
|
rse->nod_arg[e_rse_first] = NULL;
|
|
|
|
}
|
|
|
|
if (rse->nod_arg[e_rse_skip]) {
|
|
|
|
parent_rse->nod_arg[e_rse_skip] = rse->nod_arg[e_rse_skip];
|
|
|
|
rse->nod_arg[e_rse_skip] = NULL;
|
|
|
|
}
|
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
request->req_context->push(parent_context);
|
2003-05-05 00:02:42 +02:00
|
|
|
// replace original contexts with parent context
|
2003-01-11 03:49:13 +01:00
|
|
|
remap_streams_to_parent_context(rse->nod_arg[e_rse_streams], parent_context);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-11-17 15:50:33 +01:00
|
|
|
// Process GROUP BY clause, if any
|
2005-02-10 22:14:52 +01:00
|
|
|
if (node = input->nod_arg[e_qry_group]) {
|
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
|
2003-06-13 09:56:08 +02:00
|
|
|
++request->req_in_group_by_clause;
|
2005-05-28 00:45:31 +02:00
|
|
|
aggregate->nod_arg[e_agg_group] =
|
2005-02-10 22:14:52 +01:00
|
|
|
pass1_group_by_list(request, input->nod_arg[e_qry_group], selectList);
|
2003-06-13 09:56:08 +02:00
|
|
|
--request->req_in_group_by_clause;
|
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
|
2003-09-02 01:22:22 +02:00
|
|
|
bool field;
|
2005-05-28 00:45:31 +02:00
|
|
|
if (pass1_found_field(aggregate->nod_arg[e_agg_group],
|
2002-09-29 01:52:36 +02:00
|
|
|
request->req_scope_level, FIELD_MATCH_TYPE_LOWER, &field) ||
|
2005-05-28 00:45:31 +02:00
|
|
|
pass1_found_aggregate(aggregate->nod_arg[e_agg_group],
|
2003-09-02 01:22:22 +02:00
|
|
|
request->req_scope_level, FIELD_MATCH_TYPE_LOWER_EQUAL, true))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_agg_group_err, 0);
|
2003-06-13 09:56:08 +02:00
|
|
|
// Cannot use an aggregate in a GROUP BY clause
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2002-06-29 08:56:51 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
// Parse a user-specified access PLAN
|
2004-10-13 20:37:53 +02:00
|
|
|
rse->nod_arg[e_rse_plan] =
|
|
|
|
PASS1_node(request, input->nod_arg[e_qry_plan], 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
|
2003-05-07 03:57:18 +02:00
|
|
|
// sub-selects a new contextnumber should be generated
|
2004-10-13 20:37:53 +02:00
|
|
|
if (input->nod_arg[e_qry_distinct]) {
|
2003-06-30 16:31:00 +02:00
|
|
|
if (update_lock) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
2005-05-28 00:45:31 +02:00
|
|
|
isc_arg_gds, isc_token_err, // Token unknown
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_arg_gds, isc_random, isc_arg_string, "WITH LOCK", 0);
|
2003-06-30 16:31:00 +02:00
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
|
|
|
|
++request->req_in_select_list;
|
|
|
|
target_rse->nod_arg[e_rse_reduced] = pass1_sel_list(request, selectList);
|
|
|
|
--request->req_in_select_list;
|
2003-02-23 02:36:22 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-10-14 18:35:13 +02:00
|
|
|
// Unless there was a parent, we're done
|
|
|
|
if (!parent_context)
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2004-10-14 18:35:13 +02:00
|
|
|
rse->nod_flags = flags;
|
|
|
|
return rse;
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Reset context of select items to point to the parent stream
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2004-10-14 18:35:13 +02:00
|
|
|
parent_rse->nod_arg[e_rse_items] =
|
|
|
|
remap_fields(request, rse->nod_arg[e_rse_items], parent_context);
|
|
|
|
rse->nod_arg[e_rse_items] = NULL;
|
|
|
|
|
|
|
|
// AB: Check for invalid contructions inside selected-items list
|
|
|
|
list = parent_rse->nod_arg[e_rse_items];
|
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ptr++)
|
2004-10-14 18:35:13 +02:00
|
|
|
{
|
2005-05-20 01:41:17 +02:00
|
|
|
if (invalid_reference(parent_context, *ptr,
|
|
|
|
aggregate->nod_arg[e_agg_group], false, false))
|
|
|
|
{
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_agg_column_err,
|
|
|
|
isc_arg_string, "select list", 0);
|
|
|
|
// Invalid expression in the select list
|
|
|
|
// (not contained in either an aggregate or the GROUP BY clause)
|
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
|
|
|
} // end scope block
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Reset context of order items to point to the parent stream
|
2004-10-14 18:35:13 +02:00
|
|
|
|
|
|
|
if (order) {
|
|
|
|
parent_rse->nod_arg[e_rse_sort] =
|
|
|
|
remap_fields(request, rse->nod_arg[e_rse_sort], parent_context);
|
|
|
|
rse->nod_arg[e_rse_sort] = NULL;
|
|
|
|
|
|
|
|
// AB: Check for invalid contructions inside the ORDER BY clause
|
|
|
|
list = target_rse->nod_arg[e_rse_sort];
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_nod* const* ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2003-10-05 08:37:26 +02:00
|
|
|
if (invalid_reference(parent_context, *ptr,
|
|
|
|
aggregate->nod_arg[e_agg_group], false, false))
|
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_agg_column_err,
|
2004-10-14 18:35:13 +02:00
|
|
|
isc_arg_string, "ORDER BY clause", 0);
|
|
|
|
// Invalid expression in the ORDER BY clause
|
2003-06-13 09:56:08 +02:00
|
|
|
// (not contained in either an aggregate or the GROUP BY clause)
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// And, of course, reduction clauses must also apply to the parent
|
2003-01-11 03:49:13 +01:00
|
|
|
|
2004-10-14 18:35:13 +02:00
|
|
|
if (input->nod_arg[e_qry_distinct]) {
|
|
|
|
parent_rse->nod_arg[e_rse_reduced] =
|
|
|
|
remap_fields(request, parent_rse->nod_arg[e_rse_reduced], parent_context);
|
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Process HAVING clause, if any
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2004-10-14 18:35:13 +02:00
|
|
|
if ( (node = input->nod_arg[e_qry_having]) ) {
|
|
|
|
++request->req_in_having_clause;
|
|
|
|
parent_rse->nod_arg[e_rse_boolean] = PASS1_node(request, node, false);
|
|
|
|
--request->req_in_having_clause;
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2004-10-14 18:35:13 +02:00
|
|
|
parent_rse->nod_arg[e_rse_boolean] =
|
|
|
|
remap_fields(request, parent_rse->nod_arg[e_rse_boolean], parent_context);
|
2004-10-13 20:37:53 +02:00
|
|
|
|
2004-10-14 18:35:13 +02:00
|
|
|
// AB: Check for invalid contructions inside the HAVING clause
|
|
|
|
list = parent_rse->nod_arg[e_rse_boolean];
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
if (invalid_reference(parent_context, *ptr,
|
|
|
|
aggregate->nod_arg[e_agg_group], false, false))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2004-10-14 18:35:13 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_agg_having_err,
|
|
|
|
isc_arg_string, "HAVING clause", 0);
|
|
|
|
// Invalid expression in the HAVING clause
|
|
|
|
// (neither an aggregate nor contained in the GROUP BY clause)
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
}
|
2002-09-29 01:52:36 +02:00
|
|
|
|
2004-10-13 20:37:53 +02:00
|
|
|
#ifdef CHECK_HAVING
|
2004-10-14 18:35:13 +02:00
|
|
|
if (aggregate)
|
|
|
|
{
|
|
|
|
if (invalid_reference(parent_rse->nod_arg[e_rse_boolean],
|
|
|
|
aggregate->nod_arg[e_agg_group]))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2004-10-14 18:35:13 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_field_ref_err, 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
// invalid field reference
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
2004-10-14 18:35:13 +02:00
|
|
|
#endif
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-10-14 18:35:13 +02:00
|
|
|
parent_rse->nod_flags = flags;
|
|
|
|
return parent_rse;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
pass1_searched_case
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Handle a reference to a searched case expression.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_searched_case( dsql_req* request, dsql_nod* input, bool proc_flag)
|
2002-08-03 17:27:20 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(input->nod_arg[0], dsql_type_nod);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_searched_case, 2);
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* list = input->nod_arg[0];
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// build boolean-expression list
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
DsqlNodStack stack;
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ptr += 2)
|
|
|
|
{
|
|
|
|
pass1_put_args_on_stack(request, *ptr, stack, proc_flag);
|
|
|
|
}
|
|
|
|
node->nod_arg[e_searched_case_search_conditions] = MAKE_list(stack);
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// build when_result list including else_result at the end
|
|
|
|
// else_result is included for easy handling in MAKE_desc()
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
DsqlNodStack stack;
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
for (++ptr; ptr < end; ptr += 2) {
|
|
|
|
pass1_put_args_on_stack(request, *ptr, stack, proc_flag);
|
|
|
|
}
|
|
|
|
pass1_put_args_on_stack(request, input->nod_arg[1], stack, proc_flag);
|
|
|
|
node->nod_arg[e_searched_case_results] = MAKE_list(stack);
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Set describer for output node
|
|
|
|
MAKE_desc(request, &node->nod_desc, node, NULL);
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Set parameter-types if parameters are there in the result nodes
|
2005-04-26 15:32:15 +02:00
|
|
|
dsql_nod* case_results = node->nod_arg[e_searched_case_results];
|
2005-04-26 13:46:07 +02:00
|
|
|
dsql_nod** ptr = case_results->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + case_results->nod_count;
|
2003-11-18 08:58:35 +01:00
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, *ptr, node, false);
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
|
|
|
|
2002-08-03 17:27:20 +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_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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_sel_list( dsql_req* request, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
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;
|
2003-11-18 08:58:35 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2005-02-10 22:14:52 +01:00
|
|
|
stack.push(PASS1_node(request, *ptr, false));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_list(stack);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_simple_case
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Handle a reference to a simple case expression.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_simple_case( dsql_req* request, dsql_nod* input, bool proc_flag)
|
2002-08-03 17:27:20 +02:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2003-11-10 10:16:38 +01:00
|
|
|
DEV_BLKCHK(input->nod_arg[0], dsql_type_nod);
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_simple_case, 3);
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
// build case_operand node
|
2005-05-28 00:45:31 +02:00
|
|
|
node->nod_arg[e_simple_case_case_operand] =
|
2002-08-03 17:27:20 +02:00
|
|
|
PASS1_node(request, input->nod_arg[0], proc_flag);
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* list = input->nod_arg[1];
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
// build when_operand list
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2004-08-22 02:53:04 +02:00
|
|
|
DsqlNodStack stack;
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ptr += 2)
|
|
|
|
{
|
|
|
|
pass1_put_args_on_stack(request, *ptr, stack, proc_flag);
|
|
|
|
}
|
|
|
|
node->nod_arg[e_simple_case_when_operands] = MAKE_list(stack);
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
// build when_result list including else_result at the end
|
|
|
|
// else_result is included for easy handling in MAKE_desc()
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2004-08-22 02:53:04 +02:00
|
|
|
DsqlNodStack stack;
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
for (++ptr; ptr < end; ptr += 2) {
|
|
|
|
pass1_put_args_on_stack(request, *ptr, stack, proc_flag);
|
|
|
|
}
|
|
|
|
pass1_put_args_on_stack(request, input->nod_arg[2], stack, proc_flag);
|
|
|
|
node->nod_arg[e_simple_case_results] = MAKE_list(stack);
|
2003-11-18 08:58:35 +01:00
|
|
|
} // end scope block
|
2002-08-03 17:27:20 +02:00
|
|
|
|
2004-08-22 02:53:04 +02:00
|
|
|
// Check if there is a parameter in the case/when operand list
|
|
|
|
bool setParameters = (node->nod_arg[e_simple_case_case_operand]->nod_type == nod_parameter);
|
2005-05-28 00:45:31 +02:00
|
|
|
if (!setParameters)
|
2004-08-22 02:53:04 +02:00
|
|
|
{
|
|
|
|
list = node->nod_arg[e_simple_case_when_operands];
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ++ptr)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
if ((*ptr)->nod_type == nod_parameter)
|
2004-08-22 02:53:04 +02:00
|
|
|
{
|
|
|
|
setParameters = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
// build list for making describe information from
|
2003-09-02 01:22:22 +02:00
|
|
|
// case_operand and when_operands this is used for
|
2004-08-22 02:53:04 +02:00
|
|
|
// setting parameter describers if used in this case.
|
2005-05-28 00:45:31 +02:00
|
|
|
if (setParameters)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2004-08-22 02:53:04 +02:00
|
|
|
list = node->nod_arg[e_simple_case_when_operands];
|
|
|
|
dsql_nod* node1 = MAKE_node(nod_list, list->nod_count + 1);
|
2003-11-18 08:58:35 +01:00
|
|
|
|
2004-08-22 02:53:04 +02:00
|
|
|
{ // scope block
|
|
|
|
int i = 0;
|
|
|
|
node1->nod_arg[i++] = node->nod_arg[e_simple_case_case_operand];
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count;
|
|
|
|
ptr < end; ++ptr, ++i)
|
|
|
|
{
|
|
|
|
node1->nod_arg[i] = *ptr;
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc_from_list(request, &node1->nod_desc, node1, NULL, "CASE");
|
2004-08-22 02:53:04 +02:00
|
|
|
// Set parameter describe information
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, node->nod_arg[e_simple_case_case_operand], node1, false);
|
2004-08-22 02:53:04 +02:00
|
|
|
} // end scope block
|
|
|
|
|
|
|
|
{ // scope block
|
|
|
|
dsql_nod* simple_when = node->nod_arg[e_simple_case_when_operands];
|
|
|
|
dsql_nod** ptr = simple_when->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + simple_when->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr < end; ptr++)
|
2004-08-22 02:53:04 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, *ptr, node1, false);
|
2004-08-22 02:53:04 +02:00
|
|
|
}
|
|
|
|
} // end scope block
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2004-08-22 02:53:04 +02:00
|
|
|
// Clean up temporary used node
|
|
|
|
delete node1;
|
|
|
|
}
|
2002-08-26 21:40:59 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
// Set describer for output node
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc(request, &node->nod_desc, node, NULL);
|
2003-09-02 01:22:22 +02:00
|
|
|
// Set parameter describe information for evt. results parameters
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* simple_res = node->nod_arg[e_simple_case_results];
|
|
|
|
dsql_nod** ptr = simple_res->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + simple_res->nod_count;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr < end; ptr++)
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, *ptr, node, false);
|
2002-08-11 10:04:54 +02:00
|
|
|
}
|
|
|
|
|
2002-08-03 17:27:20 +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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
2005-02-10 22:14:52 +01:00
|
|
|
@param selectList
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2005-02-10 22:14:52 +01:00
|
|
|
static dsql_nod* pass1_sort( dsql_req* request, dsql_nod* input, dsql_nod* selectList)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
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
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
if (input->nod_type != nod_list) {
|
2005-05-28 00:45:31 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds,
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_dsql_command_err, isc_arg_gds, isc_order_by_err, 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
// invalid ORDER BY clause
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +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;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node1 = *ptr;
|
2003-01-11 03:49:13 +01:00
|
|
|
if (node1->nod_type != nod_order) {
|
2005-05-28 00:45:31 +02:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds,
|
2003-11-11 13:19:20 +01:00
|
|
|
isc_dsql_command_err, isc_arg_gds, isc_order_by_err, 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
// invalid ORDER BY clause
|
|
|
|
}
|
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
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* collate = 0;
|
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
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2005-02-10 22:14:52 +01:00
|
|
|
if (node1->nod_type == nod_field_name) {
|
|
|
|
// check for alias or field node
|
|
|
|
node1 = pass1_field(request, node1, false, selectList);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
else if (node1->nod_type == nod_constant &&
|
2004-10-13 20:37:53 +02:00
|
|
|
node1->nod_desc.dsc_dtype == dtype_long)
|
|
|
|
{
|
2004-01-21 08:18:30 +01:00
|
|
|
const ULONG position = (IPTR) (node1->nod_arg[0]);
|
2005-05-28 00:45:31 +02:00
|
|
|
if ((position < 1) || !selectList ||
|
2005-02-10 22:14:52 +01:00
|
|
|
(position > (ULONG) selectList->nod_count))
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_column_pos_err,
|
|
|
|
isc_arg_string, "ORDER BY", 0);
|
2003-06-13 09:56:08 +02:00
|
|
|
// Invalid column position used in the ORDER BY clause
|
2003-01-09 17:50:24 +01:00
|
|
|
}
|
2003-04-06 13:20:24 +02:00
|
|
|
// substitute ordinal with appropriate field
|
2005-02-10 22:14:52 +01:00
|
|
|
node1 = PASS1_node(request, selectList->nod_arg[position - 1], false);
|
2003-01-09 17:50:24 +01:00
|
|
|
}
|
2005-02-10 22:14:52 +01:00
|
|
|
else {
|
|
|
|
node1 = PASS1_node(request, node1, false);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-04-06 13:20:24 +02:00
|
|
|
if (collate) {
|
|
|
|
// finally apply collation order, if necessary
|
|
|
|
node1 = pass1_collate(request, 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_udf
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Handle a reference to a user defined function.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* pass1_udf( dsql_req* request, dsql_nod* input, bool proc_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
const dsql_str* name = (dsql_str*) input->nod_arg[0];
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(name, dsql_type_str);
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_udf* userFunc = METD_get_function(request, name);
|
|
|
|
if (!userFunc)
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 804,
|
|
|
|
isc_arg_gds, isc_dsql_function_err,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, name->str_data, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* node = MAKE_node(nod_udf, input->nod_count);
|
|
|
|
node->nod_arg[0] = (dsql_nod*) userFunc;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (input->nod_count == 2) {
|
2004-04-18 16:22:27 +02:00
|
|
|
DsqlNodStack stack;
|
2004-05-24 14:09:12 +02:00
|
|
|
USHORT arg_pos = 0;
|
|
|
|
pass1_udf_args(request, input->nod_arg[1], userFunc, arg_pos, stack, proc_flag);
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[1] = MAKE_list(stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_udf_args
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Handle references to function arguments.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param udf
|
|
|
|
@param arg_pos
|
|
|
|
@param stack
|
|
|
|
@param proc_flag
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static void pass1_udf_args(dsql_req* request, dsql_nod* input,
|
2004-05-24 14:09:12 +02:00
|
|
|
dsql_udf* userFunc, USHORT& arg_pos, DsqlNodStack& stack, bool proc_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2003-11-10 10:16:38 +01:00
|
|
|
DEV_BLKCHK (userFunc, dsql_type_udf);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (input->nod_type != nod_list) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* temp = PASS1_node (request, input, proc_flag);
|
|
|
|
dsql_nod* args = userFunc->udf_arguments;
|
2002-06-29 08:56:51 +02:00
|
|
|
if (temp->nod_type == nod_parameter) {
|
|
|
|
if (args && args->nod_count > arg_pos) {
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, temp, args->nod_arg[arg_pos], false);
|
2002-06-29 08:56:51 +02:00
|
|
|
}
|
|
|
|
else {
|
2005-05-28 00:45:31 +02:00
|
|
|
;
|
2002-06-29 08:56:51 +02:00
|
|
|
/* We should complain here in the future! The parameter is
|
|
|
|
out of bounds or the function doesn't declare input params. */
|
|
|
|
}
|
|
|
|
}
|
2004-04-18 16:22:27 +02:00
|
|
|
stack.push(temp);
|
2004-05-24 14:09:12 +02:00
|
|
|
arg_pos++;
|
2002-06-29 08:56:51 +02:00
|
|
|
return;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2004-05-24 14:09:12 +02:00
|
|
|
pass1_udf_args(request, *ptr, userFunc, arg_pos, stack, proc_flag);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
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_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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
@param order_list
|
2003-11-07 15:10:16 +01:00
|
|
|
@param rows
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* pass1_union( dsql_req* request, dsql_nod* input,
|
2004-10-13 20:37:53 +02:00
|
|
|
dsql_nod* order_list, dsql_nod* rows, USHORT flags)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(order_list, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// set up the rse node for the union.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* union_rse = MAKE_node(nod_rse, e_rse_count);
|
|
|
|
dsql_nod* union_node = union_rse->nod_arg[e_rse_streams] =
|
2001-05-23 15:26:42 +02:00
|
|
|
MAKE_node(nod_union, input->nod_count);
|
2006-08-01 22:37:58 +02:00
|
|
|
union_node->nod_flags = input->nod_flags;
|
|
|
|
|
|
|
|
// generate a context for the union itself.
|
|
|
|
dsql_ctx* union_context = FB_NEW(*tdsql->getDefaultPool())
|
|
|
|
dsql_ctx(*tdsql->getDefaultPool());
|
|
|
|
|
|
|
|
if (input->nod_flags & NOD_UNION_RECURSIVE) {
|
|
|
|
union_context->ctx_context = request->req_recursive_ctx_id;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
union_context->ctx_context = request->req_context_number++;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// process all the sub-rse's.
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
dsql_nod** uptr = union_node->nod_arg;
|
|
|
|
const DsqlContextStack::const_iterator base(*request->req_context);
|
|
|
|
dsql_nod** ptr = input->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count; ptr < end;
|
|
|
|
++ptr, ++uptr)
|
2004-04-18 16:22:27 +02:00
|
|
|
{
|
2005-05-20 01:41:17 +02:00
|
|
|
request->req_scope_level++;
|
|
|
|
*uptr = pass1_rse(request, *ptr, NULL, NULL, NULL, 0);
|
|
|
|
request->req_scope_level--;
|
|
|
|
while (*(request->req_context) != base)
|
|
|
|
{
|
|
|
|
request->req_union_context.push(request->req_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))
|
|
|
|
request->req_context->push(request->req_recursive_ctx);
|
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.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* items = union_node->nod_arg[0]->nod_arg[e_rse_items];
|
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
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
for (int i = 1; i < union_node->nod_count; i++) {
|
|
|
|
const dsql_nod* nod1 = union_node->nod_arg[i]->nod_arg[e_rse_items];
|
|
|
|
if (items->nod_count != nod1->nod_count) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_dsql_count_mismatch, // overload of msg
|
|
|
|
0);
|
|
|
|
}
|
2003-08-15 02:02:18 +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
|
|
|
// Comment below is belongs to the old code (way a union was handled).
|
|
|
|
|
|
|
|
/* SQL II, section 9.3, pg 195 governs which data types
|
2003-11-10 10:16:38 +01:00
|
|
|
* are considered equivalent for a UNION
|
2003-08-18 23:37:47 +02:00
|
|
|
* The following restriction is in some ways more restrictive
|
|
|
|
* (cannot UNION CHAR with VARCHAR for instance)
|
|
|
|
* (or cannot union CHAR of different lengths)
|
|
|
|
* and in someways less restrictive
|
|
|
|
* (SCALE is not looked at)
|
|
|
|
* Workaround: use a direct CAST() statement in the SQL
|
|
|
|
* statement to force desired datatype.
|
|
|
|
*/
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
// loop through the list nodes and cast whenever possible.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* tmp_list = MAKE_node(nod_list, union_node->nod_count);
|
2003-10-05 08:37:26 +02:00
|
|
|
for (int j = 0; j < items->nod_count; j++) {
|
2003-11-18 08:58:35 +01:00
|
|
|
int i; // please the MS compiler
|
2003-08-15 02:02:18 +02:00
|
|
|
for (i = 0; i < union_node->nod_count; i++) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* nod1 = union_node->nod_arg[i]->nod_arg[e_rse_items];
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc(request, &nod1->nod_arg[j]->nod_desc, nod1->nod_arg[j], NULL);
|
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.
|
2003-08-25 00:15:23 +02:00
|
|
|
if (i > 0) {
|
|
|
|
if (nod1->nod_arg[j]->nod_desc.dsc_flags & DSC_nullable) {
|
|
|
|
items->nod_arg[j]->nod_desc.dsc_flags |= DSC_nullable;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
dsc desc;
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc_from_list(request, &desc, tmp_list, NULL, "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);
|
2003-08-15 02:02:18 +02:00
|
|
|
for (i = 0; i < union_node->nod_count; i++) {
|
|
|
|
pass1_union_auto_cast(union_node->nod_arg[i], desc, j);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
items = union_node->nod_arg[0]->nod_arg[e_rse_items];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-08-18 23:37:47 +02:00
|
|
|
// Create mappings for union.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* union_items = MAKE_node(nod_list, items->nod_count);
|
2003-11-18 08:58:35 +01:00
|
|
|
{ // scope block
|
2005-05-20 01:41:17 +02:00
|
|
|
SSHORT count = 0;
|
|
|
|
dsql_nod** uptr = items->nod_arg;
|
|
|
|
dsql_nod** ptr = union_items->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + union_items->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
dsql_nod* map_node = MAKE_node(nod_map, e_map_count);
|
|
|
|
*ptr = map_node;
|
|
|
|
map_node->nod_arg[e_map_context] = (dsql_nod*) union_context;
|
|
|
|
dsql_map* map = FB_NEW(*tdsql->getDefaultPool()) dsql_map;
|
|
|
|
map_node->nod_arg[e_map_map] = (dsql_nod*) map;
|
|
|
|
|
|
|
|
// set up the dsql_map* between the sub-rses and the union context.
|
|
|
|
map->map_position = count++;
|
|
|
|
map->map_node = *uptr++;
|
|
|
|
map->map_next = union_context->ctx_map;
|
|
|
|
union_context->ctx_map = map;
|
|
|
|
}
|
|
|
|
union_rse->nod_arg[e_rse_items] = 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.
|
2001-05-23 15:26:42 +02: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;
|
|
|
|
for (const dsql_nod* const* const end = ptr + order_list->nod_count;
|
2003-10-05 08:37:26 +02:00
|
|
|
ptr < end; ptr++, uptr++)
|
|
|
|
{
|
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
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
if (position->nod_type != nod_constant ||
|
|
|
|
position->nod_desc.dsc_dtype != dtype_long)
|
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_order_by_err, // invalid ORDER BY clause.
|
2001-05-23 15:26:42 +02:00
|
|
|
0);
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2004-01-21 08:18:30 +01:00
|
|
|
const SLONG number = (IPTR) position->nod_arg[0];
|
2003-08-18 23:37:47 +02:00
|
|
|
if (number < 1 || number > union_items->nod_count) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err,
|
|
|
|
isc_arg_gds, isc_order_by_err, // invalid ORDER BY clause.
|
2001-05-23 15:26:42 +02:00
|
|
|
0);
|
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];
|
2003-04-06 13:20:24 +02:00
|
|
|
if (collate) {
|
2005-05-28 00:45:31 +02:00
|
|
|
order2->nod_arg[e_order_field] =
|
2003-04-06 13:20:24 +02:00
|
|
|
pass1_collate(request, order2->nod_arg[e_order_field], collate);
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
union_rse->nod_arg[e_rse_sort] = sort;
|
|
|
|
}
|
|
|
|
|
2003-11-07 15:10:16 +01:00
|
|
|
if (rows) {
|
|
|
|
if (rows->nod_arg[e_rows_length]) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub = PASS1_node(request, rows->nod_arg[e_rows_length], false);
|
2003-11-07 15:10:16 +01:00
|
|
|
union_rse->nod_arg[e_rse_first] = sub;
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, sub, rows, false);
|
2003-11-07 15:10:16 +01:00
|
|
|
}
|
|
|
|
if (rows->nod_arg[e_rows_skip]) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* sub = PASS1_node(request, rows->nod_arg[e_rows_skip], false);
|
2003-11-07 15:10:16 +01:00
|
|
|
union_rse->nod_arg[e_rse_skip] = sub;
|
2005-05-28 00:45:31 +02:00
|
|
|
set_parameter_type(request, sub, rows, false);
|
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.
|
|
|
|
if (!(input->nod_flags & NOD_UNION_ALL)) {
|
2001-05-23 15:26:42 +02:00
|
|
|
union_rse->nod_arg[e_rse_reduced] = union_items;
|
2003-08-18 23:37:47 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-10-13 20:37:53 +02:00
|
|
|
union_rse->nod_flags = flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
return union_rse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static void pass1_union_auto_cast(dsql_nod* input, const dsc& desc,
|
|
|
|
SSHORT position, bool in_select_list)
|
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-08-15 02:02:18 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
case nod_list:
|
|
|
|
case nod_union:
|
2005-05-22 05:11:41 +02:00
|
|
|
if (in_select_list)
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
if ((position < 0) || (position >= input->nod_count)) {
|
|
|
|
// Internal dsql error: position out of range.
|
|
|
|
//
|
|
|
|
// !!! THIS MESSAGE SHOULD BE CHANGED !!!
|
|
|
|
//
|
2003-11-11 13:19:20 +01:00
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_dsql_command_err, 0);
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* select_item = input->nod_arg[position];
|
2003-08-15 02:02:18 +02: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) ||
|
2003-10-05 08:37:26 +02:00
|
|
|
(select_item->nod_desc.dsc_sub_type != desc.dsc_sub_type))
|
|
|
|
{
|
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;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
// Pick a existing cast if available else make a new one.
|
|
|
|
if ((select_item->nod_type == nod_alias) &&
|
|
|
|
(select_item->nod_arg[e_alias_value]) &&
|
2003-10-05 08:37:26 +02:00
|
|
|
(select_item->nod_arg[e_alias_value]->nod_type == nod_cast))
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
cast_node = select_item->nod_arg[e_alias_value];
|
|
|
|
}
|
2005-11-29 00:06:53 +01:00
|
|
|
else if ((select_item->nod_type == nod_derived_field) &&
|
|
|
|
(select_item->nod_arg[e_derived_field_value]) &&
|
|
|
|
(select_item->nod_arg[e_derived_field_value]->nod_type == nod_cast))
|
|
|
|
{
|
|
|
|
cast_node = select_item->nod_arg[e_derived_field_value];
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
else if (select_item->nod_type == nod_cast) {
|
|
|
|
cast_node = select_item;
|
|
|
|
}
|
|
|
|
else {
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2003-08-15 02:02:18 +02:00
|
|
|
cast_node = MAKE_node(nod_cast, e_cast_count);
|
2004-08-16 14:28:43 +02:00
|
|
|
dsql_fld* afield = FB_NEW_RPT(*tdsql->getDefaultPool(), 0) dsql_fld;
|
2003-11-10 10:16:38 +01:00
|
|
|
cast_node->nod_arg[e_cast_target] = (dsql_nod*) afield;
|
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.
|
|
|
|
if (select_item->nod_type == nod_alias) {
|
|
|
|
cast_node->nod_arg[e_cast_source] = select_item->nod_arg[e_alias_value];
|
|
|
|
}
|
2005-11-29 00:06:53 +01:00
|
|
|
else if (select_item->nod_type == nod_derived_field) {
|
|
|
|
cast_node->nod_arg[e_cast_source] = select_item->nod_arg[e_derived_field_value];
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
else {
|
|
|
|
cast_node->nod_arg[e_cast_source] = select_item;
|
|
|
|
}
|
2004-06-30 00:15:10 +02:00
|
|
|
// When a cast is created we're losing our fieldname, thus
|
|
|
|
// create a alias to keep it.
|
|
|
|
if (select_item->nod_type == nod_field) {
|
|
|
|
dsql_fld* sub_field = (dsql_fld*) select_item->nod_arg[e_fld_field];
|
|
|
|
// Create new node for alias and copy fieldname
|
|
|
|
alias_node = MAKE_node(nod_alias, e_alias_count);
|
|
|
|
// Copy fieldname to a new string.
|
2005-05-28 00:45:31 +02:00
|
|
|
dsql_str* str_alias = FB_NEW_RPT(*tdsql->getDefaultPool(),
|
2004-06-30 00:15:10 +02:00
|
|
|
strlen(sub_field->fld_name)) dsql_str;
|
|
|
|
strcpy(str_alias->str_data, sub_field->fld_name);
|
|
|
|
str_alias->str_length = strlen(sub_field->fld_name);
|
|
|
|
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
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_fld* field = (dsql_fld*) cast_node->nod_arg[e_cast_target];
|
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;
|
|
|
|
if (desc.dsc_dtype <= dtype_any_text) {
|
|
|
|
field->fld_ttype = desc.dsc_sub_type;
|
|
|
|
field->fld_character_set_id = INTL_GET_CHARSET(&desc);
|
|
|
|
field->fld_collation_id = INTL_GET_COLLATE(&desc);
|
|
|
|
}
|
|
|
|
else if (desc.dsc_dtype == dtype_blob) {
|
|
|
|
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;
|
|
|
|
if (select_item->nod_desc.dsc_flags & DSC_nullable) {
|
|
|
|
cast_node->nod_desc.dsc_flags |= DSC_nullable;
|
|
|
|
}
|
|
|
|
if (select_item->nod_type == nod_alias) {
|
|
|
|
select_item->nod_arg[e_alias_value] = cast_node;
|
|
|
|
select_item->nod_desc = desc;
|
|
|
|
}
|
2005-11-29 00:06:53 +01:00
|
|
|
else if (select_item->nod_type == nod_derived_field) {
|
|
|
|
select_item->nod_arg[e_derived_field_value] = cast_node;
|
|
|
|
select_item->nod_desc = desc;
|
|
|
|
}
|
2003-08-15 02:02:18 +02: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.
|
|
|
|
if (alias_node) {
|
|
|
|
alias_node->nod_arg[e_alias_value] = cast_node;
|
|
|
|
alias_node->nod_desc = cast_node->nod_desc;
|
|
|
|
input->nod_arg[position] = alias_node;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
input->nod_arg[position] = cast_node;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod** ptr = input->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2003-08-15 02:02:18 +02:00
|
|
|
pass1_union_auto_cast(*ptr, desc, position);
|
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
case nod_rse:
|
|
|
|
{
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* streams = input->nod_arg[e_rse_streams];
|
2003-08-15 02:02:18 +02:00
|
|
|
pass1_union_auto_cast(streams, desc, position);
|
|
|
|
if (streams->nod_type == nod_union) {
|
|
|
|
// 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.
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* union_items = input->nod_arg[e_rse_items];
|
|
|
|
dsql_nod* sub_rse_items = streams->nod_arg[0]->nod_arg[e_rse_items];
|
|
|
|
dsql_map* map = (dsql_map*) union_items->nod_arg[position]->nod_arg[e_map_map];
|
2003-08-30 18:49:15 +02:00
|
|
|
map->map_node = sub_rse_items->nod_arg[position];
|
2003-08-24 17:22:11 +02:00
|
|
|
union_items->nod_arg[position]->nod_desc = desc;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
pass1_union_auto_cast(input->nod_arg[e_rse_items], desc, position, true);
|
|
|
|
}
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2003-08-15 02:02:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Nothing
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Process UPDATE statement.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
2005-06-13 14:45:42 +02:00
|
|
|
@param proc_flag
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
**/
|
2005-06-13 14:45:42 +02:00
|
|
|
static dsql_nod* pass1_update( dsql_req* request, dsql_nod* input, bool proc_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-16 11:09:18 +02:00
|
|
|
// NOTE!!! Macro SQL_COMPLIANT_UPDATE shows what code should be
|
|
|
|
// enabled and what one should be removed when we decide
|
|
|
|
// to make our UPDATE statements SQL-compliant. Currently,
|
|
|
|
// it's targeted for Firebird 3.0.
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
org_values.add(assign->nod_arg[0]);
|
|
|
|
new_values.add(assign->nod_arg[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2005-06-13 14:45:42 +02:00
|
|
|
if (cursor && proc_flag) {
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* anode = MAKE_node(nod_modify_current, e_mdc_count);
|
2006-05-16 11:09:18 +02:00
|
|
|
dsql_ctx* context = pass1_cursor_context(request, cursor, relation);
|
|
|
|
anode->nod_arg[e_mdc_context] = (dsql_nod*) context;
|
|
|
|
#ifdef SQL_COMPLIANT_UPDATE
|
|
|
|
// Process old context values
|
|
|
|
request->req_context->push(context);
|
|
|
|
request->req_scope_level++;
|
2006-05-17 04:43:12 +02:00
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
}
|
|
|
|
request->req_scope_level--;
|
|
|
|
request->req_context->pop();
|
|
|
|
#endif
|
|
|
|
// Process relation
|
2003-11-10 10:16:38 +01:00
|
|
|
anode->nod_arg[e_mdc_update] = PASS1_node(request, relation, false);
|
2006-05-16 11:09:18 +02:00
|
|
|
#ifndef SQL_COMPLIANT_UPDATE
|
|
|
|
// Process old context values
|
2006-05-17 04:43:12 +02:00
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// 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
|
|
|
{
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
}
|
2006-09-03 03:09:23 +02:00
|
|
|
|
|
|
|
anode->nod_arg[e_mdc_return] =
|
|
|
|
process_returning(request, input->nod_arg[e_upd_return], proc_flag);
|
|
|
|
|
2004-04-18 16:22:27 +02:00
|
|
|
request->req_context->pop();
|
2006-05-16 11:09:18 +02:00
|
|
|
// Recreate list of assignments
|
|
|
|
anode->nod_arg[e_mdc_statement] = list =
|
|
|
|
MAKE_node(nod_list, list->nod_count);
|
|
|
|
for (int i = 0; i < list->nod_count; ++i)
|
|
|
|
{
|
|
|
|
dsql_nod* assign = MAKE_node(nod_assign, 2);
|
|
|
|
assign->nod_arg[0] = org_values[i];
|
|
|
|
assign->nod_arg[1] = new_values[i];
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
request->req_type = (cursor) ? REQ_UPDATE_CURSOR : REQ_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);
|
2003-09-02 01:22:22 +02:00
|
|
|
node->nod_arg[e_mod_update] = PASS1_node(request, relation, false);
|
2006-09-03 03:09:23 +02:00
|
|
|
node->nod_arg[e_mod_return] =
|
|
|
|
process_returning(request, input->nod_arg[e_upd_return], proc_flag);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-16 11:09:18 +02:00
|
|
|
#ifndef SQL_COMPLIANT_UPDATE
|
|
|
|
// Process old context values
|
2006-05-17 04:43:12 +02:00
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// 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
|
|
|
{
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
request->req_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
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* rse;
|
2006-05-16 11:09:18 +02:00
|
|
|
if (cursor) {
|
2003-11-02 13:28:30 +01:00
|
|
|
rse = pass1_cursor_reference(request, cursor, relation);
|
2006-05-16 11:09:18 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else {
|
|
|
|
rse = MAKE_node(nod_rse, e_rse_count);
|
2006-09-03 03:09:23 +02:00
|
|
|
rse->nod_flags = (USHORT)(IPTR) input->nod_arg[e_upd_rse_flags];
|
|
|
|
|
2004-10-27 11:33:08 +02:00
|
|
|
dsql_nod* temp = MAKE_node(nod_list, 1);
|
|
|
|
rse->nod_arg[e_rse_streams] = temp;
|
2006-05-16 11:09:18 +02:00
|
|
|
temp->nod_arg[0] = PASS1_node(request, relation, false);
|
2004-10-27 11:33:08 +02:00
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_upd_boolean]) ) {
|
|
|
|
rse->nod_arg[e_rse_boolean] = PASS1_node(request, temp, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_upd_plan]) ) {
|
|
|
|
rse->nod_arg[e_rse_plan] = PASS1_node(request, temp, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_upd_sort]) ) {
|
|
|
|
rse->nod_arg[e_rse_sort] = pass1_sort(request, temp, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (temp = input->nod_arg[e_upd_rows]) ) {
|
|
|
|
rse->nod_arg[e_rse_first] =
|
|
|
|
PASS1_node(request, temp->nod_arg[e_rows_length], false);
|
|
|
|
rse->nod_arg[e_rse_skip] =
|
|
|
|
PASS1_node(request, temp->nod_arg[e_rows_skip], false);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-10-27 11:33:08 +02:00
|
|
|
node->nod_arg[e_mod_source] = rse->nod_arg[e_rse_streams]->nod_arg[0];
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_arg[e_mod_rse] = rse;
|
|
|
|
|
2006-05-16 11:09:18 +02:00
|
|
|
#ifdef SQL_COMPLIANT_UPDATE
|
|
|
|
// Process old context values
|
2006-05-17 04:43:12 +02:00
|
|
|
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
|
2006-05-16 11:09:18 +02:00
|
|
|
{
|
|
|
|
*ptr = PASS1_node(request, *ptr, false);
|
|
|
|
}
|
|
|
|
#endif
|
2004-04-18 16:22:27 +02:00
|
|
|
request->req_context->pop();
|
2006-05-16 11:09:18 +02:00
|
|
|
|
|
|
|
// Recreate list of assignments
|
|
|
|
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];
|
2006-05-17 06:33:44 +02:00
|
|
|
if (!set_parameter_type(request, sub1, sub2, false))
|
|
|
|
{
|
|
|
|
set_parameter_type(request, sub2, sub1, false);
|
|
|
|
}
|
2006-05-16 11:09:18 +02:00
|
|
|
dsql_nod* assign = MAKE_node(nod_assign, 2);
|
2006-05-17 06:33:44 +02:00
|
|
|
assign->nod_arg[0] = sub1;
|
|
|
|
assign->nod_arg[1] = 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
|
|
|
|
|
|
|
set_parameters_name(node->nod_arg[e_mod_statement],
|
|
|
|
node->nod_arg[e_mod_update]);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2005-10-06 08:08:10 +02:00
|
|
|
|
2004-01-16 11:43:21 +01:00
|
|
|
/**
|
|
|
|
resolve_variable_name
|
|
|
|
|
|
|
|
**/
|
2004-01-28 08:50:41 +01:00
|
|
|
static dsql_nod* resolve_variable_name(const dsql_nod* var_nodes, const dsql_str* var_name)
|
2004-01-16 11:43:21 +01:00
|
|
|
{
|
2004-01-28 08:50:41 +01:00
|
|
|
dsql_nod* const* ptr = var_nodes->nod_arg;
|
|
|
|
dsql_nod* const* const end = ptr + var_nodes->nod_count;
|
2004-01-16 11:43:21 +01:00
|
|
|
|
|
|
|
for (; ptr < end; ptr++) {
|
2004-01-28 08:50:41 +01:00
|
|
|
dsql_nod* var_node = *ptr;
|
2004-01-16 11:43:21 +01:00
|
|
|
if (var_node->nod_type == nod_variable)
|
|
|
|
{
|
2004-02-02 12:02:12 +01:00
|
|
|
const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable];
|
2004-01-16 11:43:21 +01:00
|
|
|
DEV_BLKCHK(variable, dsql_type_var);
|
|
|
|
if (!strcmp
|
2004-01-28 08:50:41 +01:00
|
|
|
(reinterpret_cast<const char*>(var_name->str_data),
|
2005-05-28 00:45:31 +02:00
|
|
|
variable->var_name))
|
2004-01-28 08:50:41 +01:00
|
|
|
{
|
2004-01-16 11:43:21 +01:00
|
|
|
return var_node;
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2004-01-16 11:43:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-06 08:08:10 +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_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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param input
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* pass1_variable( dsql_req* request, dsql_nod* input)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
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
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(input, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_str* var_name = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (input->nod_type == nod_field_name) {
|
|
|
|
if (input->nod_arg[e_fln_context]) {
|
2004-11-17 19:27:48 +01:00
|
|
|
if (request->req_flags & REQ_trigger) // triggers only
|
2005-02-10 22:14:52 +01:00
|
|
|
return pass1_field(request, input, false, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
2005-10-06 08:08:10 +02:00
|
|
|
field_unknown(0, 0, input);
|
2001-05-23 15:26:42 +02: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
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* var_nodes;
|
2004-11-17 19:27:48 +01:00
|
|
|
if (request->req_flags & REQ_procedure) // procedures and triggers
|
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* procedure_node = request->req_ddl_node;
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(procedure_node);
|
2004-11-17 19:27:48 +01:00
|
|
|
if (!(request->req_flags & REQ_trigger)) // no, procedures only
|
|
|
|
{
|
2003-11-02 13:28:30 +01:00
|
|
|
// try to resolve variable name against input and output parameters
|
2003-11-18 08:58:35 +01:00
|
|
|
var_nodes = procedure_node->nod_arg[e_prc_inputs];
|
|
|
|
if (var_nodes)
|
|
|
|
{
|
|
|
|
//position = 0;
|
|
|
|
dsql_nod** ptr = var_nodes->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end =
|
|
|
|
ptr + var_nodes->nod_count; ptr < end; ptr++) //, position++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* var_node = *ptr;
|
2004-02-02 12:02:12 +01:00
|
|
|
const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable];
|
2003-11-10 10:16:38 +01:00
|
|
|
DEV_BLKCHK(variable, dsql_type_var);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!strcmp
|
2003-10-05 08:37:26 +02:00
|
|
|
(reinterpret_cast<const char*>(var_name->str_data),
|
2003-11-10 10:16:38 +01:00
|
|
|
variable->var_name))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
|
|
|
return var_node;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
|
|
|
var_nodes = procedure_node->nod_arg[e_prc_outputs];
|
|
|
|
if (var_nodes)
|
|
|
|
{
|
|
|
|
//position = 0;
|
|
|
|
dsql_nod** ptr = var_nodes->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end =
|
|
|
|
ptr + var_nodes->nod_count; ptr < end; ptr++) //, position++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* var_node = *ptr;
|
2004-02-02 12:02:12 +01:00
|
|
|
const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable];
|
2003-11-10 10:16:38 +01:00
|
|
|
DEV_BLKCHK(variable, dsql_type_var);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!strcmp
|
2003-10-05 08:37:26 +02:00
|
|
|
(reinterpret_cast<const char*>(var_name->str_data),
|
2003-11-10 10:16:38 +01:00
|
|
|
variable->var_name))
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
|
|
|
return var_node;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
var_nodes = procedure_node->nod_arg[e_prc_dcls];
|
|
|
|
}
|
|
|
|
else
|
2003-11-02 13:28:30 +01:00
|
|
|
var_nodes = procedure_node->nod_arg[e_trg_actions]->nod_arg[e_trg_act_dcls];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (var_nodes)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-11-02 13:28:30 +01:00
|
|
|
// try to resolve variable name against local variables
|
2003-11-18 08:58:35 +01:00
|
|
|
//position = 0;
|
|
|
|
dsql_nod** ptr = var_nodes->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end =
|
|
|
|
ptr + var_nodes->nod_count; ptr < end; ptr++) //, position++)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod* var_node = *ptr;
|
2003-11-02 13:28:30 +01:00
|
|
|
if (var_node->nod_type == nod_variable)
|
2003-10-05 08:37:26 +02:00
|
|
|
{
|
2004-02-02 12:02:12 +01:00
|
|
|
const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable];
|
2003-11-10 10:16:38 +01:00
|
|
|
DEV_BLKCHK(variable, dsql_type_var);
|
2003-11-02 13:28:30 +01:00
|
|
|
if (!strcmp
|
|
|
|
(reinterpret_cast<const char*>(var_name->str_data),
|
2003-11-10 10:16:38 +01:00
|
|
|
variable->var_name))
|
2003-11-02 13:28:30 +01:00
|
|
|
{
|
|
|
|
return var_node;
|
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-10-05 08:37:26 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
if (request->req_blk_node)
|
|
|
|
{
|
2004-01-28 08:50:41 +01:00
|
|
|
dsql_nod* var_node;
|
2004-01-16 11:43:21 +01:00
|
|
|
|
|
|
|
if (var_nodes = request->req_blk_node->nod_arg[e_exe_blk_dcls])
|
|
|
|
if (var_node = resolve_variable_name(var_nodes, var_name))
|
|
|
|
return var_node;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-01-16 11:43:21 +01:00
|
|
|
if (var_nodes = request->req_blk_node->nod_arg[e_exe_blk_inputs])
|
|
|
|
if (var_node = resolve_variable_name(var_nodes, var_name))
|
|
|
|
return var_node;
|
|
|
|
|
|
|
|
if (var_nodes = request->req_blk_node->nod_arg[e_exe_blk_outputs])
|
|
|
|
if (var_node = resolve_variable_name(var_nodes, var_name))
|
|
|
|
return var_node;
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// field unresolved
|
2003-09-29 14:43:14 +02:00
|
|
|
// CVC: That's all [the fix], folks!
|
2002-06-29 08:56:51 +02:00
|
|
|
|
|
|
|
if (var_name)
|
2005-10-06 08:08:10 +02:00
|
|
|
field_unknown(0, (TEXT*) var_name->str_data, input);
|
2005-05-28 00:45:31 +02:00
|
|
|
else
|
2005-10-06 08:08:10 +02:00
|
|
|
field_unknown(0, 0, input);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
post_map
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief Post an item to a map for a context.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param node
|
|
|
|
@param context
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* post_map( dsql_nod* node, dsql_ctx* context)
|
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
|
|
|
|
2004-05-22 16:23:10 +02:00
|
|
|
tsql* tdsql = DSQL_get_thread_data();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Check to see if the item has already been posted
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-10-05 08:37:26 +02:00
|
|
|
int count = 0;
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_map* map;
|
2003-10-05 08:37:26 +02:00
|
|
|
for (map = context->ctx_map; map; map = map->map_next, ++count)
|
2003-09-02 01:22:22 +02:00
|
|
|
if (node_match(node, map->map_node, false))
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2003-08-30 18:49:15 +02:00
|
|
|
if (!map) {
|
2004-08-16 14:28:43 +02:00
|
|
|
map = FB_NEW(*tdsql->getDefaultPool()) dsql_map;
|
2003-10-05 08:37:26 +02:00
|
|
|
map->map_position = (USHORT) count;
|
2003-08-30 18:49:15 +02:00
|
|
|
map->map_next = context->ctx_map;
|
|
|
|
context->ctx_map = map;
|
|
|
|
map->map_node = node;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod* new_node = MAKE_node(nod_map, e_map_count);
|
2001-05-23 15:26:42 +02:00
|
|
|
new_node->nod_count = 0;
|
2003-11-10 10:16:38 +01:00
|
|
|
new_node->nod_arg[e_map_context] = (dsql_nod*) context;
|
|
|
|
new_node->nod_arg[e_map_map] = (dsql_nod*) map;
|
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_field
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-05-05 00:02:42 +02:00
|
|
|
@brief Called to map fields used in an aggregate-context
|
2005-05-28 00:45:31 +02:00
|
|
|
after all pass1 calls (SELECT-, ORDER BY-lists).
|
2003-05-05 00:02:42 +02:00
|
|
|
Walk completly through the given node 'field' and
|
|
|
|
map the fields with same scope_level as the given context
|
|
|
|
to the given context with the post_map function.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param field
|
|
|
|
@param context
|
|
|
|
@param current_level
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_nod* remap_field(dsql_req* request, dsql_nod* field,
|
|
|
|
dsql_ctx* context, USHORT current_level)
|
2003-01-11 03:49:13 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(field, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
|
2005-11-30 10:03:53 +01:00
|
|
|
if (!field)
|
|
|
|
return NULL;
|
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
switch (field->nod_type) {
|
|
|
|
|
|
|
|
case nod_alias:
|
2005-05-28 00:45:31 +02:00
|
|
|
field->nod_arg[e_alias_value] =
|
|
|
|
remap_field(request, field->nod_arg[e_alias_value], context, current_level);
|
2003-09-04 17:02:22 +02:00
|
|
|
return field;
|
|
|
|
|
|
|
|
case nod_derived_field:
|
|
|
|
{
|
|
|
|
// If we got a field from a derived table we should not remap anything
|
|
|
|
// deeper in the alias, but this "virtual" field should be mapped to
|
2005-05-20 01:41:17 +02:00
|
|
|
// the given context (of course only if we're in the same scope-level).
|
2003-11-18 08:58:35 +01:00
|
|
|
const USHORT lscope_level =
|
|
|
|
(USHORT)(U_IPTR)field->nod_arg[e_derived_field_scope];
|
2003-08-24 04:36:46 +02:00
|
|
|
if (lscope_level == context->ctx_scope_level) {
|
|
|
|
return post_map(field, context);
|
|
|
|
}
|
2005-11-09 00:49:50 +01:00
|
|
|
else if (context->ctx_scope_level < lscope_level) {
|
|
|
|
field->nod_arg[e_derived_field_value] =
|
|
|
|
remap_field(request, field->nod_arg[e_derived_field_value],
|
|
|
|
context, current_level);
|
|
|
|
}
|
2003-09-04 17:02:22 +02:00
|
|
|
return field;
|
2003-08-24 04:36:46 +02:00
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
|
|
|
|
case nod_field:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
const dsql_ctx* lcontext =
|
|
|
|
reinterpret_cast<dsql_ctx*>(field->nod_arg[e_fld_context]);
|
|
|
|
if (lcontext->ctx_scope_level == context->ctx_scope_level) {
|
|
|
|
return post_map(field, context);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return field;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case nod_map:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
dsql_ctx* lcontext =
|
|
|
|
reinterpret_cast<dsql_ctx*>(field->nod_arg[e_map_context]);
|
2005-11-09 00:49:50 +01:00
|
|
|
if (lcontext->ctx_scope_level != context->ctx_scope_level) {
|
|
|
|
dsql_map* lmap = reinterpret_cast<dsql_map*>(field->nod_arg[e_map_map]);
|
|
|
|
lmap->map_node = remap_field(request, lmap->map_node, context,
|
|
|
|
lcontext->ctx_scope_level);
|
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
return field;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
|
|
|
|
case nod_agg_count:
|
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_average:
|
|
|
|
case nod_agg_total:
|
|
|
|
case nod_agg_average2:
|
|
|
|
case nod_agg_total2:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
USHORT ldeepest_level = request->req_scope_level;
|
|
|
|
USHORT lcurrent_level = current_level;
|
|
|
|
if (aggregate_found2(request, field, &lcurrent_level, &ldeepest_level, false)) {
|
|
|
|
if (request->req_scope_level == ldeepest_level) {
|
|
|
|
return post_map(field, context);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (field->nod_count) {
|
2004-04-03 01:20:29 +02:00
|
|
|
field->nod_arg[e_agg_function_expression] =
|
2005-05-28 00:45:31 +02:00
|
|
|
remap_field(request, field->nod_arg[e_agg_function_expression],
|
2004-04-03 01:20:29 +02:00
|
|
|
context, current_level);
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
|
|
|
return field;
|
|
|
|
}
|
2003-05-07 03:57:18 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (field->nod_count) {
|
2004-04-03 01:20:29 +02:00
|
|
|
field->nod_arg[e_agg_function_expression] =
|
2005-05-28 00:45:31 +02:00
|
|
|
remap_field(request, field->nod_arg[e_agg_function_expression],
|
2004-04-03 01:20:29 +02:00
|
|
|
context, current_level);
|
2003-05-07 03:57:18 +02:00
|
|
|
}
|
|
|
|
return field;
|
|
|
|
}
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_via:
|
|
|
|
field->nod_arg[e_via_rse] =
|
|
|
|
remap_field(request, field->nod_arg[e_via_rse], context, current_level);
|
|
|
|
field->nod_arg[e_via_value_1] = field->nod_arg[e_via_rse]->nod_arg[e_rse_items]->nod_arg[0];
|
|
|
|
return field;
|
|
|
|
|
|
|
|
case nod_rse:
|
|
|
|
current_level++;
|
2005-05-28 00:45:31 +02:00
|
|
|
field->nod_arg[e_rse_streams] =
|
2003-01-11 03:49:13 +01:00
|
|
|
remap_field(request, field->nod_arg[e_rse_streams], context, current_level);
|
2005-11-30 10:03:53 +01:00
|
|
|
field->nod_arg[e_rse_boolean] =
|
|
|
|
remap_field(request, field->nod_arg[e_rse_boolean], context, current_level);
|
|
|
|
field->nod_arg[e_rse_items] =
|
|
|
|
remap_field(request, field->nod_arg[e_rse_items], context, current_level);
|
2003-01-11 03:49:13 +01:00
|
|
|
current_level--;
|
|
|
|
return field;
|
|
|
|
|
|
|
|
case nod_coalesce:
|
|
|
|
case nod_simple_case:
|
|
|
|
case nod_searched_case:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
dsql_nod** ptr = field->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + field->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
*ptr = remap_field(request, *ptr, context, current_level);
|
|
|
|
}
|
|
|
|
return field;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case nod_aggregate:
|
2005-05-28 00:45:31 +02:00
|
|
|
field->nod_arg[e_agg_rse] =
|
2003-01-11 03:49:13 +01:00
|
|
|
remap_field(request, field->nod_arg[e_agg_rse], context, current_level);
|
|
|
|
return field;
|
|
|
|
|
|
|
|
case nod_order:
|
2005-05-28 00:45:31 +02:00
|
|
|
field->nod_arg[e_order_field] =
|
2003-01-11 03:49:13 +01:00
|
|
|
remap_field(request, field->nod_arg[e_order_field], context, current_level);
|
|
|
|
return field;
|
|
|
|
|
|
|
|
case nod_or:
|
|
|
|
case nod_and:
|
|
|
|
case nod_not:
|
2004-10-14 20:54:54 +02:00
|
|
|
case nod_equiv:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_eql_any:
|
|
|
|
case nod_neq_any:
|
|
|
|
case nod_gtr_any:
|
|
|
|
case nod_geq_any:
|
|
|
|
case nod_lss_any:
|
|
|
|
case nod_leq_any:
|
|
|
|
case nod_eql_all:
|
|
|
|
case nod_neq_all:
|
|
|
|
case nod_gtr_all:
|
|
|
|
case nod_geq_all:
|
|
|
|
case nod_lss_all:
|
|
|
|
case nod_leq_all:
|
2005-11-09 00:49:50 +01:00
|
|
|
case nod_any:
|
|
|
|
case nod_ansi_any:
|
|
|
|
case nod_ansi_all:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_between:
|
|
|
|
case nod_like:
|
|
|
|
case nod_containing:
|
|
|
|
case nod_starting:
|
|
|
|
case nod_exists:
|
|
|
|
case nod_singular:
|
|
|
|
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:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_internal_info:
|
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2003-01-11 03:49:13 +01:00
|
|
|
case nod_list:
|
2003-05-07 03:57:18 +02:00
|
|
|
case nod_join:
|
|
|
|
case nod_join_inner:
|
|
|
|
case nod_join_left:
|
|
|
|
case nod_join_right:
|
|
|
|
case nod_join_full:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
dsql_nod** ptr = field->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + field->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
*ptr = remap_field(request, *ptr, context, current_level);
|
|
|
|
}
|
|
|
|
return field;
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case nod_cast:
|
|
|
|
case nod_gen_id:
|
|
|
|
case nod_gen_id2:
|
|
|
|
case nod_udf:
|
|
|
|
if (field->nod_count == 2) {
|
2003-11-18 08:58:35 +01:00
|
|
|
field->nod_arg[1] = remap_field(request, field->nod_arg[1],
|
|
|
|
context, current_level);
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
return field;
|
2003-04-17 00:49:41 +02:00
|
|
|
|
|
|
|
case nod_relation:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
dsql_ctx* lrelation_context =
|
|
|
|
reinterpret_cast<dsql_ctx*>(field->nod_arg[e_rel_context]);
|
|
|
|
// Check if relation is a procedure
|
|
|
|
if (lrelation_context->ctx_procedure) {
|
2005-11-30 10:03:53 +01:00
|
|
|
// Remap the input parameters
|
|
|
|
lrelation_context->ctx_proc_inputs =
|
|
|
|
remap_field(request, lrelation_context->ctx_proc_inputs, context, current_level);
|
2003-04-17 00:49:41 +02:00
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
return field;
|
2003-04-17 00:49:41 +02:00
|
|
|
}
|
2005-05-18 20:55:57 +02:00
|
|
|
|
|
|
|
case nod_derived_table:
|
2005-05-24 01:16:49 +02:00
|
|
|
remap_field(request, field->nod_arg[e_derived_table_rse], context, current_level);
|
|
|
|
return field;
|
2005-05-22 05:11:41 +02:00
|
|
|
|
2003-01-11 03:49:13 +01:00
|
|
|
default:
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_fields
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
@brief Remap fields inside a field list against
|
2003-05-05 00:02:42 +02:00
|
|
|
an artificial context.
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
|
|
|
|
@param request
|
|
|
|
@param fields
|
|
|
|
@param context
|
|
|
|
|
|
|
|
**/
|
2003-11-10 10:16:38 +01:00
|
|
|
static dsql_nod* remap_fields(dsql_req* request, dsql_nod* fields, dsql_ctx* context)
|
2003-01-11 03:49:13 +01:00
|
|
|
{
|
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(fields, dsql_type_nod);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
|
2005-11-09 00:49:50 +01:00
|
|
|
if (fields->nod_type == nod_list) {
|
|
|
|
for (int i = 0; i < fields->nod_count; i++) {
|
|
|
|
fields->nod_arg[i] = remap_field(request, fields->nod_arg[i], context,
|
|
|
|
request->req_scope_level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fields = remap_field(request, fields, context, request->req_scope_level);
|
2003-01-11 03:49:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
switch (input->nod_type) {
|
|
|
|
case nod_list:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
dsql_nod** ptr = input->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + input->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
remap_streams_to_parent_context(*ptr, parent_context);
|
2004-10-13 20:37:53 +02:00
|
|
|
}
|
2003-11-18 08:58:35 +01:00
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_relation:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
dsql_ctx* context = (dsql_ctx*) input->nod_arg[e_rel_context];
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
|
|
|
context->ctx_parent = parent_context;
|
|
|
|
}
|
2005-05-22 05:11:41 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_join:
|
|
|
|
remap_streams_to_parent_context(input->nod_arg[e_join_left_rel],
|
|
|
|
parent_context);
|
|
|
|
remap_streams_to_parent_context(input->nod_arg[e_join_rght_rel],
|
|
|
|
parent_context);
|
|
|
|
break;
|
|
|
|
|
2003-08-15 02:02:18 +02:00
|
|
|
case nod_derived_table:
|
|
|
|
// nothing to do here.
|
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2003-11-04 00:59:24 +01:00
|
|
|
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
|
|
|
|
|
|
|
@param request
|
|
|
|
@param name
|
|
|
|
@param qualifier
|
|
|
|
@param context
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static dsql_fld* resolve_context( dsql_req* request, const dsql_str* qualifier,
|
2004-01-10 19:04:40 +01:00
|
|
|
dsql_ctx* context, bool isCheckConstraint)
|
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.
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(request, dsql_type_req);
|
|
|
|
DEV_BLKCHK(qualifier, dsql_type_str);
|
|
|
|
DEV_BLKCHK(context, dsql_type_ctx);
|
2001-05-23 15:26:42 +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
|
|
|
|
2004-01-09 03:23:46 +01: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.
|
|
|
|
// if (!qualifier && context->ctx_scope_level != request->req_scope_level) {
|
|
|
|
// return NULL;
|
|
|
|
// }
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-10-25 22:42:29 +02: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
|
|
|
|
// the qualifier present.
|
|
|
|
// An exception is a check-constraint that is allowed to reference fields
|
|
|
|
// without the qualifier.
|
|
|
|
if (!isCheckConstraint && (context->ctx_flags & CTX_system) && (!qualifier)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-01-10 19:04:40 +01:00
|
|
|
TEXT* table_name = NULL;
|
2005-07-25 16:43:28 +02:00
|
|
|
if (context->ctx_internal_alias) {
|
|
|
|
table_name = context->ctx_internal_alias;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2004-01-10 19:04:40 +01:00
|
|
|
// AB: For a check constraint we should ignore the alias if the alias
|
2004-11-03 00:07:09 +01:00
|
|
|
// contains the "NEW" alias. This is because it is possible
|
2005-05-28 00:45:31 +02:00
|
|
|
// to reference a field by the complete table-name as alias
|
2004-01-10 19:04:40 +01:00
|
|
|
// (see EMPLOYEE table in examples for a example).
|
2005-05-28 00:45:31 +02:00
|
|
|
if (isCheckConstraint && 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
|
2005-05-28 00:45:31 +02:00
|
|
|
if (!(qualifier && !strcmp(reinterpret_cast<const char*>(qualifier->str_data), table_name)))
|
2004-11-03 00:07:09 +01:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
if (!strcmp(table_name, NEW_CONTEXT))
|
2004-06-30 00:15:10 +02:00
|
|
|
{
|
|
|
|
table_name = NULL;
|
|
|
|
}
|
2005-08-01 21:05:57 +02:00
|
|
|
else if (!strcmp(table_name, OLD_CONTEXT))
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
if (table_name == NULL) {
|
2004-01-09 03:23:46 +01:00
|
|
|
if (relation) {
|
|
|
|
table_name = relation->rel_name;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
table_name = procedure->prc_name;
|
|
|
|
}
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2004-09-26 03:49:52 +02:00
|
|
|
fb_utils::exact_name(table_name);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-01-09 03:23:46 +01:00
|
|
|
// If a context qualifier is present, make sure this is the proper context
|
|
|
|
if (qualifier && strcmp(reinterpret_cast<const char*>(qualifier->str_data), table_name)) {
|
2003-10-05 08:37:26 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// Lookup field in relation or procedure
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_fld* field;
|
2003-08-15 02:02:18 +02:00
|
|
|
if (relation) {
|
2001-05-23 15:26:42 +02:00
|
|
|
field = relation->rel_fields;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
|
|
|
else {
|
2001-05-23 15:26:42 +02:00
|
|
|
field = procedure->prc_outputs;
|
2003-08-15 02:02:18 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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_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
|
|
|
|
|
|
|
|
**/
|
2005-05-28 00:45:31 +02:00
|
|
|
static bool set_parameter_type(dsql_req* request, dsql_nod* in_node, dsql_nod* node, bool force_varchar)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
DEV_BLKCHK(in_node, dsql_type_nod);
|
2002-04-04 18:41:41 +02:00
|
|
|
DEV_BLKCHK(node, dsql_type_nod);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-07 15:10:16 +01:00
|
|
|
if (in_node == NULL)
|
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (in_node->nod_type)
|
|
|
|
{
|
2003-09-02 01:22:22 +02:00
|
|
|
case nod_parameter:
|
|
|
|
{
|
2005-12-12 18:27:10 +01:00
|
|
|
if (!node) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
MAKE_desc(request, &in_node->nod_desc, node, NULL);
|
|
|
|
|
2006-04-24 19:24:26 +02:00
|
|
|
if (in_node->nod_desc.dsc_dtype <= dtype_any_text &&
|
|
|
|
request->req_dbb->dbb_att_charset != CS_NONE &&
|
2005-05-28 00:45:31 +02:00
|
|
|
request->req_dbb->dbb_att_charset != CS_BINARY)
|
|
|
|
{
|
2005-09-03 09:47:32 +02:00
|
|
|
int diff = 0;
|
|
|
|
switch (in_node->nod_desc.dsc_dtype)
|
|
|
|
{
|
|
|
|
case dtype_varying:
|
|
|
|
diff = sizeof(USHORT);
|
|
|
|
break;
|
|
|
|
case dtype_cstring:
|
|
|
|
diff = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
in_node->nod_desc.dsc_length -= diff;
|
2006-04-24 19:24:26 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
USHORT fromCharSet = INTL_GET_CHARSET(&in_node->nod_desc);
|
|
|
|
USHORT toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ?
|
|
|
|
fromCharSet : request->req_dbb->dbb_att_charset;
|
|
|
|
|
|
|
|
if (toCharSet != fromCharSet)
|
|
|
|
{
|
|
|
|
USHORT fromCharSetBPC = METD_get_charset_bpc(request, fromCharSet);
|
|
|
|
USHORT toCharSetBPC = METD_get_charset_bpc(request, toCharSet);
|
|
|
|
|
2006-01-15 19:11:31 +01:00
|
|
|
INTL_ASSIGN_TTYPE(&in_node->nod_desc, INTL_CS_COLL_TO_TTYPE(toCharSet,
|
|
|
|
(fromCharSet == toCharSet ? INTL_GET_COLLATE(&in_node->nod_desc) : 0)));
|
|
|
|
|
2005-06-10 04:03:08 +02:00
|
|
|
in_node->nod_desc.dsc_length =
|
|
|
|
UTLD_char_length_to_byte_length(in_node->nod_desc.dsc_length / fromCharSetBPC, toCharSetBPC);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
|
|
|
|
2005-09-03 09:47:32 +02:00
|
|
|
in_node->nod_desc.dsc_length += diff;
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
|
|
|
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_par* parameter = (dsql_par*) in_node->nod_arg[e_par_parameter];
|
2006-09-03 03:09:23 +02:00
|
|
|
|
|
|
|
if (!parameter)
|
|
|
|
{
|
|
|
|
in_node->nod_arg[e_par_parameter] = (dsql_nod*)
|
|
|
|
(parameter = MAKE_parameter(request->req_send,
|
|
|
|
true, true,
|
|
|
|
(USHORT)(IPTR) in_node->nod_arg[e_par_index],
|
|
|
|
NULL));
|
|
|
|
in_node->nod_arg[e_par_index] = (dsql_nod*) parameter->par_index;
|
|
|
|
}
|
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
DEV_BLKCHK(parameter, dsql_type_par);
|
|
|
|
parameter->par_desc = in_node->nod_desc;
|
|
|
|
parameter->par_node = in_node;
|
|
|
|
|
|
|
|
// Parameters should receive precisely the data that the user
|
|
|
|
// passes in. Therefore for text strings lets use varying
|
2005-05-28 00:45:31 +02:00
|
|
|
// strings to insure that we don't add trailing blanks.
|
2003-09-02 01:22:22 +02:00
|
|
|
|
|
|
|
// However, there are situations this leads to problems - so
|
|
|
|
// we use the force_varchar parameter to prevent this
|
|
|
|
// datatype assumption from occuring.
|
|
|
|
|
|
|
|
if (force_varchar) {
|
2005-09-03 09:47:32 +02:00
|
|
|
if (parameter->par_desc.dsc_dtype == dtype_text)
|
|
|
|
{
|
2003-09-02 01:22:22 +02:00
|
|
|
parameter->par_desc.dsc_dtype = dtype_varying;
|
2005-09-03 09:47:32 +02:00
|
|
|
// The error msgs is inaccurate, but causing dsc_length
|
|
|
|
// to be outsise range can be worse.
|
|
|
|
if (parameter->par_desc.dsc_length > MAX_COLUMN_SIZE - sizeof(USHORT))
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -204,
|
|
|
|
//isc_arg_gds, isc_dsql_datatype_err,
|
|
|
|
isc_arg_gds, isc_imp_exc,
|
|
|
|
//isc_arg_gds, isc_field_name,
|
|
|
|
//isc_arg_string, parameter->par_name,
|
|
|
|
0);
|
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
parameter->par_desc.dsc_length += sizeof(USHORT);
|
|
|
|
}
|
|
|
|
else if (parameter->par_desc.dsc_dtype > dtype_any_text) {
|
|
|
|
// The LIKE & similar parameters must be varchar type
|
|
|
|
// strings - so force this parameter to be varchar
|
|
|
|
// and take a guess at a good length for it.
|
|
|
|
parameter->par_desc.dsc_dtype = dtype_varying;
|
2003-11-18 08:58:35 +01:00
|
|
|
parameter->par_desc.dsc_length = LIKE_PARAM_LEN + sizeof(USHORT);
|
2003-09-02 01:22:22 +02:00
|
|
|
parameter->par_desc.dsc_sub_type = 0;
|
|
|
|
parameter->par_desc.dsc_scale = 0;
|
2004-05-21 08:16:17 +02:00
|
|
|
parameter->par_desc.dsc_ttype() = ttype_dynamic;
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-02 01:22:22 +02:00
|
|
|
case nod_cast:
|
2005-12-12 18:27:10 +01:00
|
|
|
{
|
|
|
|
dsql_nod* par_node = in_node->nod_arg[e_cast_source];
|
|
|
|
dsql_fld* field = (dsql_fld*) in_node->nod_arg[e_cast_target];
|
|
|
|
if (par_node->nod_type == nod_parameter) {
|
|
|
|
dsql_par* parameter =
|
|
|
|
(dsql_par*) par_node->nod_arg[e_par_parameter];
|
|
|
|
DEV_BLKCHK(parameter, dsql_type_par);
|
|
|
|
parameter->par_desc = par_node->nod_desc;
|
|
|
|
parameter->par_node = par_node;
|
|
|
|
MAKE_desc_from_field(¶meter->par_desc, field);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-12-12 18:27:10 +01:00
|
|
|
case nod_missing:
|
2003-09-02 01:22:22 +02:00
|
|
|
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:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2003-09-02 01:22:22 +02:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2003-09-02 01:22:22 +02:00
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2003-09-02 01:22:22 +02:00
|
|
|
case nod_limit:
|
2003-11-07 15:10:16 +01:00
|
|
|
case nod_rows:
|
2006-04-24 19:24:26 +02:00
|
|
|
case nod_agg_list:
|
2003-09-02 01:22:22 +02:00
|
|
|
{
|
|
|
|
bool result = false;
|
2003-11-18 08:58:35 +01:00
|
|
|
dsql_nod** ptr = in_node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + in_node->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
result |= set_parameter_type(request, *ptr, node, force_varchar);
|
2003-09-02 01:22:22 +02:00
|
|
|
}
|
|
|
|
return result;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
default:
|
|
|
|
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
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
const dsql_ctx* context = (dsql_ctx*) rel_node->nod_arg[0];
|
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
|
|
|
|
2003-11-10 10:16:38 +01:00
|
|
|
dsql_nod** ptr = list_node->nod_arg;
|
2003-11-18 08:58:35 +01:00
|
|
|
for (const dsql_nod* const* const end = ptr + list_node->nod_count;
|
2003-10-05 08:37:26 +02:00
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
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)
|
|
|
|
set_parameter_name((*ptr)->nod_arg[0],
|
|
|
|
(*ptr)->nod_arg[1], relation);
|
|
|
|
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
|
|
|
|
2003-02-15 04:01:51 +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
|
|
|
|
"insert" or "update" statements are parsed. If the
|
|
|
|
statements have input parameters, than the parameter
|
|
|
|
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
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +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;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Could it be something else ??? */
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(fld_node->nod_type == nod_field);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (fld_node->nod_desc.dsc_dtype != dtype_array)
|
|
|
|
return;
|
|
|
|
|
2005-05-22 05:11:41 +02:00
|
|
|
switch (par_node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_parameter:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
2004-02-02 12:02:12 +01:00
|
|
|
dsql_par* parameter = (dsql_par*) par_node->nod_arg[e_par_parameter];
|
2003-11-18 08:58:35 +01:00
|
|
|
DEV_BLKCHK(parameter, dsql_type_par);
|
|
|
|
const dsql_fld* field = (dsql_fld*) fld_node->nod_arg[e_fld_field];
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
parameter->par_name = field->fld_name;
|
|
|
|
parameter->par_rel_name = relation->rel_name;
|
|
|
|
return;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_upcase:
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_lowcase:
|
2004-08-01 20:01:54 +02:00
|
|
|
case nod_extract:
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2004-08-01 20:01:54 +02:00
|
|
|
case nod_limit:
|
|
|
|
case nod_rows:
|
2003-11-18 08:58:35 +01:00
|
|
|
{
|
|
|
|
dsql_nod** ptr = par_node->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + par_node->nod_count;
|
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
set_parameter_name(*ptr, fld_node, relation);
|
|
|
|
}
|
|
|
|
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
|
2003-05-02 17:20:17 +02:00
|
|
|
to request having error handlers.
|
|
|
|
|
|
|
|
|
|
|
|
@param request
|
|
|
|
@param node
|
|
|
|
|
|
|
|
**/
|
2005-08-22 12:12:13 +02:00
|
|
|
static dsql_nod* pass1_savepoint(const dsql_req* request, dsql_nod* node)
|
|
|
|
{
|
2003-05-02 17:20:17 +02:00
|
|
|
if (request->req_error_handlers) {
|
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
|
|
|
|
|
|
|
|
|
|
|
void dsql_req::addCTEs(dsql_nod* with)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(with, dsql_type_nod);
|
|
|
|
fb_assert(with->nod_type == nod_with);
|
2006-08-02 03:22:11 +02:00
|
|
|
|
2006-08-01 22:37:58 +02:00
|
|
|
if (req_ctes.getCount()) {
|
|
|
|
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104,
|
|
|
|
isc_arg_gds, isc_random,
|
|
|
|
isc_arg_string, "WITH clause can't be nested",
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (with->nod_flags & NOD_UNION_RECURSIVE)
|
|
|
|
req_flags |= REQ_CTE_recursive;
|
|
|
|
|
|
|
|
const dsql_nod* list = with->nod_arg[0];
|
|
|
|
const dsql_nod* const* end = list->nod_arg + list->nod_count;
|
|
|
|
for (dsql_nod* const* cte = list->nod_arg; cte < end; cte++)
|
|
|
|
{
|
|
|
|
fb_assert((*cte)->nod_type == nod_derived_table);
|
|
|
|
|
|
|
|
if (with->nod_flags & NOD_UNION_RECURSIVE) {
|
|
|
|
req_curr_ctes.push(*cte);
|
|
|
|
req_ctes.add(pass1_recursive_cte(this, *cte, false));
|
|
|
|
req_curr_ctes.pop();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
req_ctes.add(*cte);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dsql_nod* dsql_req::findCTE(const dsql_str* name)
|
|
|
|
{
|
2006-08-02 03:22:11 +02:00
|
|
|
for (size_t i = 0; i < req_ctes.getCount(); i++)
|
2006-08-01 22:37:58 +02:00
|
|
|
{
|
|
|
|
dsql_nod* cte = req_ctes[i];
|
|
|
|
const dsql_str* cte_name = (dsql_str*) cte->nod_arg[e_derived_table_alias];
|
|
|
|
|
|
|
|
if (name->str_length == cte_name->str_length &&
|
|
|
|
strncmp(name->str_data, cte_name->str_data, cte_name->str_length) == 0)
|
2006-08-02 03:22:11 +02:00
|
|
|
{
|
2006-08-01 22:37:58 +02:00
|
|
|
return cte;
|
2006-08-02 03:22:11 +02:00
|
|
|
}
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
2006-08-02 03:22:11 +02:00
|
|
|
return NULL;
|
2006-08-01 22:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void dsql_req::clearCTEs()
|
|
|
|
{
|
|
|
|
req_flags &= ~REQ_CTE_recursive;
|
|
|
|
req_ctes.clear();
|
|
|
|
req_cte_aliases.clear();
|
|
|
|
}
|