8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 07:23:04 +01:00
firebird-mirror/src/dsql/pass1.cpp

5469 lines
156 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: Dynamic SQL runtime support
* MODULE: pass1.c
* 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.07.30 Arno Brinkman: Added pass1_coalesce, pass1_simple_case, pass1_searched_case
* and pass1_put_args_on_stack
*
* 2002.08.04 Arno Brinkman: Added ignore_cast as parameter to node_match,
* Changed invalid_reference procedure for allow EXTRACT, SUBSTRING, CASE,
* COALESCE and NULLIF functions in GROUP BY and as select_items.
* Removed aggregate_in_list procedure.
*
* 2002.08.07 Dmitry Yemanov: Disabled BREAK statement in triggers
*
* 2002.08.10 Dmitry Yemanov: ALTER VIEW
*
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
* exception handling in SPs/triggers,
* implemented ROWS_AFFECTED system variable
*
* 2002.09.29 Arno Brinkman: Adding more checking for aggregate functions
* and adding support for 'linking' from sub-selects to aggregate functions
* which are in an lower level.
* Modified functions pass1_field, pass1_rse, copy_field, pass1_sort.
* Functions pass1_found_aggregate and pass1_found_field added.
2002-10-25 10:29:12 +02: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 Nickolay Samofatov: Added support for savepoints
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
#include <memory>
2001-05-23 15:26:42 +02:00
#include "../jrd/ib_stdio.h"
#include "../jrd/gds.h"
2001-05-23 15:26:42 +02:00
#include "../dsql/dsql.h"
#include "../dsql/node.h"
#include "../dsql/sym.h"
#include "gen/codes.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/thd.h"
#include "../jrd/intl.h"
#include "../jrd/blr.h"
#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"
#include "../dsql/pass1_proto.h"
#include "../dsql/misc_func.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/dsc_proto.h"
#include "../jrd/thd_proto.h"
#ifdef DEV_BUILD
#include "../dsql/dsql_proto.h"
#endif
ASSERT_FILENAME /* Define things assert() needs */
static BOOLEAN aggregate_found(REQ, NOD, NOD *);
static BOOLEAN aggregate_found2(REQ, NOD, NOD *, BOOLEAN *);
2002-06-29 08:56:51 +02:00
static NOD ambiguity_check (NOD, REQ, FLD, DLLS, DLLS);
2001-05-23 15:26:42 +02:00
static void assign_fld_dtype_from_dsc(FLD, DSC *);
static NOD compose(NOD, NOD, NOD_TYPE);
static NOD copy_field(NOD, CTX);
static NOD copy_fields(NOD, CTX);
2001-12-24 03:51:06 +01:00
static void explode_asterisk(NOD, NOD, DLLS *);
2001-05-23 15:26:42 +02:00
static NOD explode_outputs(REQ, PRC);
2002-06-29 08:56:51 +02:00
static void field_error(TEXT *, TEXT *, NOD);
2001-05-23 15:26:42 +02:00
static PAR find_dbkey(REQ, NOD);
static PAR find_record_version(REQ, NOD);
static BOOLEAN invalid_reference(REQ, NOD, NOD, BOOLEAN);
2001-05-23 15:26:42 +02:00
static void mark_ctx_outer_join(NOD);
static BOOLEAN node_match(NOD, NOD, BOOLEAN);
2001-05-23 15:26:42 +02:00
static NOD pass1_alias_list(REQ, NOD);
static CTX pass1_alias(REQ, STR);
static NOD pass1_any(REQ, NOD, NOD_TYPE);
static DSQL_REL pass1_base_table(REQ, DSQL_REL, STR);
static void pass1_blob(REQ, NOD);
static NOD pass1_coalesce(REQ, NOD, USHORT);
2001-05-23 15:26:42 +02:00
static NOD pass1_collate(REQ, NOD, STR);
static NOD pass1_constant(REQ, NOD);
static NOD pass1_cursor(REQ, NOD, NOD);
static CTX pass1_cursor_context(REQ, NOD, NOD);
static NOD pass1_dbkey(REQ, NOD);
static NOD pass1_delete(REQ, NOD);
static NOD pass1_field(REQ, NOD, USHORT);
static BOOLEAN pass1_found_aggregate(NOD, USHORT, USHORT, BOOLEAN);
static BOOLEAN pass1_found_field(NOD, USHORT, USHORT, BOOLEAN *);
2001-05-23 15:26:42 +02:00
static NOD pass1_insert(REQ, NOD);
static void pass1_put_args_on_stack(REQ, NOD, DLLS *, USHORT);
2001-05-23 15:26:42 +02:00
static NOD pass1_relation(REQ, NOD);
static NOD pass1_rse(REQ, NOD, NOD, NOD);
static NOD pass1_searched_case(REQ, NOD, USHORT);
2001-05-23 15:26:42 +02:00
static NOD pass1_sel_list(REQ, NOD);
static NOD pass1_simple_case(REQ, NOD, USHORT);
static NOD pass1_sort(REQ, NOD, NOD, CTX);
2001-05-23 15:26:42 +02:00
static NOD pass1_udf(REQ, NOD, USHORT);
2002-06-29 08:56:51 +02:00
static void pass1_udf_args(REQ, NOD, UDF, USHORT, DLLS *, USHORT);
2001-05-23 15:26:42 +02:00
static NOD pass1_union(REQ, NOD, NOD);
static NOD pass1_update(REQ, NOD);
static NOD pass1_variable(REQ, NOD);
static NOD post_map(NOD, CTX);
static void remap_streams_to_parent_context(NOD, CTX);
static FLD resolve_context(REQ, STR, STR, CTX);
static BOOLEAN set_parameter_type(NOD, NOD, BOOLEAN);
2001-05-23 15:26:42 +02:00
static void set_parameters_name(NOD, NOD);
static void set_parameter_name(NOD, NOD, DSQL_REL);
2002-06-29 08:56:51 +02:00
static TEXT *pass_exact_name (TEXT*);
2001-05-23 15:26:42 +02:00
STR temp_collation_name = NULL;
#define DB_KEY_STRING "DB_KEY" /* NTX: pseudo field name */
#define MAX_MEMBER_LIST 1500 /* Maximum members in "IN" list.
* For eg. SELECT * FROM T WHERE
* F IN (1, 2, 3, ...)
*
* Bug 10061, bsriram - 19-Apr-1999
*/
#define FIELD_MATCH_TYPE_EQUAL 0
#define FIELD_MATCH_TYPE_LOWER 1
#define FIELD_MATCH_TYPE_LOWER_EQUAL 2
#define FIELD_MATCH_TYPE_HIGHER 3
#define FIELD_MATCH_TYPE_HIGHER_EQUAL 4
2001-05-23 15:26:42 +02:00
2002-06-29 08:56:51 +02:00
#ifndef PRINTF
#define PRINTF ib_printf
#endif
2001-05-23 15:26:42 +02:00
CTX PASS1_make_context( REQ request, NOD relation_node)
{
/**************************************
*
* P A S S 1 _ m a k e _ c o n t e x t
*
**************************************
*
* Functional description
* Generate a context for a request.
*
**************************************/
CTX context, conflict;
STR relation_name, string;
DSQL_REL relation;
PRC procedure;
FLD field;
NOD *input;
2001-12-24 03:51:06 +01:00
DLLS stack;
2001-05-23 15:26:42 +02:00
TEXT *conflict_name;
STATUS error_code;
TSQL tdsql;
USHORT count;
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
tdsql = GET_THREAD_DATA;
relation = NULL;
procedure = NULL;
/* figure out whether this is a relation or a procedure
and give an error if it is neither */
if (relation_node->nod_type == nod_rel_proc_name)
relation_name = (STR) relation_node->nod_arg[e_rpn_name];
else
relation_name = (STR) relation_node->nod_arg[e_rln_name];
2002-06-29 08:56:51 +02:00
/* CVC: Let's skim the context, too. */
if (relation_name && relation_name->str_data)
pass_exact_name ( (TEXT*) relation_name->str_data);
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(relation_name, dsql_type_str);
2001-05-23 15:26:42 +02:00
if ((relation_node->nod_type == nod_rel_proc_name) &&
relation_node->nod_arg[e_rpn_inputs])
{
if (!(procedure = METD_get_procedure(request, relation_name)))
{
2002-06-29 08:56:51 +02:00
TEXT linecol [64];
sprintf (linecol, "At line %d, column %d.",
(int) relation_node->nod_line, (int) relation_node->nod_column);
ERRD_post( gds_sqlerr,
2001-05-23 15:26:42 +02:00
gds_arg_number,
(SLONG) - 204,
gds_arg_gds,
gds_dsql_procedure_err,
2001-05-23 15:26:42 +02:00
gds_arg_gds,
gds_random,
2001-05-23 15:26:42 +02:00
gds_arg_string,
relation_name->str_data,
2002-06-29 08:56:51 +02:00
gds_arg_gds, gds_random,
gds_arg_string, linecol,
0);
2001-05-23 15:26:42 +02:00
}
}
else
{
if (!(relation = METD_get_relation(request, relation_name)) &&
(relation_node->nod_type == nod_rel_proc_name))
{
procedure = METD_get_procedure(request, relation_name);
}
if (!relation && !procedure)
{
2002-06-29 08:56:51 +02:00
TEXT linecol [64];
sprintf (linecol, "At line %d, column %d.",
(int) relation_node->nod_line, (int) relation_node->nod_column);
ERRD_post( gds_sqlerr,
2001-05-23 15:26:42 +02:00
gds_arg_number,
(SLONG) - 204,
gds_arg_gds,
gds_dsql_relation_err,
2001-05-23 15:26:42 +02:00
gds_arg_gds,
gds_random,
2001-05-23 15:26:42 +02:00
gds_arg_string,
relation_name->str_data,
2002-06-29 08:56:51 +02:00
gds_arg_gds, gds_random,
gds_arg_string, linecol,
2001-05-23 15:26:42 +02:00
0);
}
}
2002-06-29 08:56:51 +02:00
if (procedure && !procedure->prc_out_count) {
TEXT linecol [64];
sprintf (linecol, "At line %d, column %d.",
(int) relation_node->nod_line, (int) relation_node->nod_column);
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 84,
gds_arg_gds, gds_dsql_procedure_use_err,
2002-06-29 08:56:51 +02:00
gds_arg_string, relation_name->str_data,
gds_arg_gds, gds_random,
gds_arg_string, linecol,
0);
}
2001-05-23 15:26:42 +02:00
/* Set up context block */
context = FB_NEW(*tdsql->tsql_default) ctx;
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;
/* find the context alias name, if it exists */
if (relation_node->nod_type == nod_rel_proc_name)
string = (STR) relation_node->nod_arg[e_rpn_alias];
else
string = (STR) relation_node->nod_arg[e_rln_alias];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(string, dsql_type_str);
2001-05-23 15:26:42 +02:00
if (string) {
context->ctx_alias = (TEXT *) string->str_data;
/* check to make sure the context is not already used at this same
query level (if there are no subqueries, this checks that the
alias is not used twice in the request) */
for (stack = request->req_context; stack; stack = stack->lls_next) {
conflict = (CTX) stack->lls_object;
if (conflict->ctx_scope_level != context->ctx_scope_level)
continue;
if (conflict->ctx_alias) {
conflict_name = conflict->ctx_alias;
error_code = gds_alias_conflict_err;
2001-05-23 15:26:42 +02:00
/* alias %s conflicts with an alias in the same statement */
}
else if (conflict->ctx_procedure) {
conflict_name = conflict->ctx_procedure->prc_name;
error_code = gds_procedure_conflict_error;
2001-05-23 15:26:42 +02:00
/* alias %s conflicts with a procedure in the same statement */
}
else if (conflict->ctx_relation) {
conflict_name = conflict->ctx_relation->rel_name;
error_code = gds_relation_conflict_err;
2001-05-23 15:26:42 +02:00
/* alias %s conflicts with a relation in the same statement */
}
else
continue;
if (!strcmp(conflict_name, context->ctx_alias))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 204,
2001-05-23 15:26:42 +02:00
gds_arg_gds, error_code,
gds_arg_string, conflict_name, 0);
}
}
2001-12-24 03:51:06 +01:00
if (procedure)
{
if (request->req_scope_level == 1) {
2001-05-23 15:26:42 +02:00
request->req_flags |= REQ_no_batch;
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 (relation_node->nod_arg[e_rpn_inputs])
{
2001-05-23 15:26:42 +02:00
context->ctx_proc_inputs = PASS1_node(request,
relation_node->
nod_arg[e_rpn_inputs], 0);
count = context->ctx_proc_inputs->nod_count;
}
else
2001-12-24 03:51:06 +01:00
{
2001-05-23 15:26:42 +02:00
count = 0;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2002-06-29 08:56:51 +02:00
if (!(request->req_flags & REQ_procedure)) {
2001-05-23 15:26:42 +02:00
if (count != procedure->prc_in_count)
2002-06-29 08:56:51 +02:00
{
ERRD_post(gds_prcmismat, gds_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
std::auto_ptr<nod> desc_node(FB_NEW_RPT(*tdsql->tsql_default, 0) nod);
2001-05-23 15:26:42 +02:00
for (input = context->ctx_proc_inputs->nod_arg,
field = procedure->prc_inputs;
2001-12-24 03:51:06 +01:00
field; input++, field = field->fld_next)
{
DEV_BLKCHK(field, dsql_type_fld);
DEV_BLKCHK(*input, dsql_type_nod);
// MAKE_desc_from_field(&desc_node.nod_desc, field);
// set_parameter_type(*input, &desc_node, FALSE);
MAKE_desc_from_field(&(desc_node->nod_desc), field);
set_parameter_type(*input, desc_node.get(), FALSE);
2001-05-23 15:26:42 +02:00
}
}
}
}
2002-06-29 08:56:51 +02:00
/* push the context onto the request context stack
for matching fields against */
2001-05-23 15:26:42 +02:00
LLS_PUSH(context, &request->req_context);
2002-06-29 08:56:51 +02:00
2001-05-23 15:26:42 +02:00
return context;
2002-06-29 08:56:51 +02:00
}
2001-05-23 15:26:42 +02:00
NOD PASS1_node(REQ request, NOD input, USHORT proc_flag)
{
/**************************************
*
* P A S S 1 _ n o d e
*
**************************************
*
* Functional description
* Compile a parsed request into something more interesting.
*
**************************************/
NOD node, temp, *ptr, *end, *ptr2, rse, sub1, sub2, sub3;
2001-12-24 03:51:06 +01:00
DLLS base;
2001-05-23 15:26:42 +02:00
FLD field;
CTX agg_context;
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
if (input == NULL)
return NULL;
/* Dispatch on node type. Fall thru on easy ones */
switch (input->nod_type) {
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];
field = (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);
/* If the source is nullable, so is the target */
2001-05-23 15:26:42 +02:00
MAKE_desc(&sub1->nod_desc, sub1);
if (sub1->nod_desc.dsc_flags & DSC_nullable)
node->nod_desc.dsc_flags |= DSC_nullable;
return node;
case nod_coalesce:
return pass1_coalesce(request, input, proc_flag);
case nod_simple_case:
return pass1_simple_case(request, input, proc_flag);
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:
temp_collation_name = (STR) input->nod_arg[e_coll_target];
sub1 = PASS1_node(request, input->nod_arg[e_coll_source], proc_flag);
temp_collation_name = NULL;
node =
pass1_collate(request, sub1, (STR) input->nod_arg[e_coll_target]);
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);
MAKE_desc(&sub1->nod_desc, sub1);
switch (*(SLONG *)input->nod_arg[e_extract_part]->nod_desc.dsc_address)
{
case blr_extract_year:
case blr_extract_month:
case blr_extract_day:
case blr_extract_weekday:
case blr_extract_yearday:
if (sub1->nod_desc.dsc_dtype != dtype_sql_date &&
sub1->nod_desc.dsc_dtype != dtype_timestamp)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 105,
gds_arg_gds, gds_extract_input_mismatch, 0);
2001-05-23 15:26:42 +02:00
break;
case blr_extract_hour:
case blr_extract_minute:
case blr_extract_second:
if (sub1->nod_desc.dsc_dtype != dtype_sql_time &&
sub1->nod_desc.dsc_dtype != dtype_timestamp)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 105,
gds_arg_gds, gds_extract_input_mismatch, 0);
2001-05-23 15:26:42 +02:00
break;
default:
assert(FALSE);
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;
case nod_delete:
case nod_insert:
case nod_order:
case nod_select:
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, 0);
2001-05-23 15:26:42 +02:00
case nod_select_expr:
if (proc_flag)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 206,
gds_arg_gds, gds_dsql_subselect_err, 0);
2001-05-23 15:26:42 +02:00
base = request->req_context;
node = MAKE_node(nod_via, e_via_count);
node->nod_arg[e_via_rse] = rse = PASS1_rse(request, input, NULL, NULL);
2001-05-23 15:26:42 +02:00
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 */
while (request->req_context != base)
LLS_POP(&request->req_context);
return node;
case nod_exists:
case nod_singular:
base = request->req_context;
node = MAKE_node(input->nod_type, 1);
node->nod_arg[0] = PASS1_rse(request, input->nod_arg[0], NULL, NULL);
2001-05-23 15:26:42 +02:00
/* Finish off by cleaning up contexts */
while (request->req_context != base)
LLS_POP(&request->req_context);
return node;
case nod_field_name:
if (proc_flag)
return pass1_variable(request, input);
else
return pass1_field(request, input, 0);
case nod_array:
if (proc_flag)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_invalid_array, 0);
2001-05-23 15:26:42 +02:00
else
return pass1_field(request, input, 0);
case nod_variable:
2002-06-29 08:56:51 +02:00
node = MAKE_node (input->nod_type, e_var_count);
node->nod_arg [0] = input->nod_arg [0];
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:
return pass1_relation(request, input);
case nod_constant:
return pass1_constant(request, input);
case nod_parameter:
if (proc_flag)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_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;
node->nod_arg[e_par_parameter] =
(NOD) MAKE_parameter(request->req_send, TRUE, TRUE);
return node;
case nod_udf:
return pass1_udf(request, input, proc_flag);
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:
sub2 = input->nod_arg[1];
if (sub2->nod_type == nod_list) {
USHORT list_item_count = 0;
node = NULL;
for (ptr = sub2->nod_arg, end = ptr + sub2->nod_count;
ptr < end && list_item_count < MAX_MEMBER_LIST;
list_item_count++, ptr++) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(*ptr, dsql_type_nod);
2001-05-23 15:26:42 +02:00
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)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901,
2001-05-23 15:26:42 +02:00
gds_arg_gds, isc_imp_exc,
gds_arg_gds, gds_random,
2001-05-23 15:26:42 +02:00
gds_arg_string,
"too many values (more than 1500) in member list to match against",
0);
return PASS1_node(request, node, proc_flag);
}
if (sub2->nod_type == nod_select_expr) {
if (proc_flag)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 206,
gds_arg_gds, gds_dsql_subselect_err, 0);
2001-05-23 15:26:42 +02:00
if (sub2->nod_arg[e_sel_singleton]) {
base = request->req_context;
node = MAKE_node(input->nod_type, 2);
node->nod_arg[0] = PASS1_node(request, input->nod_arg[0], 0);
node->nod_arg[1] = temp = MAKE_node(nod_via, e_via_count);
temp->nod_arg[e_via_rse] = rse =
PASS1_rse(request, sub2, NULL, NULL);
2001-05-23 15:26:42 +02:00
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 */
while (request->req_context != base)
LLS_POP(&request->req_context);
return node;
}
else {
switch (input->nod_type) {
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);
2001-12-24 03:51:06 +01:00
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:
if (proc_flag)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, 0);
2001-05-23 15:26:42 +02:00
if (request->req_inhibit_map || !(request->req_in_select_list ||
request->req_in_having_clause
|| request->req_in_order_by_clause))
/* either nested aggregate, or not part of a select
list, having clause, or order by clause
*/
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_agg_ref_err, 0);
2001-05-23 15:26:42 +02:00
++request->req_inhibit_map;
node = MAKE_node(input->nod_type, input->nod_count);
node->nod_flags = input->nod_flags;
if (input->nod_count)
node->nod_arg[0] =
PASS1_node(request, input->nod_arg[0], proc_flag);
if (request->req_outer_agg_context) {
/* it's an outer reference, post map to the outer context */
agg_context = request->req_outer_agg_context;
request->req_outer_agg_context = NULL;
}
else
/* post map to the current context */
agg_context =
reinterpret_cast < ctx * >(request->req_context->lls_object);
temp = post_map(node, agg_context);
--request->req_inhibit_map;
return temp;
/* access plan node types */
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:
case nod_index_order:
node = MAKE_node(input->nod_type, 1);
node->nod_arg[0] = input->nod_arg[0];
return node;
case nod_dom_value:
node = MAKE_node(input->nod_type, input->nod_count);
node->nod_desc = input->nod_desc;
return node;
case nod_proc_internal_info:
{
internal_info_id id =
*reinterpret_cast<internal_info_id*>(input->nod_arg[0]->nod_desc.dsc_address);
if (!(request->req_flags & REQ_procedure))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_token_err, /* Token unknown */
gds_arg_gds, gds_random, gds_arg_string, InternalInfo::getAlias(id), 0);
}
break;
2001-05-23 15:26:42 +02:00
default:
break;
}
/* Node is simply to be rebuilt -- just recurse merrily */
node = MAKE_node(input->nod_type, input->nod_count);
ptr2 = node->nod_arg;
for (ptr = input->nod_arg, end = ptr + input->nod_count; 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
*ptr2++ = PASS1_node(request, *ptr, proc_flag);
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
2001-05-23 15:26:42 +02:00
}
/* Mark relations as "possibly NULL" if they are in outer joins */
switch (node->nod_type) {
case nod_join:
switch (node->nod_arg[e_join_type]->nod_type) {
case nod_join_inner:
/* Not an outer join - no work required */
break;
case nod_join_left:
mark_ctx_outer_join(node->nod_arg[e_join_rght_rel]);
break;
case nod_join_right:
mark_ctx_outer_join(node->nod_arg[e_join_left_rel]);
break;
case nod_join_full:
mark_ctx_outer_join(node->nod_arg[e_join_rght_rel]);
mark_ctx_outer_join(node->nod_arg[e_join_left_rel]);
break;
default:
ASSERT_FAIL; /* join type expected */
break;
}
break;
default:
break;
}
/* Try to match parameters against things of known data type */
sub3 = NULL;
switch (node->nod_type) {
case nod_between:
sub3 = node->nod_arg[2];
/* FALLINTO */
case nod_assign:
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];
/* Try to force sub1 to be same type as sub2 eg: ? = FIELD case */
if (set_parameter_type(sub1, sub2, FALSE))
/* null */ ;
else
/* That didn't work - try to force sub2 same type as sub 1 eg: FIELD = ? case */
(void) set_parameter_type(sub2, sub1, FALSE);
if (sub3)
/* X BETWEEN Y AND ? case */
(void) set_parameter_type(sub3, sub2, FALSE);
break;
case nod_like:
if (node->nod_count == 3) {
sub3 = node->nod_arg[2];
}
/* FALLINTO */
case nod_containing:
case nod_starting:
sub1 = node->nod_arg[0];
sub2 = node->nod_arg[1];
/* Try to force sub1 to be same type as sub2 eg: ? LIKE FIELD case */
if (set_parameter_type(sub1, sub2, TRUE))
/* null */ ;
else
/* That didn't work - try to force sub2 same type as sub 1 eg: FIELD LIKE ? case */
(void) set_parameter_type(sub2, sub1, TRUE);
if (sub3)
/* X LIKE Y ESCAPE ? case */
(void) set_parameter_type(sub3, sub2, TRUE);
break;
default:
break;
}
return node;
}
NOD PASS1_rse(REQ request, NOD input, NOD order, NOD update_lock)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A S S 1 _ r s e
*
**************************************
*
* Functional description
* Compile a record selection expression,
* bumping up the request scope level
* everytime an rse is seen. The scope
* level controls parsing of aliases.
*
**************************************/
NOD node;
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
request->req_scope_level++;
node = pass1_rse(request, input, order, update_lock);
2001-05-23 15:26:42 +02:00
request->req_scope_level--;
return node;
}
NOD PASS1_statement(REQ request, NOD input, USHORT proc_flag)
{
/**************************************
*
* P A S S 1 _ s t a t e m e n t
*
**************************************
*
* Functional description
* Compile a parsed request into something more interesting.
*
**************************************/
NOD node, *ptr, *end, *ptr2, *end2, into_in, into_out, procedure,
cursor, temp, parameters, variables;
2001-12-24 03:51:06 +01:00
DLLS base;
2001-05-23 15:26:42 +02:00
FLD field, field2;
STR name;
USHORT count;
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
#ifdef DEV_BUILD
if (DSQL_debug > 2)
DSQL_pretty(input, 0);
#endif
base = request->req_context;
/* Dispatch on node type. Fall thru on easy ones */
switch (input->nod_type) {
case nod_def_relation:
2002-06-29 08:56:51 +02:00
case nod_redef_relation:
2001-05-23 15:26:42 +02:00
case nod_def_index:
case nod_def_view:
case nod_redef_view:
2001-05-23 15:26:42 +02:00
case nod_def_constraint:
case nod_def_exception:
case nod_mod_relation:
case nod_mod_view:
case nod_replace_view:
2001-05-23 15:26:42 +02:00
case nod_mod_exception:
case nod_del_relation:
2002-06-29 08:56:51 +02:00
case nod_del_view:
2001-05-23 15:26:42 +02:00
case nod_del_index:
case nod_del_exception:
case nod_grant:
case nod_revoke:
case nod_def_database:
case nod_mod_database:
case nod_def_generator:
case nod_def_role:
case nod_del_role:
case nod_del_generator:
case nod_def_filter:
case nod_del_filter:
case nod_def_domain:
case nod_del_domain:
case nod_mod_domain:
case nod_def_udf:
case nod_del_udf:
case nod_def_shadow:
case nod_del_shadow:
case nod_mod_index:
case nod_set_statistics:
request->req_type = REQ_DDL;
return input;
case nod_def_trigger:
case nod_mod_trigger:
case nod_replace_trigger:
2001-05-23 15:26:42 +02:00
case nod_del_trigger:
request->req_type = REQ_DDL;
request->req_flags |= REQ_procedure;
request->req_flags |= REQ_trigger;
return input;
case nod_del_procedure:
request->req_type = REQ_DDL;
request->req_flags |= REQ_procedure;
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:
case nod_replace_procedure:
2001-05-23 15:26:42 +02:00
request->req_type = REQ_DDL;
request->req_flags |= REQ_procedure;
/* Insure that variable names do not duplicate parameter names */
if (variables = input->nod_arg[e_prc_dcls]) {
for (ptr = variables->nod_arg, end = ptr + variables->nod_count;
ptr < end; ptr++) {
field = (FLD) (*ptr)->nod_arg[e_dfl_field];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field, dsql_type_fld);
2001-05-23 15:26:42 +02:00
if (parameters = input->nod_arg[e_prc_inputs])
for (ptr2 = parameters->nod_arg, end2 =
ptr2 + parameters->nod_count; ptr2 < end2; ptr2++) {
field2 = (FLD) (*ptr2)->nod_arg[e_dfl_field];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field2, dsql_type_fld);
2001-05-23 15:26:42 +02:00
if (!strcmp(field->fld_name, field2->fld_name))
ERRD_post(gds_sqlerr, gds_arg_number,
2001-05-23 15:26:42 +02:00
(SLONG) - 901, gds_arg_gds,
gds_dsql_var_conflict, gds_arg_string,
2001-05-23 15:26:42 +02:00
field->fld_name, 0);
}
if (parameters = input->nod_arg[e_prc_outputs])
for (ptr2 = parameters->nod_arg, end2 =
ptr2 + parameters->nod_count; ptr2 < end2; ptr2++) {
field2 = (FLD) (*ptr2)->nod_arg[e_dfl_field];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field2, dsql_type_fld);
2001-05-23 15:26:42 +02:00
if (!strcmp(field->fld_name, field2->fld_name))
ERRD_post(gds_sqlerr, gds_arg_number,
2001-05-23 15:26:42 +02:00
(SLONG) - 901, gds_arg_gds,
gds_dsql_var_conflict, gds_arg_string,
2001-05-23 15:26:42 +02:00
field->fld_name, 0);
}
}
}
return input;
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]) &&
(input->nod_arg[e_commit_retain]->nod_type == nod_commit_retain))
request->req_type = REQ_COMMIT_RETAIN;
else
request->req_type = REQ_COMMIT;
return input;
case nod_delete:
node = pass1_delete(request, input);
if (request->req_error_handlers) {
temp = MAKE_node(nod_list, 3);
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;
}
break;
case nod_exec_procedure:
if (!proc_flag) {
name = (STR) input->nod_arg[e_exe_procedure];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(name, dsql_type_str);
2001-05-23 15:26:42 +02:00
if (!(request->req_procedure = METD_get_procedure(request, name)))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 204,
gds_arg_gds, gds_dsql_procedure_err,
gds_arg_gds, gds_random,
2001-05-23 15:26:42 +02:00
gds_arg_string, name->str_data, 0);
request->req_type = REQ_EXEC_PROCEDURE;
}
node = MAKE_node(input->nod_type, input->nod_count);
node->nod_arg[e_exe_procedure] = input->nod_arg[e_exe_procedure];
node->nod_arg[e_exe_inputs] = PASS1_node(request,
input->nod_arg[e_exe_inputs],
proc_flag);
if (temp = input->nod_arg[e_exe_outputs])
node->nod_arg[e_exe_outputs] =
(temp->nod_type == nod_all) ? explode_outputs(request,
request->
req_procedure) :
PASS1_node(request, temp, proc_flag);
if (!proc_flag) {
if (node->nod_arg[e_exe_inputs])
count = node->nod_arg[e_exe_inputs]->nod_count;
else
count = 0;
if (count != request->req_procedure->prc_in_count)
ERRD_post(gds_prcmismat, gds_arg_string, name->str_data, 0);
2001-05-23 15:26:42 +02:00
if (count) {
// Initialize this stack variable, and make it look like a node
std::auto_ptr<nod> desc_node(FB_NEW_RPT(*getDefaultMemoryPool(), 0) nod);
2001-05-23 15:26:42 +02:00
for (ptr = node->nod_arg[e_exe_inputs]->nod_arg,
field = request->req_procedure->prc_inputs;
field; ptr++, field = field->fld_next) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field, dsql_type_fld);
DEV_BLKCHK(*ptr, dsql_type_nod);
// MAKE_desc_from_field(&desc_node.nod_desc, field);
// set_parameter_type(*ptr, &desc_node, FALSE);
MAKE_desc_from_field(&(desc_node->nod_desc), field);
set_parameter_type(*ptr, desc_node.get(), FALSE);
2001-05-23 15:26:42 +02:00
}
}
}
break;
case nod_for_select:
node = MAKE_node(input->nod_type, input->nod_count);
node->nod_flags = input->nod_flags;
cursor = node->nod_arg[e_flp_cursor] = input->nod_arg[e_flp_cursor];
if (cursor && (procedure = request->req_ddl_node) &&
2002-06-29 08:56:51 +02:00
(procedure->nod_type == nod_def_procedure ||
procedure->nod_type == nod_redef_procedure ||
procedure->nod_type == nod_mod_procedure ||
procedure->nod_type == nod_replace_procedure ||
procedure->nod_type == nod_def_trigger ||
procedure->nod_type == nod_mod_trigger ||
procedure->nod_type == nod_replace_trigger )) {
2001-05-23 15:26:42 +02:00
cursor->nod_arg[e_cur_next] = procedure->nod_arg[e_prc_cursors];
procedure->nod_arg[e_prc_cursors] = cursor;
cursor->nod_arg[e_cur_context] = node;
}
node->nod_arg[e_flp_select] = PASS1_statement(request,
input->
nod_arg[e_flp_select],
proc_flag);
into_in = input->nod_arg[e_flp_into];
node->nod_arg[e_flp_into] = into_out =
MAKE_node(into_in->nod_type, into_in->nod_count);
ptr2 = into_out->nod_arg;
for (ptr = into_in->nod_arg, end = ptr + into_in->nod_count;
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
*ptr2++ = PASS1_node(request, *ptr, proc_flag);
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
2001-05-23 15:26:42 +02:00
}
2002-06-29 08:56:51 +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. */
node->nod_arg [e_flp_number] = (NOD) request->req_loop_number++;
node->nod_arg[e_flp_action] = PASS1_statement(request,
input->nod_arg
2001-05-23 15:26:42 +02:00
[e_flp_action],
proc_flag);
2002-06-29 08:56:51 +02:00
}
2001-05-23 15:26:42 +02:00
if (cursor && procedure
2002-06-29 08:56:51 +02:00
&& (procedure->nod_type == nod_def_procedure ||
procedure->nod_type == nod_redef_procedure ||
procedure->nod_type == nod_mod_procedure ||
procedure->nod_type == nod_replace_procedure ||
procedure->nod_type == nod_def_trigger ||
procedure->nod_type == nod_mod_trigger ||
procedure->nod_type == nod_replace_trigger))
2001-05-23 15:26:42 +02:00
procedure->nod_arg[e_prc_cursors] = cursor->nod_arg[e_cur_next];
if (request->req_error_handlers &&
(input->nod_flags & NOD_SINGLETON_SELECT)) {
temp = MAKE_node(nod_list, 3);
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;
}
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);
node->nod_arg[e_if_condition] = PASS1_node(request,
input->
nod_arg[e_if_condition],
proc_flag);
node->nod_arg[e_if_true] =
PASS1_statement(request, input->nod_arg[e_if_true], proc_flag);
if (input->nod_arg[e_if_false])
node->nod_arg[e_if_false] =
PASS1_statement(request, input->nod_arg[e_if_false],
proc_flag);
else
node->nod_arg[e_if_false] = NULL;
break;
case nod_exception_stmt:
node = input;
/* if exception value is defined,
pass value node */
if (input->nod_arg[e_xcp_msg])
{
node->nod_arg[e_xcp_msg] = PASS1_node(request,
input->nod_arg[e_xcp_msg],
proc_flag);
}
else
{
node->nod_arg[e_xcp_msg] = 0;
}
if (request->req_error_handlers)
{
2001-05-23 15:26:42 +02:00
temp = MAKE_node(nod_list, 3);
temp->nod_arg[0] = MAKE_node(nod_start_savepoint, 0);
temp->nod_arg[1] = input;
temp->nod_arg[2] = MAKE_node(nod_end_savepoint, 0);
node = temp;
}
return node;
case nod_insert:
node = pass1_insert(request, input);
if (request->req_error_handlers) {
temp = MAKE_node(nod_list, 3);
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;
}
break;
case nod_block:
if (input->nod_arg[e_blk_errs])
request->req_error_handlers++;
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;
}
else {
input->nod_count = 0;
input->nod_type = nod_list;
}
2001-05-23 15:26:42 +02:00
case nod_list:
node = MAKE_node(input->nod_type, input->nod_count);
ptr2 = node->nod_arg;
for (ptr = input->nod_arg, end = ptr + input->nod_count;
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)
*ptr2++ = PASS1_node(request, *ptr, proc_flag);
else
*ptr2++ = PASS1_statement(request, *ptr, proc_flag);
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
2001-05-23 15:26:42 +02:00
}
if (input->nod_type == nod_block && input->nod_arg[e_blk_errs])
request->req_error_handlers--;
return node;
case nod_on_error:
node = MAKE_node(input->nod_type, input->nod_count);
node->nod_arg[e_err_errs] = input->nod_arg[e_err_errs];
node->nod_arg[e_err_action] = PASS1_statement(request,
input->
nod_arg[e_err_action],
proc_flag);
return node;
case nod_post:
node = MAKE_node(input->nod_type, input->nod_count);
node->nod_arg[e_pst_event] = PASS1_node(request,
input->nod_arg[e_pst_event],
proc_flag);
return node;
case nod_exec_sql:
2002-04-04 15:53:20 +02:00
node = MAKE_node(input->nod_type, input->nod_count);
node->nod_arg[e_exec_vc] = PASS1_node(request,
input->nod_arg[e_exec_vc],
proc_flag);
return node;
2001-05-23 15:26:42 +02:00
case nod_rollback:
request->req_type = REQ_ROLLBACK;
return input;
case nod_exit:
if (request->req_flags & REQ_trigger)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_token_err, /* Token unknown */
gds_arg_gds, gds_random, gds_arg_string, "EXIT", 0);
2001-05-23 15:26:42 +02:00
return input;
2002-06-29 08:56:51 +02:00
case nod_breakleave:
if (request->req_flags & REQ_trigger)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_token_err, /* Token unknown */
gds_arg_gds, gds_random, gds_arg_string, "BREAK", 0);
2002-06-29 08:56:51 +02:00
input->nod_arg [e_break_number] = (NOD) (request->req_loop_number - 1);
return input;
2001-05-23 15:26:42 +02:00
case nod_return:
if (request->req_flags & REQ_trigger)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_token_err, /* Token unknown */
gds_arg_gds, gds_random, gds_arg_string, "SUSPEND", 0);
2001-05-23 15:26:42 +02:00
input->nod_arg[e_rtn_procedure] = request->req_ddl_node;
return input;
case nod_select:
temp = input->nod_arg[e_select_update];
node = PASS1_rse(request, input->nod_arg[e_select_expr], input->nod_arg[e_select_order],
temp ? temp->nod_arg[e_fpd_list] : NULL );
if (temp) {
2001-05-23 15:26:42 +02:00
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 (((temp = node->nod_arg[e_rse_streams])
&& (temp->nod_type == nod_union)
&& temp->nod_arg[e_rse_reduced]) ||
node->nod_arg[e_rse_sort] || node->nod_arg[e_rse_reduced])
request->req_flags &= ~REQ_no_batch;
break;
case nod_trans:
request->req_type = REQ_START_TRANS;
return input;
case nod_update:
node = pass1_update(request, input);
if (request->req_error_handlers) {
temp = MAKE_node(nod_list, 3);
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;
}
break;
case nod_while:
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);
2002-06-29 08:56:51 +02:00
/* CVC: loop numbers should be incremented before analyzing the body
to preserve nesting <==> increasing level number. */
node->nod_arg [e_while_number] = (NOD) request->req_loop_number++;
2001-05-23 15:26:42 +02:00
node->nod_arg[e_while_action] =
2002-06-29 08:56:51 +02:00
PASS1_statement(request, input->nod_arg[e_while_action],
proc_flag);
2001-05-23 15:26:42 +02:00
break;
case nod_abort:
case nod_exception:
case nod_sqlcode:
case nod_gdscode:
return input;
case nod_user_savepoint:
if (request->req_flags & REQ_procedure)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_token_err, /* Token unknown */
gds_arg_gds, gds_random, gds_arg_string, "SAVEPOINT", 0);
request->req_type = REQ_SAVEPOINT;
return input;
case nod_undo_savepoint:
if (request->req_flags & REQ_procedure)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_token_err, /* Token unknown */
gds_arg_gds, gds_random, gds_arg_string, "ROLLBACK", 0);
request->req_type = REQ_SAVEPOINT;
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:
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_union_err, /* union not supported */
2001-05-23 15:26:42 +02:00
0);
break;
default:
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_dsql_construct_err, /* Unsupported DSQL construct */
2001-05-23 15:26:42 +02:00
0);
break;
}
/* Finish off by cleaning up contexts */
while (request->req_context != base)
LLS_POP(&request->req_context);
#ifdef DEV_BUILD
if (DSQL_debug > 1)
DSQL_pretty(node, 0);
#endif
return node;
}
static BOOLEAN aggregate_found( REQ request, NOD sub, NOD * proj)
{
/**************************************
*
* a g g r e g a t e _ f o u n d
*
**************************************
*
* Functional description
* Check for an aggregate expression in an
* rse select list. It could be buried in
* an expression tree.
*
**************************************/
BOOLEAN aggregate, field;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(sub, dsql_type_nod);
2001-05-23 15:26:42 +02:00
field = FALSE;
aggregate = aggregate_found2(request, sub, proj, &field);
if (field && aggregate)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_field_aggregate_err, /* field used with aggregate */
2001-05-23 15:26:42 +02:00
0);
return aggregate;
}
static BOOLEAN aggregate_found2(
REQ request,
NOD sub, NOD * proj, BOOLEAN * field)
{
/**************************************
*
* a g g r e g a t e _ f o u n d 2
*
**************************************
*
* Functional description
* Check for an aggregate expression in an
* rse select list. It could be buried in
* an expression tree.
*
* field is true if a non-aggregate field reference is seen.
*
**************************************/
BOOLEAN aggregate;
NOD *ptr, *end;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(sub, dsql_type_nod);
2001-05-23 15:26:42 +02:00
switch (sub->nod_type) {
/* handle the simple case of a straightforward aggregate */
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:
if ((sub->nod_flags & NOD_AGG_DISTINCT) &&
(request->req_dbb->dbb_flags & DBB_v3)) {
/* NOTE: leave the v3 behaviour intact, even though it */
/* is incorrect in most cases! */
/* this aggregate requires a projection to be generated */
*proj = PASS1_node(request, sub->nod_arg[0], 0);
}
return TRUE;
case nod_field:
*field = TRUE;
return FALSE;
case nod_constant:
return FALSE;
/* for expressions in which an aggregate might
be buried, recursively check for one */
case nod_select_expr:
case nod_or:
case nod_and:
case nod_not:
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_exists:
case nod_singular:
case nod_missing:
2001-05-23 15:26:42 +02:00
case nod_add:
case nod_concatenate:
case nod_divide:
case nod_multiply:
case nod_negate:
case nod_substr:
case nod_subtract:
case nod_add2:
case nod_divide2:
case nod_multiply2:
case nod_subtract2:
case nod_upcase:
case nod_extract:
case nod_coalesce:
case nod_simple_case:
case nod_searched_case:
2001-05-23 15:26:42 +02:00
case nod_list:
aggregate = FALSE;
for (ptr = sub->nod_arg, end = ptr + sub->nod_count; ptr < end; ptr++) {
if (*ptr) {
DEV_BLKCHK(*ptr, dsql_type_nod);
aggregate |= aggregate_found2(request, *ptr, proj, field);
}
2001-05-23 15:26:42 +02:00
}
return aggregate;
case nod_cast:
case nod_udf:
case nod_gen_id:
case nod_gen_id2:
if (sub->nod_count == 2)
return (aggregate_found2(request, sub->nod_arg[1], proj, field));
else
return FALSE;
default:
return FALSE;
}
}
2002-06-29 08:56:51 +02:00
static NOD ambiguity_check (NOD node, REQ request, FLD field,
DLLS relations,DLLS procedures)
{
/**************************************
*
* a m b i g u i t y
*
**************************************
*
* Functional description
* Check for ambiguity in a field
* reference. We've got these nice lists
* of procedures and relations and if there
* is more than one, things are bad.
*
**************************************/
TEXT buffer[1024], *b, *p;
DSQL_REL relation;
PRC procedure;
USHORT loop;
buffer[0] = 0;
b = buffer;
p = 0;
loop = 0;
if ((relations && relations->lls_next)
||(procedures && procedures->lls_next)
||(procedures && relations))
/* nothing to do */;
else {
if (relations)
LLS_POP (&relations);
if (procedures)
LLS_POP (&procedures);
return node;
}
while ((relations) && (relation = (DSQL_REL) LLS_POP (&relations))) {
if (strlen (b) > (sizeof (buffer) - 50))
continue;
if (++loop > 2)
strcat (buffer, "and ");
if (!(relation->rel_flags & REL_view))
strcat (buffer, "table ");
else
strcat (buffer, "view ");
strcat (buffer, relation->rel_name);
strcat (buffer, " ");
if (!p)
p = b + strlen (b);
}
while ((procedures) && (procedure = (PRC) LLS_POP (&procedures))) {
if (strlen (b) > (sizeof (buffer) - 50))
continue;
if (++loop > 2)
strcat (buffer, "and ");
strcat (b, "procedure ");
strcat (b, procedure->prc_name);
strcat (b, " ");
if (!p)
p = b + strlen (b);
}
if (p)
*--p = 0;
if (request->req_client_dialect >= SQL_DIALECT_V6) {
if (node) {
delete node;
}
ERRD_post (gds_sqlerr, gds_arg_number, (SLONG) -204,
gds_arg_gds, isc_dsql_ambiguous_field_name,
gds_arg_string, buffer,
gds_arg_string, ++p,
gds_arg_gds, gds_random,
gds_arg_string, field->fld_name,
0);
return NULL;
}
ERRD_post_warning (isc_sqlwarn, gds_arg_number, (SLONG) 204,
gds_arg_warning, isc_dsql_ambiguous_field_name,
gds_arg_string, buffer,
gds_arg_string, ++p,
gds_arg_gds, gds_random,
gds_arg_string, field->fld_name,
0);
return node;
}
2001-05-23 15:26:42 +02:00
static void assign_fld_dtype_from_dsc( FLD field, DSC * nod_desc)
{
/**************************************
*
* a s s i g n _ f l d _ d t y p e _ f r o m _ d s c
*
**************************************
*
* Functional description
* Set a field's descriptor from a DSC
* (If FLD is ever redefined this can be removed)
*
**************************************/
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;
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)
field->fld_character_set_id = nod_desc->dsc_scale;
}
static NOD compose( NOD expr1, NOD expr2, NOD_TYPE operator_)
{
/**************************************
*
* c o m p o s e
*
**************************************
*
* Functional description
* Compose two booleans.
*
**************************************/
NOD node;
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;
node = MAKE_node(operator_, 2);
node->nod_arg[0] = expr1;
node->nod_arg[1] = expr2;
return node;
}
static NOD copy_field( NOD field, CTX context)
{
/**************************************
*
* c o p y _ f i e l d
*
**************************************
*
* Functional description
* Copy a field list for a SELECT, ORDER BY
* against an artificial context.
2001-05-23 15:26:42 +02:00
*
**************************************/
NOD temp, actual, *ptr, *end, *ptr2;
STR alias;
CTX lcontext;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field, dsql_type_nod);
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
switch (field->nod_type) {
case nod_alias:
actual = field->nod_arg[e_alias_value];
alias = (STR) field->nod_arg[e_alias_alias];
DEV_BLKCHK(alias, dsql_type_str);
temp = MAKE_node(nod_alias, e_alias_count);
temp->nod_arg[e_alias_value] = copy_field(actual, context);
temp->nod_arg[e_alias_alias] = (NOD) alias;
return temp;
case nod_field:
lcontext = reinterpret_cast<CTX>(field->nod_arg[e_fld_context]);
if (lcontext->ctx_scope_level == context->ctx_scope_level) {
return post_map(field, context);
}
else {
return field;
}
case nod_via:
temp = MAKE_node(field->nod_type, field->nod_count);
temp->nod_arg[e_via_rse] = copy_field(field->nod_arg[e_via_rse], context);
temp->nod_arg[e_via_value_1] = temp->nod_arg[e_via_rse]->nod_arg[e_rse_items]->nod_arg[0];
temp->nod_arg[e_via_value_2] = field->nod_arg[e_via_value_2];
return temp;
case nod_rse:
temp = MAKE_node(field->nod_type, field->nod_count);
temp->nod_arg[e_rse_streams] = copy_field(field->nod_arg[e_rse_streams], context);
if (field->nod_arg[e_rse_boolean]) {
temp->nod_arg[e_rse_boolean] = copy_field(field->nod_arg[e_rse_boolean], context);
}
temp->nod_arg[e_rse_sort] = field->nod_arg[e_rse_sort];
temp->nod_arg[e_rse_reduced] = field->nod_arg[e_rse_reduced];
if (field->nod_arg[e_rse_items]) {
temp->nod_arg[e_rse_items] = copy_field(field->nod_arg[e_rse_items], context);
}
temp->nod_arg[e_rse_first] = field->nod_arg[e_rse_first];
temp->nod_arg[e_rse_singleton] = field->nod_arg[e_rse_singleton];
temp->nod_arg[e_rse_plan] = field->nod_arg[e_rse_plan];
temp->nod_arg[e_rse_skip] = field->nod_arg[e_rse_skip];
return temp;
case nod_coalesce:
case nod_simple_case:
case nod_searched_case:
temp = MAKE_node(field->nod_type, field->nod_count);
temp->nod_desc = field->nod_desc;
ptr2 = temp->nod_arg;
for (ptr = field->nod_arg, end = ptr + field->nod_count; ptr < end; ptr++) {
*ptr2++ = copy_field(*ptr, context);
}
return temp;
case nod_aggregate:
temp = MAKE_node(field->nod_type, field->nod_count);
temp->nod_arg[e_agg_context] = field->nod_arg[e_agg_context];
temp->nod_arg[e_agg_group] = field->nod_arg[e_agg_group];
temp->nod_arg[e_agg_rse] = copy_field(field->nod_arg[e_agg_rse], context);
return temp;
case nod_or:
case nod_and:
case nod_not:
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_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:
case nod_upcase:
case nod_internal_info:
case nod_extract:
case nod_list:
temp = MAKE_node(field->nod_type, field->nod_count);
ptr2 = temp->nod_arg;
for (ptr = field->nod_arg, end = ptr + field->nod_count; ptr < end; ptr++) {
*ptr2++ = copy_field(*ptr, context);
}
return temp;
case nod_cast:
case nod_gen_id:
case nod_gen_id2:
case nod_udf:
temp = MAKE_node(field->nod_type, field->nod_count);
temp->nod_arg[0] = field->nod_arg[0];
if (field->nod_count == 2) {
temp->nod_arg[1] = copy_field(field->nod_arg[1], context);
}
return temp;
default:
return field;
2001-05-23 15:26:42 +02:00
}
}
static NOD copy_fields( NOD fields, CTX context)
{
/**************************************
*
* c o p y _ f i e l d s
*
**************************************
*
* Functional description
* Copy a field list for a SELECT against an artificial context.
*
**************************************/
NOD list;
USHORT i;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(fields, dsql_type_nod);
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
list = MAKE_node(nod_list, fields->nod_count);
for (i = 0; i < fields->nod_count; i++) {
2001-05-23 15:26:42 +02:00
list->nod_arg[i] = copy_field(fields->nod_arg[i], context);
}
2001-05-23 15:26:42 +02:00
return list;
}
2001-12-24 03:51:06 +01:00
static void explode_asterisk( NOD node, NOD aggregate, DLLS * stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p l o d e _ a s t e r i s k
*
**************************************
*
* Functional description
* Expand an '*' in a field list to the corresponding fields.
*
**************************************/
CTX context;
DSQL_REL relation;
PRC procedure;
FLD field;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(node, dsql_type_nod);
DEV_BLKCHK(aggregate, dsql_type_nod);
2001-05-23 15:26:42 +02:00
if (node->nod_type == nod_join) {
explode_asterisk(node->nod_arg[e_join_left_rel], aggregate, stack);
explode_asterisk(node->nod_arg[e_join_rght_rel], aggregate, stack);
}
else {
context = (CTX) node->nod_arg[e_rel_context];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
if (relation = context->ctx_relation)
for (field = relation->rel_fields; field; field = field->fld_next) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field, dsql_type_fld);
2001-05-23 15:26:42 +02:00
node = MAKE_field(context, field, 0);
if (aggregate) {
if (invalid_reference
(NULL, node,
2001-05-23 15:26:42 +02:00
aggregate->
nod_arg[e_agg_group], FALSE)) ERRD_post(gds_sqlerr,
gds_arg_number,
(SLONG) - 104,
gds_arg_gds,
gds_field_ref_err,
/* invalid field reference */
0);
2001-05-23 15:26:42 +02:00
}
LLS_PUSH(MAKE_field(context, field, 0), stack);
}
else if (procedure = context->ctx_procedure)
for (field = procedure->prc_outputs; field;
2001-12-24 03:51:06 +01:00
field = field->fld_next) {DEV_BLKCHK(field, dsql_type_fld);
2001-05-23 15:26:42 +02:00
node = MAKE_field(context, field, 0);
if (aggregate) {
if (invalid_reference
(NULL, node,
2001-05-23 15:26:42 +02:00
aggregate->
nod_arg[e_agg_group], FALSE)) ERRD_post(gds_sqlerr,
gds_arg_number,
(SLONG) - 104,
gds_arg_gds,
gds_field_ref_err,
/* invalid field reference */
0);
2001-05-23 15:26:42 +02:00
}
LLS_PUSH(MAKE_field(context, field, 0), stack);
}
}
}
static NOD explode_outputs( REQ request, PRC procedure)
{
/**************************************
*
* e x p l o d e _ o u t p u t s
*
**************************************
*
* Functional description
* Generate a parameter list to correspond to procedure outputs.
*
**************************************/
NOD node, *ptr, p_node;
FLD field;
PAR parameter;
SSHORT count;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(procedure, dsql_type_prc);
2001-05-23 15:26:42 +02:00
count = procedure->prc_out_count;
node = MAKE_node(nod_list, count);
for (field = procedure->prc_outputs, ptr = node->nod_arg; field;
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);
2001-05-23 15:26:42 +02:00
*ptr = p_node = MAKE_node(nod_parameter, e_par_count);
p_node->nod_count = 0;
parameter = MAKE_parameter(request->req_receive, TRUE, TRUE);
p_node->nod_arg[e_par_parameter] = (NOD) parameter;
MAKE_desc_from_field(&parameter->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;
}
2002-06-29 08:56:51 +02:00
static void field_error( TEXT * qualifier_name, TEXT * field_name, NOD flawed_node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f i e l d _ e r r o r
*
**************************************
*
* Functional description
* Report a field parsing recognition error.
*
**************************************/
2002-06-29 08:56:51 +02:00
TEXT field_string [64], linecol [64];
2001-05-23 15:26:42 +02:00
if (qualifier_name) {
2002-07-06 07:32:02 +02:00
sprintf(field_string, "%.31s.%.31s", qualifier_name,
2001-05-23 15:26:42 +02:00
field_name ? field_name : "*");
field_name = field_string;
}
2002-06-29 08:56:51 +02:00
if (flawed_node)
sprintf (linecol, "At line %d, column %d.",
(int) flawed_node->nod_line, (int) flawed_node->nod_column);
else
sprintf (linecol, "At unknown line and column.");
2001-05-23 15:26:42 +02:00
if (field_name)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 206,
gds_arg_gds, gds_dsql_field_err,
2002-06-29 08:56:51 +02:00
gds_arg_gds, gds_random, gds_arg_string, field_name,
gds_arg_gds, gds_random,
gds_arg_string, linecol,
0);
2001-05-23 15:26:42 +02:00
else
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 206,
2002-06-29 08:56:51 +02:00
gds_arg_gds, gds_dsql_field_err,
gds_arg_gds, gds_random,
gds_arg_string, linecol,
0);
2001-05-23 15:26:42 +02:00
}
static PAR find_dbkey( REQ request, NOD relation_name)
{
/**************************************
*
* f i n d _ d b k e y
*
**************************************
*
* Functional description
* Find dbkey for named relation in request's saved dbkeys.
*
**************************************/
CTX context;
MSG message;
PAR parameter, candidate;
DSQL_REL relation;
STR rel_name;
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
message = request->req_receive;
candidate = NULL;
rel_name = (STR) relation_name->nod_arg[e_rln_name];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(rel_name, dsql_type_str);
2001-05-23 15:26:42 +02:00
for (parameter = message->msg_parameters; parameter;
parameter = parameter->par_next) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(parameter, dsql_type_par);
2001-05-23 15:26:42 +02:00
if (context = parameter->par_dbkey_ctx) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
relation = context->ctx_relation;
if (!strcmp
(reinterpret_cast < char *>(rel_name->str_data),
relation->rel_name)) {
if (candidate)
return NULL;
else
candidate = parameter;
}
}
}
return candidate;
}
static PAR find_record_version( REQ request, NOD relation_name)
{
/**************************************
*
* f i n d _ r e c o r d _ v e r s i o n
*
**************************************
*
* Functional description
* Find record version for relation in request's saved record version
*
**************************************/
CTX context;
MSG message;
PAR parameter, candidate;
DSQL_REL relation;
STR rel_name;
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
message = request->req_receive;
candidate = NULL;
rel_name = (STR) relation_name->nod_arg[e_rln_name];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(rel_name, dsql_type_str);
2001-05-23 15:26:42 +02:00
for (parameter = message->msg_parameters; parameter;
parameter = parameter->par_next) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(parameter, dsql_type_par);
2001-05-23 15:26:42 +02:00
if (context = parameter->par_rec_version_ctx) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
relation = context->ctx_relation;
if (!strcmp
(reinterpret_cast < char *>(rel_name->str_data),
relation->rel_name)) {
if (candidate)
return NULL;
else
candidate = parameter;
}
}
}
return candidate;
}
static BOOLEAN invalid_reference(REQ request, NOD node, NOD list, BOOLEAN exact_field)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n v a l i d _ r e f e r e n c e
*
**************************************
*
* Functional description
* Validate that an expanded field / context
* pair is in a specified list. Thus is used
* in one instance to check that a simple field selected
* through a grouping rse is a grouping field -
* thus a valid field reference. For the sake of
* argument, we'll match qualified to unqualified
* reference, but qualified reference must match
* completely.
*
* A list element containing a simple CAST for collation purposes
* is allowed.
*
**************************************/
NOD *ptr, *end;
BOOLEAN invalid;
CTX field_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(list, dsql_type_nod);
2001-05-23 15:26:42 +02:00
if (node == NULL) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
invalid = FALSE;
if (list) {
/* Check if this node (with ignoring of CASTs) appear also
in the list of group by. If yes then it's allowed */
for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++) {
if (node_match(node, *ptr, TRUE)) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
}
}
2002-06-29 08:56:51 +02:00
2001-05-23 15:26:42 +02:00
#ifdef CHECK_HAVING
/*
******************************************************
taken out to fix a more serious bug
******************************************************
*/
if (node->nod_type == nod_map) {
MAP map;
map = (MAP) node->nod_arg[e_map_map];
DEV_BLKCHK(map, dsql_type_map);
invalid |= invalid_reference(map->map_node, list);
}
else
2001-05-23 15:26:42 +02:00
#endif
switch (node->nod_type) {
2001-05-23 15:26:42 +02:00
default:
ASSERT_FAIL;
/* FALLINTO */
case nod_field:
/* Wouldn't it be better to call a error from this
point where return is TRUE. Then we could give
the fieldname that's making the trouble */
if (!exact_field || !request) {
return TRUE;
}
/* If we come here then this Field is used inside a
sub-select, singular- or exists-predicate.
The ctx_scope_level gives the info how deep the
context is inside the request.
So if the context-scope-level from the field is
lower as the scope-level from the request then
it is an invalid field */
field_context = (CTX) node->nod_arg[e_fld_context];
DEV_BLKCHK(field_context, dsql_type_ctx);
if (field_context->ctx_scope_level <= request->req_scope_level) {
return TRUE;
}
break;
case nod_gen_id:
case nod_gen_id2:
case nod_cast:
case nod_udf:
/* If there are no arguments given to the UDF then it's always valid */
if (node->nod_count == 2) {
invalid |= invalid_reference(request, node->nod_arg [1], list, exact_field);
}
break;
case nod_via:
case nod_exists:
case nod_singular:
//return FALSE;
for (ptr = node->nod_arg, end = ptr + node->nod_count;
ptr < end; ptr++) {
invalid |= invalid_reference(request, *ptr, list, TRUE);
}
break;
case nod_parameter:
return FALSE;
case nod_coalesce:
case nod_simple_case:
case nod_searched_case:
2001-05-23 15:26:42 +02:00
case nod_add:
case nod_concatenate:
case nod_divide:
case nod_multiply:
case nod_negate:
case nod_substr:
case nod_subtract:
case nod_upcase:
case nod_extract:
case nod_add2:
case nod_divide2:
case nod_multiply2:
case nod_subtract2:
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:
case nod_list:
for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; ptr++) {
invalid |= invalid_reference(request, *ptr, list, exact_field);
}
2001-05-23 15:26:42 +02:00
break;
case nod_alias:
invalid |=
invalid_reference(request, node->nod_arg[e_alias_value], list, exact_field);
2001-05-23 15:26:42 +02:00
break;
/* An embedded aggregate, even of an expression, is OK */
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:
/* Fall into ... */
/* misc other stuff */
case nod_aggregate:
case nod_map:
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:
2002-06-29 08:56:51 +02:00
case nod_current_role:
case nod_internal_info:
case nod_proc_internal_info:
2001-05-23 15:26:42 +02:00
return FALSE;
}
2001-05-23 15:26:42 +02:00
return invalid;
}
static void mark_ctx_outer_join( NOD node)
{
/**************************************
*
* m a r k _ c t x _ o u t e r _ j o i n
*
**************************************
*
* Functional description
* Mark the context blocks of relations in an RSE as
* participating in an Outer Join of some sort.
* This is important when we are deciding whether
* a particular field reference can be NULL or not.
* If the field is declared NOT NULL, it normally cannot
* be NULL - however, if the base relation reference is
* within the "outside" part of an outer join rse,
* it CAN be null.
*
* Our input RSE can be either a relation (table, view, or proc)
* reference or a JOIN expression.
*
**************************************/
CTX context;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(node, dsql_type_nod);
2001-05-23 15:26:42 +02:00
switch (node->nod_type) {
case nod_relation:
context = (CTX) node->nod_arg[e_rel_context];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
assert(context);
context->ctx_flags |= CTX_outer_join;
break;
case nod_join:
mark_ctx_outer_join(node->nod_arg[e_join_left_rel]);
mark_ctx_outer_join(node->nod_arg[e_join_rght_rel]);
break;
default:
ASSERT_FAIL; /* only join & relation expected */
break;
}
}
static BOOLEAN node_match( NOD node1, NOD node2, BOOLEAN ignore_cast)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n o d e _ m a t c h
*
**************************************
*
* Functional description
* Compare two nodes for equality of value.
*
* [2002-08-04]--- Arno Brinkman
* If ignore_cast is TRUE and the node1 is of
* type nod_cast then node_match is calling
* itselfs again with the node1 CASTs source.
* This is for allow CAST to other datatypes
* without complaining that it's an unknown
* column reference. (Aggeregate functions)
*
2001-05-23 15:26:42 +02:00
**************************************/
NOD *ptr1, *ptr2, *end;
MAP map1, map2;
USHORT l;
UCHAR *p1, *p2;
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
if ((!node1) && (!node2)) {
2001-05-23 15:26:42 +02:00
return TRUE;
}
if ((!node1) || (!node2)) {
return FALSE;
}
if (ignore_cast && node1->nod_type == nod_cast) {
/* If node2 is also cast and same type continue with both sources */
if (node2->nod_type == nod_cast &&
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 &&
node1->nod_desc.dsc_sub_type == node2->nod_desc.dsc_sub_type) {
return node_match(node1->nod_arg[e_cast_source], node2->nod_arg[e_cast_source], ignore_cast);
}
else {
return node_match(node1->nod_arg[e_cast_source], node2, ignore_cast);
}
}
2001-05-23 15:26:42 +02:00
/* We don't care about the alias self only about his field */
if ((node1->nod_type == nod_alias) || (node2->nod_type == nod_alias)) {
if ((node1->nod_type == nod_alias) && (node2->nod_type == nod_alias)) {
return node_match(node1->nod_arg[e_alias_value],
node2->nod_arg[e_alias_value], ignore_cast);
}
else {
if (node1->nod_type == nod_alias) {
return node_match(node1->nod_arg[e_alias_value], node2, ignore_cast);
}
if (node2->nod_type == nod_alias) {
return node_match(node1, node2->nod_arg[e_alias_value], ignore_cast);
}
}
}
if ((node1->nod_type != node2->nod_type) || (node1->nod_count != node2->nod_count)) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
2001-05-23 15:26:42 +02:00
/* This is to get rid of assertion failures when trying
to node_match nod_aggregate's children. This was happening because not
all of the chilren are of type "struct nod". Pointer to the first child
(argument) is actually a pointer to context structure.
To compare two nodes of type nod_aggregate we need first to see if they
both refer to same context structure. If they do not they are different
nodes, if they point to the same context they are the same (because
nod_aggregate is created for an rse that have aggregate expression,
group by or having clause and each rse has its own context). But just in
case we compare two other subtrees.
*/
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]) {
return FALSE;
}
return node_match( node1->nod_arg[e_agg_group],
node2->nod_arg[e_agg_group], ignore_cast) &&
2001-05-23 15:26:42 +02:00
node_match( node1->nod_arg[e_agg_rse],
node2->nod_arg[e_agg_rse], ignore_cast);
}
if (node1->nod_type == nod_relation) {
if (node1->nod_arg[e_rel_context] != node2->nod_arg[e_rel_context]) {
return FALSE;
}
return TRUE;
2001-05-23 15:26:42 +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] ||
node1->nod_arg[e_fld_context] != node2->nod_arg[e_fld_context]) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
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],
node2->nod_arg[e_fld_indices], ignore_cast);
2001-05-23 15:26:42 +02:00
}
return TRUE;
}
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 ||
node1->nod_desc.dsc_scale != node2->nod_desc.dsc_scale) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
p1 = node1->nod_desc.dsc_address;
p2 = node2->nod_desc.dsc_address;
for (l = node1->nod_desc.dsc_length; l > 0; l--) {
if (*p1++ != *p2++) {
return FALSE;
}
}
return TRUE;
}
if (node1->nod_type == nod_map) {
2001-05-23 15:26:42 +02:00
map1 = (MAP)node1->nod_arg[e_map_map];
map2 = (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);
return node_match(map1->map_node, map2->map_node, ignore_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) ||
(node1->nod_type == nod_cast)) {
2001-05-23 15:26:42 +02:00
if (node1->nod_arg[0] != node2->nod_arg[0]) {
return FALSE;
}
if (node1->nod_count == 2) {
return node_match(node1->nod_arg[1], node2->nod_arg[1], ignore_cast);
2001-05-23 15:26:42 +02:00
} else {
return TRUE;
}
}
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) ||
(node1->nod_type == nod_agg_average)) {
2001-05-23 15:26:42 +02:00
if ((node1->nod_flags & NOD_AGG_DISTINCT) !=
(node2->nod_flags & NOD_AGG_DISTINCT)) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
}
if (node1->nod_type == nod_variable) {
2001-05-23 15:26:42 +02:00
if (node1->nod_type != node2->nod_type) {
return FALSE;
}
VAR var1 = reinterpret_cast<VAR>(node1->nod_arg[e_var_variable]);
VAR var2 = reinterpret_cast<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) ||
(var1->var_msg_number != var2->var_msg_number)) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
return TRUE;
}
ptr1 = node1->nod_arg;
ptr2 = node2->nod_arg;
for (end = ptr1 + node1->nod_count; ptr1 < end; ptr1++, ptr2++) {
if (!node_match(*ptr1, *ptr2, ignore_cast)) {
2001-05-23 15:26:42 +02:00
return FALSE;
}
}
2001-05-23 15:26:42 +02:00
return TRUE;
}
static NOD pass1_any( REQ request, NOD input, NOD_TYPE ntype)
{
/**************************************
*
* p a s s 1 _ a n y
*
**************************************
*
* Functional description
* Compile a parsed request into something more interesting.
*
**************************************/
NOD node, temp, rse, select;
2001-12-24 03:51:06 +01:00
DLLS base;
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
select = input->nod_arg[1];
base = request->req_context;
temp = MAKE_node(input->nod_type, 2);
temp->nod_arg[0] = PASS1_node(request, input->nod_arg[0], 0);
node = MAKE_node(ntype, 1);
node->nod_arg[0] = rse = PASS1_rse(request, select, NULL, NULL);
2001-05-23 15:26:42 +02:00
/* adjust the scope level back to the sub-rse, so that
the fields in the select list will be properly recognized */
request->req_scope_level++;
request->req_in_select_list++;
temp->nod_arg[1] =
PASS1_node(request, select->nod_arg[e_sel_list]->nod_arg[0], 0);
request->req_in_select_list--;
request->req_scope_level--;
rse->nod_arg[e_rse_boolean] =
compose(rse->nod_arg[e_rse_boolean], temp, nod_and);
while (request->req_context != base)
LLS_POP(&request->req_context);
return node;
}
static void pass1_blob( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ b l o b
*
**************************************
*
* Functional description
* Process a blob get or put segment.
*
**************************************/
NOD field, list;
BLB blob;
PAR parameter;
TSQL tdsql;
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
tdsql = GET_THREAD_DATA;
(void) PASS1_make_context(request, input->nod_arg[e_blb_relation]);
field = pass1_field(request, input->nod_arg[e_blb_field], 0);
if (field->nod_desc.dsc_dtype != dtype_blob)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 206,
gds_arg_gds, gds_dsql_blob_err, 0);
2001-05-23 15:26:42 +02:00
request->req_type =
(input->nod_type ==
nod_get_segment) ? REQ_GET_SEGMENT : REQ_PUT_SEGMENT;
request->req_blob = blob = FB_NEW(*tdsql->tsql_default) blb;
2001-05-23 15:26:42 +02:00
blob->blb_field = field;
blob->blb_open_in_msg = request->req_send;
blob->blb_open_out_msg = FB_NEW(*tdsql->tsql_default) msg;
2001-05-23 15:26:42 +02:00
blob->blb_segment_msg = request->req_receive;
/* Create a parameter for the blob segment */
blob->blb_segment = parameter =
MAKE_parameter(blob->blb_segment_msg, TRUE, TRUE);
parameter->par_desc.dsc_dtype = dtype_text;
parameter->par_desc.dsc_ttype = ttype_binary;
parameter->par_desc.dsc_length =
((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;
/* Create a parameter for the blob id */
blob->blb_blob_id = parameter = MAKE_parameter(
(input->nod_type ==
nod_get_segment) ? blob->
blb_open_in_msg : blob->
blb_open_out_msg, TRUE,
TRUE);
parameter->par_desc = field->nod_desc;
parameter->par_desc.dsc_dtype = dtype_quad;
parameter->par_desc.dsc_scale = 0;
if (list = input->nod_arg[e_blb_filter]) {
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);
}
if (!blob->blb_from)
blob->blb_from = MAKE_constant((STR) 0, 1);
if (!blob->blb_to)
blob->blb_to = MAKE_constant((STR) 0, 1);
for (parameter = blob->blb_open_in_msg->msg_parameters; parameter;
parameter = parameter->par_next)
if (parameter->par_index >
((input->nod_type == nod_get_segment) ? 1 : 0)) {
parameter->par_desc.dsc_dtype = dtype_short;
parameter->par_desc.dsc_scale = 0;
parameter->par_desc.dsc_length = sizeof(SSHORT);
}
}
static NOD pass1_coalesce( REQ request, NOD input, USHORT proc_flag)
{
/**************************************
*
* p a s s 1 _ c o a l e s c e
*
**************************************
*
* Functional description
* Handle a reference to a coalesce function.
*
**************************************/
NOD node, *ptr, *end;
DLLS stack;
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(input, dsql_type_nod);
DEV_BLKCHK(input->nod_arg[0], dsql_type_nod);
node = MAKE_node(nod_coalesce, 1);
/* Pass list of arguments 2..n on stack and make a list from it */
stack = NULL;
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);
2002-10-19 02:56:25 +02:00
/* Set descriptor for output node */
MAKE_desc(&node->nod_desc, node);
/* Set parameter-types if parameters are there */
for (ptr = node->nod_arg[0]->nod_arg, end = ptr + node->nod_arg[0]->nod_count;
2002-10-19 02:56:25 +02:00
ptr < end; ptr++) {
set_parameter_type(*ptr, node, FALSE);
}
return node;
}
2001-05-23 15:26:42 +02:00
static NOD pass1_collate( REQ request, NOD sub1, STR collation)
{
/**************************************
*
* p a s s 1 _ c o l l a t e
*
**************************************
*
* Functional description
* 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)
*
**************************************/
NOD node;
FLD field;
TSQL tdsql;
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
tdsql = GET_THREAD_DATA;
node = MAKE_node(nod_cast, e_cast_count);
field = FB_NEW_RPT(*tdsql->tsql_default, 1) fld;
2001-05-23 15:26:42 +02:00
field->fld_name[0] = 0;
node->nod_arg[e_cast_target] = (NOD) field;
node->nod_arg[e_cast_source] = sub1;
MAKE_desc(&sub1->nod_desc, sub1);
if (sub1->nod_desc.dsc_dtype <= dtype_any_text) {
assign_fld_dtype_from_dsc(field, &sub1->nod_desc);
field->fld_character_length = 0;
if (sub1->nod_desc.dsc_dtype == dtype_varying)
field->fld_length += sizeof(USHORT);
}
else {
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 204,
gds_arg_gds, gds_dsql_datatype_err,
gds_arg_gds, gds_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;
}
static NOD pass1_constant( REQ request, NOD constant)
{
/**************************************
*
* p a s s 1 _ c o n s t a n t
*
**************************************
*
* Functional description
* Turn an international string reference into internal
* subtype ID.
*
**************************************/
STR string;
INTLSYM resolved;
INTLSYM resolved_collation = NULL;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(constant, dsql_type_nod);
2001-05-23 15:26:42 +02:00
if (constant->nod_desc.dsc_dtype > dtype_any_text)
return constant;
string = (STR) constant->nod_arg[0];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(string, dsql_type_str);
2001-05-23 15:26:42 +02:00
if (!string || !string->str_charset)
return constant;
resolved =
METD_get_charset(request, strlen(string->str_charset),
reinterpret_cast < UCHAR * >(string->str_charset));
if (!resolved)
/* character set name is not defined */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 504, gds_arg_gds,
gds_charset_not_found, gds_arg_string, string->str_charset,
2001-05-23 15:26:42 +02:00
0);
if (temp_collation_name) {
resolved_collation = METD_get_collation(request, temp_collation_name);
if (!resolved_collation)
/*
** Specified collation not found
*/
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 204, gds_arg_gds,
gds_dsql_datatype_err, gds_arg_gds,
gds_collation_not_found, gds_arg_string,
2001-05-23 15:26:42 +02:00
temp_collation_name->str_data, 0);
resolved = resolved_collation;
}
INTL_ASSIGN_TTYPE(&constant->nod_desc, resolved->intlsym_ttype);
return constant;
}
static NOD pass1_cursor( REQ request, NOD cursor, NOD relation_name)
{
/**************************************
*
* p a s s 1 _ c u r s o r
*
**************************************
*
* Functional description
* Turn a cursor reference into a record selection expression.
*
**************************************/
SYM symbol;
NOD rse, node, temp, relation_node;
REQ parent;
STR string;
PAR parameter, source, rv_source;
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
/* Lookup parent request */
string = (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
symbol =
HSHD_lookup(request->req_dbb,
reinterpret_cast < char *>(string->str_data),
static_cast < SSHORT > (string->str_length), SYM_cursor,
0);
if (!symbol)
/* cursor is not defined */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 504,
gds_arg_gds, gds_dsql_cursor_err, 0);
2001-05-23 15:26:42 +02:00
parent = (REQ) symbol->sym_object;
/* Verify that the cursor is appropriate and updatable */
rv_source = find_record_version(parent, relation_name);
if (parent->req_type != REQ_SELECT_UPD ||
!(source = find_dbkey(parent, relation_name)) ||
(!rv_source && !(request->req_dbb->dbb_flags & DBB_v3)))
/* cursor is not updatable */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 510,
gds_arg_gds, gds_dsql_cursor_update_err, 0);
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;
/* Build record selection expression */
rse = MAKE_node(nod_rse, e_rse_count);
rse->nod_arg[e_rse_streams] = temp = MAKE_node(nod_list, 1);
temp->nod_arg[0] = relation_node = pass1_relation(request, relation_name);
rse->nod_arg[e_rse_boolean] = node = MAKE_node(nod_eql, 2);
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;
parameter = request->req_dbkey =
MAKE_parameter(request->req_send, FALSE, FALSE);
temp->nod_arg[e_par_parameter] = (NOD) parameter;
parameter->par_desc = source->par_desc;
/* record version will be set only for V4 - for the parent select cursor */
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 =
MAKE_parameter(request->req_send, FALSE, FALSE);
temp->nod_arg[e_par_parameter] = (NOD) parameter;
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;
}
static CTX pass1_cursor_context( REQ request, NOD cursor, NOD relation_name)
{
/**************************************
*
* p a s s 1 _ c u r s o r _ c o n t e x t
*
**************************************
*
* Functional description
* Turn a cursor reference into a record selection expression.
*
**************************************/
NOD node, temp, *ptr, *end, procedure, r_node;
CTX context, candidate;
STR string, cname, rname;
DSQL_REL relation;
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
string = (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
procedure = request->req_ddl_node;
context = NULL;
for (node = procedure->nod_arg[e_prc_cursors]; node;
node = node->nod_arg[e_cur_next]) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(node, dsql_type_nod);
2001-05-23 15:26:42 +02:00
cname = (STR) node->nod_arg[e_cur_name];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(cname, dsql_type_str);
2001-05-23 15:26:42 +02:00
if (!strcmp
(reinterpret_cast < const char *>(string->str_data),
reinterpret_cast < const char *>(cname->str_data))) {
temp = node->nod_arg[e_cur_context];
temp = temp->nod_arg[e_flp_select];
temp = temp->nod_arg[e_rse_streams];
for (ptr = temp->nod_arg, end = ptr + temp->nod_count;
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
r_node = *ptr;
if (r_node->nod_type == nod_relation) {
candidate = (CTX) r_node->nod_arg[e_rel_context];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(candidate, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
relation = candidate->ctx_relation;
rname = (STR) relation_name->nod_arg[e_rln_name];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(rname, dsql_type_str);
2001-05-23 15:26:42 +02:00
if (!strcmp
(reinterpret_cast < const char *>(rname->str_data),
relation->rel_name)) {
if (context)
ERRD_post(gds_sqlerr, gds_arg_number,
2001-05-23 15:26:42 +02:00
(SLONG) - 504, gds_arg_gds,
gds_dsql_cursor_err, 0);
2001-05-23 15:26:42 +02:00
else
context = candidate;
}
}
}
if (context)
return context;
else
break;
}
}
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 504,
gds_arg_gds, gds_dsql_cursor_err, 0);
2001-05-23 15:26:42 +02:00
return (NULL); /* Added to remove compiler warnings */
}
static NOD pass1_dbkey( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ d b k e y
*
**************************************
*
* Functional description
* Resolve a dbkey to an available context.
*
**************************************/
NOD node, rel_node;
STR qualifier;
2001-12-24 03:51:06 +01:00
DLLS stack;
2001-05-23 15:26:42 +02:00
CTX context;
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
if (!(qualifier = (STR) input->nod_arg[0])) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(qualifier, dsql_type_str);
2001-05-23 15:26:42 +02:00
/* No qualifier, if only one context then use, else error */
if ((stack = request->req_context) && !stack->lls_next)
{
context = (CTX) stack->lls_object;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
node = MAKE_node(nod_dbkey, 1);
rel_node = MAKE_node(nod_relation, e_rel_count);
rel_node->nod_arg[0] = (NOD) context;
node->nod_arg[0] = rel_node;
return node;
}
}
else {
for (stack = request->req_context; stack; stack = stack->lls_next)
{
context = (CTX) stack->lls_object;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
if ((!(context->ctx_relation) ||
strcmp(reinterpret_cast < const char *>(qualifier->str_data),
context->ctx_relation->rel_name)) &&
(!(context->ctx_alias)||
strcmp(reinterpret_cast<const char*>(qualifier->str_data),
context->ctx_alias)))
{
continue;
}
node = MAKE_node(nod_dbkey, 1);
rel_node = MAKE_node(nod_relation, e_rel_count);
rel_node->nod_arg[0] = (NOD) context;
node->nod_arg[0] = rel_node;
return node;
}
}
/* field unresolved */
field_error(reinterpret_cast <
2002-06-29 08:56:51 +02:00
char *>(qualifier ? qualifier->str_data : 0), DB_KEY_STRING, input);
2001-05-23 15:26:42 +02:00
return NULL;
}
static NOD pass1_delete( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ d e l e t e
*
**************************************
*
* Functional description
* Process INSERT statement.
*
**************************************/
NOD rse, node, temp, cursor, relation;
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
cursor = input->nod_arg[e_del_cursor];
relation = input->nod_arg[e_del_relation];
if (cursor && (request->req_flags & REQ_procedure)) {
node = MAKE_node(nod_erase_current, e_erc_count);
node->nod_arg[e_erc_context] =
(NOD) pass1_cursor_context(request, cursor, relation);
return node;
}
request->req_type = (cursor) ? REQ_DELETE_CURSOR : REQ_DELETE;
node = MAKE_node(nod_erase, e_era_count);
/* Generate record selection expression */
if (cursor)
rse = pass1_cursor(request, cursor, relation);
else {
rse = MAKE_node(nod_rse, e_rse_count);
rse->nod_arg[e_rse_streams] = temp = MAKE_node(nod_list, 1);
temp->nod_arg[0] = PASS1_node(request, relation, 0);
if (temp = input->nod_arg[e_del_boolean])
rse->nod_arg[e_rse_boolean] = PASS1_node(request, temp, 0);
}
node->nod_arg[e_era_rse] = rse;
temp = rse->nod_arg[e_rse_streams];
node->nod_arg[e_era_relation] = temp->nod_arg[0];
LLS_POP(&request->req_context);
return node;
}
static NOD pass1_field( REQ request, NOD input, USHORT list)
{
/**************************************
*
* p a s s 1 _ f i e l d
*
**************************************
*
* Functional description
* Resolve a field name to an available context.
* If list is TRUE, then this function can detect and
* return a relation node if there is no name. This
* is used for cases of "SELECT <table_name>.* ...".
2002-06-29 08:56:51 +02:00
* CVC: The function attempts to detect
* if an unqualified field appears in more than one context
* and hence it returns the number of occurrences. This was
* added to allow the caller to detect ambiguous commands like
* select * from t1 join t2 on t1.f=t2.f order by common_field.
* While inoffensive on inner joins, it changes the result on outer joins.
2001-05-23 15:26:42 +02:00
*
**************************************/
2002-06-29 08:56:51 +02:00
NOD node = 0, indices; /* Changes made need this var initialized. */
2001-05-23 15:26:42 +02:00
STR name, qualifier;
FLD field;
2001-12-24 03:51:06 +01:00
DLLS stack;
2001-05-23 15:26:42 +02:00
CTX context;
2002-06-29 08:56:51 +02:00
DLLS relations, procedures;
NOD ddl_node;
BOOLEAN is_check_constraint;
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
2002-06-29 08:56:51 +02:00
/* CVC: This shameful hack added to allow CHECK constraint implementation via triggers
to be able to work. */
if ((ddl_node = request->req_ddl_node) != 0 && ddl_node->nod_type == nod_def_constraint)
is_check_constraint = TRUE;
else
is_check_constraint = FALSE;
2001-05-23 15:26:42 +02:00
/* handle an array element */
if (input->nod_type == nod_array) {
indices = input->nod_arg[e_ary_indices];
input = input->nod_arg[e_ary_array];
}
else
indices = NULL;
if (input->nod_count == 1) {
name = (STR) input->nod_arg[0];
qualifier = NULL;
}
else {
name = (STR) input->nod_arg[1];
qualifier = (STR) input->nod_arg[0];
}
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
2002-06-29 08:56:51 +02:00
/* CVC: Let's strip trailing blanks or comparisons may fail in dialect 3. */
if (name && name->str_data)
pass_exact_name ( (TEXT*) name->str_data);
/* 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
to field_error() near the end of this function. None of the multiple callers
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.
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.
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.
5.- Doesn't attempt to use "relations" and "procedures" after ambiguity_check()
has been called, because this function will POP() those stacks but they
are passed as values, hence they aren't updated upon return.
Claudio Valderrama - 2001.1.29.
*/
relations = procedures = NULL;
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 */
for (stack = request->req_context; stack; stack = stack->lls_next)
{
/* resolve_context() checks the type of the */
/* given context, so the cast to CTX is safe. */
2002-06-29 08:56:51 +02:00
context = reinterpret_cast<CTX>(stack->lls_object);
field = resolve_context (request, name, qualifier, context);
2001-05-23 15:26:42 +02:00
if (field)
{
if (list && !name) {
node = MAKE_node(nod_relation, e_rel_count);
2002-06-29 08:56:51 +02:00
node->nod_arg[e_rel_context] = reinterpret_cast<NOD>(stack->lls_object);
2001-05-23 15:26:42 +02:00
return node;
}
if (!name)
break;
2002-06-29 08:56:51 +02:00
for (; field; field = field->fld_next) {
if (!strcmp(reinterpret_cast < const char *>(name->str_data), field->fld_name)) {
if (!is_check_constraint && !qualifier) {
if (context->ctx_relation)
LLS_PUSH (context->ctx_relation, &relations);
if (context->ctx_procedure)
LLS_PUSH (context->ctx_procedure, &procedures);
}
break;
}
}
2001-05-23 15:26:42 +02:00
if (qualifier && !field)
break;
if (field) {
/* Intercept any reference to a field with datatype that
did not exist prior to V6 and post an error */
2002-06-29 08:56:51 +02:00
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(gds_sqlerr, gds_arg_number, (SLONG) - 206,
gds_arg_gds, gds_dsql_field_err,
gds_arg_gds, gds_random,
2001-05-23 15:26:42 +02:00
gds_arg_string, field->fld_name,
gds_arg_gds,
isc_sql_dialect_datatype_unsupport,
gds_arg_number, request->req_client_dialect,
gds_arg_string,
DSC_dtype_tostring(static_cast < UCHAR >
(field->fld_dtype)), 0);
return NULL;
2002-06-29 08:56:51 +02: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. */
if (node)
continue;
2001-05-23 15:26:42 +02:00
if (indices)
indices = PASS1_node(request, indices, FALSE);
node = MAKE_field(context, field, indices);
2002-06-29 08:56:51 +02:00
/* remember the agg context so that we can post the agg to its map. */
2001-05-23 15:26:42 +02:00
if (context->ctx_parent) {
if (request->req_inhibit_map) {
if (request->req_outer_agg_context &&
request->req_outer_agg_context != context->ctx_parent) {
/* AB: If we come here then in an aggregate is referenced
to two fields which have a different "parent_context"
meaning pointing to a different aggregate context
and there's no way to do calculations between 2
different aggregate contexts */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_field_ref_err, 0); /* invalid field reference */
}
2001-05-23 15:26:42 +02:00
request->req_outer_agg_context = context->ctx_parent;
}
2001-05-23 15:26:42 +02:00
}
2002-06-29 08:56:51 +02:00
if (is_check_constraint || qualifier) {
break;
}
2001-05-23 15:26:42 +02:00
}
}
}
2002-06-29 08:56:51 +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.
*/
if (!is_check_constraint && !qualifier && field) {
node = ambiguity_check (node, request, field, relations, procedures);
}
else {
/* This else is superflous as we post none in the loop above
when there's a check constraint or qualifier, but be safe if
the code structure changes in the future. */
while (relations) {
LLS_POP (&relations);
}
while (procedures) {
LLS_POP (&procedures);
}
}
if (node) {
return node;
}
2001-05-23 15:26:42 +02:00
field_error(qualifier ? (TEXT *) qualifier->str_data : (TEXT *) 0,
2002-06-29 08:56:51 +02:00
name ? (TEXT *) name->str_data : (TEXT *) 0, input);
2001-05-23 15:26:42 +02:00
2002-06-29 08:56:51 +02:00
/* CVC: field_error() calls ERRD_post() that never returns, so the next line
is only to make the compiler happy. */
2001-05-23 15:26:42 +02:00
return NULL;
}
static BOOLEAN pass1_found_aggregate(NOD node, USHORT check_scope_level,
USHORT match_type, BOOLEAN current_scope_level_equal)
{
/**************************************
*
* p a s s 1 _ f o u n d _ a g g r e g a t e
*
**************************************
*
* Functional description
* Check the fields inside an aggregate
* and check if the field scope_level
* meets the specified conditions.
* In the first call current_scope_level_equal
* should always be true, because this is used
* internally!
*
**************************************/
NOD *ptr, *end;
BOOLEAN found, field;
MAP map_;
DEV_BLKCHK(node, dsql_type_nod);
if (node == NULL) return FALSE;
found = FALSE;
switch (node->nod_type) {
case nod_gen_id:
case nod_gen_id2:
case nod_cast:
case nod_udf:
/* If arguments are given to the UDF then there's a node list */
if (node->nod_count == 2) {
found |= pass1_found_aggregate(node->nod_arg [1],
check_scope_level, match_type, current_scope_level_equal);
}
break;
case nod_exists:
case nod_singular:
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:
case nod_upcase:
case nod_extract:
case nod_add2:
case nod_divide2:
case nod_multiply2:
case nod_subtract2:
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:
for (ptr = node->nod_arg, end = ptr + node->nod_count;
ptr < end; ptr++)
found |= pass1_found_aggregate(*ptr, check_scope_level, match_type, current_scope_level_equal);
break;
case nod_via:
/* Pass only the rse from the nod_via */
found |= pass1_found_aggregate(node->nod_arg[e_via_rse],
check_scope_level, match_type, current_scope_level_equal);
break;
case nod_rse:
/* Pass rse_boolean (where clause) and rse_items (select items) */
found |= pass1_found_aggregate(node->nod_arg[e_rse_boolean],
check_scope_level, match_type, FALSE);
found |= pass1_found_aggregate(node->nod_arg[e_rse_items],
check_scope_level, match_type, FALSE);
break;
case nod_alias:
found |= pass1_found_aggregate(node->nod_arg[e_alias_value],
check_scope_level, match_type, current_scope_level_equal);
break;
case nod_aggregate:
/* Pass only rse_group (group by clause) */
found |= pass1_found_aggregate(node->nod_arg[e_agg_group],
check_scope_level, match_type, current_scope_level_equal);
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:
field = FALSE;
if (node->nod_count) {
found |= pass1_found_field(node->nod_arg[0], check_scope_level,
match_type, &field);
}
if (!field) {
/* For example COUNT(*) is always same scope_level (node->nod_count = 0)
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;
}
case FIELD_MATCH_TYPE_HIGHER_EQUAL:
return TRUE;
case FIELD_MATCH_TYPE_LOWER:
case FIELD_MATCH_TYPE_HIGHER:
return FALSE;
default:
ASSERT_FAIL;
}
}
break;
case nod_map:
map_ = reinterpret_cast <MAP>(node->nod_arg[e_map_map]);
found |= pass1_found_aggregate(map_->map_node,
check_scope_level, match_type, current_scope_level_equal);
break;
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:
return FALSE;
default:
ASSERT_FAIL;
}
return found;
}
static BOOLEAN pass1_found_field(NOD node, USHORT check_scope_level,
USHORT match_type, BOOLEAN * field)
{
/**************************************
*
* p a s s 1 _ f o u n d _ f i e l d
*
**************************************
*
* Functional description
* Check the fields inside an aggregate
* and check if the field scope_level
* meets the specified conditions.
*
**************************************/
NOD *ptr, *end;
BOOLEAN found;
CTX field_context;
MAP map_;
DEV_BLKCHK(node, dsql_type_nod);
if (node == NULL) return FALSE;
found = FALSE;
switch (node->nod_type) {
case nod_field:
field_context = (CTX) node->nod_arg[e_fld_context];
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);
case FIELD_MATCH_TYPE_LOWER:
return (field_context->ctx_scope_level < check_scope_level);
case FIELD_MATCH_TYPE_LOWER_EQUAL:
return (field_context->ctx_scope_level <= check_scope_level);
case FIELD_MATCH_TYPE_HIGHER:
return (field_context->ctx_scope_level > check_scope_level);
case FIELD_MATCH_TYPE_HIGHER_EQUAL:
return (field_context->ctx_scope_level >= check_scope_level);
default:
ASSERT_FAIL;
}
break;
case nod_gen_id:
case nod_gen_id2:
case nod_cast:
case nod_udf:
/* If arguments are given to the UDF then there's a node list */
if (node->nod_count == 2) {
found |= pass1_found_field(node->nod_arg [1], check_scope_level,
match_type, field);
}
break;
case nod_exists:
case nod_singular:
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:
case nod_upcase:
case nod_extract:
case nod_add2:
case nod_divide2:
case nod_multiply2:
case nod_subtract2:
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:
for (ptr = node->nod_arg, end = ptr + node->nod_count;
ptr < end; ptr++)
found |= pass1_found_field(*ptr, check_scope_level,
match_type, field);
break;
case nod_via:
/* Pass only the rse from the nod_via */
found |= pass1_found_field(node->nod_arg[e_via_rse],
check_scope_level, match_type, field);
break;
case nod_rse:
/* Pass rse_boolean (where clause) and rse_items (select items) */
found |= pass1_found_field(node->nod_arg[e_rse_boolean],
check_scope_level, match_type, field);
found |= pass1_found_field(node->nod_arg[e_rse_items],
check_scope_level, match_type, field);
break;
case nod_alias:
found |= pass1_found_field(node->nod_arg[e_alias_value],
check_scope_level, match_type, field);
break;
case nod_aggregate:
/* Pass only rse_group (group by clause) */
found |= pass1_found_field(node->nod_arg[e_agg_group],
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:
if (node->nod_count) {
found |= pass1_found_field(node->nod_arg[0], check_scope_level,
match_type, field);
}
break;
case nod_map:
map_ = reinterpret_cast <MAP>(node->nod_arg[e_map_map]);
found |= pass1_found_field(map_->map_node, check_scope_level,
match_type, field);
break;
case nod_dbkey:
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:
return FALSE;
default:
ASSERT_FAIL;
}
return found;
}
2001-05-23 15:26:42 +02:00
static NOD pass1_insert( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ i n s e r t
*
**************************************
*
* Functional description
* Process INSERT statement.
*
**************************************/
NOD rse, node, *ptr, *ptr2, *end, fields, values, temp;
FLD field;
DSQL_REL relation;
CTX context;
2001-12-24 03:51:06 +01:00
DLLS stack;
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;
node = MAKE_node(nod_store, e_sto_count);
/* Process SELECT expression, if present */
if (rse = input->nod_arg[e_ins_select]) {
node->nod_arg[e_sto_rse] = rse = PASS1_rse(request, rse, 0, NULL);
2001-05-23 15:26:42 +02:00
values = rse->nod_arg[e_rse_items];
}
else
values = PASS1_node(request, input->nod_arg[e_ins_values], 0);
/* Process relation */
node->nod_arg[e_sto_relation] = temp =
pass1_relation(request, input->nod_arg[e_ins_relation]);
context = (CTX) temp->nod_arg[0];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
relation = context->ctx_relation;
/* If there isn't a field list, generate one */
2002-07-06 07:32:02 +02:00
if (fields = input->nod_arg[e_ins_fields]) {
2001-05-23 15:26:42 +02:00
fields = PASS1_node(request, fields, 0);
2002-07-06 07:32:02 +02:00
/* begin IBO hack */
for (ptr = fields->nod_arg, end = ptr + fields->nod_count; ptr < end; ptr++) {
CTX tmp_ctx = 0;
DEV_BLKCHK (*ptr, dsql_type_nod);
temp = *ptr;
if (temp->nod_type == nod_field &&
(tmp_ctx = (CTX) temp->nod_arg [e_fld_context]) != 0 &&
tmp_ctx->ctx_relation &&
strcmp (tmp_ctx->ctx_relation->rel_name, relation->rel_name)) {
DSQL_REL bad_rel = tmp_ctx->ctx_relation;
FLD bad_fld = (FLD) temp->nod_arg [e_fld_field];
// At this time, "fields" has been replaced by the processed list in
// the same variable, so we refer again to input->nod_arg [e_ins_fields].
field_error (bad_rel ? (TEXT *) bad_rel->rel_name : (TEXT *) NULL_PTR,
bad_fld ? (TEXT *) bad_fld->fld_name : (TEXT *) NULL_PTR,
input->nod_arg [e_ins_fields]->nod_arg [ptr - fields->nod_arg]);
}
}
/* end IBO hack */
}
2001-05-23 15:26:42 +02:00
else {
2002-06-29 08:56:51 +02:00
/* CVC: Ann Harrison requested to skip COMPUTED fields in INSERT w/o field list. */
2001-05-23 15:26:42 +02:00
stack = NULL;
2002-06-29 08:56:51 +02:00
for (field = relation->rel_fields; field; field = field->fld_next) {
if (field->fld_flags & FLD_computed) {
continue;
}
2001-05-23 15:26:42 +02:00
LLS_PUSH(MAKE_field(context, field, 0), &stack);
2002-06-29 08:56:51 +02:00
}
2001-05-23 15:26:42 +02:00
fields = MAKE_list(stack);
}
/* Match field fields and values */
if (fields->nod_count != values->nod_count)
/* count of column list and value list don't match */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804,
gds_arg_gds, gds_dsql_var_count_err, 0);
2001-05-23 15:26:42 +02:00
stack = NULL;
for (ptr = fields->nod_arg, end = ptr + fields->nod_count, ptr2 =
values->nod_arg; ptr < end; ptr++, ptr2++) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(*ptr, dsql_type_nod);
DEV_BLKCHK(*ptr2, dsql_type_nod);
2001-05-23 15:26:42 +02:00
temp = MAKE_node(nod_assign, 2);
temp->nod_arg[0] = *ptr2;
temp->nod_arg[1] = *ptr;
LLS_PUSH(temp, &stack);
temp = *ptr2;
set_parameter_type(temp, *ptr, FALSE);
}
node->nod_arg[e_sto_statement] = MAKE_list(stack);
set_parameters_name(node->nod_arg[e_sto_statement],
node->nod_arg[e_sto_relation]);
return node;
}
static void pass1_put_args_on_stack( REQ request, NOD input, DLLS *stack, USHORT proc_flag)
{
/**************************************
*
* p a s s 1 _ p u t _ a r g s _ o n _ s t a c k
*
**************************************
*
* Functional description
2002-10-19 02:56:25 +02:00
* Put recursive non list nodes on the stack
*
**************************************/
NOD *ptr, *end;
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) {
LLS_PUSH(PASS1_node(request, input, proc_flag), stack);
return;
}
2002-10-19 02:56:25 +02:00
for (ptr = input->nod_arg, end = ptr + input->nod_count; ptr < end; ptr++) {
pass1_put_args_on_stack(request, *ptr, stack, proc_flag);
}
}
2001-05-23 15:26:42 +02:00
static NOD pass1_relation( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ r e l a t i o n
*
**************************************
*
* Functional description
* Prepare a relation name for processing.
* Allocate a new relation node.
*
**************************************/
NOD node;
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
node = MAKE_node(nod_relation, e_rel_count);
node->nod_arg[e_rel_context] = (NOD) PASS1_make_context(request, input);
return node;
}
static NOD pass1_alias_list( REQ request, NOD alias_list)
{
/**************************************
*
* p a s s 1 _ a l i a s _ l i s t
*
**************************************
*
* Functional description
* 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
* in a view stack. They are used to fully specify a
* base relation of a view. The aliases used in the
* view stack are those used in the view definition.
*
**************************************/
CTX context, new_context;
DSQL_REL relation;
NOD *arg, *end;
USHORT alias_length;
TEXT *p, *q;
2001-12-24 03:51:06 +01:00
DLLS stack;
2001-05-23 15:26:42 +02:00
STR alias;
TSQL tdsql;
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
tdsql = GET_THREAD_DATA;
arg = alias_list->nod_arg;
end = alias_list->nod_arg + alias_list->nod_count;
/* check the first alias in the list with the relations
in the current context for a match */
if (context = pass1_alias(request, (STR) * arg)) {
if (alias_list->nod_count == 1)
return (NOD) context;
relation = context->ctx_relation;
}
/* if the first alias didn't specify a table in the context stack,
look through all contexts to find one which might be a view with
a base table having a matching table name or alias */
if (!context)
for (stack = request->req_context; stack; stack = stack->lls_next) {
context = (CTX) stack->lls_object;
if (context->ctx_scope_level == request->req_scope_level &&
context->ctx_relation)
if (relation =
pass1_base_table(request, context->ctx_relation,
(STR) * arg))
break;
context = NULL;
}
if (!context)
/* there is no alias or table named %s at this scope level */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err,
gds_arg_gds, gds_dsql_no_relation_alias,
2001-05-23 15:26:42 +02:00
gds_arg_string, ((STR) * arg)->str_data, 0);
/* find the base table using the specified alias list, skipping the first one
since we already matched it to the context */
for (arg++; arg < end; arg++)
if (!(relation = pass1_base_table(request, relation, (STR) * arg)))
break;
if (!relation)
/* there is no alias or table named %s at this scope level */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err,
gds_arg_gds, gds_dsql_no_relation_alias,
2001-05-23 15:26:42 +02:00
gds_arg_string, ((STR) * arg)->str_data, 0);
/* make up a dummy context to hold the resultant relation */
new_context = FB_NEW(*tdsql->tsql_default) ctx;
2001-05-23 15:26:42 +02:00
new_context->ctx_context = context->ctx_context;
new_context->ctx_relation = relation;
/* concatenate all the contexts to form the alias name;
calculate the length leaving room for spaces and a null */
alias_length = alias_list->nod_count;
for (arg = alias_list->nod_arg; arg < end; arg++) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(*arg, dsql_type_str);
2001-05-23 15:26:42 +02:00
alias_length += static_cast < USHORT > (((STR) * arg)->str_length);
}
alias = FB_NEW_RPT(*tdsql->tsql_default, alias_length) str;
2001-05-23 15:26:42 +02:00
alias->str_length = alias_length;
p = new_context->ctx_alias = (TEXT *) alias->str_data;
for (arg = alias_list->nod_arg; arg < end; arg++) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(*arg, dsql_type_str);
2001-05-23 15:26:42 +02:00
for (q = (TEXT *) ((STR) * arg)->str_data; *q;)
*p++ = *q++;
*p++ = ' ';
}
p[-1] = 0;
return (NOD) new_context;
}
static CTX pass1_alias( REQ request, STR alias)
{
/**************************************
*
* p a s s 1 _ a l i a s
*
**************************************
*
* Functional description
* The passed relation or alias represents
* a context which was previously specified
* in the from list. Find and return the
* proper context.
*
**************************************/
2001-12-24 03:51:06 +01:00
DLLS stack;
2001-05-23 15:26:42 +02:00
CTX context, relation_context = NULL;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(alias, dsql_type_str);
2001-05-23 15:26:42 +02:00
2002-06-29 08:56:51 +02:00
/* look through all contexts at this scope level
to find one that has a relation name or alias
name which matches the identifier passed */
2001-05-23 15:26:42 +02:00
for (stack = request->req_context; stack; stack = stack->lls_next) {
context = (CTX) stack->lls_object;
if (context->ctx_scope_level != request->req_scope_level)
continue;
2002-06-29 08:56:51 +02:00
/* CVC: Getting rid of trailing spaces */
if (alias && alias->str_data)
pass_exact_name ( (TEXT*) alias->str_data);
2001-05-23 15:26:42 +02:00
/* check for matching alias */
if (context->ctx_alias &&
!strcmp(context->ctx_alias,
reinterpret_cast <
const char *>(alias->str_data))) return context;
/* 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 */
if (context->ctx_relation &&
!strcmp(context->ctx_relation->rel_name,
reinterpret_cast < const char *>(alias->str_data))) {
if (relation_context)
/* the table %s is referenced twice; use aliases to differentiate */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err,
gds_arg_gds, gds_dsql_self_join,
2001-05-23 15:26:42 +02:00
gds_arg_string, alias->str_data, 0);
relation_context = context;
}
}
return relation_context;
}
static DSQL_REL pass1_base_table( REQ request, DSQL_REL relation, STR alias)
{
/**************************************
*
* p a s s 1 _ b a s e _ t a b l e
*
**************************************
*
* Functional description
* Check if the relation in the passed context
* has a base table which matches the passed alias.
*
**************************************/
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,
reinterpret_cast <
UCHAR * >(relation->rel_name),
alias->str_data, 0);
}
static NOD pass1_rse( REQ request, NOD input, NOD order, NOD update_lock)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a s s 1 _ r s e
*
**************************************
*
* Functional description
* Compile a record selection expression.
* The input node may either be a "select_expression"
* or a "list" (an implicit union).
*
**************************************/
NOD rse, parent_rse, target_rse, aggregate, node, list, sub, *ptr, *end,
proj, slist_node, *ptr2;
2001-12-24 03:51:06 +01:00
DLLS stack;
2001-05-23 15:26:42 +02:00
CTX parent_context;
TSQL tdsql;
ULONG position;
BOOLEAN field;
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
tdsql = GET_THREAD_DATA;
aggregate = proj = NULL;
/* Handle implicit union case first. Maybe it's not a union */
if (input->nod_type == nod_list) {
if (input->nod_count == 1)
return PASS1_rse(request, input->nod_arg[0], order, update_lock);
2001-05-23 15:26:42 +02:00
else
return pass1_union(request, input, order);
}
/* Save the original base of the context stack and process relations */
parent_context = NULL;
parent_rse = NULL;
rse = target_rse = MAKE_node(nod_rse, e_rse_count);
rse->nod_arg[e_rse_lock] = update_lock;
2001-05-23 15:26:42 +02:00
rse->nod_arg[e_rse_streams] =
PASS1_node(request, input->nod_arg[e_sel_from], 0);
/* A GROUP BY, HAVING, or any aggregate function in the select list will
force an aggregate */
if ((node = input->nod_arg[e_sel_list]) && (node->nod_type == nod_list))
for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end;
ptr++) {
sub = *ptr;
if (sub->nod_type == nod_alias)
sub = sub->nod_arg[e_alias_value];
if (aggregate_found(request, sub, &proj)) {
parent_context = FB_NEW(*tdsql->tsql_default) ctx;
2001-05-23 15:26:42 +02:00
break;
}
}
if (!parent_context && (input->nod_arg[e_sel_group] ||
input->nod_arg[e_sel_having]))
parent_context = FB_NEW(*tdsql->tsql_default) ctx;
2001-05-23 15:26:42 +02:00
if (parent_context) {
parent_context->ctx_context = request->req_context_number++;
parent_context->ctx_scope_level = request->req_scope_level;
2001-05-23 15:26:42 +02:00
aggregate = MAKE_node(nod_aggregate, e_agg_count);
if (proj) {
rse->nod_arg[e_rse_reduced] = MAKE_node(nod_list, 1);
rse->nod_arg[e_rse_reduced]->nod_arg[0] = proj;
}
aggregate->nod_arg[e_agg_context] = (NOD) parent_context;
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;
}
2002-06-29 08:56:51 +02:00
/* Process LIMIT, if any */
if (node = input->nod_arg [e_sel_limit]) {
/* CVC: This line is a hint because set_parameter_type() doesn't receive
the dialect currently as an argument. */
node->nod_desc.dsc_scale = request->req_client_dialect;
if (node->nod_arg [e_limit_length]) {
sub = PASS1_node (request, node->nod_arg [e_limit_length], 0);
target_rse->nod_arg [e_rse_first] = sub;
2002-06-29 08:56:51 +02:00
set_parameter_type (sub, node, FALSE);
}
if (node->nod_arg [e_limit_skip]) {
sub = PASS1_node (request, node->nod_arg [e_limit_skip], 0);
target_rse->nod_arg [e_rse_skip] = sub;
2002-06-29 08:56:51 +02:00
set_parameter_type (sub, node, FALSE);
}
}
2001-05-23 15:26:42 +02:00
/* Process boolean, if any */
if (node = input->nod_arg[e_sel_where]) {
++request->req_in_where_clause;
rse->nod_arg[e_rse_boolean] = PASS1_node(request, node, 0);
--request->req_in_where_clause;
/* AB: An aggregate pointing to it's own parent_context isn't
allowed, HAVING should be used in stead */
if (pass1_found_aggregate(rse->nod_arg[e_rse_boolean],
request->req_scope_level, FIELD_MATCH_TYPE_EQUAL, TRUE))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_field_ref_err, 0);/* invalid field reference */
2001-05-23 15:26:42 +02:00
}
/* Process GROUP BY clause, if any */
if (node = input->nod_arg[e_sel_group])
{
/* if there are positions in the group by clause then replace them
by the (newly pass) items from the select_list */
aggregate->nod_arg[e_agg_group] = MAKE_node(node->nod_type,node->nod_count);
ptr2 = aggregate->nod_arg[e_agg_group]->nod_arg;
for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; ptr++, ptr2++)
{
sub = *ptr;
if (sub->nod_type == nod_position)
{
if ((slist_node = input->nod_arg[e_sel_list]) &&
(slist_node->nod_type == nod_list))
{
/* an select list is there */
position = (ULONG) (sub->nod_arg[0]);
if ((position < 1) || (position > (ULONG) slist_node->nod_count))
{
/* !! This error should be replaced by an good GROUP BY clause error !! */
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err,
gds_arg_gds, gds_field_aggregate_err, 0);
}
*ptr2 = PASS1_node(request, slist_node->nod_arg[position - 1], 0);
}
}
else
{
*ptr2 = PASS1_node(request, sub, 0);
}
}
/* AB: An field pointing to another parent_context isn't
allowed and GROUP BY items can't contain aggregates */
if (pass1_found_field(aggregate->nod_arg[e_agg_group],
request->req_scope_level, FIELD_MATCH_TYPE_LOWER, &field) ||
pass1_found_aggregate(aggregate->nod_arg[e_agg_group],
request->req_scope_level, FIELD_MATCH_TYPE_LOWER_EQUAL, TRUE))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_field_ref_err, 0); /* invalid field reference */
2002-06-29 08:56:51 +02:00
}
2001-05-23 15:26:42 +02:00
if (parent_context) {
2001-05-23 15:26:42 +02:00
LLS_PUSH(parent_context, &request->req_context);
/* AB: remap_... moved from after plan process to here */
/* If there is a parent context, replace original contexts with parent context */
remap_streams_to_parent_context(rse->nod_arg[e_rse_streams],
parent_context);
}
2001-05-23 15:26:42 +02:00
2002-06-29 08:56:51 +02:00
#ifdef DEV_BUILD
if (DSQL_debug > 2)
DSQL_pretty(input, 0);
#endif
2001-05-23 15:26:42 +02:00
/* Process select list, if any. If not, generate one */
if (node = input->nod_arg[e_sel_list]) {
++request->req_in_select_list;
target_rse->nod_arg[e_rse_items] = list =
pass1_sel_list(request, node);
--request->req_in_select_list;
if (aggregate) {
if (input->nod_arg[e_sel_group] || input->nod_arg[e_sel_having] ||
pass1_found_aggregate(target_rse->nod_arg[e_rse_items],
request->req_scope_level, FIELD_MATCH_TYPE_EQUAL, TRUE)) {
for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++)
if (invalid_reference(request, *ptr, aggregate->nod_arg[e_agg_group], FALSE))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_field_ref_err, 0);/* invalid field reference */
}
else {
/* We've detected a aggregate in a sub-select and the current-rse
isn't a aggregate at all, so relocate and remove unneeded stuff */
rse->nod_arg[e_rse_items] = target_rse->nod_arg[e_rse_items];
rse->nod_arg[e_rse_first] = target_rse->nod_arg[e_rse_first];
rse->nod_arg[e_rse_skip] = target_rse->nod_arg[e_rse_skip];
target_rse = rse;
parent_rse = NULL;
remap_streams_to_parent_context(rse->nod_arg[e_rse_streams], NULL);
delete aggregate;
aggregate = NULL;
LLS_POP(&request->req_context);
parent_context = NULL;
}
}
2001-05-23 15:26:42 +02:00
}
else {
stack = NULL;
list = rse->nod_arg[e_rse_streams];
for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end;
ptr++)
explode_asterisk(*ptr, aggregate, &stack);
target_rse->nod_arg[e_rse_items] = MAKE_list(stack);
}
/* parse a user-specified access plan */
if (node = input->nod_arg[e_sel_plan]) {
/* disallow plans in a trigger for the short term,
until we can figure out why they don't work: bug #6057 */
2002-10-25 10:29:12 +02:00
/* dimitr: I've commented out the following protection, because it's proven
by Ignacio J. Ortega and myself that now triggers work
with indexes in plans as expected, so it seems that the reason
for the mentioned bug #6057 is already fixed (2002-10-25)
2001-05-23 15:26:42 +02:00
if (request->req_flags & REQ_trigger)
2002-10-25 10:29:12 +02:00
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_token_err,
gds_arg_gds, gds_random, gds_arg_string, "PLAN", 0);
2002-10-25 10:29:12 +02:00
*/
2001-05-23 15:26:42 +02:00
rse->nod_arg[e_rse_plan] = PASS1_node(request, node, 0);
}
if (input->nod_arg[e_sel_distinct])
target_rse->nod_arg[e_rse_reduced] = target_rse->nod_arg[e_rse_items];
/* Process ORDER clause, if any */
if (order) {
++request->req_in_order_by_clause;
target_rse->nod_arg[e_rse_sort] =
pass1_sort(request, order, input->nod_arg[e_sel_list], parent_context);
2001-05-23 15:26:42 +02:00
--request->req_in_order_by_clause;
}
/* Unless there was a parent, we're done */
if (!parent_context) {
rse->nod_arg[e_rse_singleton] = input->nod_arg[e_sel_singleton];
return rse;
}
/* Reset context of select items to point to the parent stream */
parent_rse->nod_arg[e_rse_items] =
copy_fields(parent_rse->nod_arg[e_rse_items], parent_context);
/* And, of course, reduction clauses must also apply to the parent */
if (input->nod_arg[e_sel_distinct])
parent_rse->nod_arg[e_rse_reduced] = parent_rse->nod_arg[e_rse_items];
/* Process HAVING clause, if any */
if (node = input->nod_arg[e_sel_having]) {
++request->req_in_having_clause;
parent_rse->nod_arg[e_rse_boolean] = PASS1_node(request, node, 0);
--request->req_in_having_clause;
/* AB: An aggregate pointing to another parent_context isn't
allowed. fields in HAVING items can ONLY point to the same
scope_level items */
if (pass1_found_field(parent_rse->nod_arg[e_rse_boolean],
request->req_scope_level, FIELD_MATCH_TYPE_LOWER, &field))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_field_ref_err, 0);/* invalid field reference */
2001-05-23 15:26:42 +02:00
#ifdef CHECK_HAVING
if (aggregate)
if (invalid_reference(parent_rse->nod_arg[e_rse_boolean],
aggregate->nod_arg[e_agg_group]))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_field_ref_err,
2001-05-23 15:26:42 +02:00
/* invalid field reference */
0);
#endif
}
parent_rse->nod_arg[e_rse_singleton] = input->nod_arg[e_sel_singleton];
return parent_rse;
}
static NOD pass1_searched_case( REQ request, NOD input, USHORT proc_flag)
{
/**************************************
*
* p a s s 1 _ s e a r c h e d _ c a s e
*
**************************************
*
* Functional description
* Handle a reference to a searched case expression.
*
**************************************/
NOD node, list, *ptr, *end;
DLLS stack;
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(input, dsql_type_nod);
DEV_BLKCHK(input->nod_arg[0], dsql_type_nod);
node = MAKE_node(nod_searched_case, 2);
list = input->nod_arg[0];
/* build boolean-expression list */
stack = NULL;
2002-10-19 02:56:25 +02:00
for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++,ptr++) {
pass1_put_args_on_stack(request, *ptr, &stack, proc_flag);
}
node->nod_arg[e_searched_case_search_conditions] = MAKE_list(stack);
/* build when_result list including else_result at the end */
/* else_result is included for easy handling in MAKE_desc() */
stack = NULL;
2002-10-19 02:56:25 +02:00
for (ptr = list->nod_arg, end = ptr + list->nod_count, ptr++; ptr < end; ptr++,ptr++) {
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);
/* Set describer for output node */
MAKE_desc(&node->nod_desc, node);
/* Set parameter-types if parameters are there */
for (ptr = node->nod_arg[e_searched_case_search_conditions]->nod_arg,
end = ptr + node->nod_arg[e_searched_case_search_conditions]->nod_count;
2002-10-19 02:56:25 +02:00
ptr < end; ptr++) {
set_parameter_type(*ptr, node, FALSE);
}
return node;
}
2001-05-23 15:26:42 +02:00
static NOD pass1_sel_list( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ s e l _ l i s t
*
**************************************
*
* Functional description
* Compile a select list, which may contain things
* like "<table_name>.*".
*
**************************************/
NOD node, *ptr, *end, frnode;
2001-12-24 03:51:06 +01:00
DLLS stack;
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
/*
For each node in the list, if it's a field node, see if it's of
the form <tablename>.*. If so, explode the asterisk. If not,
just stack up the node.
*/
stack = NULL;
for (ptr = input->nod_arg, end = ptr + input->nod_count; 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_field_name) {
/* check for field or relation node */
frnode = pass1_field(request, *ptr, 1);
if (frnode->nod_type == nod_field)
LLS_PUSH(frnode, &stack);
else
explode_asterisk(frnode, NULL, &stack);
}
else
LLS_PUSH(PASS1_node(request, *ptr, 0), &stack);
}
node = MAKE_list(stack);
return node;
}
static NOD pass1_simple_case( REQ request, NOD input, USHORT proc_flag)
{
/**************************************
*
* p a s s 1 _ s i m p l e _ c a s e
*
**************************************
*
* Functional description
* Handle a reference to a simple case expression.
*
**************************************/
USHORT i;
NOD node, node1, list, *ptr, *end;
DLLS stack;
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(input, dsql_type_nod);
DEV_BLKCHK(input->nod_arg [0], dsql_type_nod);
node = MAKE_node(nod_simple_case, 3);
/* build case_operand node */
node->nod_arg[e_simple_case_case_operand] =
PASS1_node(request, input->nod_arg[0], proc_flag);
list = input->nod_arg[1];
/* build when_operand list */
stack = NULL;
2002-10-19 02:56:25 +02:00
for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++,ptr++) {
pass1_put_args_on_stack(request, *ptr, &stack, proc_flag);
2002-10-19 02:56:25 +02:00
}
node->nod_arg[e_simple_case_when_operands] = MAKE_list(stack);
/* build when_result list including else_result at the end */
/* else_result is included for easy handling in MAKE_desc() */
stack = NULL;
2002-10-19 02:56:25 +02:00
for (ptr = list->nod_arg, end = ptr + list->nod_count, ptr++; ptr < end; ptr++,ptr++) {
pass1_put_args_on_stack(request, *ptr, &stack, proc_flag);
2002-10-19 02:56:25 +02:00
}
pass1_put_args_on_stack(request, input->nod_arg [2], &stack, proc_flag);
node->nod_arg[e_simple_case_results] = MAKE_list(stack);
/* build list for making describe information from
case_operand and when_operands this is used for
setting parameter describers if used in this case */
list = node->nod_arg[e_simple_case_when_operands];
node1 = MAKE_node(nod_list, list->nod_count + 1);
i = 0;
node1->nod_arg[i++] = node->nod_arg[e_simple_case_case_operand];
2002-10-19 02:56:25 +02:00
for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++, i++) {
node1->nod_arg[i] = *ptr;
2002-10-19 02:56:25 +02:00
}
MAKE_desc_from_list(&node1->nod_desc, node1);
/* Set parameter describe information */
set_parameter_type(node->nod_arg[e_simple_case_case_operand], node1, FALSE);
for (ptr = node->nod_arg[e_simple_case_when_operands]->nod_arg,
end = ptr + node->nod_arg[e_simple_case_when_operands]->nod_count;
ptr < end; ptr++) {
set_parameter_type(*ptr, node1, FALSE);
}
/* Clean up temporary used node */
delete node1;
/* Set describer for output node */
MAKE_desc(&node->nod_desc, node);
/* Set parameter describe information for evt. results parameters */
for (ptr = node->nod_arg[e_simple_case_results]->nod_arg,
end = ptr + node->nod_arg[e_simple_case_results]->nod_count;
ptr < end; ptr++) {
set_parameter_type(*ptr, node, FALSE);
}
return node;
}
static NOD pass1_sort( REQ request, NOD input, NOD s_list, CTX parent_context)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a s s 1 _ s o r t
*
**************************************
*
* Functional description
* Compile a parsed sort list
*
**************************************/
NOD node, *ptr, *end, *ptr2, node1, node2;
ULONG position;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(input, dsql_type_nod);
DEV_BLKCHK(s_list, dsql_type_nod);
2001-05-23 15:26:42 +02:00
if (input->nod_type != nod_list)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_order_by_err, /* invalid ORDER BY clause */
2001-05-23 15:26:42 +02:00
0);
/* Node is simply to be rebuilt -- just recurse merrily */
node = MAKE_node(input->nod_type, input->nod_count);
ptr2 = node->nod_arg;
for (ptr = input->nod_arg, end = ptr + input->nod_count; 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
node1 = *ptr;
if (node1->nod_type != nod_order)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_order_by_err, /* invalid ORDER BY clause */
2001-05-23 15:26:42 +02:00
0);
node2 = MAKE_node(nod_order, e_order_count);
2002-09-10 20:28:23 +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 */
2001-05-23 15:26:42 +02:00
node1 = node1->nod_arg[0];
2002-06-29 08:56:51 +02:00
if (node1->nod_type == nod_field_name) {
if (parent_context) {
node2->nod_arg[0] = copy_field(pass1_field(request, node1, 0), parent_context);
}
else {
node2->nod_arg[0] = pass1_field(request, node1, 0);
}
2002-06-29 08:56:51 +02:00
}
2001-05-23 15:26:42 +02:00
else if (node1->nod_type == nod_position) {
position = (ULONG) (node1->nod_arg[0]);
if ((position < 1) || !s_list
|| (position >
(ULONG) s_list->nod_count)) ERRD_post(gds_sqlerr,
2001-05-23 15:26:42 +02:00
gds_arg_number,
(SLONG) - 104,
gds_arg_gds,
gds_dsql_command_err, gds_arg_gds, gds_order_by_err, /* invalid ORDER BY clause */
2001-05-23 15:26:42 +02:00
0);
if (parent_context) {
node2->nod_arg[0] = copy_field(PASS1_node(request, s_list->nod_arg[position - 1], 0), parent_context);
}
else {
node2->nod_arg[0] =
PASS1_node(request, s_list->nod_arg[position - 1], 0);
}
2001-05-23 15:26:42 +02:00
}
else
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_order_by_err, /* invalid ORDER BY clause */
2001-05-23 15:26:42 +02:00
0);
if ((*ptr)->nod_arg[e_order_collate]) {
2001-12-24 03:51:06 +01:00
DEV_BLKCHK((*ptr)->nod_arg[e_order_collate], dsql_type_str);
2001-05-23 15:26:42 +02:00
node2->nod_arg[0] =
pass1_collate(request, node2->nod_arg[0],
(STR) (*ptr)->nod_arg[e_order_collate]);
}
*ptr2++ = node2;
}
return node;
}
static NOD pass1_udf( REQ request, NOD input, USHORT proc_flag)
{
/**************************************
*
* p a s s 1 _ u d f
*
**************************************
*
* Functional description
* Handle a reference to a user defined function.
*
**************************************/
STR name;
UDF udf;
NOD node;
2001-12-24 03:51:06 +01:00
DLLS stack;
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
name = (STR) input->nod_arg[0];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(name, dsql_type_str);
2001-05-23 15:26:42 +02:00
udf = METD_get_function(request, name);
if (!udf)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804,
gds_arg_gds, gds_dsql_function_err,
gds_arg_gds, gds_random,
2001-05-23 15:26:42 +02:00
gds_arg_string, name->str_data, 0);
node = MAKE_node(nod_udf, input->nod_count);
node->nod_arg[0] = (NOD) udf;
if (input->nod_count == 2) {
stack = NULL;
2002-06-29 08:56:51 +02:00
pass1_udf_args(request, input->nod_arg[1], udf, 0, &stack, proc_flag);
2001-05-23 15:26:42 +02:00
node->nod_arg[1] = MAKE_list(stack);
}
return node;
}
2002-06-29 08:56:51 +02:00
static void pass1_udf_args(REQ request, NOD input, UDF udf, USHORT arg_pos,
DLLS * stack, USHORT proc_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a s s 1 _ u d f _ a r g s
*
**************************************
*
* Functional description
* Handle references to function arguments.
*
**************************************/
NOD *ptr, *end;
2002-06-29 08:56:51 +02:00
NOD temp, args;
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);
2002-06-29 08:56:51 +02:00
DEV_BLKCHK (udf, dsql_type_udf);
2001-05-23 15:26:42 +02:00
if (input->nod_type != nod_list) {
2002-06-29 08:56:51 +02:00
temp = PASS1_node (request, input, proc_flag);
args = udf->udf_arguments;
if (temp->nod_type == nod_parameter) {
if (args && args->nod_count > arg_pos) {
set_parameter_type (temp, args->nod_arg [arg_pos], FALSE);
}
else {
;
/* We should complain here in the future! The parameter is
out of bounds or the function doesn't declare input params. */
}
}
LLS_PUSH (temp, stack);
return;
2001-05-23 15:26:42 +02:00
}
for (ptr = input->nod_arg, end = ptr + input->nod_count; ptr < end; ptr++)
2002-06-29 08:56:51 +02:00
pass1_udf_args(request, *ptr, udf, arg_pos++, stack, proc_flag);
2001-05-23 15:26:42 +02:00
}
static NOD pass1_union( REQ request, NOD input, NOD order_list)
{
/**************************************
*
* p a s s 1 _ u n i o n
*
**************************************
*
* Functional description
* Handle a UNION of substreams, generating
* a mapping of all the fields and adding an
* implicit PROJECT clause to ensure that all
* the records returned are unique.
*
**************************************/
NOD map_node, *ptr, *end, items, union_node, *uptr, nod1;
NOD union_rse, union_items, order1, order2, sort, position;
CTX union_context;
2001-12-24 03:51:06 +01:00
MAP map_;
2001-05-23 15:26:42 +02:00
SSHORT count = 0;
ULONG number;
SSHORT i, j; /* for-loop counters */
TSQL tdsql;
2001-12-24 03:51:06 +01:00
DLLS base;
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
tdsql = GET_THREAD_DATA;
/* set up the rse node for the union */
union_rse = MAKE_node(nod_rse, e_rse_count);
union_rse->nod_arg[e_rse_streams] = union_node =
MAKE_node(nod_union, input->nod_count);
/* process all the sub-rse's */
uptr = union_node->nod_arg;
base = request->req_context;
for (ptr = input->nod_arg, end = ptr + input->nod_count; ptr < end;
ptr++, uptr++) {
*uptr = PASS1_rse(request, *ptr, 0, NULL);
2002-06-29 08:56:51 +02:00
while (request->req_context != base) {
LLS_PUSH(request->req_context->lls_object, &request->req_union_context);
LLS_POP(&request->req_context);
}
2001-05-23 15:26:42 +02:00
}
/* generate a context for the union itself */
union_context = FB_NEW(*tdsql->tsql_default) ctx;
2001-05-23 15:26:42 +02:00
union_context->ctx_context = request->req_context_number++;
/* generate the list of fields to select */
items = union_node->nod_arg[0]->nod_arg[e_rse_items];
/* loop through the list nodes, checking to be sure that they have the same
number and type of items */
for (i = 1; i < union_node->nod_count; i++) {
nod1 = union_node->nod_arg[i]->nod_arg[e_rse_items];
if (items->nod_count != nod1->nod_count)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_dsql_count_mismatch, /* overload of msg */
2001-05-23 15:26:42 +02:00
0);
for (j = 0; j < nod1->nod_count; j++) {
MAKE_desc(&items->nod_arg[j]->nod_desc, items->nod_arg[j]);
MAKE_desc(&nod1->nod_arg[j]->nod_desc, nod1->nod_arg[j]);
/* SQL II, section 9.3, pg 195 governs which data types
* are considered equivilant for a UNION
* The following restriction is in some ways more restrictive
* (cannot UNION CHAR with VARCHAR for instance)
* (or cannot union CHAR of different lengths)
* and in someways less restrictive
* (SCALE is not looked at)
* Workaround: use a direct CAST() statement in the SQL
* statement to force desired datatype.
*/
if (((nod1->nod_arg[j]->nod_desc.dsc_dtype) !=
(items->nod_arg[j]->nod_desc.dsc_dtype)) ||
((nod1->nod_arg[j]->nod_desc.dsc_length) !=
(items->nod_arg[j]->nod_desc.dsc_length)))
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_dsql_datatype_err, /* overload of msg */
2001-05-23 15:26:42 +02:00
0);
/**
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.
**/
if (nod1->nod_arg[j]->nod_desc.dsc_flags & DSC_nullable)
items->nod_arg[j]->nod_desc.dsc_flags |= DSC_nullable;
}
}
union_items = MAKE_node(nod_list, items->nod_count);
uptr = items->nod_arg;
for (ptr = union_items->nod_arg, end = ptr + union_items->nod_count;
ptr < end; ptr++) {
*ptr = map_node = MAKE_node(nod_map, e_map_count);
map_node->nod_arg[e_map_context] = (NOD) union_context;
map_ = FB_NEW(*tdsql->tsql_default) map;
2001-12-24 03:51:06 +01:00
map_node->nod_arg[e_map_map] = (NOD) map_;
2001-05-23 15:26:42 +02:00
/* set up the MAP between the sub-rses and the union context */
2001-12-24 03:51:06 +01:00
map_->map_position = count++;
map_->map_node = *uptr++;
map_->map_next = union_context->ctx_map;
union_context->ctx_map = map_;
2001-05-23 15:26:42 +02:00
}
union_rse->nod_arg[e_rse_items] = union_items;
/* Process ORDER clause, if any */
if (order_list) {
sort = MAKE_node(nod_list, order_list->nod_count);
uptr = sort->nod_arg;
for (ptr = order_list->nod_arg, end = ptr + order_list->nod_count;
ptr < end; ptr++, uptr++) {
order1 = *ptr;
position = order1->nod_arg[0];
if (position->nod_type != nod_position)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_order_by_err, /* invalid ORDER BY clause */
2001-05-23 15:26:42 +02:00
0);
number = (ULONG) position->nod_arg[0];
if (number < 1 || number > union_items->nod_count)
ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104,
gds_arg_gds, gds_dsql_command_err, gds_arg_gds, gds_order_by_err, /* invalid ORDER BY clause */
2001-05-23 15:26:42 +02:00
0);
/* make a new order node pointing at the Nth item in the select list */
*uptr = order2 = MAKE_node(nod_order, 2);
order2->nod_arg[0] = union_items->nod_arg[number - 1];
order2->nod_arg[1] = order1->nod_arg[1];
}
union_rse->nod_arg[e_rse_sort] = sort;
}
/* PROJECT on all the select items unless UNION ALL was specified */
if (!(input->nod_flags & NOD_UNION_ALL))
union_rse->nod_arg[e_rse_reduced] = union_items;
return union_rse;
}
static NOD pass1_update( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ u p d a t e
*
**************************************
*
* Functional description
* Process UPDATE statement.
*
**************************************/
NOD rse, node, temp, relation, cursor;
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
cursor = input->nod_arg[e_upd_cursor];
relation = input->nod_arg[e_upd_relation];
if (cursor && (request->req_flags & REQ_procedure)) {
node = MAKE_node(nod_modify_current, e_mdc_count);
node->nod_arg[e_mdc_context] =
(NOD) pass1_cursor_context(request, cursor, relation);
node->nod_arg[e_mdc_update] = PASS1_node(request, relation, 0);
node->nod_arg[e_mdc_statement] =
PASS1_statement(request, input->nod_arg[e_upd_statement], 0);
LLS_POP(&request->req_context);
return node;
}
request->req_type = (cursor) ? REQ_UPDATE_CURSOR : REQ_UPDATE;
node = MAKE_node(nod_modify, e_mod_count);
node->nod_arg[e_mod_update] = PASS1_node(request, relation, 0);
node->nod_arg[e_mod_statement] =
PASS1_statement(request, input->nod_arg[e_upd_statement], 0);
set_parameters_name(node->nod_arg[e_mod_statement],
node->nod_arg[e_mod_update]);
/* Generate record selection expression */
if (cursor)
rse = pass1_cursor(request, cursor, relation);
else {
rse = MAKE_node(nod_rse, e_rse_count);
rse->nod_arg[e_rse_streams] = temp = MAKE_node(nod_list, 1);
temp->nod_arg[0] = node->nod_arg[e_mod_update];
if (temp = input->nod_arg[e_upd_boolean])
rse->nod_arg[e_rse_boolean] = PASS1_node(request, temp, 0);
}
temp = rse->nod_arg[e_rse_streams];
node->nod_arg[e_mod_source] = temp->nod_arg[0];
node->nod_arg[e_mod_rse] = rse;
LLS_POP(&request->req_context);
return node;
}
static NOD pass1_variable( REQ request, NOD input)
{
/**************************************
*
* p a s s 1 _ v a r i a b l e
*
**************************************
*
* Functional description
* Resolve a variable name to an available variable.
*
**************************************/
NOD procedure_node, var_nodes, var_node, *ptr, *end;
2002-06-29 08:56:51 +02:00
STR var_name = 0;
2001-05-23 15:26:42 +02:00
VAR var;
SSHORT position;
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
if (input->nod_type == nod_field_name) {
if (input->nod_arg[e_fln_context]) {
if (request->req_flags & REQ_trigger)
return pass1_field(request, input, 0);
else
2002-06-29 08:56:51 +02:00
field_error(0, 0, input);
2001-05-23 15:26:42 +02:00
}
var_name = (STR) input->nod_arg[e_fln_name];
}
else
var_name = (STR) input->nod_arg[e_vrn_name];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(var_name, dsql_type_str);
2001-05-23 15:26:42 +02:00
if ((procedure_node = request->req_ddl_node) &&
(procedure_node->nod_type == nod_def_procedure ||
2002-06-29 08:56:51 +02:00
procedure_node->nod_type == nod_redef_procedure ||
2001-05-23 15:26:42 +02:00
procedure_node->nod_type == nod_mod_procedure ||
procedure_node->nod_type == nod_replace_procedure ||
2001-05-23 15:26:42 +02:00
procedure_node->nod_type == nod_def_trigger ||
procedure_node->nod_type == nod_mod_trigger ||
procedure_node->nod_type == nod_replace_trigger)) {
2001-05-23 15:26:42 +02:00
if (procedure_node->nod_type == nod_def_procedure ||
procedure_node->nod_type == nod_redef_procedure ||
procedure_node->nod_type == nod_mod_procedure ||
procedure_node->nod_type == nod_replace_procedure) {
2001-05-23 15:26:42 +02:00
/* Try to resolve variable name against input, output
and local variables */
if (var_nodes = procedure_node->nod_arg[e_prc_inputs])
for (position = 0, ptr = var_nodes->nod_arg, end =
ptr + var_nodes->nod_count; ptr < end; ptr++, position++) {
var_node = *ptr;
var = (VAR) var_node->nod_arg[e_var_variable];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(var, dsql_type_var);
2001-05-23 15:26:42 +02:00
if (!strcmp
(reinterpret_cast < const char *>(var_name->str_data),
var->var_name)) return var_node;
}
if (var_nodes = procedure_node->nod_arg[e_prc_outputs])
for (position = 0, ptr = var_nodes->nod_arg, end =
ptr + var_nodes->nod_count; ptr < end; ptr++, position++) {
var_node = *ptr;
var = (VAR) var_node->nod_arg[e_var_variable];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(var, dsql_type_var);
2001-05-23 15:26:42 +02:00
if (!strcmp
(reinterpret_cast < const char *>(var_name->str_data),
var->var_name)) return var_node;
}
var_nodes = procedure_node->nod_arg[e_prc_dcls];
}
else
var_nodes = procedure_node->nod_arg[e_trg_actions]->nod_arg[0];
if (var_nodes)
for (position = 0, ptr = var_nodes->nod_arg, end =
ptr + var_nodes->nod_count; ptr < end; ptr++, position++) {
var_node = *ptr;
var = (VAR) var_node->nod_arg[e_var_variable];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(var, dsql_type_var);
2001-05-23 15:26:42 +02:00
if (!strcmp
(reinterpret_cast < const char *>(var_name->str_data),
var->var_name)) return var_node;
}
}
2002-06-29 08:56:51 +02:00
/* field unresolved */
2001-05-23 15:26:42 +02:00
2002-06-29 08:56:51 +02:00
/* CVC: That's all [the fix], folks! */
if (var_name)
field_error (0, (TEXT *) var_name->str_data, input);
else
field_error (0, 0, input);
2001-05-23 15:26:42 +02:00
return NULL;
}
static NOD post_map( NOD node, CTX context)
{
/**************************************
*
* p o s t _ m a p
*
**************************************
*
* Functional description
* Post an item to a map for a context.
*
**************************************/
NOD new_node;
2001-12-24 03:51:06 +01:00
MAP map_;
2001-05-23 15:26:42 +02:00
USHORT count;
TSQL tdsql;
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
tdsql = GET_THREAD_DATA;
/* Check to see if the item has already been posted */
2001-12-24 03:51:06 +01:00
for (map_ = context->ctx_map, count = 0; map_; map_ = map_->map_next, count++)
if (node_match(node, map_->map_node, FALSE))
2001-05-23 15:26:42 +02:00
break;
2001-12-24 03:51:06 +01:00
if (!map_) {
map_ = FB_NEW(*tdsql->tsql_default) map;
2001-12-24 03:51:06 +01:00
map_->map_position = count;
map_->map_next = context->ctx_map;
context->ctx_map = map_;
map_->map_node = node;
2001-05-23 15:26:42 +02:00
}
new_node = MAKE_node(nod_map, e_map_count);
new_node->nod_count = 0;
new_node->nod_arg[e_map_context] = (NOD) context;
2001-12-24 03:51:06 +01:00
new_node->nod_arg[e_map_map] = (NOD) map_;
2001-05-23 15:26:42 +02:00
new_node->nod_desc = node->nod_desc;
return new_node;
}
static void remap_streams_to_parent_context( NOD input, CTX parent_context)
{
/**************************************
*
* r e m a p _ s t r e a m s _ t o _ p a r e n t _ c o n t e x t
*
**************************************
*
* Functional description
* For each relation in the list, flag the relation's context
* as having a parent context. Be sure to handle joins
* (Bug 6674).
*
**************************************/
CTX context;
NOD *ptr, *end;
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:
for (ptr = input->nod_arg, end = ptr + input->nod_count; ptr < end;
ptr++)
remap_streams_to_parent_context(*ptr, parent_context);
break;
case nod_relation:
context = (CTX) input->nod_arg[e_rel_context];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
context->ctx_parent = parent_context;
break;
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;
default:
ASSERT_FAIL;
break;
}
}
static FLD resolve_context( REQ request, STR name, STR qualifier, CTX context)
{
/**************************************
*
* r e s o l v e _ c o n t e x t
*
**************************************
*
* Functional description
* Attempt to resolve field against context.
* Return first field in context if successful,
* NULL if not.
*
**************************************/
DSQL_REL relation;
PRC procedure;
FLD field;
TEXT *table_name;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(request, dsql_type_req);
DEV_BLKCHK(name, dsql_type_str);
DEV_BLKCHK(qualifier, dsql_type_str);
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
relation = context->ctx_relation;
procedure = context->ctx_procedure;
if (!relation && !procedure)
return NULL;
/* if there is no qualifier, then we cannot match against
a context of a different scoping level */
if (!qualifier && context->ctx_scope_level != request->req_scope_level)
return NULL;
if (relation)
table_name = relation->rel_name;
else
table_name = procedure->prc_name;
/* 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) && (!context->ctx_alias
|| strcmp(reinterpret_cast <
const char *>(qualifier->str_data),
context->ctx_alias))) return NULL;
/* Lookup field in relation or procedure */
if (relation)
field = relation->rel_fields;
else
field = procedure->prc_outputs;
return field;
}
static BOOLEAN set_parameter_type(
NOD in_node,
NOD node, BOOLEAN force_varchar )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s e t _ p a r a m e t e r _ t y p e
*
**************************************
*
* Functional description
* Setup the datatype of a parameter.
*
**************************************/
NOD *ptr, *end;
PAR parameter;
BOOLEAN result = 0;
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(in_node, dsql_type_nod);
DEV_BLKCHK(node, dsql_type_nod);
2001-05-23 15:26:42 +02:00
if (in_node == NULL)
return FALSE;
switch (in_node->nod_type) {
case nod_parameter:
MAKE_desc(&in_node->nod_desc, node);
parameter = (PAR) in_node->nod_arg[e_par_parameter];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(parameter, dsql_type_par);
2001-05-23 15:26:42 +02:00
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
strings to insure that we don't add trailing blanks.
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) {
if (parameter->par_desc.dsc_dtype == dtype_text) {
parameter->par_desc.dsc_dtype = dtype_varying;
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;
parameter->par_desc.dsc_length = 30 + sizeof(USHORT);
parameter->par_desc.dsc_sub_type = 0;
parameter->par_desc.dsc_scale = 0;
parameter->par_desc.dsc_ttype = ttype_dynamic;
}
}
return TRUE;
case nod_cast:
/* Unable to guess parameters within a CAST() statement - as
* any datatype could be the input to cast.
*/
return FALSE;
case nod_add:
case nod_add2:
case nod_concatenate:
case nod_divide:
case nod_divide2:
case nod_multiply:
case nod_multiply2:
case nod_negate:
case nod_substr:
case nod_subtract:
case nod_subtract2:
case nod_upcase:
case nod_extract:
2002-06-29 08:56:51 +02:00
case nod_limit:
2001-05-23 15:26:42 +02:00
result = 0;
for (ptr = in_node->nod_arg, end = ptr + in_node->nod_count;
ptr < end; ptr++)
result |= set_parameter_type(*ptr, node, force_varchar);
return result;
default:
return FALSE;
}
}
static void set_parameters_name( NOD list_node, NOD rel_node)
{
/**************************************
*
* s e t _ p a r a m e t e r s _ n a m e
*
**************************************
*
* Functional description
* Setup parameter parameters name.
*
**************************************/
NOD *ptr, *end;
CTX context;
DSQL_REL relation;
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
context = (CTX) rel_node->nod_arg[0];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
2001-05-23 15:26:42 +02:00
relation = context->ctx_relation;
for (ptr = list_node->nod_arg, end = ptr + list_node->nod_count;
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
assert(FALSE);
}
}
static void set_parameter_name( NOD par_node, NOD fld_node, DSQL_REL relation)
{
/**************************************
*
* s e t _ p a r a m e t e r _ n a m e
*
**************************************
*
* Functional description
* 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.
*
**************************************/
NOD *ptr, *end;
PAR parameter;
FLD field;
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
/* Could it be something else ??? */
assert(fld_node->nod_type == nod_field);
if (fld_node->nod_desc.dsc_dtype != dtype_array)
return;
switch (par_node->nod_type) {
case nod_parameter:
parameter = (PAR) par_node->nod_arg[e_par_parameter];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(parameter, dsql_type_par);
2001-05-23 15:26:42 +02:00
field = (FLD) fld_node->nod_arg[e_fld_field];
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field, dsql_type_fld);
2001-05-23 15:26:42 +02:00
parameter->par_name = field->fld_name;
parameter->par_rel_name = relation->rel_name;
return;
case nod_add:
case nod_add2:
case nod_concatenate:
case nod_divide:
case nod_divide2:
case nod_multiply:
case nod_multiply2:
case nod_negate:
case nod_substr:
case nod_subtract:
case nod_subtract2:
case nod_upcase:
for (ptr = par_node->nod_arg, end = ptr + par_node->nod_count;
ptr < end; ptr++)
set_parameter_name(*ptr, fld_node, relation);
return;
default:
return;
}
}
2002-06-29 08:56:51 +02:00
static TEXT *pass_exact_name (TEXT* str)
{
/**************************************
*
* p a s s _ e x a c t _ n a m e
*
**************************************
*
* Functional description
* Skim trailing blanks from identifiers.
* CVC: I had to add this function because metd_exact_name
* usage forced the inclusion of metd.c that currently
* isn't used on other directories.
*
**************************************/
TEXT *p;
for (p = str; *p; ++p) { // go to end of string
;
}
for (--p; p >= str && *p == '\x20'; --p) { // remove spaces from end
;
}
/* Write *++p = 0; if you like instead of two lines. */
++p;
*p = 0;
return str;
}