8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 06:43:02 +01:00
firebird-mirror/src/gpre/exp.cpp

1569 lines
39 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
// PROGRAM: C preprocessor
// MODULE: exp.cpp
// DESCRIPTION: 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): ______________________________________.
// TMN (Mike Nordell) 11.APR.2001 - Reduce compiler warnings
//
//
//____________________________________________________________
//
//
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <stdlib.h>
#include <string.h>
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/common.h"
#include "../gpre/gpre.h"
#include "../jrd/intl.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"
2003-10-15 00:22:32 +02:00
const int ZERO_BASED = 0;
const int ONE_BASED = 1;
2001-05-23 15:26:42 +02:00
static bool check_relation(void);
static GPRE_NOD lookup_field(gpre_ctx*);
2002-11-11 20:19:43 +01:00
static GPRE_NOD make_and(GPRE_NOD, GPRE_NOD);
2004-02-02 12:02:12 +01:00
static GPRE_NOD make_list(gpre_lls*);
static GPRE_NOD normalize_index(dim*, GPRE_NOD, USHORT);
static GPRE_NOD par_and(gpre_req*);
static GPRE_NOD par_array(gpre_req*, gpre_fld*, bool, bool);
static GPRE_NOD par_boolean(gpre_req*);
static GPRE_NOD par_field(gpre_req*);
static GPRE_NOD par_multiply(gpre_req*, gpre_fld*);
static GPRE_NOD par_native_value(gpre_req*, gpre_fld*);
static GPRE_NOD par_not(gpre_req*);
static GPRE_NOD par_over(gpre_ctx*);
static GPRE_NOD par_primitive_value(gpre_req*, gpre_fld*);
static GPRE_NOD par_relational(gpre_req*);
static GPRE_NOD par_udf(gpre_req*, USHORT, gpre_fld*);
static GPRE_NOD par_value(gpre_req*, gpre_fld*);
2001-05-23 15:26:42 +02:00
static gpre_fld* global_count_field;
static gpre_fld* global_subscript_field;
2001-05-23 15:26:42 +02:00
struct rel_ops {
2001-05-23 15:26:42 +02:00
enum nod_t rel_op;
enum kwwords rel_kw;
SSHORT rel_args;
};
static const rel_ops relops[] = {
2001-12-24 03:51:06 +01:00
{ nod_eq, KW_EQ, 2 },
2003-02-18 08:43:29 +01:00
{ nod_eq, KW_EQUALS, 2 },
{ nod_ne, KW_NE, 2 },
{ nod_gt, KW_GT, 2 },
{ nod_ge, KW_GE, 2 },
{ nod_le, KW_LE, 2 },
{ nod_lt, KW_LT, 2 },
{ nod_containing, KW_CONTAINING, 2 },
{ nod_matches, KW_MATCHES, 2 },
{ nod_like, KW_LIKE, 2 },
{ nod_starting, KW_STARTING, 2 },
{ nod_missing, KW_MISSING, 1 },
{ nod_between, KW_BETWEEN, 3},
{ nod_any, KW_none, 0}
};
2001-05-23 15:26:42 +02:00
struct dtypes {
2001-05-23 15:26:42 +02:00
enum kwwords dtype_keyword;
USHORT dtype_dtype;
};
static const dtypes data_types[] = {
2001-12-24 03:51:06 +01:00
{ KW_CHAR, dtype_text },
2003-02-18 08:43:29 +01:00
{ KW_VARYING, dtype_varying },
{ KW_STRING, dtype_cstring },
{ KW_SHORT, dtype_short },
{ KW_LONG, dtype_long },
{ KW_QUAD, dtype_quad },
{ KW_FLOAT, dtype_real },
{ KW_DOUBLE, dtype_double },
{ KW_DATE, dtype_date },
{ KW_none, 0}
};
2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
// Parse array subscript.
//
GPRE_NOD EXP_array(gpre_req* request, gpre_fld* field, bool subscript_flag, bool sql_flag)
2001-05-23 15:26:42 +02:00
{
return par_array(request, field, subscript_flag, sql_flag);
}
//____________________________________________________________
//
// Parse a datatype cast (sans leading period).
//
gpre_fld* EXP_cast(gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
const dtypes* dtype = data_types;
2003-12-03 09:19:24 +01:00
while (true) {
if (dtype->dtype_keyword == KW_none)
2001-05-23 15:26:42 +02:00
return NULL;
2003-10-15 00:22:32 +02:00
else if (MSC_match(dtype->dtype_keyword))
2001-05-23 15:26:42 +02:00
break;
2003-12-03 09:19:24 +01:00
++dtype;
}
2001-05-23 15:26:42 +02:00
gpre_fld* cast = (gpre_fld*) MSC_alloc(FLD_LEN);
2001-05-23 15:26:42 +02:00
cast->fld_symbol = field->fld_symbol;
switch (cast->fld_dtype = dtype->dtype_dtype) {
case dtype_varying:
cast->fld_length++;
2003-12-03 09:19:24 +01:00
// fall back
2001-05-23 15:26:42 +02:00
case dtype_cstring:
cast->fld_length++;
2003-12-03 09:19:24 +01:00
// fall back
2001-05-23 15:26:42 +02:00
case dtype_text:
if (gpreGlob.sw_cstring && !(cast->fld_dtype == dtype_cstring)) {
2001-05-23 15:26:42 +02:00
cast->fld_length++;
cast->fld_dtype = dtype_cstring;
}
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_L_BRCKET) && !MSC_match(KW_LT))
CPR_s_error("left bracket or <");
cast->fld_length += EXP_pos_USHORT_ordinal(true);
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_R_BRCKET) && !MSC_match(KW_GT))
CPR_s_error("right bracket or >");
2001-05-23 15:26:42 +02:00
break;
case dtype_quad:
cast->fld_length = 4;
break;
/** Begin date/time/timestamp **/
case dtype_sql_date:
cast->fld_length = sizeof(ISC_DATE);
break;
case dtype_sql_time:
cast->fld_length = sizeof(ISC_TIME);
break;
case dtype_timestamp:
cast->fld_length = sizeof(ISC_TIMESTAMP);
break;
/** End date/time/timestamp **/
case dtype_int64:
cast->fld_length = sizeof(ISC_INT64);
break;
case dtype_long:
cast->fld_length = sizeof(SLONG);
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_SCALE))
cast->fld_scale = EXP_SSHORT_ordinal(true);
2001-05-23 15:26:42 +02:00
break;
case dtype_short:
cast->fld_length = sizeof(SSHORT);
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_SCALE))
cast->fld_scale = EXP_SSHORT_ordinal(true);
2001-05-23 15:26:42 +02:00
break;
2003-10-15 00:22:32 +02:00
case dtype_real:
2001-05-23 15:26:42 +02:00
cast->fld_length = 4;
break;
case dtype_double:
cast->fld_length = 8;
break;
}
return cast;
}
//____________________________________________________________
//
// Parse a clause of the form "<context> IN <relation>".
// If the error flag is true, and the parse fails, quietly
// return NULL. Otherwise issue error messages where appropriate,
// and return a CONTEXT block as value.
//
gpre_ctx* EXP_context(gpre_req* request, gpre_sym* initial_symbol)
2001-05-23 15:26:42 +02:00
{
// Use the token (context name) to make up a symbol
// block. Then check for the keyword IN. If it's
// missing, either complain or punt, depending on the
// error flag. In either case, be sure to get rid of
// the symbol. If things look kosher, continue.
gpre_sym* symbol = initial_symbol;
if (!symbol) {
2001-05-23 15:26:42 +02:00
symbol = PAR_symbol(SYM_context);
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_IN)) {
2003-10-15 03:18:01 +02:00
MSC_free((UCHAR *) symbol);
2003-10-15 00:22:32 +02:00
CPR_s_error("IN");
2001-05-23 15:26:42 +02:00
}
}
symbol->sym_type = SYM_context;
gpre_rel* relation = EXP_relation();
gpre_ctx* context = MSC_context(request);
2001-05-23 15:26:42 +02:00
context->ctx_symbol = symbol;
context->ctx_relation = relation;
symbol->sym_object = context;
return context;
}
//____________________________________________________________
//
// Parse a qualified field clause. If recognized,
// return both the field block (as value) and the
// context block (by reference).
//
gpre_fld* EXP_field(gpre_ctx** rcontext)
2001-05-23 15:26:42 +02:00
{
gpre_sym* symbol;
for (symbol = gpreGlob.token_global.tok_symbol; symbol; symbol = symbol->sym_homonym) {
2001-05-23 15:26:42 +02:00
if (symbol->sym_type == SYM_context)
break;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
if (!symbol)
2003-10-15 00:22:32 +02:00
CPR_s_error("context variable");
2001-05-23 15:26:42 +02:00
gpre_ctx* context = symbol->sym_object;
gpre_rel* relation = context->ctx_relation;
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_DOT))
CPR_s_error("dot after context variable");
2001-05-23 15:26:42 +02:00
SQL_resolve_identifier("<Field Name>", NULL, NAME_SIZE);
gpre_fld* field = MET_field(relation, gpreGlob.token_global.tok_string);
if (!field) {
TEXT s[ERROR_LENGTH];
2001-05-23 15:26:42 +02:00
sprintf(s, "field \"%s\" is not defined in relation %s",
gpreGlob.token_global.tok_string, relation->rel_symbol->sym_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
*rcontext = context;
return field;
}
//____________________________________________________________
//
// Eat a left parenthesis, complain if not there.
//
void EXP_left_paren(const TEXT* string)
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_LEFT_PAREN))
CPR_s_error((string) ? string : "left parenthesis");
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse a native literal constant value.
//
2002-11-11 20:19:43 +01:00
GPRE_NOD EXP_literal(void)
2001-05-23 15:26:42 +02:00
{
switch (gpreGlob.sw_sql_dialect) {
2001-05-23 15:26:42 +02:00
case 1:
if (!(gpreGlob.token_global.tok_type == tok_number || isQuoted(gpreGlob.token_global.tok_type)))
2001-05-23 15:26:42 +02:00
return NULL;
break;
default:
if (!(gpreGlob.token_global.tok_type == tok_number || gpreGlob.token_global.tok_type == tok_sglquoted))
2001-05-23 15:26:42 +02:00
return NULL;
}
ref* reference = (REF) MSC_alloc(REF_LEN);
gpre_nod* node = MSC_unary(nod_literal, (GPRE_NOD) reference);
if (isQuoted(gpreGlob.token_global.tok_type)) {
TEXT* string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 3);
reference->ref_value = string;
2001-05-23 15:26:42 +02:00
strcat(string, "\'");
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string + 1);
strcat((string + gpreGlob.token_global.tok_length + 1), "\'");
// What kind of hack is this? The token has not been enlarged nor modified.
gpreGlob.token_global.tok_length += 2;
2001-05-23 15:26:42 +02:00
}
else {
TEXT* 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);
2001-05-23 15:26:42 +02:00
}
// ** Begin date/time/timestamp *
switch (gpreGlob.token_global.tok_keyword) {
2001-05-23 15:26:42 +02:00
case KW_DATE:
reference->ref_flags |= REF_sql_date;
break;
case KW_TIME:
reference->ref_flags |= REF_sql_time;
break;
case KW_TIMESTAMP:
reference->ref_flags |= REF_timestamp;
break;
/** Do not put a default here **/
}
// ** End date/time/timestamp *
if ((gpreGlob.token_global.tok_type == tok_sglquoted && (gpreGlob.token_global.tok_charset)) ||
((isQuoted(gpreGlob.token_global.tok_type) && (gpreGlob.sw_sql_dialect == 1))
&& (gpreGlob.token_global.tok_charset)))
2001-05-23 15:26:42 +02:00
{
reference->ref_flags |= REF_ttype;
gpre_sym* symbol = gpreGlob.token_global.tok_charset;
2001-05-23 15:26:42 +02:00
reference->ref_ttype =
((INTLSYM) (symbol->sym_object))->intlsym_ttype;
}
else if (gpreGlob.sw_language == lang_internal) {
// literals referenced in an Internal request are always correct charset
2001-05-23 15:26:42 +02:00
reference->ref_flags |= REF_ttype;
reference->ref_ttype = ttype_metadata;
}
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return node;
}
//____________________________________________________________
//
// Parse and convert to binary a numeric token.
// Restrict to LONG range.
//
SINT64 EXP_SINT64_ordinal(bool advance_flag)
2001-05-23 15:26:42 +02:00
{
const bool negate = (MSC_match(KW_MINUS));
2001-05-23 15:26:42 +02:00
if (gpreGlob.token_global.tok_type != tok_number)
2003-10-15 00:22:32 +02:00
CPR_s_error("<number>");
2001-05-23 15:26:42 +02:00
const char format[8] = "%"QUADFORMAT"d";
SINT64 n;
sscanf(gpreGlob.token_global.tok_string, format, &n);
char buffer[64];
2001-05-23 15:26:42 +02:00
sprintf(buffer, format, n);
if (strcmp(buffer, gpreGlob.token_global.tok_string) != 0)
2001-05-23 15:26:42 +02:00
PAR_error("Numeric value out of range");
if (advance_flag)
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return (negate) ? -n : n;
}
//____________________________________________________________
//
// Parse and convert to binary a numeric token.
// Restrict to LONG range.
//
SLONG EXP_SLONG_ordinal(bool advance_flag)
2001-05-23 15:26:42 +02:00
{
const bool negate = (MSC_match(KW_MINUS));
2001-05-23 15:26:42 +02:00
if (gpreGlob.token_global.tok_type != tok_number)
2003-10-15 00:22:32 +02:00
CPR_s_error("<number>");
2001-05-23 15:26:42 +02:00
const SLONG n = atoi(gpreGlob.token_global.tok_string);
char buffer[32];
2003-04-01 13:49:33 +02:00
sprintf(buffer, "%"SLONGFORMAT, n);
if (strcmp(buffer, gpreGlob.token_global.tok_string) != 0)
2001-05-23 15:26:42 +02:00
PAR_error("Numeric value out of range");
if (advance_flag)
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return (negate) ? -n : n;
}
//____________________________________________________________
//
// Parse and convert to binary a numeric token.
// A SSHORT is desired.
//
SSHORT EXP_SSHORT_ordinal(bool advance_flag)
2001-05-23 15:26:42 +02:00
{
const bool negate = (MSC_match(KW_MINUS));
2001-05-23 15:26:42 +02:00
if (gpreGlob.token_global.tok_type != tok_number)
2003-10-15 00:22:32 +02:00
CPR_s_error("<number>");
2001-05-23 15:26:42 +02:00
const SLONG n = atoi(gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
if (negate && n > -1L * MIN_SSHORT)
PAR_error("Numeric value out of range");
else if (!negate && n > (SLONG) MAX_SSHORT)
PAR_error("Numeric value out of range");
if (advance_flag)
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return (SSHORT) ((negate) ? -n : n);
}
//____________________________________________________________
//
// Parse and convert to binary a numeric token.
// Restrict to LONG range.
//
ULONG EXP_ULONG_ordinal(bool advance_flag)
2001-05-23 15:26:42 +02:00
{
if (gpreGlob.token_global.tok_type != tok_number)
2003-10-15 00:22:32 +02:00
CPR_s_error("<unsigned number>");
2001-05-23 15:26:42 +02:00
const ULONG n = atoi(gpreGlob.token_global.tok_string);
char buffer[32];
2003-04-01 13:49:33 +02:00
sprintf(buffer, "%"ULONGFORMAT, n);
if (strcmp(buffer, gpreGlob.token_global.tok_string) != 0)
2001-05-23 15:26:42 +02:00
PAR_error("Numeric value out of range");
if (advance_flag)
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return n;
}
//____________________________________________________________
//
// Parse and convert to binary a numeric token.
//
USHORT EXP_USHORT_ordinal(bool advance_flag)
2001-05-23 15:26:42 +02:00
{
if (gpreGlob.token_global.tok_type != tok_number)
2003-10-15 00:22:32 +02:00
CPR_s_error("<unsigned number>");
2001-05-23 15:26:42 +02:00
const ULONG n = atoi(gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
if (n > MAX_USHORT)
PAR_error("Numeric value out of range");
if (advance_flag)
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return (USHORT) n;
}
//____________________________________________________________
//
// Parse and convert to binary a numeric token.
// Which must be non-zero.
//
USHORT EXP_pos_USHORT_ordinal(bool advance_flag)
2001-05-23 15:26:42 +02:00
{
const USHORT n = EXP_USHORT_ordinal(advance_flag);
2001-05-23 15:26:42 +02:00
if (n == 0)
PAR_error("Expected positive value");
return n;
}
//____________________________________________________________
//
// We have a free reference to array. Make sure the whole damn thing
// gets sucked up.
//
void EXP_post_array( REF reference)
{
gpre_fld* field = reference->ref_field;
2001-05-23 15:26:42 +02:00
if (!field->fld_array_info)
return;
reference->ref_flags |= REF_fetch_array;
gpre_ctx* context = reference->ref_context;
gpre_req* request = context->ctx_request;
ref* array_reference = MSC_reference(&request->req_array_references);
2001-05-23 15:26:42 +02:00
array_reference->ref_context = context;
array_reference->ref_field = field;
array_reference->ref_level = request->req_level;
field->fld_array_info->ary_ident = CMP_next_ident();
blb* blob = (blb*) MSC_alloc(BLB_LEN);
2001-05-23 15:26:42 +02:00
blob->blb_symbol = field->fld_symbol;
blob->blb_reference = reference;
if (!(blob->blb_seg_length = field->fld_seg_length))
blob->blb_seg_length = 512;
blob->blb_request = request;
}
//____________________________________________________________
//
// Post a field reference to a request. This
// can be called from either par_variable (free
// standing field reference) or EXP\par_value
// (cross request field reference).
//
REF EXP_post_field(gpre_fld* field, gpre_ctx* context, bool null_flag)
2001-05-23 15:26:42 +02:00
{
TEXT s[128];
gpre_req* request = context->ctx_request;
2001-05-23 15:26:42 +02:00
// If the reference is already posted, return the reference
ref* reference;
2001-05-23 15:26:42 +02:00
for (reference = request->req_references; reference;
2003-09-11 04:13:46 +02:00
reference = reference->ref_next)
{
if (reference->ref_context == context) {
gpre_fld* ref_field = reference->ref_field;
2001-05-23 15:26:42 +02:00
if (ref_field == field ||
(ref_field->fld_symbol == field->fld_symbol &&
2003-09-11 04:13:46 +02:00
ref_field->fld_array == field->fld_array))
{
2001-05-23 15:26:42 +02:00
if (!null_flag && (ref_field->fld_dtype != field->fld_dtype ||
ref_field->fld_length != field->fld_length
|| ref_field->fld_scale !=
2003-09-11 04:13:46 +02:00
field->fld_scale))
{
2001-05-23 15:26:42 +02:00
if (reference->ref_flags & REF_null)
reference->ref_field = field;
else {
sprintf(s, "field %s is inconsistently cast",
field->fld_symbol->sym_string);
PAR_error(s);
}
}
if (request->req_level > reference->ref_level)
reference->ref_level = request->req_level;
if (!null_flag)
reference->ref_flags &= ~REF_null;
return reference;
}
}
2003-09-11 04:13:46 +02:00
}
2001-05-23 15:26:42 +02:00
// This is first occurrence of field, make a new reference
2003-10-15 00:22:32 +02:00
reference = MSC_reference(&request->req_references);
2001-05-23 15:26:42 +02:00
reference->ref_context = context;
reference->ref_field = field;
reference->ref_level = request->req_level;
if (null_flag)
reference->ref_flags |= REF_null;
return reference;
}
//____________________________________________________________
//
// Match a trailing parenthesis. If isn't one, generate an error
// and return FALSE.
//
2003-09-11 04:13:46 +02:00
bool EXP_match_paren(void)
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_RIGHT_PAREN))
2003-09-11 04:13:46 +02:00
return true;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
CPR_s_error("right parenthesis");
2003-09-11 04:13:46 +02:00
return false; // silence compiler warning
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse and look up a qualified relation name.
2001-05-23 15:26:42 +02:00
//
gpre_rel* EXP_relation(void)
2001-05-23 15:26:42 +02:00
{
if (!gpreGlob.isc_databases)
2001-05-23 15:26:42 +02:00
PAR_error("no database for operation");
// The current token is (i.e. should be) either a relation
// name or a database name. If it's a database name, search
// it for the relation name. If it's an unqualified relation
// name, search all databases for the name
gpre_rel* relation = NULL;
2001-05-23 15:26:42 +02:00
SQL_resolve_identifier("<identifier>", NULL, NAME_SIZE);
gpre_sym* symbol = MSC_find_symbol(gpreGlob.token_global.tok_symbol, SYM_database);
if (symbol) {
dbb* db = (DBB) symbol->sym_object;
2003-10-15 00:22:32 +02:00
PAR_get_token();
if (!MSC_match(KW_DOT))
CPR_s_error("period after database name");
SQL_resolve_identifier("<Table name>", NULL, NAME_SIZE);
relation = MET_get_relation(db, gpreGlob.token_global.tok_string, "");
2001-05-23 15:26:42 +02:00
}
else {
for (dbb* db = gpreGlob.isc_databases; db; db = db->dbb_next) {
gpre_rel* temp = MET_get_relation(db, gpreGlob.token_global.tok_string, "");
if (temp) {
2001-05-23 15:26:42 +02:00
if (!relation)
relation = temp;
else {
TEXT s[ERROR_LENGTH];
sprintf(s, "relation %s is ambiguous", gpreGlob.token_global.tok_string);
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
}
}
2001-05-23 15:26:42 +02:00
}
if (!relation)
2003-10-15 00:22:32 +02:00
CPR_s_error("relation name");
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return relation;
}
//____________________________________________________________
//
// Parse a record selection expression. If there is an
// error, return NULL. This is slightly complicated by
// the fact that PASCAL and FORTRAN have a native FOR
// statement, and ADA has a FOR <variable> IN statement.
//
// If an initial symbol is given, the caller has already
// parsed the <contect> IN part of the expression.
//
gpre_rse* EXP_rse(gpre_req* request, gpre_sym* initial_symbol)
2001-05-23 15:26:42 +02:00
{
// parse FIRST n clause, if present
2001-05-23 15:26:42 +02:00
gpre_nod* first = NULL;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_FIRST)) {
if (!global_count_field)
global_count_field = MET_make_field("jrd_count", dtype_long, 4, false);
first = par_value(request, global_count_field);
2001-05-23 15:26:42 +02:00
}
// parse first context clause
if (initial_symbol && gpreGlob.sw_language == lang_ada && !check_relation())
2001-05-23 15:26:42 +02:00
return NULL;
gpre_ctx* context = EXP_context(request, initial_symbol);
SSHORT count = 1;
2001-05-23 15:26:42 +02:00
// parse subsequent context clauses if this is a join
gpre_nod* boolean = NULL;
2003-10-15 00:22:32 +02:00
while (MSC_match(KW_CROSS)) {
2001-05-23 15:26:42 +02:00
context = EXP_context(request, 0);
count++;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_OVER))
2001-05-23 15:26:42 +02:00
boolean = make_and(boolean, par_over(context));
}
// bug_3380 - could have an "over" clause without a "cross" clause
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_OVER))
2001-05-23 15:26:42 +02:00
boolean = make_and(boolean, par_over(context));
// build rse node
gpre_rse* rec_expr = (gpre_rse*) MSC_alloc(RSE_LEN(count));
2001-05-23 15:26:42 +02:00
rec_expr->rse_count = count;
rec_expr->rse_first = first;
rec_expr->rse_boolean = boolean;
while (count) {
rec_expr->rse_context[--count] = context;
HSH_insert(context->ctx_symbol);
context = context->ctx_next;
}
// parse boolean, if any. If there is an error, ignore the
// boolean, but keep the rse
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_WITH))
2001-05-23 15:26:42 +02:00
boolean = make_and(boolean, par_boolean(request));
rec_expr->rse_boolean = boolean;
// Parse SORT clause, if any.
// CVC: It's not clear whether this var should be initialized at the same
// level than "direction".
bool insensitive = false;
while (true) {
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_SORTED)) {
MSC_match(KW_BY);
2004-02-02 12:02:12 +01:00
gpre_lls* items = NULL;
gpre_lls* directions = NULL;
bool direction = false;
2001-05-23 15:26:42 +02:00
count = 0;
while (true) {
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_ASCENDING)) {
direction = false;
2001-05-23 15:26:42 +02:00
continue;
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_DESCENDING)) {
direction = true;
2001-05-23 15:26:42 +02:00
continue;
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_EXACTCASE)) {
insensitive = false;
2001-05-23 15:26:42 +02:00
continue;
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_ANYCASE)) {
insensitive = true;
2001-05-23 15:26:42 +02:00
continue;
}
gpre_nod* item = par_value(request, 0);
2001-05-23 15:26:42 +02:00
count++;
MSC_push((GPRE_NOD) (IPTR) ((direction) ? 1 : 0), &directions);
if (insensitive) {
gpre_nod* upcase = MSC_node(nod_upcase, 1);
upcase->nod_arg[0] = item;
2003-10-15 03:18:01 +02:00
MSC_push(upcase, &items);
}
2001-05-23 15:26:42 +02:00
else
2003-10-15 03:18:01 +02:00
MSC_push(item, &items);
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_COMMA))
2001-05-23 15:26:42 +02:00
break;
}
gpre_nod* sort = MSC_node(nod_sort, (SSHORT) (count * 2));
rec_expr->rse_sort = sort;
2001-05-23 15:26:42 +02:00
sort->nod_count = count;
gpre_nod** ptr = sort->nod_arg + count * 2;
2001-05-23 15:26:42 +02:00
while (--count >= 0) {
2003-10-15 03:18:01 +02:00
*--ptr = (GPRE_NOD) MSC_pop(&items);
*--ptr = (GPRE_NOD) MSC_pop(&directions);
2001-05-23 15:26:42 +02:00
}
}
// Parse REDUCED clause, if any.
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_REDUCED)) {
MSC_match(KW_TO);
2004-02-02 12:02:12 +01:00
gpre_lls* items = NULL;
2001-05-23 15:26:42 +02:00
count = 0;
while (true) {
gpre_nod* item = par_value(request, 0);
2001-05-23 15:26:42 +02:00
count++;
2003-10-15 03:18:01 +02:00
MSC_push(item, &items);
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_COMMA))
2001-05-23 15:26:42 +02:00
break;
}
gpre_nod* sort = MSC_node(nod_projection, count);
rec_expr->rse_reduced = sort;
2001-05-23 15:26:42 +02:00
sort->nod_count = count;
gpre_nod** ptr = sort->nod_arg + count;
2001-05-23 15:26:42 +02:00
while (--count >= 0)
2003-10-15 03:18:01 +02:00
*--ptr = (GPRE_NOD) MSC_pop(&items);
2001-05-23 15:26:42 +02:00
}
else
break;
}
return rec_expr;
}
//____________________________________________________________
//
// Remove any context variables from hash table for a record
// selection expression.
//
void EXP_rse_cleanup( gpre_rse* rs)
2001-05-23 15:26:42 +02:00
{
// Clean up simple context variables
2001-05-23 15:26:42 +02:00
const gpre_ctx* const* context = rs->rse_context;
const gpre_ctx* const* const end = context + rs->rse_count;
2001-05-23 15:26:42 +02:00
for (; context < end; context++)
if ((*context)->ctx_symbol)
HSH_remove((*context)->ctx_symbol);
// If this is an aggregate, clean up the underlying rse
2003-09-05 16:55:59 +02:00
if (rs->rse_aggregate)
EXP_rse_cleanup(rs->rse_aggregate);
2001-05-23 15:26:42 +02:00
// If this is a union, clean up each of the primitive rse's
gpre_nod* node = rs->rse_union;
if (node) {
for (int i = 0; i < node->nod_count; i++)
EXP_rse_cleanup((gpre_rse*) node->nod_arg[i]);
}
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse a subscript value. This is called by PAR\par_slice.
//
GPRE_NOD EXP_subscript(gpre_req* request)
2001-05-23 15:26:42 +02:00
{
ref* reference = (REF) MSC_alloc(REF_LEN);
gpre_nod* node = MSC_unary(nod_value, (GPRE_NOD) reference);
2001-05-23 15:26:42 +02:00
// Special case literals
if (gpreGlob.token_global.tok_type == tok_number) {
2001-05-23 15:26:42 +02:00
node->nod_type = nod_literal;
TEXT* 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);
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return node;
}
reference->ref_value = PAR_native_value(true, false);
2001-05-23 15:26:42 +02:00
if (request) {
reference->ref_next = request->req_values;
request->req_values = reference;
}
return node;
}
//____________________________________________________________
//
// Check current token for either a relation or database name.
//
static bool check_relation(void)
2001-05-23 15:26:42 +02:00
{
// The current token is (i.e. should be) either a relation
// name or a database name. If it's a database name, search
// it for the relation name. If it's an unqualified relation
// name, search all databases for the name
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (symbol && symbol->sym_type == SYM_database)
return true;
2001-05-23 15:26:42 +02:00
for (dbb* db = gpreGlob.isc_databases; db; db = db->dbb_next) {
if (MET_get_relation(db, gpreGlob.token_global.tok_string, ""))
return true;
}
2001-05-23 15:26:42 +02:00
return false;
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Check to see if the current token is a field name corresponding
// to a given context. If so, return a field block (with reference
// block) for field.
//
static GPRE_NOD lookup_field(gpre_ctx* context)
2001-05-23 15:26:42 +02:00
{
SQL_resolve_identifier("<Field Name>", NULL, NAME_SIZE);
gpre_fld* field = MET_field(context->ctx_relation, gpreGlob.token_global.tok_string);
if (!field)
2001-05-23 15:26:42 +02:00
return NULL;
ref* reference = (REF) MSC_alloc(REF_LEN);
2001-05-23 15:26:42 +02:00
reference->ref_field = field;
reference->ref_context = context;
2002-11-11 20:19:43 +01:00
return MSC_unary(nod_field, (GPRE_NOD) reference);
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Combine two (potention) conjuncts into a single, valid
// boolean. Either or both on the conjunctions may be NULL.
// If both are null, return null.
//
2002-11-11 20:19:43 +01:00
static GPRE_NOD make_and( GPRE_NOD node1, GPRE_NOD node2)
2001-05-23 15:26:42 +02:00
{
if (!node1)
return node2;
if (!node2)
return NULL;
return MSC_binary(nod_and, node1, node2);
}
//____________________________________________________________
//
// Make a generic variable length node from a stack.
//
2004-02-02 12:02:12 +01:00
static GPRE_NOD make_list( gpre_lls* stack)
2001-05-23 15:26:42 +02:00
{
USHORT count = 0;
2004-02-02 12:02:12 +01:00
for (const gpre_lls* temp = stack; temp; temp = temp->lls_next)
2001-05-23 15:26:42 +02:00
++count;
gpre_nod* node = MSC_node(nod_list, count);
2001-05-23 15:26:42 +02:00
for (gpre_nod** ptr = node->nod_arg + count; stack;)
2003-10-15 03:18:01 +02:00
*--ptr = MSC_pop(&stack);
2001-05-23 15:26:42 +02:00
return node;
}
//____________________________________________________________
//
// "Normalize" the array index so that
// the index used in the rse refers to
// the same relative position in the
// dimension in the database as it is
// in the user's program.
//
static GPRE_NOD normalize_index( dim* dimension, GPRE_NOD user_index, USHORT array_base)
2001-05-23 15:26:42 +02:00
{
TEXT string[33];
bool negate = false;
2001-05-23 15:26:42 +02:00
switch (array_base) {
case ZERO_BASED:
if (dimension->dim_lower < 0)
negate = true;
2001-05-23 15:26:42 +02:00
sprintf(string, "%d", abs(dimension->dim_lower));
break;
case ONE_BASED:
if (dimension->dim_lower - 1 < 0)
negate = true;
2001-05-23 15:26:42 +02:00
sprintf(string, "%d", abs(dimension->dim_lower - 1));
break;
default:
return user_index;
}
ref* reference = (REF) MSC_alloc(REF_LEN);
char* tmp = (TEXT *) MSC_alloc(strlen(string));
reference->ref_value = tmp;
strcpy(tmp, string);
gpre_nod* adjustment_node = MSC_unary(nod_literal, (GPRE_NOD) reference);
2001-05-23 15:26:42 +02:00
gpre_nod* negate_node;
2001-05-23 15:26:42 +02:00
if (negate)
negate_node = MSC_unary(nod_negate, adjustment_node);
else
negate_node = adjustment_node;
gpre_nod* index_node = MSC_binary(nod_plus, negate_node, user_index);
2001-05-23 15:26:42 +02:00
return index_node;
}
//____________________________________________________________
//
// Parse a boolean AND.
//
static GPRE_NOD par_and( gpre_req* request)
2001-05-23 15:26:42 +02:00
{
gpre_nod* expr1 = par_not(request);
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_AND))
2001-05-23 15:26:42 +02:00
return expr1;
return MSC_binary(nod_and, expr1, par_and(request));
}
//____________________________________________________________
//
// Parse a array element reference
// (array name and subscript list)
// in an gpre_rse.
2001-05-23 15:26:42 +02:00
//
static GPRE_NOD par_array(gpre_req* request,
gpre_fld* field, bool subscript_flag, bool sql_flag)
2001-05-23 15:26:42 +02:00
{
bool paren = false;
bool bracket = false;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_LEFT_PAREN))
paren = true;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_L_BRCKET))
bracket = true;
2001-05-23 15:26:42 +02:00
else if (!subscript_flag)
2003-10-15 00:22:32 +02:00
CPR_s_error("Missing parenthesis or bracket for array reference.");
2001-05-23 15:26:42 +02:00
gpre_nod* array_node = MSC_node(nod_array,
(SSHORT) (field->fld_array_info->ary_dimension_count + 1));
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (sql_flag && ((paren && MSC_match(KW_RIGHT_PAREN)) ||
(bracket && MSC_match(KW_R_BRCKET))))
{
return array_node;
}
2001-05-23 15:26:42 +02:00
int fortran_adjustment = array_node->nod_count;
2001-05-23 15:26:42 +02:00
if (paren || bracket) {
if (!global_subscript_field)
global_subscript_field = MET_make_field("gds_array_subscript", dtype_long,
4, false);
2001-05-23 15:26:42 +02:00
// Parse a commalist of subscripts and build a tree of index nodes
2001-05-23 15:26:42 +02:00
int i = 1;
for (dim* dimension = field->fld_array_info->ary_dimension;
dimension; dimension = dimension->dim_next, i++)
{
gpre_nod* node;
2001-05-23 15:26:42 +02:00
if (!sql_flag)
node = par_value(request, global_subscript_field);
2001-05-23 15:26:42 +02:00
else {
node = SQE_value(request, false, NULL, NULL);
// For all values referenced, post the subscript field
SQE_post_field(node, global_subscript_field);
2001-05-23 15:26:42 +02:00
}
gpre_nod* index_node = MSC_unary(nod_index, node);
2001-05-23 15:26:42 +02:00
/* Languages which can't handle negative or non-positive bounds need to
be accomodated with normalization of the indices. */
switch (gpreGlob.sw_language) {
2001-05-23 15:26:42 +02:00
case lang_c:
case lang_cxx:
case lang_internal:
index_node->nod_arg[0] = normalize_index(dimension,
index_node->nod_arg[0],
ZERO_BASED);
2001-05-23 15:26:42 +02:00
break;
case lang_cobol:
index_node->nod_arg[0] = normalize_index(dimension,
index_node->nod_arg[0],
ONE_BASED);
2001-05-23 15:26:42 +02:00
break;
}
// Error checking of constants being out of range will be here in the future.
2001-05-23 15:26:42 +02:00
// Good ole Fortran's column major order needs to be accomodated.
2001-05-23 15:26:42 +02:00
if (gpreGlob.sw_language == lang_fortran)
2001-05-23 15:26:42 +02:00
array_node->nod_arg[fortran_adjustment - i] = index_node;
else
array_node->nod_arg[i] = index_node;
2003-10-15 00:22:32 +02:00
if ((dimension->dim_next) && (!MSC_match(KW_COMMA)))
CPR_s_error("Adequate number of subscripts for this array reference.");
2001-05-23 15:26:42 +02:00
}
// Match the parenthesis or bracket
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if ((paren) && (!MSC_match(KW_RIGHT_PAREN)))
CPR_s_error("Missing parenthesis for array reference.");
else if ((bracket) && !MSC_match(KW_R_BRCKET))
CPR_s_error("Missing right bracket for array reference.");
2001-05-23 15:26:42 +02:00
}
return array_node;
}
//____________________________________________________________
//
// Parse a boolean expression. Actually, just parse
// an OR node or anything of lower precedence.
//
static GPRE_NOD par_boolean( gpre_req* request)
2001-05-23 15:26:42 +02:00
{
gpre_nod* expr1 = par_and(request);
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_OR) && !MSC_match(KW_OR1))
2001-05-23 15:26:42 +02:00
return expr1;
return MSC_binary(nod_or, expr1, par_boolean(request));
}
//____________________________________________________________
//
// Parse a field reference. Anything else is an error.
//
static GPRE_NOD par_field( gpre_req* request)
2001-05-23 15:26:42 +02:00
{
const gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (!symbol)
2003-10-15 00:22:32 +02:00
CPR_s_error("qualified field reference");
bool upcase_flag = false;
gpre_nod* prefix_node = 0;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_UPPERCASE)) {
prefix_node = MSC_node(nod_upcase, 1);
upcase_flag = true;
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_LEFT_PAREN))
CPR_s_error("left parenthesis");
if (!(symbol = gpreGlob.token_global.tok_symbol))
2003-10-15 00:22:32 +02:00
CPR_s_error("qualified field reference");
2001-05-23 15:26:42 +02:00
}
gpre_ctx* context = 0;
gpre_nod* node = 0;
gpre_fld* field = 0;
2001-05-23 15:26:42 +02:00
if (symbol->sym_type == SYM_context) {
field = EXP_field(&context);
if (field->fld_array_info)
node = par_array(request, field, false, false);
2001-05-23 15:26:42 +02:00
if (MSC_match(KW_DOT)) {
gpre_fld* cast = EXP_cast(field);
if (cast)
field = cast;
}
2001-05-23 15:26:42 +02:00
}
else
2003-10-15 00:22:32 +02:00
CPR_s_error("qualified field reference");
2001-05-23 15:26:42 +02:00
// There is a legit field reference. If the reference is
// to a field in this request, make up a reference block
// and a field node, and return.
if (!field->fld_array_info)
2003-10-15 00:22:32 +02:00
node = MSC_node(nod_field, 1);
2001-05-23 15:26:42 +02:00
if (upcase_flag)
prefix_node->nod_arg[0] = node;
if (context->ctx_request == request) {
ref* reference = (REF) MSC_alloc(REF_LEN);
2001-05-23 15:26:42 +02:00
reference->ref_field = field;
reference->ref_context = context;
if (node->nod_type == nod_array)
reference->ref_flags |= REF_array_elem;
2002-11-11 20:19:43 +01:00
node->nod_arg[0] = (GPRE_NOD) reference;
2001-05-23 15:26:42 +02:00
}
else {
/* Field wants to straddle two gpreGlob.requests. We need to do
2001-05-23 15:26:42 +02:00
two things. First, post a reference to the field to
the other request. This is a variance on code found
in par_variable in par.c */
ref* reference = EXP_post_field(field, context, false);
// Next, make a value reference for this request
2001-05-23 15:26:42 +02:00
ref* value_reference = MSC_reference(&request->req_values);
2001-05-23 15:26:42 +02:00
value_reference->ref_field = reference->ref_field;
value_reference->ref_source = reference;
node->nod_type = nod_value;
2002-11-11 20:19:43 +01:00
node->nod_arg[0] = (GPRE_NOD) value_reference;
2001-05-23 15:26:42 +02:00
}
if (upcase_flag) {
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_RIGHT_PAREN))
CPR_s_error("right parenthesis");
2001-05-23 15:26:42 +02:00
return prefix_node;
}
return node;
}
//____________________________________________________________
//
// Parse a value expression. In specific, handle the lowest
// precedence operator plus/minus.
//
static GPRE_NOD par_multiply( gpre_req* request, gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
gpre_nod* node = par_primitive_value(request, field);
2001-05-23 15:26:42 +02:00
while (true) {
2004-02-02 12:02:12 +01:00
enum nod_t nod_type;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_ASTERISK))
2004-02-02 12:02:12 +01:00
nod_type = nod_times;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_SLASH))
2004-02-02 12:02:12 +01:00
nod_type = nod_divide;
2001-05-23 15:26:42 +02:00
else
return node;
gpre_nod* arg = node;
2001-05-23 15:26:42 +02:00
node =
2004-02-02 12:02:12 +01:00
MSC_binary(nod_type, arg, par_primitive_value(request, field));
2001-05-23 15:26:42 +02:00
}
}
//____________________________________________________________
//
// Parse a native C value.
//
static GPRE_NOD par_native_value( gpre_req* request, gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
TEXT s[64];
// Special case literals
if (gpreGlob.token_global.tok_type == tok_number || gpreGlob.token_global.tok_type == tok_sglquoted
|| (gpreGlob.token_global.tok_type == tok_dblquoted && gpreGlob.sw_sql_dialect == 1))
{
gpre_nod* anode = EXP_literal();
return anode;
2001-05-23 15:26:42 +02:00
}
ref* reference = (REF) MSC_alloc(REF_LEN);
gpre_nod* node = MSC_unary(nod_value, (GPRE_NOD) reference);
2001-05-23 15:26:42 +02:00
// Handle general native value references. Since these values will need
// to be exported to the database system, make sure there is a reference
// field.
reference->ref_value = PAR_native_value(false, false);
2001-05-23 15:26:42 +02:00
if (!field) {
sprintf(s, "no reference field for %s", reference->ref_value);
PAR_error(s);
}
reference->ref_next = request->req_values;
request->req_values = reference;
reference->ref_field = field;
return node;
}
//____________________________________________________________
//
// Parse either a boolean NOT or a boolean parenthetical.
//
static GPRE_NOD par_not( gpre_req* request)
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_LEFT_PAREN)) {
gpre_nod* anode = par_boolean(request);
2001-05-23 15:26:42 +02:00
EXP_match_paren();
return anode;
2001-05-23 15:26:42 +02:00
}
gpre_nod* node = par_udf(request, UDF_boolean, 0);
if (node)
2001-05-23 15:26:42 +02:00
return node;
2003-10-15 00:22:32 +02:00
if (!(MSC_match(KW_NOT)))
2001-05-23 15:26:42 +02:00
return par_relational(request);
return MSC_unary(nod_not, par_not(request));
}
//____________________________________________________________
//
// Parse the substance of an OVER clause (but not the leading keyword).
//
static GPRE_NOD par_over( gpre_ctx* context)
2001-05-23 15:26:42 +02:00
{
TEXT s[64];
gpre_nod* boolean = NULL;
2001-05-23 15:26:42 +02:00
do {
gpre_nod* field1 = lookup_field(context);
if (!field1) {
sprintf(s, "OVER field %s undefined", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
gpre_nod* field2 = NULL;
for (gpre_ctx* next = context->ctx_next; next;
next = next->ctx_next)
{
2001-05-23 15:26:42 +02:00
if (field2 = lookup_field(next))
break;
}
2001-05-23 15:26:42 +02:00
if (!field2) {
sprintf(s, "OVER field %s undefined", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
boolean = make_and(boolean, MSC_binary(nod_eq, field1, field2));
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
while (MSC_match(KW_COMMA));
2001-05-23 15:26:42 +02:00
return boolean;
}
//____________________________________________________________
//
// Parse a value expression. In specific, handle the lowest
// precedence operator plus/minus.
//
static GPRE_NOD par_primitive_value( gpre_req* request, gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_MINUS))
2001-05-23 15:26:42 +02:00
return MSC_unary(nod_negate, par_primitive_value(request, field));
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_LEFT_PAREN)) {
gpre_nod* node = par_value(request, field);
2001-05-23 15:26:42 +02:00
EXP_match_paren();
return node;
}
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_UPPERCASE)) {
gpre_nod* node = MSC_node(nod_upcase, 1);
gpre_nod* sub = par_primitive_value(request, field);
2001-05-23 15:26:42 +02:00
node->nod_arg[0] = sub;
return node;
}
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_USER_NAME))
return MSC_node(nod_user_name, 0);
2001-05-23 15:26:42 +02:00
// Check for user defined functions
gpre_nod* node = par_udf(request, UDF_value, field);
if (node)
2001-05-23 15:26:42 +02:00
return node;
const gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (!symbol || (symbol->sym_type != SYM_context))
2001-05-23 15:26:42 +02:00
return par_native_value(request, field);
return par_field(request);
}
//____________________________________________________________
//
// Parse a relational expression.
//
static GPRE_NOD par_relational( gpre_req* request)
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_ANY)) {
gpre_nod* expr = MSC_node(nod_any, 1);
2001-05-23 15:26:42 +02:00
expr->nod_count = 0;
2002-11-11 20:19:43 +01:00
expr->nod_arg[0] = (GPRE_NOD) EXP_rse(request, 0);
EXP_rse_cleanup((gpre_rse*) expr->nod_arg[0]);
2001-05-23 15:26:42 +02:00
return expr;
}
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_UNIQUE)) {
gpre_nod* expr = MSC_node(nod_unique, 1);
2001-05-23 15:26:42 +02:00
expr->nod_count = 0;
2002-11-11 20:19:43 +01:00
expr->nod_arg[0] = (GPRE_NOD) EXP_rse(request, 0);
EXP_rse_cleanup((gpre_rse*) expr->nod_arg[0]);
2001-05-23 15:26:42 +02:00
return expr;
}
// That's right, three pointer dereferences to get to the reference
// structure if there's a UDF. V3 bug#531. MaryAnn 12/4/89
gpre_nod* expr1 = par_udf(request, UDF_value, 0);
ref* reference;
if (expr1)
2001-05-23 15:26:42 +02:00
reference = (REF) (expr1->nod_arg[0]->nod_arg[0]->nod_arg[0]);
else {
expr1 = par_field(request);
reference = (REF) expr1->nod_arg[0];
}
gpre_fld* field = reference->ref_field;
2001-05-23 15:26:42 +02:00
// Check for any of the binary guys
const bool negation = MSC_match(KW_NOT);
2001-05-23 15:26:42 +02:00
const rel_ops* relop;
2001-05-23 15:26:42 +02:00
for (relop = relops;; relop++)
if ((int) relop->rel_kw == (int) KW_none)
2003-10-15 00:22:32 +02:00
CPR_s_error("relational operator");
else if (MSC_match(relop->rel_kw))
2001-05-23 15:26:42 +02:00
break;
gpre_nod* expr = NULL;
gpre_nod* expr2 = NULL;
2001-05-23 15:26:42 +02:00
if ((int) relop->rel_kw == (int) KW_STARTING) {
2003-10-15 00:22:32 +02:00
MSC_match(KW_WITH);
expr = MSC_node(relop->rel_op, relop->rel_args);
2001-05-23 15:26:42 +02:00
}
else if ((int) relop->rel_kw == (int) KW_MATCHES) {
expr2 = par_value(request, field);
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_USING))
expr = MSC_node(nod_sleuth, 3);
2001-05-23 15:26:42 +02:00
else
2003-10-15 00:22:32 +02:00
expr = MSC_node(nod_matches, 2);
2001-05-23 15:26:42 +02:00
}
else
2003-10-15 00:22:32 +02:00
expr = MSC_node(relop->rel_op, relop->rel_args);
2001-05-23 15:26:42 +02:00
expr->nod_arg[0] = expr1;
switch (expr->nod_type) {
case nod_missing:
break;
case nod_between:
expr->nod_arg[1] = expr2 = par_value(request, field);
2003-10-15 00:22:32 +02:00
MSC_match(KW_AND);
2001-05-23 15:26:42 +02:00
expr->nod_arg[2] = par_value(request, field);
break;
case nod_matches:
expr->nod_arg[1] = expr2;
break;
case nod_sleuth:
expr->nod_arg[1] = expr2;
expr->nod_arg[2] = par_value(request, field);
break;
default:
expr->nod_arg[1] = expr2 = par_value(request, field);
}
if (expr2)
if (expr1->nod_type == nod_array && expr2->nod_type == nod_value)
((REF) expr2->nod_arg[0])->ref_flags |=
((REF) expr1->nod_arg[0])->ref_flags & REF_array_elem;
else if (expr2->nod_type == nod_array && expr1->nod_type == nod_value)
((REF) expr1->nod_arg[0])->ref_flags |=
((REF) expr2->nod_arg[0])->ref_flags & REF_array_elem;
if (!negation)
return expr;
return MSC_unary(nod_not, expr);
}
//____________________________________________________________
//
// 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, USHORT type, gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
if (!request)
return NULL;
// Check for user defined functions
udf* new_udf;
for (gpre_sym* symbol = gpreGlob.token_global.tok_symbol; symbol; symbol = symbol->sym_homonym)
if (symbol->sym_type == SYM_udf && (new_udf = (udf*) symbol->sym_object) &&
// request->req_database == new_udf->udf_database &&
2003-10-15 00:22:32 +02:00
new_udf->udf_type == type)
{
gpre_nod* node = MSC_node(nod_udf, 2);
2001-05-23 15:26:42 +02:00
node->nod_count = 1;
2003-09-05 12:14:08 +02:00
node->nod_arg[1] = (GPRE_NOD) new_udf;
2003-10-15 00:22:32 +02:00
PAR_get_token();
if (!MSC_match(KW_LEFT_PAREN))
2001-05-23 15:26:42 +02:00
EXP_left_paren(0);
2004-02-02 12:02:12 +01:00
gpre_lls* stack = NULL;
2001-05-23 15:26:42 +02:00
for (;;) {
2003-10-15 03:18:01 +02:00
MSC_push(par_value(request, field), &stack);
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_COMMA))
2001-05-23 15:26:42 +02:00
break;
}
EXP_match_paren();
node->nod_arg[0] = make_list(stack);
return node;
}
return NULL;
}
//____________________________________________________________
//
// Parse a value expression. In specific, handle the lowest
// precedence operator plus/minus.
//
static GPRE_NOD par_value( gpre_req* request, gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
gpre_nod* node = par_multiply(request, field);
2001-05-23 15:26:42 +02:00
while (true) {
2004-02-02 12:02:12 +01:00
enum nod_t nod_type;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_PLUS))
2004-02-02 12:02:12 +01:00
nod_type = nod_plus;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_MINUS))
2004-02-02 12:02:12 +01:00
nod_type = nod_minus;
2001-05-23 15:26:42 +02:00
else
return node;
gpre_nod* arg = node;
2004-02-02 12:02:12 +01:00
node = MSC_binary(nod_type, arg, par_value(request, field));
2001-05-23 15:26:42 +02:00
}
}