8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 18:43:02 +01:00
firebird-mirror/src/gpre/sqe.cpp
dimitr 24fa109eea CORE-4437: Add support for the RETURNING clause to Embedded SQL.
CORE-4438: Add support for UPDATE OR INSERT statement to Embedded SQL.
Only C/C++ language support is added, other languages will be supported in trunk.
2014-05-28 05:56:23 +00:00

4029 lines
100 KiB
C++

//____________________________________________________________
//
// PROGRAM: C Preprocessor
// MODULE: sqe.cpp
// DESCRIPTION: SQL expression parser
//
// 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): ______________________________________.
//
// Revision 1.3 2000/11/16 15:54:29 fsg
// Added new switch -verbose to gpre that will dump
// parsed lines to stderr
//
// Fixed gpre bug in handling row names in WHERE clauses
// that are reserved words now (DATE etc)
// (this caused gpre to dump core when parsing tan.e)
//
// Fixed gpre bug in handling lower case table aliases
// in WHERE clauses for sql dialect 2 and 3.
// (cause a core dump in a test case from C.R. Zamana)
//
// Mike Nordell - Reduce compiler warnings
// Stephen W. Boyd - Added support for new features
//____________________________________________________________
//
#include "firebird.h"
#include <stdio.h>
#include <string.h>
#include "../gpre/gpre.h"
#include "../gpre/parse.h"
#include "../gpre/cme_proto.h"
#include "../gpre/cmp_proto.h"
#include "../gpre/exp_proto.h"
#include "../gpre/gpre_proto.h"
#include "../gpre/hsh_proto.h"
#include "../gpre/gpre_meta.h"
#include "../gpre/msc_proto.h"
#include "../gpre/par_proto.h"
#include "../gpre/sqe_proto.h"
#include "../gpre/sql_proto.h"
#include "../common/utils_proto.h"
struct scope
{
gpre_ctx* req_contexts;
USHORT req_scope_level; // scope level for SQL subquery parsing
USHORT req_in_aggregate; // now processing value expr for aggr
USHORT req_in_select_list; // processing select list
USHORT req_in_where_clause; // processing where clause
USHORT req_in_having_clause; // processing having clause
USHORT req_in_order_by_clause; // processing order by clause
USHORT req_in_subselect; // processing a subselect clause
};
static bool compare_expr(const gpre_nod*, const gpre_nod*);
static gpre_nod* copy_fields(gpre_nod*, map*);
static gpre_nod* explode_asterisk(gpre_nod*, int, gpre_rse*);
static gpre_nod* explode_asterisk_all(gpre_nod*, int, gpre_rse*, bool);
static gpre_fld* get_ref(gpre_nod*);
static gpre_nod* implicit_any(gpre_req*, gpre_nod*, nod_t, nod_t);
static gpre_nod* merge(gpre_nod*, gpre_nod*);
static gpre_nod* merge_fields(gpre_nod*, gpre_nod*, int, bool);
static gpre_nod* negate(gpre_nod*);
static void pair(gpre_nod*, gpre_nod*);
static gpre_ctx* par_alias_list(gpre_req*, gpre_nod*);
static gpre_ctx* par_alias(gpre_req*, const TEXT*);
static gpre_nod* par_and(gpre_req*, USHORT *);
static gpre_rel* par_base_table(gpre_req*, const gpre_rel*, const TEXT*);
static gpre_nod* par_case(gpre_req*);
static gpre_nod* par_collate(gpre_req*, gpre_nod*);
static gpre_nod* par_in(gpre_req*, gpre_nod*);
static gpre_ctx* par_joined_relation(gpre_req*);
static gpre_ctx* par_join_clause(gpre_req*, gpre_ctx*);
static nod_t par_join_type();
static gpre_nod* par_multiply(gpre_req*, bool, USHORT *, bool *);
static gpre_nod* par_not(gpre_req*, USHORT *);
static gpre_nod* par_nullif(gpre_req*);
static void par_order(gpre_req*, gpre_rse*, bool, bool);
static gpre_nod* par_plan(gpre_req*);
static gpre_nod* par_plan_item(gpre_req*, bool, USHORT *, bool *);
static gpre_nod* par_primitive_value(gpre_req*, bool, USHORT *, bool *);
static gpre_nod* par_relational(gpre_req*, USHORT *);
static gpre_rse* par_rse(gpre_req*, gpre_nod*, bool);
static gpre_rse* par_select(gpre_req*, gpre_rse*);
static gpre_nod* par_stat(gpre_req*);
static gpre_nod* par_subscript(gpre_req*);
static gpre_nod* par_substring(gpre_req*);
static void par_terminating_parens(USHORT *, USHORT *);
static gpre_nod* par_udf(gpre_req*);
static gpre_nod* par_udf_or_field(gpre_req*, bool);
static gpre_nod* par_udf_or_field_with_collate(gpre_req*, bool, USHORT *, bool *);
static void par_update(gpre_rse*, bool, bool);
static gpre_nod* post_fields(gpre_nod*, map*);
static gpre_nod* post_map(gpre_nod*, map*);
static gpre_nod* post_select_list(gpre_nod*, map*);
static void pop_scope(gpre_req*, scope*);
static void push_scope(gpre_req*, scope*);
static gpre_fld* resolve(gpre_nod*, gpre_ctx*, gpre_ctx**, act**);
static bool resolve_fields(gpre_nod*& fields, gpre_rse* selection);
static gpre_ctx* resolve_asterisk(const tok*, gpre_rse*);
static void set_ref(gpre_nod*, gpre_fld*);
static char* upcase_string(const char*);
static bool validate_references(const gpre_nod*, const gpre_nod*);
struct ops
{
nod_t rel_op;
kwwords_t rel_kw;
nod_t rel_negation;
};
static const ops rel_ops[] =
{
{ nod_eq, KW_EQ, nod_ne },
{ nod_eq, KW_EQUALS, nod_ne },
{ nod_ne, KW_NE, nod_eq },
{ nod_gt, KW_GT, nod_le },
{ nod_ge, KW_GE, nod_lt },
{ nod_le, KW_LE, nod_gt },
{ nod_lt, KW_LT, nod_ge },
{ nod_containing, KW_CONTAINING, nod_any },
{ nod_starting, KW_STARTING, nod_any },
{ nod_matches, KW_MATCHES, nod_any },
{ nod_any, KW_none, nod_any },
{ nod_ansi_any, KW_none, nod_ansi_any },
{ nod_ansi_all, KW_none, nod_ansi_all }
};
#ifdef NOT_USED_OR_REPLACED
static const ops scalar_stat_ops[] =
{
{ nod_count, KW_COUNT, nod_any },
{ nod_max, KW_MAX, nod_any },
{ nod_min, KW_MIN, nod_any },
{ nod_total, KW_TOTAL, nod_any },
{ nod_total, KW_SUM, nod_any },
{ nod_average, KW_AVERAGE, nod_any },
{ nod_via, KW_none, nod_any}
};
#endif
static const ops stat_ops[] =
{
{ nod_agg_count, KW_COUNT, nod_any },
{ nod_agg_max, KW_MAX, nod_any },
{ nod_agg_min, KW_MIN, nod_any },
{ nod_agg_total, KW_TOTAL, nod_any },
{ nod_agg_total, KW_SUM, nod_any },
{ nod_agg_average, KW_AVERAGE, nod_any },
{ nod_any, KW_none, nod_any },
{ nod_ansi_any, KW_none, nod_ansi_any },
{ nod_ansi_all, KW_none, nod_ansi_all }
};
static const nod_t relationals[] =
{
nod_eq, nod_ne, nod_gt, nod_ge, nod_le, nod_lt, nod_containing,
nod_starting, nod_matches, nod_any, nod_missing, nod_equiv, nod_between, nod_like,
nod_and, nod_or, nod_not, nod_ansi_any, nod_ansi_all, nod_nothing
};
//____________________________________________________________
//
// Parse an OR boolean expression.
//
gpre_nod* SQE_boolean( gpre_req* request, USHORT* paren_count)
{
USHORT local_count;
assert_IS_REQ(request);
if (!paren_count)
{
local_count = 0;
paren_count = &local_count;
}
gpre_nod* expr1 = par_and(request, paren_count);
if (!MSC_match(KW_OR) && !MSC_match(KW_OR1))
{
par_terminating_parens(paren_count, &local_count);
return expr1;
}
expr1 = MSC_binary(nod_or, expr1, SQE_boolean(request, paren_count));
par_terminating_parens(paren_count, &local_count);
return expr1;
}
//____________________________________________________________
//
// Parse a reference to a relation name
// and generate a context for it.
//
gpre_ctx* SQE_context(gpre_req* request)
{
SCHAR r_name[NAME_SIZE], db_name[NAME_SIZE], owner_name[NAME_SIZE];
SCHAR s[ERROR_LENGTH];
assert_IS_REQ(request);
gpre_ctx* context = MSC_context(request);
SQL_relation_name(r_name, db_name, owner_name);
if (!(context->ctx_relation = SQL_relation(request, r_name, db_name, owner_name, false)))
{
// check for a procedure
gpre_prc* procedure = context->ctx_procedure =
SQL_procedure(request, r_name, db_name, owner_name, false);
if (procedure)
{
if (procedure->prc_inputs)
{
if (!MSC_match(KW_LEFT_PAREN))
CPR_s_error("( <procedure input parameters> )");
// parse input references
context->ctx_prc_inputs = SQE_list(SQE_value, request, false);
USHORT local_count = 1;
par_terminating_parens(&local_count, &local_count);
if (procedure->prc_in_count != context->ctx_prc_inputs->nod_count)
{
PAR_error("count of input values doesn't match count of parameters");
}
gpre_nod** input = context->ctx_prc_inputs->nod_arg;
for (gpre_fld* field = procedure->prc_inputs; field; input++, field = field->fld_next)
{
SQE_post_field(*input, field);
}
}
}
else
{
if (owner_name[0])
sprintf(s, "table %s.%s not defined", owner_name, r_name);
else
sprintf(s, "table %s not defined", r_name);
PAR_error(s);
}
}
// If the next token is recognized as a keyword, it can't be a SQL "alias".
// It may, however, be an "end of line" token. If so, trade it in on the
// next "real" token.
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (symbol && symbol->sym_type == SYM_keyword)
{
if (!gpreGlob.token_global.tok_length)
CPR_token();
return context;
}
// we have what we assume to be an alias; check to make sure that
// it does not conflict with any relation, procedure or context names
// at the same scoping level in this query
gpre_ctx* conflict;
for (conflict = request->req_contexts; conflict; conflict = conflict->ctx_next)
{
if ((symbol = conflict->ctx_symbol) &&
(symbol->sym_type == SYM_relation || symbol->sym_type == SYM_context ||
symbol->sym_type == SYM_procedure) &&
!strcmp(symbol->sym_string, gpreGlob.token_global.tok_string) &&
conflict->ctx_scope_level == request->req_scope_level)
{
break;
}
}
if (conflict)
{
const char* error_type;
switch (symbol->sym_type)
{
case SYM_relation:
error_type = "table";
break;
case SYM_procedure:
error_type = "procedure";
break;
default:
error_type = "context";
}
fb_utils::snprintf(s, sizeof(s), "context %s conflicts with a %s in the same statement",
gpreGlob.token_global.tok_string, error_type);
PAR_error(s);
}
symbol = MSC_symbol(SYM_context, gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, 0);
symbol->sym_object = context;
context->ctx_symbol = symbol;
context->ctx_alias = symbol->sym_name;
HSH_insert(symbol);
PAR_get_token();
return context;
}
//____________________________________________________________
//
// Parse an item is a select list. This is particularly nasty
// since neither the relations nor context variables have been
// processed yet. So, rather than generating a simple field
// reference, make up a more or less dummy block containing
// a pointer to a field system and possible a qualifier symbol.
// this will be turned into a reference later.
//
gpre_nod* SQE_field(gpre_req* request, bool aster_ok)
{
gpre_req* slice_req;
assert_IS_REQ(request);
gpre_lls* upper_dim = NULL;
gpre_lls* lower_dim = NULL;
tok hold_token;
hold_token.tok_type = tok_t(0);
if (aster_ok && MSC_match(KW_ASTERISK))
{
gpre_nod* node = MSC_node(nod_asterisk, 1);
return node;
}
// if the token isn't an identifier, complain
SQL_resolve_identifier("<column name>", NULL, NAME_SIZE);
// For domains we can't be resolving tokens to field names
// in the CHECK constraint.
TEXT s[ERROR_LENGTH];
act* action;
if (request && request->req_type == REQ_ddl && (action = request->req_actions) &&
(action->act_type == ACT_create_domain || action->act_type == ACT_alter_domain))
{
fb_utils::snprintf(s, sizeof(s), "Illegal use of identifier: %s in domain constraint",
gpreGlob.token_global.tok_string);
PAR_error(s);
}
// Note: We *always* want to make a deferred name block - to handle
// scoping of alias names in subselects properly, when we haven't
// seen the list of relations (& aliases for them). This occurs
// during the select list, but by the time we see the having, group,
// order, or where we do know all aliases and should be able to
// precisely match.
// Note that the case of request == NULL should never
// occur, and request->req_contexts == NULL should only
// occur for the very first select list in a request.
// 1994-October-03 David Schnepper
// if the request is null, make a deferred name block
if (!request || !request->req_contexts || request->req_in_select_list)
{
gpre_nod* node = MSC_node(nod_deferred, 3);
node->nod_count = 0;
tok* f_token = (tok*) MSC_alloc(TOK_LEN);
node->nod_arg[0] = (gpre_nod*) f_token;
f_token->tok_length = gpreGlob.token_global.tok_length;
SQL_resolve_identifier("<identifier>", f_token->tok_string, f_token->tok_length + 1);
CPR_token();
if (MSC_match(KW_DOT))
{
if (gpreGlob.token_global.tok_keyword == KW_ASTERISK)
{
if (aster_ok)
node->nod_type = nod_asterisk;
else
PAR_error("* not allowed");
}
else
{
node->nod_arg[1] = node->nod_arg[0];
f_token = (tok*) MSC_alloc(TOK_LEN);
node->nod_arg[0] = (gpre_nod*) f_token;
f_token->tok_length = gpreGlob.token_global.tok_length;
SQL_resolve_identifier("<identifier>", f_token->tok_string, f_token->tok_length + 1);
}
CPR_token();
}
if (MSC_match(KW_L_BRCKET))
{
// We have a complete array or an array slice here
if (!MSC_match(KW_R_BRCKET))
{
slice_req = MSC_request(REQ_slice);
int count = 0;
do {
count++;
gpre_nod* tail = par_subscript(slice_req);
MSC_push(tail, &lower_dim);
if (MSC_match(KW_COLON))
{
//if (!MSC_match (KW_DOT))
// CPR_s_error ("<period>");
tail = par_subscript(slice_req);
MSC_push(tail, &upper_dim);
}
else
MSC_push(tail, &upper_dim);
} while (MSC_match(KW_COMMA));
if (!MSC_match(KW_R_BRCKET))
CPR_s_error("<right bracket>");
slc* slice = (slc*) MSC_alloc(SLC_LEN(count));
slice_req->req_slice = slice;
slc::slc_repeat* tail_ptr = &slice->slc_rpt[count];
slice->slc_dimensions = count;
slice->slc_parent_request = request;
while (lower_dim)
{
--tail_ptr;
tail_ptr->slc_lower = MSC_pop(&lower_dim);
tail_ptr->slc_upper = MSC_pop(&upper_dim);
}
node->nod_arg[2] = (gpre_nod*) slice_req;
// added this to assign the correct nod_count
// The nod type is converted to nod_field in SQE_resolve()
// The nod_count is check to confirm if the array slice
// has been initialized in cmd.cpp
node->nod_count = 3;
}
else
{
slice_req = (gpre_req*) MSC_alloc(REQ_LEN);
slice_req->req_type = REQ_slice;
node->nod_arg[2] = (gpre_nod*) slice_req;
}
}
return node;
}
gpre_ctx* context;
ref* reference = (ref*) MSC_alloc(REF_LEN);
gpre_nod* node = MSC_unary(nod_field, (gpre_nod*) reference);
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (symbol)
{
// if there is a homonym which is a context, use the context;
// otherwise we may match with a relation or procedure which
// is not in the request, resulting in a bogus error
if (symbol->sym_type != SYM_field)
{
for (gpre_sym* temp_symbol = symbol; temp_symbol; temp_symbol = temp_symbol->sym_homonym)
{
if (temp_symbol->sym_type == SYM_context)
{
symbol = temp_symbol;
break;
}
if (temp_symbol->sym_type == SYM_relation)
{
symbol = temp_symbol;
continue;
}
if (temp_symbol->sym_type == SYM_procedure)
{
if (symbol->sym_type != SYM_relation)
symbol = temp_symbol;
continue;
}
}
if (symbol->sym_type == SYM_context)
{
context = symbol->sym_object;
CPR_token();
if (!MSC_match(KW_DOT))
CPR_s_error("<period> in qualified column");
if (context->ctx_request != request)
PAR_error("context not part of this request");
SQL_resolve_identifier("<Column Name>", NULL, NAME_SIZE);
if (!(reference->ref_field =
MET_context_field(context, gpreGlob.token_global.tok_string)))
{
sprintf(s, "column \"%s\" not in context", gpreGlob.token_global.tok_string);
PAR_error(s);
}
if (SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect)
{
const USHORT field_dtype = reference->ref_field->fld_dtype;
if (dtype_sql_date == field_dtype ||
dtype_sql_time == field_dtype ||
dtype_int64 == field_dtype)
{
SQL_dialect1_bad_type(field_dtype);
}
}
reference->ref_context = context;
CPR_token();
return node;
}
if (symbol->sym_type == SYM_relation)
{
const gpre_rel* relation = (gpre_rel*) symbol->sym_object;
if (relation->rel_database != request->req_database)
PAR_error("table not in appropriate database");
CPR_token();
// if we do not see a KW_DOT, perhaps what we think is a relation
// is actually a column with the same name as the relation in
// the HSH table. if so...skip down to the end of the routine
// and find out if we do have such a column. but first, since
// we just did a CPR_token, we have to move prior_token into
// current token, and hold the current token for later.
if (!MSC_match(KW_DOT))
{
hold_token = gpreGlob.token_global;
gpreGlob.token_global = gpreGlob.prior_token;
gpreGlob.token_global.tok_symbol = 0;
}
else
{
// We've got the column name. resolve it
SQL_resolve_identifier("<Columnn Name>", NULL, NAME_SIZE);
for (context = request->req_contexts; context; context = context->ctx_next)
{
if (context->ctx_relation == relation &&
(reference->ref_field =
MET_field(context->ctx_relation, gpreGlob.token_global.tok_string)))
{
if (SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect)
{
const USHORT field_dtype = reference->ref_field->fld_dtype;
if (dtype_sql_date == field_dtype ||
dtype_sql_time == field_dtype ||
dtype_int64 == field_dtype)
{
SQL_dialect1_bad_type(field_dtype);
}
}
reference->ref_context = context;
CPR_token();
if (reference->ref_field->fld_array_info)
{
node = EXP_array(request, reference->ref_field, true, true);
node->nod_arg[0] = (gpre_nod*) reference;
}
return node;
}
}
fb_utils::snprintf(s, sizeof(s), "column \"%s\" not in context",
gpreGlob.token_global.tok_string);
PAR_error(s);
}
}
else if (symbol->sym_type == SYM_procedure)
{
const gpre_prc* procedure = (gpre_prc*) symbol->sym_object;
if (procedure->prc_database != request->req_database)
PAR_error("procedure not in appropriate database");
CPR_token();
if (!MSC_match(KW_DOT))
CPR_s_error("<period> in qualified column");
SQL_resolve_identifier("<Column Name>", NULL, NAME_SIZE);
for (context = request->req_contexts; context; context = context->ctx_next)
{
if (context->ctx_procedure == procedure &&
(reference->ref_field =
MET_context_field(context, gpreGlob.token_global.tok_string)))
{
if (SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect)
{
const USHORT field_dtype = reference->ref_field->fld_dtype;
if (dtype_sql_date == field_dtype ||
dtype_sql_time == field_dtype ||
dtype_int64 == field_dtype)
{
SQL_dialect1_bad_type(field_dtype);
}
}
reference->ref_context = context;
if (reference->ref_field->fld_array_info)
{
node = EXP_array(request, reference->ref_field, true, true);
node->nod_arg[0] = (gpre_nod*) reference;
}
CPR_token();
return node;
}
}
fb_utils::snprintf(s, sizeof(s),
"column \"%s\" not in context", gpreGlob.token_global.tok_string);
PAR_error(s);
}
}
}
// Hmmm. So it wasn't a qualified field. Try any field.
SQL_resolve_identifier("<Column Name>", NULL, NAME_SIZE);
for (context = request->req_contexts; context; context = context->ctx_next)
{
if (reference->ref_field = MET_context_field(context, gpreGlob.token_global.tok_string))
{
if (SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect)
{
const USHORT field_dtype = reference->ref_field->fld_dtype;
if (dtype_sql_date == field_dtype ||
dtype_sql_time == field_dtype ||
dtype_int64 == field_dtype)
{
SQL_dialect1_bad_type(field_dtype);
}
}
reference->ref_context = context;
// if we skipped down from the SYM_relation case above, we need to
// switch token and prior_token back to their original values to
// continue.
if (hold_token.tok_type != 0)
{
gpreGlob.prior_token = gpreGlob.token_global;
gpreGlob.token_global = hold_token;
}
else
CPR_token();
if (reference->ref_field->fld_array_info)
{
node = EXP_array(request, reference->ref_field, true, true);
node->nod_arg[0] = (gpre_nod*) reference;
}
if (request->req_map)
return post_map(node, request->req_map);
return node;
}
}
CPR_s_error("<column name>");
return NULL; // silence compiler
}
//____________________________________________________________
//
// Parse a list of "things", separated by commas. Return the
// whole mess in a list node.
//
gpre_nod* SQE_list(pfn_SQE_list_cb routine, gpre_req* request, bool aster_ok)
{
assert_IS_REQ(request);
gpre_lls* stack = NULL;
int count = 0;
do {
count++;
MSC_push((*routine) (request, aster_ok, NULL, NULL), &stack);
} while (MSC_match(KW_COMMA));
gpre_nod* list = MSC_node(nod_list, (SSHORT) count);
gpre_nod** ptr = &list->nod_arg[count];
while (stack)
*--ptr = (gpre_nod*) MSC_pop(&stack);
return list;
}
//____________________________________________________________
//
// Parse procedure input parameters which are constants or
// host variable reference and, perhaps, a missing
// flag reference, which may be prefaced by the noiseword,
// "INDICATOR".
//
ref* SQE_parameter(gpre_req* request)
{
ref* reference;
SCHAR* string;
assert_IS_REQ(request);
if (gpreGlob.token_global.tok_type == tok_number)
{
reference = (ref*) MSC_alloc(REF_LEN);
string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
reference->ref_value = string;
reference->ref_flags |= REF_literal;
CPR_token();
return reference;
}
if ((isQuoted(gpreGlob.token_global.tok_type) && gpreGlob.sw_sql_dialect == 1) ||
gpreGlob.token_global.tok_type == tok_sglquoted)
{
// Since we have stripped the quotes, it is time now to put it back
// so that the host language will interpret it correctly as a string
// literal.
reference = (ref*) MSC_alloc(REF_LEN);
string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 3);
string[0] = '\"';
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string + 1);
string[gpreGlob.token_global.tok_length + 1] = '\"';
string[gpreGlob.token_global.tok_length + 2] = 0;
reference->ref_value = string;
reference->ref_flags |= REF_literal;
CPR_token();
return reference;
}
if (gpreGlob.token_global.tok_keyword == KW_PLUS || gpreGlob.token_global.tok_keyword == KW_MINUS)
{
int sign = 0;
if (gpreGlob.token_global.tok_keyword == KW_MINUS)
sign = 1;
CPR_token();
if (gpreGlob.token_global.tok_type != tok_number)
CPR_s_error("<host variable> or <constant>");
reference = (ref*) MSC_alloc(REF_LEN);
char* s = string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1 + sign);
if (sign)
*s++ = '-';
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, s);
reference->ref_value = string;
reference->ref_flags |= REF_literal;
CPR_token();
return reference;
}
if (!MSC_match(KW_COLON))
CPR_s_error("<host variable> or <constant>");
if (gpreGlob.token_global.tok_type != tok_ident)
CPR_s_error("<host variable> or <constant>");
reference = (ref*) MSC_alloc(REF_LEN);
// This loop is a waste of time because the flag SYM_variable is never activated
for (gpre_sym* symbol = gpreGlob.token_global.tok_symbol; symbol; symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_variable)
{
reference->ref_field = (gpre_fld*) symbol->sym_object;
break;
}
}
reference->ref_value = PAR_native_value(false, false);
MSC_match(KW_INDICATOR);
if (MSC_match(KW_COLON))
reference->ref_null_value = PAR_native_value(false, false);
return reference;
}
//____________________________________________________________
//
// Given an expression node, for values that don't have a
// field, post the given field.
// Procedure called from EXP_array to post the "subscript field".
//
void SQE_post_field( gpre_nod* input, gpre_fld* field)
{
if (!input || !field)
return;
assert_IS_NOD(input);
switch (input->nod_type)
{
case nod_value:
{
ref* reference = (ref*) input->nod_arg[0];
if (!reference->ref_field)
{
// We're guessing that hostvar reference->ref_value matches
// the datatype of field->fld_dtype
reference->ref_field = field;
}
return;
}
case nod_field:
case nod_literal:
case nod_deferred:
case nod_array:
return;
case nod_map_ref:
{
mel* element = (mel*) input->nod_arg[0];
gpre_nod* node = element->mel_expr;
SQE_post_field(node, field);
return;
}
default:
{
gpre_nod** ptr = input->nod_arg;
for (const gpre_nod* const* const end = ptr + input->nod_count; ptr < end; ptr++)
{
SQE_post_field(*ptr, field);
}
return;
}
}
}
//____________________________________________________________
//
// Post an external reference to a request. If the expression
// in question already exists, re-use it. If there isn't a field,
// generate a pseudo-field to hold datatype information. If there
// isn't a context, well, there isn't a context.
//
ref* SQE_post_reference(gpre_req* request, gpre_fld* field, gpre_ctx* context, gpre_nod* node)
{
ref* reference;
assert_IS_REQ(request);
assert_IS_NOD(node);
// If the beast is already a field reference, get component parts
if (node && node->nod_type == nod_field)
{
reference = (ref*) node->nod_arg[0];
field = reference->ref_field;
context = reference->ref_context;
}
// See if there is already a reference to this guy. If so, return it.
for (reference = request->req_references; reference; reference = reference->ref_next)
{
if ((reference->ref_expr && compare_expr(node, reference->ref_expr)) ||
(!reference->ref_expr && field == reference->ref_field &&
context == reference->ref_context))
{
return reference;
}
}
// If there isn't a field given, make one up
if (!field)
{
field = (gpre_fld*) MSC_alloc(FLD_LEN);
CME_get_dtype(node, field);
if (field->fld_dtype && (field->fld_dtype <= dtype_any_text))
field->fld_flags |= FLD_text;
}
// No reference -- make one
reference = (ref*) MSC_alloc(REF_LEN);
reference->ref_context = context;
reference->ref_field = field;
reference->ref_expr = node;
reference->ref_next = request->req_references;
request->req_references = reference;
return reference;
}
//____________________________________________________________
//
// Resolve a kludgy field node build by par_s_item into
// a bona fide field reference. If a request
// is supplied, resolve the reference to any context available
// in the request. Otherwise resolve the field to a given
// record selection expression.
//
// If the expression contains a global aggregate, return true,
// otherwise false.
//
bool SQE_resolve(gpre_nod** node_ptr, gpre_req* request, gpre_rse* selection)
{
bool result = false;
act* slice_action = 0;
gpre_nod* node = *node_ptr;
assert_IS_REQ(request);
assert_IS_NOD(node);
switch (node->nod_type)
{
case nod_plus:
case nod_minus:
case nod_times:
case nod_divide:
case nod_negate:
case nod_and:
case nod_or:
case nod_not:
case nod_eq:
case nod_ne:
case nod_ge:
case nod_gt:
case nod_le:
case nod_lt:
case nod_containing:
case nod_like:
case nod_between:
case nod_starting:
case nod_upcase:
case nod_lowcase:
case nod_concatenate:
case nod_cast:
case nod_case:
case nod_case1:
case nod_substring:
{
gpre_nod** ptr = node->nod_arg;
const gpre_nod* const* const end = ptr + node->nod_count;
for (; ptr < end; ptr++)
result |= SQE_resolve(ptr, request, selection);
return result;
}
case nod_agg_max:
case nod_agg_min:
case nod_agg_total:
case nod_agg_average:
case nod_agg_count:
if (node->nod_arg[0])
{
SQE_resolve(&node->nod_arg[0], request, selection);
gpre_nod* node_arg = node->nod_arg[0];
const ref* reference = (ref*) node_arg->nod_arg[0];
if (node_arg->nod_type == nod_field && reference &&
reference->ref_field && reference->ref_field->fld_array_info)
{
PAR_error("Array columns not permitted in aggregate functions");
}
}
return true;
case nod_udf:
if (node->nod_arg[0])
{
gpre_nod** ptr = node->nod_arg[0]->nod_arg;
const gpre_nod* const* const end = ptr + node->nod_arg[0]->nod_count;
for (; ptr < end; ptr++)
result |= SQE_resolve(ptr, request, selection);
}
return result;
case nod_gen_id:
return SQE_resolve(&node->nod_arg[0], request, selection);
// Begin date/time/timestamp support
case nod_extract:
result |= SQE_resolve(&node->nod_arg[1], request, selection);
return result;
// End date/time/timestamp support
case nod_coalesce:
for (int i = 0; i < node->nod_count; i++)
result |= SQE_resolve(&node->nod_arg[0]->nod_arg[i], request, selection);
return result;
case nod_deferred:
break;
default:
return false;
}
tok* f_token = (tok*) node->nod_arg[0];
f_token->tok_symbol = HSH_lookup(f_token->tok_string);
tok* q_token = (tok*) node->nod_arg[1];
if (q_token)
q_token->tok_symbol = HSH_lookup(q_token->tok_string);
gpre_fld* field = NULL;
gpre_ctx* context = NULL;
if (request)
{
for (context = request->req_contexts; context; context = context->ctx_next)
{
if (!context->ctx_stream && (field = resolve(node, context, 0, &slice_action)))
break;
}
}
else
{
for (SSHORT i = 0; i < selection->rse_count; i++)
{
if (field = resolve(node, selection->rse_context[i], &context, &slice_action))
break;
}
}
if (!field)
{
SCHAR s[ERROR_LENGTH];
if (q_token)
fb_utils::snprintf(s, sizeof(s), "column \"%s.%s\" cannot be resolved",
q_token->tok_string, f_token->tok_string);
else
fb_utils::snprintf(s, sizeof(s), "column \"%s\" cannot be resolved",
f_token->tok_string);
PAR_error(s);
}
// Make sure that a dialect-1 program isn't trying to select a
// dialect-3-only field type.
if ((SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect) &&
(dtype_sql_date == field->fld_dtype || dtype_sql_time == field->fld_dtype ||
dtype_int64 == field->fld_dtype))
{
SQL_dialect1_bad_type(field->fld_dtype);
}
ref* reference = (ref*) MSC_alloc(REF_LEN);
reference->ref_field = field;
reference->ref_context = context;
reference->ref_slice = (slc*) slice_action;
*node_ptr = MSC_unary(nod_field, (gpre_nod*) reference);
return false;
}
//____________________________________________________________
//
// Parse a SELECT (sans keyword) expression.
//
gpre_rse* SQE_select(gpre_req* request, bool view_flag)
{
gpre_lls* context_stack = NULL;
gpre_ctx* context = 0;
bool have_union = false;
assert_IS_REQ(request);
map* const old_map = request->req_map;
// Get components of union. Most likely there isn't one, so this is
// probably wasted work.
gpre_rse* select = NULL;
gpre_rse* rse1 = NULL;
select = rse1 = par_select(request, NULL);
// "Look for ... the UNION label ... "
while (MSC_match(KW_UNION))
{
have_union = true;
const bool union_all = MSC_match(KW_ALL);
if (!MSC_match(KW_SELECT))
CPR_s_error("SELECT");
MSC_push((gpre_nod*) request->req_contexts, &context_stack);
request->req_contexts = NULL;
request->req_map = NULL;
gpre_rse* rse2 = par_select(request, rse1);
// We've got a bona fide union. Make a union node to hold sub-rse
// and then a new rse to point to it.
select = (gpre_rse*) MSC_alloc(RSE_LEN(1));
select->rse_context[0] = context = MSC_context(request);
gpre_nod* node = MSC_node(nod_union, 2);
select->rse_union = node;
node->nod_arg[0] = (gpre_nod*) rse1;
node->nod_arg[1] = (gpre_nod*) rse2;
map* new_map = (map*) MSC_alloc(sizeof(map));
rse1->rse_map = new_map;
new_map->map_context = context;
select->rse_fields = post_select_list(rse1->rse_fields, new_map);
rse2->rse_map = new_map = (map*) MSC_alloc(sizeof(map));
new_map->map_context = context;
post_select_list(rse2->rse_fields, new_map);
select->rse_into = rse1->rse_into;
if (!union_all)
select->rse_reduced = select->rse_fields;
// Result of this UNION might be the left side of the NEXT UNION
rse1 = select;
}
// Restore the context lists that were forgotten
// <context> holds the most recently allocated context, which is
// already linked into the request block
while (context_stack)
{
while (context->ctx_next)
context = context->ctx_next;
context->ctx_next = (gpre_ctx*) MSC_pop(&context_stack);
}
// Pick up any dangling ORDER clause
++request->req_in_order_by_clause;
par_order(request, select, have_union, view_flag);
--request->req_in_order_by_clause;
par_update(select, have_union, view_flag);
request->req_map = old_map;
return select;
}
//____________________________________________________________
//
// Parse either of the low precedence operators + and -.
//
gpre_nod* SQE_value(gpre_req* request, bool aster_ok, USHORT* paren_count, bool* bool_flag)
{
USHORT local_count;
bool local_flag;
assert_IS_REQ(request);
if (!paren_count)
{
local_count = 0;
paren_count = &local_count;
}
if (!bool_flag)
{
local_flag = false;
bool_flag = &local_flag;
}
MSC_match(KW_PLUS);
gpre_nod* node = par_multiply(request, aster_ok, paren_count, bool_flag);
assert_IS_NOD(node);
if (node->nod_type == nod_asterisk)
{
par_terminating_parens(paren_count, &local_count);
return node;
}
nod_t nod_type;
while (true)
{
if (MSC_match(KW_PLUS))
nod_type = nod_plus;
else if (MSC_match(KW_MINUS))
nod_type = nod_minus;
else if (MSC_match(KW_OR1))
nod_type = nod_concatenate;
else
{
par_terminating_parens(paren_count, &local_count);
return node;
}
node = MSC_binary(nod_type, node, par_multiply(request, false, paren_count, bool_flag));
}
}
//____________________________________________________________
//
// Parse either a literal NULL expression or a value
// expression.
//
gpre_nod* SQE_value_or_null(gpre_req* request, bool aster_ok, USHORT* paren_count, bool* bool_flag)
{
if (MSC_match(KW_NULL))
return MSC_node(nod_null, 0);
return SQE_value(request, aster_ok, paren_count, bool_flag);
}
//____________________________________________________________
//
// Parse host variable reference and, perhaps, a missing
// flag reference, which may be prefaced by the noiseword,
// "INDICATOR".
//
gpre_nod* SQE_variable(gpre_req* request, bool /*aster_ok*/, USHORT* /*paren_count*/, bool* /*bool_flag*/)
{
assert_IS_REQ(request);
if (!MSC_match(KW_COLON))
CPR_s_error("<colon>");
if (isQuoted(gpreGlob.token_global.tok_type))
CPR_s_error("<host variable>");
ref* reference = (ref*) MSC_alloc(REF_LEN);
// This loop is a waste of time because the flag SYM_variable is never activated
for (gpre_sym* symbol = gpreGlob.token_global.tok_symbol; symbol; symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_variable)
{
reference->ref_field = (gpre_fld*) symbol->sym_object;
break;
}
}
reference->ref_value = PAR_native_value(false, false);
MSC_match(KW_INDICATOR);
if (MSC_match(KW_COLON))
reference->ref_null_value = PAR_native_value(false, false);
return (gpre_nod*) reference;
}
//____________________________________________________________
//
// Compare two expressions symbollically. If they're the same,
// return true, otherwise false.
//
static bool compare_expr(const gpre_nod* node1, const gpre_nod* node2)
{
assert_IS_NOD(node1);
assert_IS_NOD(node2);
if (node1->nod_type != node2->nod_type)
return false;
switch (node1->nod_type)
{
case nod_field:
{
const ref* ref1 = (ref*) node1->nod_arg[0];
const ref* ref2 = (ref*) node2->nod_arg[0];
return ref1->ref_context == ref2->ref_context && ref1->ref_field == ref2->ref_field &&
ref1->ref_master == ref2->ref_master;
}
case nod_map_ref:
return node1->nod_arg[0] == node2->nod_arg[0];
case nod_udf:
case nod_gen_id:
return node1->nod_arg[0] == node2->nod_arg[0] && node1->nod_arg[1] == node2->nod_arg[1];
default:
return false;
}
}
//____________________________________________________________
//
// Copy a field list for a SELECT against an artificial context.
//
static gpre_nod* copy_fields( gpre_nod* fields, map* fields_map)
{
assert_IS_NOD(fields);
gpre_nod* list = MSC_node(nod_list, fields->nod_count);
for (USHORT i = 0; i < fields->nod_count; i++)
list->nod_arg[i] = post_fields(fields->nod_arg[i], fields_map);
return list;
}
//____________________________________________________________
//
// Expand an '*' in a field list to the corresponding fields.
//
static gpre_nod* explode_asterisk( gpre_nod* fields, int n, gpre_rse* selection)
{
TEXT s[ERROR_LENGTH];
assert_IS_NOD(fields);
gpre_nod* node = fields->nod_arg[n];
tok* q_token = (tok*) node->nod_arg[0];
if (q_token)
{
// expand for single relation
gpre_ctx* context = resolve_asterisk(q_token, selection);
if (context)
fields = merge_fields(fields, MET_fields(context), n, true);
else
{
sprintf(s, "columns \"%s.*\" cannot be resolved", q_token->tok_string);
PAR_error(s);
}
}
else
{
// expand for all relations in context list
fields = explode_asterisk_all(fields, n, selection, true);
}
return fields;
}
//____________________________________________________________
//
// Expand an '*' for all relations
// in the context list.
//
static gpre_nod* explode_asterisk_all(gpre_nod* fields,
int n,
gpre_rse* selection,
bool replace)
{
assert_IS_NOD(fields);
for (int i = 0; i < selection->rse_count; i++)
{
gpre_ctx* context = selection->rse_context[i];
const int old_count = fields->nod_count;
if (context->ctx_stream)
fields = explode_asterisk_all(fields, n, context->ctx_stream, replace);
else
fields = merge_fields(fields, MET_fields(context), n, replace);
n += fields->nod_count - old_count;
replace = false;
}
return fields;
}
//____________________________________________________________
//
// Get an element of an expression to act as a reference
// field for determining the data type of a host variable.
//
static gpre_fld* get_ref( gpre_nod* expr)
{
gpre_fld* field;
assert_IS_NOD(expr);
if (expr->nod_type == nod_via || expr->nod_type == nod_cast)
{
field = (gpre_fld*) MSC_alloc(FLD_LEN);
CME_get_dtype(expr, field);
if (field->fld_dtype && (field->fld_dtype <= dtype_any_text))
field->fld_flags |= FLD_text;
return field;
}
ref* reference;
switch (expr->nod_type)
{
case nod_field:
reference = (ref*) expr->nod_arg[0];
return reference->ref_field;
case nod_array:
reference = (ref*) expr->nod_arg[0];
return reference->ref_field->fld_array;
case nod_agg_count:
case nod_agg_max:
case nod_agg_min:
case nod_agg_total:
case nod_agg_average:
case nod_plus:
case nod_minus:
case nod_times:
case nod_divide:
case nod_negate:
case nod_upcase:
case nod_lowcase:
case nod_concatenate:
{
gpre_nod** ptr = expr->nod_arg;
for (const gpre_nod* const* const end = ptr + expr->nod_count; ptr < end; ptr++)
{
if (field = get_ref(*ptr))
return field;
}
break;
}
// Begin date/time/timestamp support
case nod_extract:
if (field = get_ref(expr->nod_arg[1]))
return field;
break;
// End date/time/timestamp support
case nod_map_ref:
{
mel* element = (mel*) expr->nod_arg[0];
gpre_nod* node = element->mel_expr;
return get_ref(node);
}
}
return 0;
}
//____________________________________________________________
//
// Finish off processing an implicit ANY clause. Assume that the word
// "select" has already been recognized. If the outer thing is a group
// by, so we're looking at a "having", re-resolve the input value to the
// map.
//
static gpre_nod* implicit_any(gpre_req* request,
gpre_nod* value,
nod_t comparison,
nod_t any_all)
{
gpre_nod* node;
scope previous_scope;
assert_IS_REQ(request);
assert_IS_NOD(value);
gpre_ctx* original = request->req_contexts;
request->req_in_subselect++;
push_scope(request, &previous_scope);
if (!(original->ctx_relation || original->ctx_procedure) && request->req_map)
value = post_fields(value, request->req_map);
// Handle the ALL and DISTINCT options
const bool distinct = (!MSC_match(KW_ALL) && MSC_match(KW_DISTINCT));
request->req_in_select_list++;
gpre_nod* value2 = SQE_value(request, false, NULL, NULL);
request->req_in_select_list--;
gpre_nod* field_list = MSC_node(nod_list, 1);
field_list->nod_arg[0] = value2;
gpre_rse* selection = par_rse(request, field_list, distinct);
value2 = selection->rse_fields->nod_arg[0];
gpre_rse* sub = selection->rse_aggregate;
if (sub)
{
if (validate_references(value2, sub->rse_group_by))
PAR_error("simple column reference not allowed in aggregate context");
if (sub->rse_group_by)
{
node = MSC_binary(comparison, value, value2);
pair(node->nod_arg[0], node->nod_arg[1]);
selection->rse_boolean = merge(selection->rse_boolean, node);
if (any_all == nod_ansi_all)
node = MSC_node(nod_ansi_all, 1);
else
node = MSC_node(nod_ansi_any, 1);
node->nod_count = 0;
node->nod_arg[0] = (gpre_nod*) selection;
}
else
{
gpre_nod* node2 = MSC_node(nod_via, 3);
node2->nod_count = 0;
node2->nod_arg[0] = (gpre_nod*) selection;
node2->nod_arg[2] = MSC_node(nod_null, 0);
node2->nod_arg[1] = value2;
node = MSC_binary(comparison, value, node2);
pair(node->nod_arg[0], node->nod_arg[1]);
}
}
else
{
node = MSC_binary(comparison, value, value2);
pair(node->nod_arg[0], node->nod_arg[1]);
selection->rse_boolean = merge(selection->rse_boolean, node);
if (any_all == nod_ansi_all)
node = MSC_node(nod_ansi_all, 1);
else
node = MSC_node(nod_ansi_any, 1);
node->nod_count = 0;
node->nod_arg[0] = (gpre_nod*) selection;
}
EXP_rse_cleanup(selection);
pop_scope(request, &previous_scope);
request->req_in_subselect--;
return node;
}
//____________________________________________________________
//
// Merge two (possibly null) booleans into a single conjunct.
//
static gpre_nod* merge( gpre_nod* expr1, gpre_nod* expr2)
{
if (!expr1)
return expr2;
if (!expr2)
return expr1;
assert_IS_NOD(expr1);
assert_IS_NOD(expr1);
return MSC_binary(nod_and, expr1, expr2);
}
//____________________________________________________________
//
// Merge 2 field lists
// 2nd list is added over nth entry in 1st list
// if replace is true, otherwise it is added
// after the nth entry.
//
static gpre_nod* merge_fields(gpre_nod* fields_1,
gpre_nod* fields_2,
int n,
bool replace)
{
assert_IS_NOD(fields_1);
assert_IS_NOD(fields_2);
int count = fields_1->nod_count + fields_2->nod_count;
if (replace)
count--;
gpre_nod* fields = MSC_node(nod_list, (SSHORT) count);
count = n;
if (!replace)
count++;
for (int i = 0; i < count; i++)
fields->nod_arg[i] = fields_1->nod_arg[i];
for (int i = 0; i < fields_2->nod_count; i++)
fields->nod_arg[i + count] = fields_2->nod_arg[i];
int offset = 0;
if (replace)
{
count++;
offset = 1;
}
for (int i = count; i < fields_1->nod_count; i++)
fields->nod_arg[i + fields_2->nod_count - offset] = fields_1->nod_arg[i];
return fields;
}
//____________________________________________________________
//
// Construct negation of expression.
//
static gpre_nod* negate( gpre_nod* expr)
{
assert_IS_NOD(expr);
return MSC_unary(nod_not, expr);
}
//____________________________________________________________
//
// Given two value expressions associated in a relational
// expression, see if one is a field reference and the other
// is a host language variable.. If so, match the field to the
// host language variable.
//
static void pair( gpre_nod* expr1, gpre_nod* expr2)
{
assert_IS_NOD(expr1);
assert_IS_NOD(expr2);
// Verify that an array field without subscripts is not
// being used inappropriately
if (expr1 && expr2)
{
if (expr1->nod_type == nod_array && !expr1->nod_arg[1])
PAR_error("Invalid array column reference");
if (expr2->nod_type == nod_array && !expr2->nod_arg[1])
PAR_error("Invalid array column reference");
}
gpre_fld* field = 0;
if (expr2)
field = get_ref(expr2);
if (!field)
field = get_ref(expr1);
if (!field)
return;
set_ref(expr1, field);
if (!expr2)
return;
gpre_fld* temp = get_ref(expr1);
if (temp)
field = temp;
set_ref(expr2, field);
}
//____________________________________________________________
//
// 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.
//
static gpre_ctx* par_alias_list( gpre_req* request, gpre_nod* alias_list)
{
assert_IS_REQ(request);
assert_IS_NOD(alias_list);
gpre_nod** arg = alias_list->nod_arg;
const gpre_nod* const* const 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
gpre_rel* relation = 0; // unreliable test many lines below without initializing.
gpre_ctx* context = par_alias(request, (const TEXT*) *arg);
if (context)
{
if (alias_list->nod_count == 1)
return context;
relation = context->ctx_relation;
}
SCHAR error_string[ERROR_LENGTH];
// 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 (context = request->req_contexts; context; context = context->ctx_next)
{
if (context->ctx_scope_level != request->req_scope_level)
continue;
if (!context->ctx_relation)
continue;
if (relation = par_base_table(request, context->ctx_relation, (const TEXT*) *arg))
{
break;
}
}
if (!context)
{
fb_utils::snprintf(error_string, sizeof(error_string),
"there is no alias or table named %s at this scope level",
(TEXT*) *arg);
PAR_error(error_string);
}
// 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 = par_base_table(request, relation, (const TEXT*) *arg)))
break;
}
if (!relation)
{
fb_utils::snprintf(error_string, sizeof(error_string),
"there is no alias or table named %s at this scope level",
(TEXT*) *arg);
PAR_error(error_string);
}
// make up a dummy context to hold the resultant relation
gpre_ctx* new_context = (gpre_ctx*) MSC_alloc(CTX_LEN);
new_context->ctx_request = request;
new_context->ctx_internal = context->ctx_internal;
new_context->ctx_relation = relation;
// concatenate all the contexts to form the alias name;
// calculate the length leaving room for spaces and a null
USHORT alias_length = alias_list->nod_count;
for (arg = alias_list->nod_arg; arg < end; arg++)
alias_length += strlen((TEXT*) *arg);
TEXT* alias = (TEXT*) MSC_alloc(alias_length);
// CVC: Warning: Using space as separator may conflict with dialect 3's embedded blanks;
// but gpre is not much worried about dialects... except in this file!
TEXT* p = new_context->ctx_alias = alias;
for (arg = alias_list->nod_arg; arg < end; arg++)
{
for (const TEXT* q = (TEXT*) *arg; *q;)
*p++ = *q++;
*p++ = ' ';
}
p[-1] = 0;
return new_context;
}
//____________________________________________________________
//
// The passed relation or alias represents
// a context which was previously specified
// in the from list. Find and return the
// proper context.
//
static gpre_ctx* par_alias( gpre_req* request, const TEXT* alias)
{
SCHAR error_string[ERROR_LENGTH];
assert_IS_REQ(request);
// look through all contexts at this scope level
// to find one that has a relation name or alias
// name which matches the identifier passed
gpre_ctx* relation_context = NULL;
for (gpre_ctx* context = request->req_contexts; context; context = context->ctx_next)
{
if (context->ctx_scope_level != request->req_scope_level)
continue;
// check for matching alias
if (context->ctx_alias)
{
const TEXT* p = context->ctx_alias;
const TEXT* q = alias;
for (; *p && *q; p++, q++)
{
if (UPPER(*p) != UPPER(*q))
break;
}
if (!*p && !*q)
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_symbol->sym_string, alias))
{
if (relation_context)
{
fb_utils::snprintf(error_string, sizeof(error_string),
"the table %s is referenced twice; use aliases to differentiate",
alias);
PAR_error(error_string);
}
relation_context = context;
}
}
return relation_context;
}
//____________________________________________________________
//
// Check if the relation in the passed context
// has a base table which matches the passed alias.
//
static gpre_rel* par_base_table( gpre_req* request, const gpre_rel* relation, const TEXT* alias)
{
assert_IS_REQ(request);
return MET_get_view_relation(request, relation->rel_symbol->sym_string, alias, 0);
}
//____________________________________________________________
//
// Parse a CASE clause
//
// There are two types of CASE clauses.
// 1) CASE WHEN (cond1) THEN value1 ... ELSE else_value END which is returned as nod_case
// In this case nod_arg[0] = cond1, nod_arg[1] = value1, ... nod_arg[n] = else_value
// 2) CASE value0 WHEN test_value1 THEN match_value1 ... ELSE else_value END which is
// returned as nod_case1
// In this case nod_arg[0] = value0, nod_arg[1] = test_value1, nod_arg[2] = match_value1, ...,
// nod_arg[n] = else_value
//
static gpre_nod* par_case(gpre_req* request)
{
gpre_lls* stack = NULL;
int count = 0;
nod_t nod_type;
if (MSC_match(KW_WHEN))
{
// Case 1 - CASE WHEN cond1 THEN value1 ...
nod_type = nod_case;
count++;
MSC_push(SQE_boolean(request, NULL), &stack);
while (MSC_match(KW_THEN))
{
count++;
MSC_push(SQE_value_or_null(request, false, NULL, NULL), &stack);
if (MSC_match(KW_WHEN))
{
count++;
MSC_push(SQE_boolean(request, NULL), &stack);
}
}
// If we have an odd number of nodes then we are missing a THEN
if ((count % 2) == 1)
{
CPR_s_error("THEN");
}
}
else
{
// Case 2 - CASE value0 WHEN test_value1 THEN match_value1 ...
nod_type = nod_case1;
count++;
MSC_push(SQE_value_or_null(request, false, NULL, NULL), &stack);
while (MSC_match(KW_WHEN))
{
count++;
MSC_push(SQE_value_or_null(request, false, NULL, NULL), &stack);
if (MSC_match(KW_THEN))
{
count++;
MSC_push(SQE_value_or_null(request, false, NULL, NULL), &stack);
}
else
{
CPR_s_error("THEN");
}
}
}
// ELSE else_value END
if (MSC_match(KW_ELSE))
{
count++;
MSC_push(SQE_value_or_null(request, false, NULL, NULL), &stack);
}
if (! MSC_match(KW_END))
{
CPR_s_error("END");
}
// Return a node containing all of the expressions parsed as part of CASE
gpre_nod* node = MSC_node(nod_type, (SSHORT) count);
gpre_nod** p = &node->nod_arg[count - 1];
while (stack)
{
*p-- = (gpre_nod*) MSC_pop(&stack);
}
return node;
}
//____________________________________________________________
//
// Parse an AND boolean expression.
//
static gpre_nod* par_and( gpre_req* request, USHORT* paren_count)
{
assert_IS_REQ(request);
gpre_nod* expr1 = par_not(request, paren_count);
if (!MSC_match(KW_AND))
return expr1;
return merge(expr1, par_and(request, paren_count));
}
//____________________________________________________________
//
//
static gpre_nod* par_collate( gpre_req* request, gpre_nod* arg)
{
assert_IS_REQ(request);
assert_IS_NOD(arg);
gpre_nod* node = MSC_node(nod_cast, 2);
node->nod_count = 1;
node->nod_arg[0] = arg;
gpre_fld* field = (gpre_fld*) MSC_alloc(FLD_LEN);
node->nod_arg[1] = (gpre_nod*) field;
CME_get_dtype(arg, field);
if (field->fld_dtype > dtype_any_text)
{
// cast expression to VARYING with implementation-defined
// maximum length
field->fld_dtype = dtype_varying;
field->fld_char_length = 30;
field->fld_length = 0; // calculated by SQL_adjust_field_dtype
field->fld_scale = 0;
field->fld_sub_type = 0;
}
else if (field->fld_sub_type) {
field->fld_character_set = MET_get_text_subtype(field->fld_sub_type);
}
SQL_par_field_collate(request, field);
SQL_adjust_field_dtype(field);
return node;
}
//____________________________________________________________
//
// Parse a SQL "IN" expression. This comes in two flavors:
//
// <value> IN (<value_comma_list>)
// <value> IN (SELECT <column> <from_nonsense>)
//
static gpre_nod* par_in( gpre_req* request, gpre_nod* value)
{
gpre_nod* node;
SCHAR s[ERROR_LENGTH];
assert_IS_REQ(request);
assert_IS_NOD(value);
EXP_left_paren(0);
// If the next token isn't SELECT, we must have the comma list flavor.
if (MSC_match(KW_SELECT))
node = implicit_any(request, value, nod_eq, nod_ansi_any);
else
{
node = NULL;
while (true)
{
gpre_nod* value2 = par_primitive_value(request, false, 0, NULL);
if (value2->nod_type == nod_value)
{
ref* ref2 = (ref*) value2->nod_arg[0];
if (value->nod_type == nod_field)
{
ref* ref1 = (ref*) value->nod_arg[0];
ref2->ref_field = ref1->ref_field;
}
else
{
fb_utils::snprintf(s, sizeof(s), "datatype of %s can not be determined",
gpreGlob.token_global.tok_string);
PAR_error(s);
}
}
if (!node)
node = MSC_binary(nod_eq, value, value2);
else
node = MSC_binary(nod_or, node, MSC_binary(nod_eq, value, value2));
if (!MSC_match(KW_COMMA))
break;
}
}
if (!EXP_match_paren())
return NULL;
return node;
}
//____________________________________________________________
//
// Parse a join relation clause.
//
static gpre_ctx* par_joined_relation( gpre_req* request)
{
gpre_ctx* context1;
assert_IS_REQ(request);
if (MSC_match(KW_LEFT_PAREN))
{
context1 = par_joined_relation(request);
EXP_match_paren();
}
else if (!(context1 = SQE_context(request)))
return NULL;
return par_join_clause(request, context1);
}
//____________________________________________________________
//
// Parse a join relation clause.
//
static gpre_ctx* par_join_clause( gpre_req* request, gpre_ctx* context1)
{
assert_IS_REQ(request);
const nod_t join_type = par_join_type();
if (join_type == nod_nothing)
return context1;
gpre_ctx* context2 = par_joined_relation(request);
if (!context2)
CPR_s_error("<joined table clause>");
if (!MSC_match(KW_ON))
CPR_s_error("ON");
gpre_nod* node = SQE_boolean(request, NULL);
gpre_rse* selection = (gpre_rse*) MSC_alloc(RSE_LEN(2));
selection->rse_count = 2;
selection->rse_context[0] = context1;
selection->rse_context[1] = context2;
selection->rse_boolean = node;
selection->rse_join_type = join_type;
context1 = MSC_context(request);
context1->ctx_stream = selection;
return par_join_clause(request, context1);
}
//____________________________________________________________
//
// Parse a join type.
//
static nod_t par_join_type()
{
nod_t nod_type;
if (MSC_match(KW_INNER))
nod_type = nod_join_inner;
else if (MSC_match(KW_LEFT))
nod_type = nod_join_left;
else if (MSC_match(KW_RIGHT))
nod_type = nod_join_right;
else if (MSC_match(KW_FULL))
nod_type = nod_join_full;
else if (MSC_match(KW_JOIN))
return nod_join_inner;
else
return nod_nothing;
if (nod_type != nod_join_inner)
MSC_match(KW_OUTER);
if (!MSC_match(KW_JOIN))
CPR_s_error("JOIN");
return nod_type;
}
//____________________________________________________________
//
// Parse either of the high precedence operators * and /.
//
static gpre_nod* par_multiply(gpre_req* request, bool aster_ok, USHORT* paren_count, bool* bool_flag)
{
assert_IS_REQ(request);
gpre_nod* node = par_primitive_value(request, aster_ok, paren_count, bool_flag);
if (node->nod_type == nod_asterisk)
return node;
if (gpreGlob.token_global.tok_keyword == KW_COLLATE)
return par_collate(request, node);
nod_t nod_type;
while (true)
{
if (MSC_match(KW_ASTERISK))
nod_type = nod_times;
else if (MSC_match(KW_SLASH))
nod_type = nod_divide;
else
return node;
node = MSC_binary(nod_type, node, par_primitive_value(request, false, paren_count, bool_flag));
}
}
//____________________________________________________________
//
// Parse an NOT boolean expression.
//
static gpre_nod* par_not( gpre_req* request, USHORT* paren_count)
{
assert_IS_REQ(request);
if (MSC_match(KW_NOT))
return negate(par_not(request, paren_count));
nod_t type = nod_nothing;
if (MSC_match(KW_EXISTS))
type = nod_any;
else if (MSC_match(KW_SINGULAR))
type = nod_unique;
if (type == nod_any || type == nod_unique)
{
scope saved_scope;
push_scope(request, &saved_scope);
EXP_left_paren(0);
if (!MSC_match(KW_SELECT))
CPR_s_error("SELECT");
request->req_in_select_list++;
gpre_nod* field;
if (MSC_match(KW_ASTERISK))
field = NULL;
else if (!(field = par_udf(request)))
field = SQE_field(request, false);
request->req_in_select_list--;
gpre_nod* node = MSC_node(type, 1);
node->nod_count = 0;
gpre_rse* selection = par_rse(request, 0, false);
node->nod_arg[0] = (gpre_nod*) selection;
if (field)
{
SQE_resolve(&field, 0, selection);
gpre_nod* expr = MSC_unary(nod_missing, field);
selection->rse_boolean = merge(negate(expr), selection->rse_boolean);
}
EXP_rse_cleanup((gpre_rse*) node->nod_arg[0]);
pop_scope(request, &saved_scope);
EXP_match_paren();
return node;
}
return par_relational(request, paren_count);
}
//____________________________________________________________
//
// Parse NULLIF built-in function.
//
// NULLIF(exp1, exp2) is really just a shortcut for
// CASE exp1 WHEN exp2 THEN NULL ELSE exp1 END, so
// we generate a nod_case1 node.
static gpre_nod* par_nullif(gpre_req* request)
{
gpre_nod* node = MSC_node(nod_case1, 4);
EXP_left_paren(0);
node->nod_arg[0] = SQE_value(request, false, NULL, NULL);
if (! MSC_match(KW_COMMA))
CPR_s_error("comma");
node->nod_arg[1] = SQE_value(request, false, NULL, NULL);
node->nod_arg[2] = MSC_node(nod_null, 0);
node->nod_arg[3] = node->nod_arg[0];
USHORT local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
//____________________________________________________________
//
// Parse ORDER clause of SELECT expression. This is
// particularly difficult since the ORDER clause can
// refer to fields by position.
//
static void par_order(gpre_req* request, gpre_rse* select, bool union_f, bool view_flag)
{
gpre_nod* sort;
USHORT i;
assert_IS_REQ(request);
if (!MSC_match(KW_ORDER))
return;
if (view_flag)
PAR_error("sort clause not allowed in a view definition");
MSC_match(KW_BY);
gpre_lls* items = NULL;
gpre_lls* directions = NULL;
int count = 0;
bool direction = false;
gpre_nod* values = select->rse_fields;
while (true)
{
direction = false;
if (gpreGlob.token_global.tok_type == tok_number)
{
i = EXP_USHORT_ordinal(false);
if (i < 1 || i > values->nod_count)
CPR_s_error("<ordinal column position>");
sort = values->nod_arg[i - 1];
PAR_get_token();
if (gpreGlob.token_global.tok_keyword == KW_COLLATE)
sort = par_collate(request, sort);
}
else
{
if (union_f)
CPR_s_error("<column position in union>");
sort = SQE_value(request, false, NULL, NULL);
map* request_map;
if (request && (request_map = request->req_map))
sort = post_map(sort, request_map);
}
if (MSC_match(KW_ASCENDING))
direction = false;
else if (MSC_match(KW_DESCENDING))
direction = true;
count++;
MSC_push((gpre_nod*)(IPTR) direction, &directions);
MSC_push(sort, &items);
if (!MSC_match(KW_COMMA))
break;
}
select->rse_sort = sort = MSC_node(nod_sort, (SSHORT) (count * 2));
sort->nod_count = count;
gpre_nod** ptr = sort->nod_arg + count * 2;
while (items)
{
*--ptr = (gpre_nod*) MSC_pop(&items);
*--ptr = (gpre_nod*) MSC_pop(&directions);
}
}
//____________________________________________________________
//
// Allow the user to specify the access plan
// for a query as part of a select expression.
//
static gpre_nod* par_plan( gpre_req* request)
{
assert_IS_REQ(request);
// parse the join type
nod_t nod_type;
if (MSC_match(KW_JOIN))
nod_type = nod_join;
else if (MSC_match(KW_MERGE))
nod_type = nod_merge;
else if (MSC_match(KW_SORT) && MSC_match(KW_MERGE))
nod_type = nod_merge;
else
nod_type = nod_join;
// make up the plan expression node
gpre_nod* plan_expression = MSC_node(nod_plan_expr, 2);
if (nod_type != nod_join)
plan_expression->nod_arg[0] = MSC_node(nod_type, 0);
// parse the plan items at this level
EXP_left_paren(0);
plan_expression->nod_arg[1] = SQE_list(par_plan_item, request, false);
if (!EXP_match_paren())
return NULL;
return plan_expression;
}
//____________________________________________________________
//
// Parse an individual plan item for an
// access plan.
//
static gpre_nod* par_plan_item(gpre_req* request, bool /*aster_ok*/, USHORT* /*paren_count*/, bool* /*bool_flag*/)
{
assert_IS_REQ(request);
// check for a plan expression
tok& token = gpreGlob.token_global;
switch (token.tok_keyword)
{
case KW_JOIN:
case KW_SORT:
case KW_MERGE:
case KW_LEFT_PAREN:
return par_plan(request);
}
// parse the list of one or more table names or
// aliases (more than one is used when there is
// a need to differentiate base tables of a view)
int count;
gpre_lls* stack = NULL;
for (count = 0; token.tok_type == tok_ident; count++)
{
if (token.tok_keyword == KW_NATURAL || token.tok_keyword == KW_ORDER ||
token.tok_keyword == KW_INDEX)
{
break;
}
MSC_push((gpre_nod*) upcase_string(token.tok_string), &stack);
PAR_get_token();
}
if (!count)
CPR_s_error("<table name> or <alias>");
gpre_nod** ptr;
gpre_nod* alias_list = MSC_node(nod_list, (SSHORT) count);
for (ptr = &alias_list->nod_arg[count]; stack;)
*--ptr = (gpre_nod*) MSC_pop(&stack);
// lookup the contexts for the aliases
gpre_nod* index_list = NULL;
gpre_ctx* context = par_alias_list(request, alias_list);
// parse the access type
gpre_nod* access_type = NULL;
switch (token.tok_keyword)
{
case KW_NATURAL:
access_type = MSC_node(nod_natural, 0);
PAR_get_token();
break;
case KW_ORDER:
access_type = MSC_node(nod_index_order, 1);
access_type->nod_count = 0;
PAR_get_token();
if (token.tok_type != tok_ident)
CPR_s_error("<index name>");
access_type->nod_arg[0] = (gpre_nod*) upcase_string(token.tok_string);
PAR_get_token();
break;
case KW_INDEX:
access_type = MSC_node(nod_index, 1);
access_type->nod_count = 0;
PAR_get_token();
EXP_left_paren(0);
stack = NULL;
for (count = 0; token.tok_type == tok_ident;)
{
MSC_push((gpre_nod*) upcase_string(token.tok_string), &stack);
PAR_get_token();
count++;
if (!MSC_match(KW_COMMA))
break;
}
if (!count)
CPR_s_error("<table name> or <alias>");
index_list = MSC_node(nod_list, (SSHORT) count);
access_type->nod_arg[0] = index_list;
for (ptr = &index_list->nod_arg[count]; stack;)
*--ptr = (gpre_nod*) MSC_pop(&stack);
if (!EXP_match_paren())
return NULL;
break;
default:
CPR_s_error("NATURAL, ORDER, or INDEX");
}
// generate the plan item node
gpre_nod* plan_item = MSC_node(nod_plan_item, 3);
plan_item->nod_count = 2;
plan_item->nod_arg[0] = alias_list;
plan_item->nod_arg[1] = access_type;
plan_item->nod_arg[2] = (gpre_nod*) context;
return plan_item;
}
//____________________________________________________________
//
// Parse a value expression. The value could be any of the
// following:
//
// "quoted string"
// _CHARSET"quoted string"
// 123
// +1.234E-3
// field
// relation.field
// context.field
// user defined function
//
static gpre_nod* par_primitive_value(gpre_req* request, bool aster_ok,
USHORT* paren_count, bool* bool_flag)
{
USHORT local_count;
assert_IS_REQ(request);
gpre_nod* node = 0;
if (!paren_count)
{
local_count = 0;
paren_count = &local_count;
}
if (MSC_match(KW_SELECT))
return par_stat(request);
if (MSC_match(KW_MINUS))
return MSC_unary(nod_negate, par_primitive_value(request, false, paren_count, false));
MSC_match(KW_PLUS);
if (MSC_match(KW_USER)) {
return MSC_node(nod_user_name, 0);
}
if (MSC_match(KW_VALUE))
{
// If request is NULL we must be processing a subquery - and
// without the request to refer to we're kinda hosed
if (!request)
PAR_error("VALUE cannot be used in this context");
const act* action = request->req_actions;
if (request->req_type != REQ_ddl || !action ||
!(action->act_type == ACT_create_domain || action->act_type == ACT_alter_domain))
{
PAR_error("VALUE cannot be used in this context");
}
return MSC_node(nod_dom_value, 0);
}
if (MSC_match(KW_LEFT_PAREN))
{
(*paren_count)++;
if (bool_flag && *bool_flag)
node = SQE_boolean(request, paren_count);
else
node = SQE_value(request, false, paren_count, bool_flag);
EXP_match_paren();
(*paren_count)--;
return node;
}
// Check for an aggregate statistical expression. If we already have a
// map defined for the request, we're part of either HAVING or a trailing
// ORDER clause. In this case, post only the complete expression, and not
// the sub-expressions.
map* tmp_map = NULL;
for (const ops* op = stat_ops; op->rel_kw != KW_none; op++)
{
MSC_match(KW_ALL);
if (MSC_match(op->rel_kw))
{
if (request &&
(request->req_in_aggregate ||
!(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 (in any subquery)
PAR_error("Invalid aggregate reference");
}
node = MSC_node(op->rel_op, 2);
node->nod_count = 1;
EXP_left_paren("left parenthesis in statistical function");
const bool distinct = MSC_match(KW_DISTINCT);
if (request)
{
tmp_map = request->req_map;
request->req_map = NULL;
++request->req_in_aggregate;
}
if (node->nod_type == nod_agg_count && MSC_match(KW_ASTERISK))
node->nod_count = 0;
else
{
node->nod_arg[0] = SQE_value(request, false, NULL, NULL);
// Disallow arrays as arguments to aggregate functions
const gpre_nod* node_arg = node->nod_arg[0];
if (node_arg && node_arg->nod_type == nod_array)
PAR_error("Array columns not permitted in aggregate functions");
}
if (distinct)
node->nod_arg[1] = node->nod_arg[0];
EXP_match_paren();
if (request)
{
if (tmp_map)
node = post_map(node, tmp_map);
request->req_map = tmp_map;
--request->req_in_aggregate;
}
return node;
}
}
// If it's a number or a quoted string, it's a literal
if (gpreGlob.token_global.tok_type == tok_number ||
(isQuoted(gpreGlob.token_global.tok_type) && gpreGlob.sw_sql_dialect == 1) ||
gpreGlob.token_global.tok_type == tok_sglquoted)
{
node = EXP_literal();
return node;
}
// moved this timestamp support down some lines, because it caused
// gpre to segfault when it was done here.
// FSG 15.Nov.2000
// If the next token is a colon, it is a variable reference
if (gpreGlob.token_global.tok_keyword == KW_COLON)
{
if (!request)
{
// We must be processing a subquery - and without the request to
// post the :hostvar to we can't continue.
// (core dump when de-refer of NULL request)
PAR_error(":hostvar reference not supported in this context");
return NULL;
}
ref* reference = (ref*) SQE_variable(request, false, NULL, NULL);
node = MSC_unary(nod_value, (gpre_nod*) reference);
reference->ref_next = request->req_values;
request->req_values = reference;
return node;
}
// Must be a field or a udf. If there is a map, post the field to it.
node = par_udf_or_field(request, aster_ok);
//if (request && (map = request->req_map))
// return post_map (node, map);
// I don't know what it's good for, but let's try it anyway if we haven't found
// anything that makes sense until now
if (!node)
{
// Begin date/time/timestamp support
const kwwords_t kw_word = gpreGlob.token_global.tok_keyword;
if (MSC_match(KW_DATE) || MSC_match(KW_TIME) || MSC_match(KW_TIMESTAMP))
{
gpreGlob.token_global.tok_keyword = kw_word;
node = EXP_literal();
return node;
}
// End date/time/timestamp support
}
return node;
}
//____________________________________________________________
//
// Parse relational expression.
//
static gpre_nod* par_relational(gpre_req* request,
USHORT* paren_count)
{
assert_IS_REQ(request);
bool local_flag = true;
gpre_nod* expr1 = SQE_value(request, false, paren_count, &local_flag);
if (gpreGlob.token_global.tok_keyword == KW_RIGHT_PAREN)
return expr1;
if (gpreGlob.token_global.tok_keyword == KW_SEMI_COLON)
{
for (const nod_t* relational_ops = relationals; *relational_ops != nod_nothing;
relational_ops++)
{
if (expr1->nod_type == *relational_ops)
return expr1;
}
}
bool negation = false;
if (MSC_match(KW_NOT))
negation = true;
// Check for one of the binary operators
gpre_nod* node;
if (MSC_match(KW_IN))
node = par_in(request, expr1);
else if (MSC_match(KW_BETWEEN))
{
node = MSC_node(nod_between, 3);
node->nod_arg[0] = expr1;
node->nod_arg[1] = SQE_value(request, false, NULL, NULL);
MSC_match(KW_AND);
node->nod_arg[2] = SQE_value(request, false, NULL, NULL);
pair(node->nod_arg[0], node->nod_arg[1]);
pair(node->nod_arg[0], node->nod_arg[2]);
}
else if (MSC_match(KW_LIKE))
{
node = MSC_node(nod_like, 3);
node->nod_arg[0] = expr1;
node->nod_arg[1] = SQE_value(request, false, NULL, NULL);
pair(node->nod_arg[0], node->nod_arg[1]);
if (MSC_match(KW_ESCAPE))
{
gpre_nod* expr2 = SQE_value(request, false, NULL, NULL);
node->nod_arg[2] = expr2;
if (expr2->nod_type == nod_value)
{
ref* ref_value = (ref*) expr2->nod_arg[0];
ref_value->ref_field = MET_make_field("like_escape_character", dtype_text, 2, false);
}
}
else
node->nod_count = 2;
}
else if (MSC_match(KW_IS))
{
if (MSC_match(KW_NOT))
negation = !negation;
if (MSC_match(KW_NULL))
{
if (expr1->nod_type == nod_array)
expr1->nod_type = nod_field;
node = MSC_unary(nod_missing, expr1);
}
else if (MSC_match(KW_DISTINCT))
{
if (!MSC_match(KW_FROM))
CPR_s_error("FROM");
node = MSC_binary(nod_equiv, expr1, SQE_value(request, false, NULL, NULL));
pair(node->nod_arg[0], node->nod_arg[1]);
}
else
CPR_s_error("NULL or DISTINCT");
}
else
{
node = NULL;
const ops* op;
for (op = rel_ops; op->rel_kw != KW_none; op++)
{
if (MSC_match(op->rel_kw))
break;
}
if (op->rel_kw == KW_none)
{
for (const nod_t* relational_ops = relationals;
*relational_ops != nod_nothing; relational_ops++)
{
if (expr1->nod_type == *relational_ops)
return expr1;
}
CPR_s_error("<relational operator>");
}
if (op->rel_kw == KW_STARTING)
MSC_match(KW_WITH);
if (MSC_match(KW_ANY))
{
if (!MSC_match(KW_LEFT_PAREN) || !MSC_match(KW_SELECT))
CPR_s_error("<select clause> for ANY");
node = implicit_any(request, expr1, op->rel_op, nod_any);
EXP_match_paren();
}
else if (MSC_match(KW_ALL))
{
if (!MSC_match(KW_LEFT_PAREN) || !MSC_match(KW_SELECT))
CPR_s_error("<select clause> for ALL");
if (op->rel_negation == nod_any || op->rel_negation == nod_ansi_any ||
op->rel_negation == nod_ansi_all)
{
CPR_s_error("<relational operator> for ALL");
}
node = implicit_any(request, expr1, op->rel_op, nod_ansi_all);
EXP_match_paren();
}
else
{
node = MSC_binary(op->rel_op, expr1, SQE_value(request, false, NULL, NULL));
pair(node->nod_arg[0], node->nod_arg[1]);
}
}
return negation ? negate(node) : node;
}
// Out of alphabetical order.
static bool resolve_fields(gpre_nod*& fields, gpre_rse* selection)
{
bool aggregate = false;
gpre_nod** ptr = fields->nod_arg;
int count = fields->nod_count;
for (int i = 0; i < count; i++)
{
gpre_nod*& node = ptr[i];
if (node->nod_type == nod_asterisk)
{
const int old_count = count;
fields = explode_asterisk(fields, i, selection);
count = fields->nod_count;
i += count - old_count;
ptr = fields->nod_arg;
}
else
{
aggregate |= SQE_resolve(&node, NULL, selection);
pair(node, 0);
}
}
return aggregate;
}
//____________________________________________________________
//
// Parse the SQL equivalent of a record selection expression --
// FROM, WHERE, and ORDER clauses. A field list may or may not
// be present.
//
static gpre_rse* par_rse(gpre_req* request, gpre_nod* fields, bool distinct)
{
gpre_lls* stack = NULL;
assert_IS_REQ(request);
assert_IS_NOD(fields);
// Get list and count of relations
if (!MSC_match(KW_FROM))
CPR_s_error("FROM");
int count = 0;
gpre_ctx* context;
do {
if (context = par_joined_relation(request))
{
MSC_push((gpre_nod*) context, &stack);
count++;
}
else
return NULL;
} while (MSC_match(KW_COMMA));
// Now allocate a record select expression
// block for the beast and fill in what we already know.
gpre_rse* select = (gpre_rse*) MSC_alloc(RSE_LEN(count));
select->rse_count = count;
while (count--)
select->rse_context[count] = (gpre_ctx*) MSC_pop(&stack);
// If a field list has been presented, resolve references now
bool aggregate = false;
if (fields)
aggregate = resolve_fields(fields, select);
select->rse_fields = fields;
if (distinct)
select->rse_reduced = fields;
// Handle a boolean, if present
if (MSC_match(KW_WITH))
{
++request->req_in_where_clause;
select->rse_boolean = SQE_boolean(request, 0);
--request->req_in_where_clause;
}
if (MSC_match(KW_GROUP))
{
MSC_match(KW_BY);
select->rse_group_by = SQE_list(par_udf_or_field_with_collate, request, false);
gpre_nod** ptr = select->rse_group_by->nod_arg;
for (const gpre_nod* const* const end = ptr + select->rse_group_by->nod_count;
ptr < end; ptr++)
{
if ((*ptr)->nod_type == nod_array)
PAR_error("Array columns not permitted in GROUP BY clause");
}
}
if (select->rse_group_by || aggregate)
{
if (validate_references(select->rse_fields, select->rse_group_by))
PAR_error("simple column reference not allowed in aggregate context");
gpre_rse* sub_rse = select;
map* subselect_map = (map*) MSC_alloc(sizeof(map));
sub_rse->rse_map = subselect_map;
if (select->rse_group_by)
request->req_map = subselect_map;
subselect_map->map_context = MSC_context(request);
select = (gpre_rse*) MSC_alloc(RSE_LEN(0));
select->rse_aggregate = sub_rse;
if (fields)
select->rse_fields = copy_fields(sub_rse->rse_fields, subselect_map);
if (MSC_match(KW_HAVING))
{
++request->req_in_having_clause;
select->rse_boolean = SQE_boolean(request, 0);
--request->req_in_having_clause;
if (validate_references(select->rse_boolean, sub_rse->rse_group_by))
{
PAR_error("simple column reference in HAVING must be referenced in GROUP BY");
}
}
}
// parse a user-specified access plan
if (MSC_match(KW_PLAN))
select->rse_plan = par_plan(request);
return select;
}
//____________________________________________________________
//
// Parse a SELECT (sans keyword) expression (except UNION). This
// is called exclusively by SQE_select, which handles unions. Note:
// if "union_rse" is non-null, we are a subsequent SELECT in a union.
// In this case, check datatypes of the field against the rse field
// list.
//
static gpre_rse* par_select( gpre_req* request, gpre_rse* union_rse)
{
assert_IS_REQ(request);
// Handle FIRST and SKIP clauses
gpre_nod* rse_first = NULL;
if (MSC_match(KW_FIRST))
{
rse_first = MSC_node(nod_list, 1);
rse_first->nod_arg[0] = SQE_value(request, false, NULL, NULL);
}
gpre_nod* rse_skip = NULL;
if (MSC_match(KW_SKIP))
{
rse_skip = MSC_node(nod_list, 1);
rse_skip->nod_arg[0] = SQE_value(request, false, NULL, NULL);
}
// Handle the ALL and DISTINCT options
const bool distinct = (!MSC_match(KW_ALL) && MSC_match(KW_DISTINCT));
// Make select list out of select items
++request->req_in_select_list;
gpre_nod* s_list = SQE_list(SQE_value_or_null, request, true);
--request->req_in_select_list;
// If this is not a declare cursor statement and an INTO list is present,
// parse it.
gpre_nod* into_list = NULL;
if (!(request->req_flags & REQ_sql_declare_cursor))
{
into_list = MSC_match(KW_INTO) ? SQE_list(SQE_variable, request, false) : NULL;
}
gpre_rse* select = par_rse(request, s_list, distinct);
if (rse_first)
resolve_fields(rse_first, select);
select->rse_sqlfirst = rse_first;
if (rse_skip)
resolve_fields(rse_skip, select);
select->rse_sqlskip = rse_skip;
if (select->rse_into = into_list)
select->rse_flags |= RSE_singleton;
if (union_rse && s_list->nod_count != union_rse->rse_fields->nod_count)
PAR_error("select lists for UNION don't match");
return select;
}
//____________________________________________________________
//
// Parse a dumb SQL scalar statistical expression. Somebody else
// has already eaten the SELECT on the front.
//
static gpre_nod* par_stat( gpre_req* request)
{
assert_IS_REQ(request);
request->req_in_subselect++;
scope previous_scope;
push_scope(request, &previous_scope);
const bool distinct = (!MSC_match(KW_ALL) && MSC_match(KW_DISTINCT));
request->req_in_select_list++;
gpre_nod* item = par_udf(request);
if (!item)
item = SQE_value(request, false, NULL, NULL);
request->req_in_select_list--;
gpre_nod* field_list = MSC_node(nod_list, 1);
field_list->nod_arg[0] = item;
gpre_rse* select = par_rse(request, field_list, distinct);
select->rse_flags |= RSE_singleton;
item = select->rse_fields->nod_arg[0];
gpre_nod* node = MSC_node(nod_via, 3);
node->nod_count = 0;
node->nod_arg[0] = (gpre_nod*) select;
node->nod_arg[2] = MSC_node(nod_null, 0);
node->nod_arg[1] = item;
EXP_rse_cleanup(select);
pop_scope(request, &previous_scope);
request->req_in_subselect--;
return node;
}
//____________________________________________________________
//
// Parse a subscript value.
//
static gpre_nod* par_subscript( gpre_req* request)
{
assert_IS_REQ(request);
ref* reference = (ref*) MSC_alloc(REF_LEN);
gpre_nod* node = MSC_unary(nod_value, (gpre_nod*) reference);
// Special case literals
if (gpreGlob.token_global.tok_type == tok_number)
{
node->nod_type = nod_literal;
char* string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1);
reference->ref_value = string;
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
PAR_get_token();
return node;
}
if (!MSC_match(KW_COLON))
CPR_s_error("<colon>");
reference->ref_value = PAR_native_value(false, false);
if (request)
{
reference->ref_next = request->req_values;
request->req_values = reference;
}
return node;
}
//____________________________________________________________
//
// Parse the SUBSTRING built-in function
//
static gpre_nod* par_substring(gpre_req* request)
{
gpre_nod* node = MSC_node(nod_substring, 3);
EXP_left_paren(0);
node->nod_arg[0] = SQE_value(request, false, NULL, NULL);
if (! MSC_match(KW_FROM))
CPR_s_error("FROM");
node->nod_arg[1] = EXP_literal();
if (! node->nod_arg[1])
CPR_s_error("numeric literal");
const ref* reference = (ref*) node->nod_arg[1]->nod_arg[0];
const TEXT* string = reference->ref_value;
if (*string == '"' || *string == '\'')
CPR_s_error("numeric literal");
if (MSC_match(KW_FOR))
{
node->nod_arg[2] = EXP_literal();
if (! node->nod_arg[2])
CPR_s_error("numeric literal");
reference = (ref*) node->nod_arg[2]->nod_arg[0];
string = reference->ref_value;
if ((*string == '"') || (*string == '\''))
CPR_s_error("numeric literal");
}
else
{
// No FOR clause given, fake up a numeric literal of 32767 (the max length
// of a VarChar) to force copying to end of string.
ref* reference1 = (ref*) MSC_alloc(REF_LEN);
node->nod_arg[2] = MSC_unary(nod_literal, (gpre_nod*) reference1);
string = (TEXT*) MSC_alloc(6);
MSC_copy("32767", 5, (char*) string);
reference1->ref_value = string;
}
USHORT local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
//____________________________________________________________
//
// Match several trailing parentheses.
//
static void par_terminating_parens(USHORT* paren_count, USHORT* local_count)
{
// Suspicious condition.
if (*paren_count && paren_count == local_count)
do {
EXP_match_paren();
} while (--(*paren_count));
}
//____________________________________________________________
//
// Parse a user defined function. If the current token isn't one,
// return NULL. Otherwise try to parse one. If things go badly,
// complain bitterly.
//
static gpre_nod* par_udf( gpre_req* request)
{
if (!request)
return NULL;
assert_IS_REQ(request);
// Check for user defined functions
// resolve only if an identifier
if ((isQuoted(gpreGlob.token_global.tok_type)) || gpreGlob.token_global.tok_type == tok_ident)
SQL_resolve_identifier("<Udf Name>", NULL, NAME_SIZE);
gpre_nod* node;
USHORT local_count;
udf* an_udf;
if (request->req_database)
an_udf = MET_get_udf(request->req_database, gpreGlob.token_global.tok_string);
else
{
// no database was specified, check the metadata for all the databases
// for the existence of the udf
an_udf = NULL;
for (gpre_dbb* db = gpreGlob.isc_databases; db; db = db->dbb_next)
{
udf* tmp_udf = MET_get_udf(db, gpreGlob.token_global.tok_string);
if (tmp_udf)
if (an_udf)
{
// udf was found in more than one database
SCHAR s[ERROR_LENGTH];
sprintf(s, "UDF %s is ambiguous", gpreGlob.token_global.tok_string);
PAR_error(s);
}
else
{
an_udf = tmp_udf;
request->req_database = db;
}
}
}
if (an_udf)
{
if ((SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect) &&
(dtype_sql_date == an_udf->udf_dtype ||
dtype_sql_time == an_udf->udf_dtype ||
dtype_int64 == an_udf->udf_dtype))
{
SQL_dialect1_bad_type(an_udf->udf_dtype);
}
node = MSC_node(nod_udf, 2);
node->nod_count = 1;
node->nod_arg[1] = (gpre_nod*) an_udf;
PAR_get_token();
EXP_left_paren(0);
if (gpreGlob.token_global.tok_keyword != KW_RIGHT_PAREN)
{
// parse udf parameter references
node->nod_arg[0] = SQE_list(SQE_value, request, false);
if (an_udf->udf_args != node->nod_arg[0]->nod_count)
PAR_error("count of UDF parameters doesn't match definition");
// Match parameter types to the declared parameters
gpre_nod** input = node->nod_arg[0]->nod_arg;
for (gpre_fld* field = an_udf->udf_inputs; field; input++, field = field->fld_next)
{
SQE_post_field(*input, field);
}
}
else
{
node->nod_arg[0] = (gpre_nod*) 0;
node->nod_count = 0;
}
local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
if (!request)
return NULL;
// Check for GEN_ID ()
if (MSC_match(KW_GEN_ID))
{
TEXT* gen_name = (TEXT *) MSC_alloc(NAME_SIZE);
node = MSC_node(nod_gen_id, 2);
node->nod_count = 1;
EXP_left_paren(0);
SQL_resolve_identifier("<Generator Name>", gen_name, NAME_SIZE);
node->nod_arg[1] = (gpre_nod*) gen_name;
PAR_get_token();
if (!MSC_match(KW_COMMA))
CPR_s_error("<comma>");
node->nod_arg[0] = SQE_value(request, false, NULL, NULL);
local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
// Check for context variables
// Begin date/time/timestamp
if (MSC_match(KW_CURRENT_DATE))
return MSC_node(nod_current_date, 0);
if (MSC_match(KW_CURRENT_TIME))
return MSC_node(nod_current_time, 0);
if (MSC_match(KW_CURRENT_TIMESTAMP))
return MSC_node(nod_current_timestamp, 0);
// End date/time/timestamp
if (MSC_match(KW_CURRENT_CONNECTION))
return MSC_node(nod_current_connection, 0);
if (MSC_match(KW_CURRENT_ROLE))
return MSC_node(nod_current_role, 0);
if (MSC_match(KW_CURRENT_TRANSACTION))
return MSC_node(nod_current_transaction, 0);
if (MSC_match(KW_CURRENT_USER))
return MSC_node(nod_user_name, 0);
// End context variables
// Check for SQL II defined functions
// Begin date/time/timestamp
if (MSC_match(KW_EXTRACT))
{
node = MSC_node(nod_extract, 2);
EXP_left_paren(0);
const kwwords_t kw_word = gpreGlob.token_global.tok_keyword;
if (MSC_match(KW_YEAR) || MSC_match(KW_MONTH) || MSC_match(KW_DAY) ||
MSC_match(KW_HOUR) || MSC_match(KW_MINUTE) || MSC_match(KW_SECOND) ||
MSC_match(KW_WEEKDAY) || MSC_match(KW_YEARDAY))
{
node->nod_arg[0] = (gpre_nod*) kw_word;
if (!MSC_match(KW_FROM))
CPR_s_error("FROM");
}
else
CPR_s_error("valid extract part");
node->nod_arg[1] = SQE_value(request, false, NULL, NULL);
local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
// End date/time/timestamp
if (MSC_match(KW_UPPER))
{
node = MSC_node(nod_upcase, 1);
EXP_left_paren(0);
node->nod_arg[0] = SQE_value(request, false, NULL, NULL);
local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
if (MSC_match(KW_LOWER))
{
node = MSC_node(nod_lowcase, 1);
EXP_left_paren(0);
node->nod_arg[0] = SQE_value(request, false, NULL, NULL);
local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
if (MSC_match(KW_CAST))
{
node = MSC_node(nod_cast, 2);
node->nod_count = 1;
EXP_left_paren(0);
node->nod_arg[0] = SQE_value_or_null(request, false, 0, 0);
if (!MSC_match(KW_AS))
CPR_s_error("AS");
gpre_fld* field = (gpre_fld*) MSC_alloc(FLD_LEN);
node->nod_arg[1] = (gpre_nod*) field;
SQL_par_field_dtype(request, field, false);
SQL_par_field_collate(request, field);
SQL_adjust_field_dtype(field);
local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
if (MSC_match(KW_COALESCE))
{
node = MSC_node(nod_coalesce, 1);
EXP_left_paren(0);
node->nod_arg[0] = SQE_list(SQE_value, request, false);
local_count = 1;
par_terminating_parens(&local_count, &local_count);
return node;
}
if (MSC_match(KW_CASE))
{
node = par_case(request);
return node;
}
if (MSC_match(KW_NULLIF))
{
node = par_nullif(request);
return node;
}
if (MSC_match(KW_SUBSTRING))
{
node = par_substring(request);
return node;
}
return NULL;
}
//____________________________________________________________
//
// Parse a user defined function or a field name.
//
static gpre_nod* par_udf_or_field(gpre_req* request, bool aster_ok)
{
assert_IS_REQ(request);
gpre_nod* node = par_udf(request);
if (!node)
node = SQE_field(request, aster_ok);
return node;
}
//____________________________________________________________
//
// Parse a user defined function or a field name.
// Allow the collate clause to follow.
//
static gpre_nod* par_udf_or_field_with_collate(gpre_req* request,
bool aster_ok, USHORT* /*paren_count*/, bool* /*bool_flag*/)
{
assert_IS_REQ(request);
gpre_nod* node = par_udf_or_field(request, aster_ok);
if (gpreGlob.token_global.tok_keyword == KW_COLLATE)
node = par_collate(request, node);
return node;
}
//____________________________________________________________
//
// Parse FOR UPDATE WITH LOCK clause
//
static void par_update(gpre_rse* select, bool have_union, bool view_flag)
{
// Parse FOR UPDATE if present
if (MSC_match(KW_FOR))
{
if (! MSC_match(KW_UPDATE))
{
CPR_s_error("UPDATE");
return;
}
if (MSC_match(KW_OF))
{
do {
CPR_token();
} while (MSC_match(KW_COMMA));
}
select->rse_flags |= RSE_for_update;
}
// Parse WITH LOCK if present
if (MSC_match(KW_WITH))
{
if (! MSC_match(KW_LOCK))
{
CPR_s_error("LOCK");
return;
}
if (have_union)
{
PAR_error("WITH LOCK in UNION");
return;
}
if (view_flag)
{
PAR_error("WITH LOCK in VIEW");
return;
}
select->rse_flags |= RSE_with_lock;
}
}
//____________________________________________________________
//
// Post a field or aggregate to a map. This is used to references
// to aggregates and unions. Return a reference to the map (rather
// than the expression itself). Post only the aggregates and fields,
// not the computations around them.
//
static gpre_nod* post_fields( gpre_nod* node, map* to_map)
{
assert_IS_NOD(node);
switch (node->nod_type)
{
// Removed during fix to BUG_8021 - this would post a literal to
// the map record for each literal used in an expression - which
// would result in unneccesary data movement as the literal is more
// easily experssed in the assignment portion of the mapping select
// operation.
// case nod_literal:
// 1995-Jul-10 David Schnepper
case nod_field:
case nod_agg_max:
case nod_agg_min:
case nod_agg_average:
case nod_agg_total:
case nod_agg_count:
case nod_map_ref:
return post_map(node, to_map);
case nod_udf:
case nod_gen_id:
node->nod_arg[0] = post_fields(node->nod_arg[0], to_map);
break;
case nod_list:
case nod_upcase:
case nod_lowcase:
case nod_concatenate:
case nod_cast:
case nod_plus:
case nod_minus:
case nod_times:
case nod_divide:
case nod_negate:
{
gpre_nod** ptr = node->nod_arg;
for (const gpre_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++)
{
*ptr = post_fields(*ptr, to_map);
}
break;
}
// Begin date/time/timestamp support
case nod_extract:
node->nod_arg[1] = post_fields(node->nod_arg[1], to_map);
break;
// End date/time/timestamp support
}
return node;
}
//____________________________________________________________
//
// Post a value expression to a map. This is used to references
// to aggregates and unions. Return a reference to the map (rather
// than the expression itself).
//
static gpre_nod* post_map( gpre_nod* node, map* to_map)
{
mel* element;
assert_IS_NOD(node);
// Search existing map for equivalent expression. If we find one,
// return a reference to it.
if (node->nod_type == nod_map_ref)
{
element = (mel*) node->nod_arg[0];
if (element->mel_context == to_map->map_context)
return node;
}
for (element = to_map->map_elements; element; element = element->mel_next)
{
if (compare_expr(node, element->mel_expr))
return MSC_unary(nod_map_ref, (gpre_nod*) element);
}
// We need to make up a new map reference
element = (mel*) MSC_alloc(sizeof(mel));
element->mel_next = to_map->map_elements;
to_map->map_elements = element;
element->mel_position = to_map->map_count++;
element->mel_expr = node;
element->mel_context = to_map->map_context;
// Make up a reference to the map element
return MSC_unary(nod_map_ref, (gpre_nod*) element);
}
//____________________________________________________________
//
// Copy a selection list to the map generated for a UNION
// construct. Note at this level we want the full expression
// selected posted, not just the portions that come from the
// stream. Thus CAST and other operations will be passed into
// a UNION. See BUG_8021 & BUG_8000 for examples.
//
static gpre_nod* post_select_list( gpre_nod* fields, map* to_map)
{
assert_IS_NOD(fields);
gpre_nod* list = MSC_node(nod_list, fields->nod_count);
for (USHORT i = 0; i < fields->nod_count; i++)
list->nod_arg[i] = post_map(fields->nod_arg[i], to_map);
return list;
}
//____________________________________________________________
//
// Restore saved scoping information to the request block
//
static void pop_scope(gpre_req* request, scope* save_scope)
{
assert_IS_REQ(request);
request->req_contexts = save_scope->req_contexts;
request->req_scope_level = save_scope->req_scope_level;
request->req_in_aggregate = save_scope->req_in_aggregate;
request->req_in_select_list = save_scope->req_in_select_list;
request->req_in_where_clause = save_scope->req_in_where_clause;
request->req_in_having_clause = save_scope->req_in_having_clause;
request->req_in_order_by_clause = save_scope->req_in_order_by_clause;
}
//____________________________________________________________
//
// Save scoping information from the request block
//
static void push_scope(gpre_req* request, scope* save_scope)
{
assert_IS_REQ(request);
save_scope->req_contexts = request->req_contexts;
save_scope->req_scope_level = request->req_scope_level;
save_scope->req_in_aggregate = request->req_in_aggregate;
save_scope->req_in_select_list = request->req_in_select_list;
save_scope->req_in_where_clause = request->req_in_where_clause;
save_scope->req_in_having_clause = request->req_in_having_clause;
save_scope->req_in_order_by_clause = request->req_in_order_by_clause;
save_scope->req_in_subselect = request->req_in_subselect;
request->req_scope_level++;
request->req_in_aggregate = 0;
request->req_in_select_list = 0;
request->req_in_where_clause = 0;
request->req_in_having_clause = 0;
request->req_in_order_by_clause = 0;
request->req_in_subselect = 0;
}
//____________________________________________________________
//
// Attempt to resolve a field in a context. If successful, return
// the field. Otherwise return NULL. Let somebody else worry about
// errors.
//
static gpre_fld* resolve(gpre_nod* node,
gpre_ctx* context, gpre_ctx** found_context, act** slice_action)
{
gpre_fld* field;
assert_IS_NOD(node);
gpre_rse* rs_stream = context->ctx_stream;
if (rs_stream)
{
for (SSHORT i = 0; i < rs_stream->rse_count; i++)
if (field = resolve(node, rs_stream->rse_context[i], found_context, slice_action))
{
return field;
}
return NULL;
}
tok* f_token = (tok*) node->nod_arg[0];
tok* q_token = (tok*) node->nod_arg[1];
if (!(context->ctx_relation || context->ctx_procedure))
return NULL;
// Handle unqualified fields first for simplicity
if (!q_token)
field = MET_context_field(context, f_token->tok_string);
else
{
// Now search alternatives for the qualifier
gpre_sym* symbol = HSH_lookup(q_token->tok_string);
// This caused gpre to dump core if there are lower case
// table aliases in a where clause used with dialect 2 or 3
//if ( (symbol == NULL) && (sw_case || gpreGlob.sw_sql_dialect == SQL_DIALECT_V5))
// symbol = HSH_lookup2 (q_token->tok_string);
// So I replaced it with the following, don't know
// why we don't do a HSH_lookup2 in any case, but so it may be.
// FSG 16.Nov.2000
if (symbol == NULL)
symbol = HSH_lookup2(q_token->tok_string);
for (gpre_sym* temp_symbol = symbol; temp_symbol; temp_symbol = temp_symbol->sym_homonym)
{
if (temp_symbol->sym_type == SYM_context)
{
symbol = temp_symbol;
break;
}
if (temp_symbol->sym_type == SYM_relation)
{
symbol = temp_symbol;
continue;
}
if (temp_symbol->sym_type == SYM_procedure)
{
if (symbol->sym_type == SYM_relation)
continue;
symbol = temp_symbol;
}
}
field = NULL;
switch (symbol->sym_type)
{
case SYM_relation:
if ((gpre_rel*) symbol->sym_object == context->ctx_relation)
field = MET_field(context->ctx_relation, f_token->tok_string);
break;
case SYM_procedure:
if ((gpre_prc*) symbol->sym_object == context->ctx_procedure)
field = MET_context_field(context, f_token->tok_string);
break;
case SYM_context:
if (symbol->sym_object == context)
field = MET_context_field(context, f_token->tok_string);
break;
}
}
if (field && found_context)
*found_context = context;
// Check for valid array field
// Check dimensions
// Set remaining fields for slice
slc* slice;
gpre_req* slice_req = (gpre_req*) node->nod_arg[2];
if (slice_req && (slice = slice_req->req_slice) && slice_action)
{
slice = slice_req->req_slice;
ary* ary_info = field->fld_array_info;
if (!ary_info)
CPR_s_error("<array column>");
if (ary_info->ary_dimension_count != slice->slc_dimensions)
PAR_error("subscript count mismatch");
slice->slc_field = field;
slice->slc_parent_request = context->ctx_request;
// The action type maybe ACT_get_slice or ACT_put_slice
// set as a place holder
act* action = MSC_action(slice_req, ACT_get_slice);
action->act_object = (ref*) slice;
*slice_action = action;
}
else if ((slice_req = (gpre_req*) node->nod_arg[2]) && slice_action)
{
// The action type maybe ACT_get_slice or ACT_put_slice
// set as a place holder
act* action = MSC_action(slice_req, ACT_get_slice);
*slice_action = action;
}
return field;
}
//____________________________________________________________
//
// Attempt to resolve an asterisk in a context.
// If successful, return the context. Otherwise return NULL.
//
static gpre_ctx* resolve_asterisk( const tok* q_token, gpre_rse* selection)
{
for (int i = 0; i < selection->rse_count; i++)
{
gpre_ctx* context = selection->rse_context[i];
gpre_rse* rs_stream = context->ctx_stream;
if (rs_stream)
{
if (context = resolve_asterisk(q_token, rs_stream))
return context;
continue;
}
gpre_sym* symbol = HSH_lookup(q_token->tok_string);
for (; symbol; symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_relation &&
(gpre_rel*) symbol->sym_object == context->ctx_relation)
{
return context;
}
if (symbol->sym_type == SYM_procedure &&
(gpre_prc*) symbol->sym_object == context->ctx_procedure)
{
return context;
}
if (symbol->sym_type == SYM_context && (gpre_ctx*) symbol->sym_object == context)
{
return context;
}
}
}
return NULL;
}
//____________________________________________________________
//
// Set field reference for any host variables in expr to field_ref.
//
static void set_ref( gpre_nod* expr, gpre_fld* field_ref)
{
assert_IS_NOD(expr);
ref* re = (ref*) expr->nod_arg[0];
switch (expr->nod_type)
{
case nod_value:
re->ref_field = field_ref;
break;
case nod_agg_count:
case nod_agg_max:
case nod_agg_min:
case nod_agg_total:
case nod_agg_average:
case nod_plus:
case nod_minus:
case nod_times:
case nod_divide:
case nod_negate:
case nod_upcase:
case nod_lowcase:
case nod_concatenate:
case nod_cast:
{
gpre_nod** ptr = expr->nod_arg;
for (const gpre_nod* const* const end = ptr + expr->nod_count; ptr < end; ptr++)
{
set_ref(*ptr, field_ref);
}
break;
}
// Begin date/time/timestamp support
case nod_extract:
set_ref(expr->nod_arg[1], field_ref);
break;
// End date/time/timestamp support
}
}
//____________________________________________________________
//
// Return the uppercase version of
// the input string.
//
static char* upcase_string(const char* p)
{
char* const s = (char *) MSC_alloc(strlen(p) + 1);
char* q = s;
USHORT l = 0;
char c;
while ((c = *p++) && (++l <= NAME_SIZE)) {
*q++ = UPPER7(c);
}
*q = 0;
return s;
}
//____________________________________________________________
//
// validate that top level field references
// in a select with a group by, real or imagined,
// resolve to grouping fields. Ignore constants
// and aggregates. If there's no group_by list,
// then it's an imaginary group by (a top level
// aggregation, and nothing can be referenced
// directly.
//
static bool validate_references(const gpre_nod* fields, const gpre_nod* group_by)
{
assert_IS_NOD(fields);
assert_IS_NOD(group_by);
if (!fields)
return false;
if (fields->nod_type == nod_field)
{
if (!group_by)
return true;
const ref* fref = (ref*) fields->nod_arg[0];
bool context_match = false;
const gpre_nod* const* ptr = group_by->nod_arg;
for (const gpre_nod* const* const end = ptr + group_by->nod_count; ptr < end; ptr++)
{
const ref* gref = (ref*) (*ptr)->nod_arg[0];
if (gref->ref_context == fref->ref_context)
{
if (gref->ref_field == fref->ref_field)
return false;
context_match = true;
}
}
return context_match;
}
switch (fields->nod_type)
{
case nod_agg_count:
case nod_agg_max:
case nod_agg_min:
case nod_agg_total:
case nod_agg_average:
case nod_aggregate:
return false;
}
if (fields->nod_type == nod_any || fields->nod_type == nod_ansi_any ||
fields->nod_type == nod_ansi_all)
{
const gpre_rse* any = (gpre_rse*) fields->nod_arg[0];
return validate_references(any->rse_boolean, group_by);
}
if (fields->nod_type == nod_gen_id || fields->nod_type == nod_udf)
return validate_references(fields->nod_arg[0], group_by);
bool invalid = false;
const gpre_nod* const* ptr = fields->nod_arg;
for (const gpre_nod* const* const end = ptr + fields->nod_count; ptr < end; ptr++)
{
switch ((*ptr)->nod_type)
{
case nod_map_ref:
{
const mel* element = (mel*) (*ptr)->nod_arg[0];
const gpre_nod* node = element->mel_expr;
if (node->nod_type != nod_agg_count &&
node->nod_type != nod_agg_max &&
node->nod_type != nod_agg_min &&
node->nod_type != nod_agg_total &&
node->nod_type != nod_agg_average &&
node->nod_type != nod_aggregate)
{
invalid |= validate_references(node, group_by);
}
break;
}
case nod_field:
case nod_plus:
case nod_minus:
case nod_times:
case nod_negate:
case nod_divide:
case nod_and:
case nod_like:
case nod_missing:
case nod_not:
case nod_or:
case nod_eq:
case nod_ne:
case nod_gt:
case nod_ge:
case nod_le:
case nod_lt:
case nod_upcase:
case nod_lowcase:
case nod_concatenate:
case nod_cast:
invalid |= validate_references(*ptr, group_by);
break;
}
}
return invalid;
}