mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 04:43:02 +01:00
d58a557d63
The issues are: 1) '1.5' / '0.5' doesn't work in Dialect 1 2) avg ('1.5') doesn't work in Dialect 1 3) 5 * '1.5' produces INT result instead of DOUBLE PRECISION in Dialect 1 4) sum ('1.5') produces NUMERIC(15, 2) result instead of DOUBLE PRECISION in Dialect 1 5) - '1.5' doesn't work in Dialect 1 6) '1.5' * '0.5' and '1.5' / '0.5' are not forbidden in Dialect 3
2015 lines
47 KiB
C++
2015 lines
47 KiB
C++
//____________________________________________________________
|
|
//
|
|
// PROGRAM: C preprocessor
|
|
// MODULE: cme.cpp
|
|
// DESCRIPTION: Request expression compiler
|
|
//
|
|
// 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, buffer ptr bug
|
|
//
|
|
//
|
|
//____________________________________________________________
|
|
//
|
|
//
|
|
|
|
#include "firebird.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "../jrd/ibase.h"
|
|
#include "../gpre/gpre.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../intl/charsets.h"
|
|
#include "../gpre/cme_proto.h"
|
|
#include "../gpre/cmp_proto.h"
|
|
#include "../gpre/gpre_proto.h"
|
|
#include "../gpre/gpre_meta.h"
|
|
#include "../gpre/movg_proto.h"
|
|
#include "../gpre/par_proto.h"
|
|
#include "../gpre/prett_proto.h"
|
|
#include "../jrd/dsc_proto.h"
|
|
#include "../gpre/msc_proto.h"
|
|
|
|
static void cmp_array(GPRE_NOD, gpre_req*);
|
|
static void cmp_array_element(GPRE_NOD, gpre_req*);
|
|
static void cmp_cast(GPRE_NOD, gpre_req*);
|
|
static void cmp_field(const gpre_nod*, gpre_req*);
|
|
static void cmp_literal(const gpre_nod*, gpre_req*);
|
|
static void cmp_map(MAP, gpre_req*);
|
|
static void cmp_plan(const gpre_nod*, gpre_req*);
|
|
static void cmp_sdl_dtype(const gpre_fld*, REF);
|
|
static void cmp_udf(GPRE_NOD, gpre_req*);
|
|
static void cmp_value(const gpre_nod*, gpre_req*);
|
|
static USHORT get_string_len(const gpre_fld*);
|
|
static void stuff_sdl_dimension(const dim*, REF, SSHORT);
|
|
static void stuff_sdl_element(REF, const gpre_fld*);
|
|
static void stuff_sdl_loops(REF, const gpre_fld*);
|
|
static void stuff_sdl_number(const SLONG, REF);
|
|
|
|
const int USER_LENGTH = 32;
|
|
//#define STUFF(blr) *request->req_blr++ = (UCHAR) (blr)
|
|
//#define STUFF_WORD(blr) STUFF (blr); STUFF (blr >> 8)
|
|
//#define STUFF_CSTRING(blr) stuff_cstring (request, blr)
|
|
|
|
//#define STUFF_SDL(sdl) *reference->ref_sdl++ = (UCHAR) (sdl)
|
|
//#define STUFF_SDL_WORD(sdl) STUFF_SDL (sdl); STUFF_SDL (sdl >> 8);
|
|
//#define STUFF_SDL_LONG(sdl) STUFF_SDL (sdl); STUFF_SDL (sdl >> 8); STUFF_SDL (sdl >>16); STUFF_SDL (sdl >> 24);
|
|
|
|
static bool debug_on;
|
|
|
|
struct op_table
|
|
{
|
|
enum nod_t op_type;
|
|
UCHAR op_blr;
|
|
};
|
|
|
|
const op_table operators[] =
|
|
{
|
|
{ nod_eq , blr_eql },
|
|
{ nod_ge , blr_geq },
|
|
{ nod_gt , blr_gtr },
|
|
{ nod_le , blr_leq },
|
|
{ nod_lt , blr_lss },
|
|
{ nod_ne , blr_neq },
|
|
{ nod_missing , blr_missing },
|
|
{ nod_between , blr_between },
|
|
{ nod_and , blr_and },
|
|
{ nod_or , blr_or },
|
|
{ nod_not , blr_not },
|
|
{ nod_matches , blr_matching },
|
|
{ nod_starting , blr_starting },
|
|
{ nod_containing , blr_containing },
|
|
{ nod_plus , blr_add },
|
|
{ nod_from , blr_from },
|
|
{ nod_via , blr_via },
|
|
{ nod_minus , blr_subtract },
|
|
{ nod_times , blr_multiply },
|
|
{ nod_divide , blr_divide },
|
|
{ nod_negate , blr_negate },
|
|
{ nod_null , blr_null },
|
|
{ nod_user_name , blr_user_name },
|
|
// { count2 }
|
|
// { nod_count, blr_count2 },
|
|
//
|
|
{ nod_count , blr_count },
|
|
{ nod_max , blr_maximum },
|
|
{ nod_min , blr_minimum },
|
|
{ nod_average , blr_average },
|
|
{ nod_total , blr_total },
|
|
{ nod_any , blr_any },
|
|
{ nod_unique , blr_unique },
|
|
{ nod_agg_count , blr_agg_count2 },
|
|
{ nod_agg_count , blr_agg_count },
|
|
{ nod_agg_max , blr_agg_max },
|
|
{ nod_agg_min , blr_agg_min },
|
|
{ nod_agg_total , blr_agg_total },
|
|
{ nod_agg_average , blr_agg_average },
|
|
{ nod_upcase , blr_upcase },
|
|
{ nod_lowcase , blr_lowcase },
|
|
{ nod_sleuth , blr_matching2 },
|
|
{ nod_concatenate , blr_concatenate },
|
|
{ nod_cast , blr_cast },
|
|
{ nod_ansi_any , blr_ansi_any },
|
|
{ nod_gen_id , blr_gen_id },
|
|
{ nod_ansi_all , blr_ansi_all },
|
|
{ nod_current_date, blr_current_date },
|
|
{ nod_current_time, blr_current_time },
|
|
{ nod_current_timestamp, blr_current_timestamp },
|
|
{ nod_any, 0 }
|
|
};
|
|
|
|
|
|
static inline void assign_dtype(gpre_fld* f, const gpre_fld* field)
|
|
{
|
|
f->fld_dtype = field->fld_dtype;
|
|
f->fld_length = field->fld_length;
|
|
f->fld_scale = field->fld_scale;
|
|
f->fld_precision = field->fld_precision;
|
|
f->fld_sub_type = field->fld_sub_type;
|
|
f->fld_charset_id = field->fld_charset_id;
|
|
f->fld_collate_id = field->fld_collate_id;
|
|
f->fld_ttype = field->fld_ttype;
|
|
}
|
|
|
|
// One of d1, d2 is time, the other is date
|
|
static inline bool is_date_and_time(const USHORT d1, const USHORT d2)
|
|
{
|
|
return (d1 == dtype_sql_time && d2 == dtype_sql_date) ||
|
|
(d2 == dtype_sql_time && d1 == dtype_sql_date);
|
|
}
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Compile a random expression.
|
|
//
|
|
|
|
void CME_expr(GPRE_NOD node, gpre_req* request)
|
|
{
|
|
gpre_ctx* context;
|
|
const ref* reference;
|
|
TEXT s[128];
|
|
|
|
switch (node->nod_type)
|
|
{
|
|
case nod_field:
|
|
if (!(reference = (REF) node->nod_arg[0]))
|
|
{
|
|
CPR_error("CME_expr: reference missing");
|
|
return;
|
|
}
|
|
cmp_field(node, request);
|
|
if (reference->ref_flags & REF_fetch_array)
|
|
cmp_array(node, request);
|
|
return;
|
|
|
|
case nod_array:
|
|
cmp_array_element(node, request);
|
|
return;
|
|
|
|
case nod_index:
|
|
CME_expr(node->nod_arg[0], request);
|
|
return;
|
|
|
|
case nod_value:
|
|
cmp_value(node, request);
|
|
if ((reference = (REF) node->nod_arg[0]) &&
|
|
(reference->ref_flags & REF_fetch_array))
|
|
{
|
|
cmp_array(node, request);
|
|
}
|
|
return;
|
|
|
|
case nod_negate:
|
|
if (node->nod_arg[0]->nod_type != nod_literal)
|
|
break;
|
|
case nod_literal:
|
|
cmp_literal(node, request);
|
|
return;
|
|
|
|
case nod_like:
|
|
{
|
|
request->add_byte((node->nod_count == 2) ? blr_like : blr_ansi_like);
|
|
gpre_nod** ptr = node->nod_arg;
|
|
for (const gpre_nod* const* const end = ptr + node->nod_count;
|
|
ptr < end; ptr++)
|
|
{
|
|
CME_expr(*ptr, request);
|
|
}
|
|
return;
|
|
}
|
|
|
|
case nod_udf:
|
|
cmp_udf(node, request);
|
|
return;
|
|
|
|
case nod_gen_id:
|
|
{
|
|
request->add_byte(blr_gen_id);
|
|
const TEXT* p = (TEXT *) (node->nod_arg[1]);
|
|
|
|
// check if this generator really exists
|
|
|
|
if (!MET_generator(p, request->req_database))
|
|
{
|
|
sprintf(s, "generator %s not found", p);
|
|
CPR_error(s);
|
|
}
|
|
request->add_byte(strlen(p));
|
|
while (*p)
|
|
request->add_byte(*p++);
|
|
CME_expr(node->nod_arg[0], request);
|
|
return;
|
|
}
|
|
|
|
case nod_cast:
|
|
cmp_cast(node, request);
|
|
return;
|
|
|
|
case nod_agg_count:
|
|
if ((node->nod_arg[0]) &&
|
|
!(request->req_database->dbb_flags & DBB_v3))
|
|
{
|
|
if (node->nod_arg[1])
|
|
request->add_byte(blr_agg_count_distinct);
|
|
else
|
|
request->add_byte(blr_agg_count2);
|
|
CME_expr(node->nod_arg[0], request);
|
|
}
|
|
else
|
|
request->add_byte(blr_agg_count);
|
|
return;
|
|
|
|
// ** Begin date/time/timestamp support *
|
|
case nod_extract:
|
|
request->add_byte(blr_extract);
|
|
switch ((KWWORDS) (IPTR) node->nod_arg[0])
|
|
{
|
|
case KW_YEAR:
|
|
request->add_byte(blr_extract_year);
|
|
break;
|
|
case KW_MONTH:
|
|
request->add_byte(blr_extract_month);
|
|
break;
|
|
case KW_DAY:
|
|
request->add_byte(blr_extract_day);
|
|
break;
|
|
case KW_HOUR:
|
|
request->add_byte(blr_extract_hour);
|
|
break;
|
|
case KW_MINUTE:
|
|
request->add_byte(blr_extract_minute);
|
|
break;
|
|
case KW_SECOND:
|
|
request->add_byte(blr_extract_second);
|
|
break;
|
|
case KW_WEEKDAY:
|
|
request->add_byte(blr_extract_weekday);
|
|
break;
|
|
case KW_YEARDAY:
|
|
request->add_byte(blr_extract_yearday);
|
|
break;
|
|
default:
|
|
CPR_error("CME_expr:Invalid extract part");
|
|
}
|
|
CME_expr(node->nod_arg[1], request);
|
|
return;
|
|
|
|
// ** End date/time/timestamp support *
|
|
// count2
|
|
// case nod_count:
|
|
// if (node->nod_arg [1])
|
|
// break;
|
|
// STUFF (blr_count);
|
|
// CME_rse (node->nod_arg [0], request);
|
|
// return;
|
|
//
|
|
|
|
case nod_agg_total:
|
|
if ((node->nod_arg[1]) &&
|
|
!(request->req_database->dbb_flags & DBB_v3))
|
|
{
|
|
request->add_byte(blr_agg_total_distinct);
|
|
}
|
|
else
|
|
request->add_byte(blr_agg_total);
|
|
CME_expr(node->nod_arg[0], request);
|
|
return;
|
|
|
|
case nod_agg_average:
|
|
if ((node->nod_arg[1]) &&
|
|
!(request->req_database->dbb_flags & DBB_v3))
|
|
{
|
|
request->add_byte(blr_agg_average_distinct);
|
|
}
|
|
else
|
|
request->add_byte(blr_agg_average);
|
|
CME_expr(node->nod_arg[0], request);
|
|
return;
|
|
|
|
case nod_dom_value:
|
|
request->add_byte(blr_fid);
|
|
request->add_byte(0); // Context
|
|
request->add_word(0); // Field id
|
|
return;
|
|
|
|
case nod_map_ref:
|
|
{
|
|
const mel* element = (MEL) node->nod_arg[0];
|
|
context = element->mel_context;
|
|
request->add_byte(blr_fid);
|
|
request->add_byte(context->ctx_internal);
|
|
request->add_word(element->mel_position);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const op_table* nod2blr_operator;
|
|
for (nod2blr_operator = operators;
|
|
nod2blr_operator->op_type != node->nod_type;
|
|
++nod2blr_operator)
|
|
{
|
|
if (!nod2blr_operator->op_blr)
|
|
{
|
|
CPR_bugcheck("node type not implemented");
|
|
return;
|
|
}
|
|
}
|
|
|
|
request->add_byte(nod2blr_operator->op_blr);
|
|
gpre_nod** ptr = node->nod_arg;
|
|
|
|
for (const gpre_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++)
|
|
CME_expr(*ptr, request);
|
|
|
|
switch (node->nod_type)
|
|
{
|
|
case nod_any:
|
|
case nod_ansi_any:
|
|
case nod_ansi_all:
|
|
case nod_unique:
|
|
// count2 next line would be deleted
|
|
case nod_count:
|
|
CME_rse((gpre_rse*) node->nod_arg[0], request);
|
|
break;
|
|
|
|
case nod_max:
|
|
case nod_min:
|
|
case nod_average:
|
|
case nod_total:
|
|
case nod_from:
|
|
//
|
|
// case nod_count:
|
|
//
|
|
CME_rse((gpre_rse*) node->nod_arg[0], request);
|
|
CME_expr(node->nod_arg[1], request);
|
|
break;
|
|
|
|
case nod_via:
|
|
CME_rse((gpre_rse*) node->nod_arg[0], request);
|
|
CME_expr(node->nod_arg[1], request);
|
|
CME_expr(node->nod_arg[2], request);
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Compute datatype, length, and scale of an expression.
|
|
//
|
|
|
|
void CME_get_dtype(const gpre_nod* node, gpre_fld* f)
|
|
{
|
|
gpre_fld field1, field2;
|
|
SSHORT dtype_max;
|
|
const TEXT* string;
|
|
const ref* reference;
|
|
const gpre_fld* tmp_field;
|
|
const udf* a_udf;
|
|
|
|
f->fld_dtype = 0;
|
|
f->fld_length = 0;
|
|
f->fld_scale = 0;
|
|
f->fld_sub_type = 0;
|
|
f->fld_charset_id = 0;
|
|
f->fld_collate_id = 0;
|
|
f->fld_ttype = 0;
|
|
|
|
switch (node->nod_type)
|
|
{
|
|
case nod_null:
|
|
/* This occurs when SQL statement specifies a literal NULL, eg:
|
|
* SELECT NULL FROM TABLE1;
|
|
* As we don't have a <dtype_null, HOSTTYPE> datatype pairing,
|
|
* we don't know how to map this NULL to a host-language
|
|
* datatype. Therefore we now describe it as a
|
|
* CHAR(1) CHARACTER SET NONE type.
|
|
* No value will ever be sent back, as the value of the select
|
|
* will be NULL - this is only for purposes of allocating
|
|
* values in the message DESCRIBING
|
|
* the statement.
|
|
* Other parts of gpre aren't too happy with a dtype_unknown datatype
|
|
*/
|
|
f->fld_dtype = dtype_text;
|
|
f->fld_length = 1;
|
|
f->fld_ttype = ttype_none;
|
|
f->fld_charset_id = CS_NONE;
|
|
return;
|
|
|
|
case nod_map_ref:
|
|
{
|
|
const mel* element = (MEL) node->nod_arg[0];
|
|
CME_get_dtype(element->mel_expr, f);
|
|
return;
|
|
}
|
|
|
|
case nod_value:
|
|
case nod_field:
|
|
case nod_array:
|
|
reference = (REF) node->nod_arg[0];
|
|
if (!(tmp_field = reference->ref_field))
|
|
CPR_error("CME_get_dtype: node type not supported");
|
|
if (!(tmp_field->fld_dtype) || !(tmp_field->fld_length))
|
|
PAR_error("Inappropriate self-reference of field");
|
|
|
|
assign_dtype(f, tmp_field);
|
|
return;
|
|
|
|
case nod_agg_count:
|
|
case nod_count:
|
|
f->fld_dtype = dtype_long;
|
|
f->fld_length = sizeof(SLONG);
|
|
return;
|
|
|
|
case nod_gen_id:
|
|
if ((gpreGlob.sw_sql_dialect == SQL_DIALECT_V5) || (gpreGlob.sw_server_version < 6))
|
|
{
|
|
f->fld_dtype = dtype_long;
|
|
f->fld_length = sizeof(SLONG);
|
|
}
|
|
else
|
|
{
|
|
f->fld_dtype = dtype_int64;
|
|
f->fld_length = sizeof(ISC_INT64);
|
|
}
|
|
return;
|
|
|
|
case nod_max:
|
|
case nod_min:
|
|
case nod_from:
|
|
CME_get_dtype(node->nod_arg[1], f);
|
|
return;
|
|
|
|
case nod_agg_max:
|
|
case nod_agg_min:
|
|
case nod_negate:
|
|
CME_get_dtype(node->nod_arg[0], f);
|
|
if ((gpreGlob.sw_sql_dialect == SQL_DIALECT_V5)
|
|
&& (f->fld_dtype == dtype_int64))
|
|
{
|
|
f->fld_precision = 0;
|
|
f->fld_scale = 0;
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_length = sizeof(double);
|
|
}
|
|
return;
|
|
|
|
// ** Begin date/time/timestamp support *
|
|
case nod_extract:
|
|
{
|
|
KWWORDS kw_word = (KWWORDS) (IPTR) node->nod_arg[0];
|
|
CME_get_dtype(node->nod_arg[1], f);
|
|
switch (f->fld_dtype)
|
|
{
|
|
case dtype_timestamp:
|
|
break;
|
|
|
|
case dtype_sql_date:
|
|
if (kw_word == KW_HOUR || kw_word == KW_MINUTE
|
|
|| kw_word == KW_SECOND)
|
|
{
|
|
CPR_error("Invalid extract part for SQL DATE type");
|
|
}
|
|
break;
|
|
|
|
case dtype_sql_time:
|
|
if (kw_word != KW_HOUR && kw_word != KW_MINUTE
|
|
&& kw_word != KW_SECOND)
|
|
{
|
|
CPR_error("Invalid extract part for SQL TIME type");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
CPR_error("Invalid use of EXTRACT function");
|
|
}
|
|
switch (kw_word)
|
|
{
|
|
case KW_YEAR:
|
|
case KW_MONTH:
|
|
case KW_DAY:
|
|
case KW_WEEKDAY:
|
|
case KW_YEARDAY:
|
|
case KW_HOUR:
|
|
case KW_MINUTE:
|
|
f->fld_dtype = dtype_short;
|
|
f->fld_length = sizeof(short);
|
|
break;
|
|
case KW_SECOND:
|
|
f->fld_dtype = dtype_long;
|
|
f->fld_length = sizeof(long);
|
|
f->fld_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
|
break;
|
|
default:
|
|
CPR_error("Invalid EXTRACT part");
|
|
}
|
|
return;
|
|
}
|
|
|
|
case nod_current_date:
|
|
f->fld_dtype = dtype_sql_date;
|
|
f->fld_length = sizeof(ISC_DATE);
|
|
return;
|
|
|
|
case nod_current_time:
|
|
f->fld_dtype = dtype_sql_time;
|
|
f->fld_length = sizeof(ISC_TIME);
|
|
return;
|
|
|
|
case nod_current_timestamp:
|
|
f->fld_dtype = dtype_timestamp;
|
|
f->fld_length = sizeof(ISC_TIMESTAMP);
|
|
return;
|
|
|
|
// ** End date/time/timestamp support *
|
|
|
|
case nod_times:
|
|
CME_get_dtype(node->nod_arg[0], &field1);
|
|
CME_get_dtype(node->nod_arg[1], &field2);
|
|
if (gpreGlob.sw_sql_dialect == SQL_DIALECT_V5)
|
|
{
|
|
if (field1.fld_dtype == dtype_int64)
|
|
{
|
|
field1.fld_dtype = dtype_double;
|
|
field1.fld_scale = 0;
|
|
field1.fld_length = sizeof(double);
|
|
}
|
|
if (field2.fld_dtype == dtype_int64)
|
|
{
|
|
field2.fld_dtype = dtype_double;
|
|
field2.fld_scale = 0;
|
|
field2.fld_length = sizeof(double);
|
|
}
|
|
dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
|
|
if (DTYPE_IS_DATE(dtype_max) || DTYPE_IS_BLOB(dtype_max))
|
|
{
|
|
CPR_error("Invalid use of date/blob/array value");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dtype_max = DSC_multiply_result[field1.fld_dtype][field2.fld_dtype];
|
|
if (dtype_max == dtype_unknown)
|
|
{
|
|
CPR_error("Invalid operand used in multiplication");
|
|
}
|
|
else if (dtype_max == DTYPE_CANNOT)
|
|
{
|
|
CPR_error("expression evaluation not supported");
|
|
}
|
|
}
|
|
|
|
switch (dtype_max)
|
|
{
|
|
case dtype_short:
|
|
case dtype_long:
|
|
f->fld_dtype = dtype_long;
|
|
f->fld_scale = field1.fld_scale + field2.fld_scale;
|
|
f->fld_length = sizeof(SLONG);
|
|
break;
|
|
#ifdef NATIVE_QUAD
|
|
case dtype_quad:
|
|
f->fld_dtype = dtype_quad;
|
|
f->fld_scale = field1.fld_scale + field2.fld_scale;
|
|
f->fld_length = sizeof(ISC_QUAD);
|
|
break;
|
|
#endif
|
|
case dtype_int64:
|
|
f->fld_dtype = dtype_int64;
|
|
f->fld_scale = field1.fld_scale + field2.fld_scale;
|
|
f->fld_length = sizeof(ISC_INT64);
|
|
break;
|
|
default:
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_scale = 0;
|
|
f->fld_length = sizeof(double);
|
|
break;
|
|
}
|
|
return;
|
|
|
|
case nod_concatenate:
|
|
CME_get_dtype(node->nod_arg[0], &field1);
|
|
CME_get_dtype(node->nod_arg[1], &field2);
|
|
dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
|
|
if (field1.fld_dtype > dtype_any_text)
|
|
{
|
|
f->fld_dtype = dtype_varying;
|
|
f->fld_char_length =
|
|
get_string_len(&field1) + get_string_len(&field2);
|
|
f->fld_length = f->fld_char_length + sizeof(USHORT);
|
|
f->fld_charset_id = CS_ASCII;
|
|
f->fld_ttype = ttype_ascii;
|
|
return;
|
|
}
|
|
assign_dtype(f, &field1);
|
|
f->fld_length = get_string_len(&field1) + get_string_len(&field2);
|
|
if (f->fld_dtype == dtype_cstring)
|
|
f->fld_length += 1;
|
|
else if (f->fld_dtype == dtype_varying)
|
|
f->fld_length += sizeof(USHORT);
|
|
return;
|
|
|
|
case nod_plus:
|
|
case nod_minus:
|
|
CME_get_dtype(node->nod_arg[0], &field1);
|
|
CME_get_dtype(node->nod_arg[1], &field2);
|
|
if (gpreGlob.sw_sql_dialect == SQL_DIALECT_V5)
|
|
{
|
|
if (field1.fld_dtype == dtype_int64)
|
|
{
|
|
field1.fld_dtype = dtype_double;
|
|
field1.fld_scale = 0;
|
|
field1.fld_length = sizeof(double);
|
|
}
|
|
if (field2.fld_dtype == dtype_int64)
|
|
{
|
|
field2.fld_dtype = dtype_double;
|
|
field2.fld_scale = 0;
|
|
field2.fld_length = sizeof(double);
|
|
}
|
|
|
|
if (DTYPE_IS_DATE(field1.fld_dtype) &&
|
|
DTYPE_IS_DATE(field2.fld_dtype) &&
|
|
!((node->nod_type == nod_minus) ||
|
|
is_date_and_time(field1.fld_dtype, field2.fld_dtype)))
|
|
{
|
|
CPR_error("Invalid use of timestamp/date/time value");
|
|
return; // silence non initialized warning
|
|
}
|
|
else {
|
|
dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
|
|
if (DTYPE_IS_BLOB(dtype_max)) {
|
|
CPR_error("Invalid use of blob/array value");
|
|
return; // silence non initialized warning
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ** Dialect is > 1 *
|
|
|
|
if (node->nod_type == nod_plus)
|
|
dtype_max = DSC_add_result[field1.fld_dtype][field2.fld_dtype];
|
|
else
|
|
dtype_max = DSC_sub_result[field1.fld_dtype][field2.fld_dtype];
|
|
if (dtype_max == dtype_unknown) {
|
|
CPR_error("Illegal operands used in addition");
|
|
return; // silence non initialized warning
|
|
}
|
|
else if (dtype_max == DTYPE_CANNOT) {
|
|
CPR_error("expression evaluation not supported");
|
|
return; // silence non initialized warning
|
|
}
|
|
}
|
|
|
|
switch (dtype_max)
|
|
{
|
|
case dtype_short:
|
|
case dtype_long:
|
|
f->fld_dtype = dtype_long;
|
|
f->fld_scale = MIN(field1.fld_scale, field2.fld_scale);
|
|
f->fld_length = sizeof(SLONG);
|
|
break;
|
|
#ifdef NATIVE_QUAD
|
|
case dtype_quad:
|
|
f->fld_dtype = dtype_quad;
|
|
f->fld_scale = MIN(field1.fld_scale, field2.fld_scale);
|
|
f->fld_length = sizeof(ISC_QUAD);
|
|
break;
|
|
#endif
|
|
// ** Begin date/time/timestamp support *
|
|
case dtype_sql_date:
|
|
f->fld_dtype = dtype_sql_date;
|
|
f->fld_scale = 0;
|
|
f->fld_length = sizeof(ISC_DATE);
|
|
break;
|
|
case dtype_sql_time:
|
|
f->fld_dtype = dtype_sql_time;
|
|
f->fld_scale = 0;
|
|
f->fld_length = sizeof(ISC_TIME);
|
|
break;
|
|
case dtype_timestamp:
|
|
f->fld_dtype = dtype_timestamp;
|
|
f->fld_scale = 0;
|
|
f->fld_length = sizeof(ISC_TIMESTAMP);
|
|
break;
|
|
// ** End date/time/timestamp support *
|
|
case dtype_int64:
|
|
f->fld_dtype = dtype_int64;
|
|
f->fld_scale = MIN(field1.fld_scale, field2.fld_scale);
|
|
f->fld_length = sizeof(ISC_INT64);
|
|
break;
|
|
default:
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_scale = 0;
|
|
f->fld_length = sizeof(double);
|
|
break;
|
|
}
|
|
return;
|
|
|
|
case nod_via:
|
|
CME_get_dtype(node->nod_arg[1], &field1);
|
|
CME_get_dtype(node->nod_arg[2], &field2);
|
|
if (field1.fld_dtype >= field2.fld_dtype)
|
|
{
|
|
assign_dtype(f, &field1);
|
|
}
|
|
else
|
|
{
|
|
assign_dtype(f, &field2);
|
|
}
|
|
return;
|
|
|
|
|
|
case nod_divide:
|
|
CME_get_dtype(node->nod_arg[0], &field1);
|
|
CME_get_dtype(node->nod_arg[1], &field2);
|
|
if (gpreGlob.sw_sql_dialect == SQL_DIALECT_V5)
|
|
{
|
|
if (field1.fld_dtype == dtype_int64)
|
|
{
|
|
field1.fld_dtype = dtype_double;
|
|
field1.fld_scale = 0;
|
|
field1.fld_length = sizeof(double);
|
|
}
|
|
if (field2.fld_dtype == dtype_int64)
|
|
{
|
|
field2.fld_dtype = dtype_double;
|
|
field2.fld_scale = 0;
|
|
field2.fld_length = sizeof(double);
|
|
}
|
|
dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
|
|
if (DTYPE_IS_DATE(dtype_max) || DTYPE_IS_BLOB(dtype_max))
|
|
CPR_error("Invalid use of date/blob/array value");
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_length = sizeof(double);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
dtype_max = DSC_multiply_result[field1.fld_dtype][field2.fld_dtype];
|
|
if (dtype_max == dtype_unknown)
|
|
CPR_error("Illegal operands used in division");
|
|
else if (dtype_max == DTYPE_CANNOT)
|
|
CPR_error("expression evaluation not supported");
|
|
}
|
|
|
|
if (dtype_max == dtype_int64)
|
|
{
|
|
f->fld_dtype = dtype_int64;
|
|
f->fld_scale = field1.fld_scale + field2.fld_scale;
|
|
f->fld_length = sizeof(ISC_INT64);
|
|
}
|
|
else
|
|
{
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_scale = 0;
|
|
f->fld_length = sizeof(double);
|
|
}
|
|
return;
|
|
|
|
case nod_average:
|
|
case nod_agg_average:
|
|
if (node->nod_type == nod_average)
|
|
CME_get_dtype(node->nod_arg[1], f);
|
|
else
|
|
CME_get_dtype(node->nod_arg[0], f);
|
|
if (!DTYPE_IS_NUMERIC(f->fld_dtype))
|
|
CPR_error("expression evaluation not supported");
|
|
if (gpreGlob.sw_sql_dialect != SQL_DIALECT_V5)
|
|
{
|
|
if (DTYPE_IS_EXACT(f->fld_dtype))
|
|
{
|
|
f->fld_dtype = dtype_int64;
|
|
f->fld_length = sizeof(SINT64);
|
|
}
|
|
else
|
|
{
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_length = sizeof(double);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case nod_agg_total:
|
|
case nod_total:
|
|
if (node->nod_type == nod_total)
|
|
CME_get_dtype(node->nod_arg[1], f);
|
|
else
|
|
CME_get_dtype(node->nod_arg[0], f);
|
|
if (gpreGlob.sw_sql_dialect == SQL_DIALECT_V5)
|
|
{
|
|
if ((f->fld_dtype == dtype_short) || (f->fld_dtype == dtype_long))
|
|
{
|
|
f->fld_dtype = dtype_long;
|
|
f->fld_length = sizeof(SLONG);
|
|
}
|
|
else
|
|
{
|
|
f->fld_precision = 0;
|
|
f->fld_scale = 0;
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_length = sizeof(double);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ** Dialect is 2 or 3 *
|
|
|
|
if (DTYPE_IS_EXACT(f->fld_dtype))
|
|
{
|
|
f->fld_dtype = dtype_int64;
|
|
f->fld_length = sizeof(SINT64);
|
|
}
|
|
else
|
|
{
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_length = sizeof(double);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case nod_literal:
|
|
reference = (REF) node->nod_arg[0];
|
|
string = reference->ref_value;
|
|
if (*string != '"' && *string != '\'')
|
|
{
|
|
/* Value didn't start with a quotemark - must be a numeric
|
|
that we stuffed away as a string during the parse */
|
|
if (strpbrk(string, "Ee"))
|
|
{
|
|
f->fld_dtype = dtype_double;
|
|
f->fld_length = sizeof(double);
|
|
}
|
|
else
|
|
{
|
|
int scale;
|
|
|
|
const char* s_ptr = string;
|
|
|
|
/** Get the scale **/
|
|
const char* ptr = strpbrk(string, ".");
|
|
if (!ptr)
|
|
scale = 0;
|
|
else
|
|
{
|
|
scale = (string + (strlen(string) - 1)) - ptr;
|
|
scale = -scale;
|
|
}
|
|
|
|
/** Get rid of the decimal point **/
|
|
UINT64 uint64_val = 0;
|
|
while (*s_ptr)
|
|
{
|
|
if (*s_ptr != '.')
|
|
uint64_val = (uint64_val * 10) + (*s_ptr - '0');
|
|
s_ptr++;
|
|
}
|
|
|
|
if (uint64_val <= MAX_SLONG)
|
|
{
|
|
f->fld_dtype = dtype_long;
|
|
f->fld_scale = scale;
|
|
f->fld_length = sizeof(SLONG);
|
|
}
|
|
else
|
|
{
|
|
f->fld_dtype = dtype_int64;
|
|
f->fld_scale = scale;
|
|
f->fld_length = sizeof(SINT64);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Did the reference include a character set specification? */
|
|
|
|
if (reference->ref_flags & REF_ttype)
|
|
f->fld_ttype = reference->ref_ttype;
|
|
|
|
if (reference->ref_flags & REF_sql_date)
|
|
{
|
|
f->fld_dtype = dtype_sql_date;
|
|
f->fld_length = sizeof(ISC_DATE);
|
|
}
|
|
else if (reference->ref_flags & REF_sql_time)
|
|
{
|
|
f->fld_dtype = dtype_sql_time;
|
|
f->fld_length = sizeof(ISC_TIME);
|
|
}
|
|
else if (reference->ref_flags & REF_timestamp)
|
|
{
|
|
f->fld_dtype = dtype_timestamp;
|
|
f->fld_length = sizeof(ISC_TIMESTAMP);
|
|
}
|
|
else
|
|
{
|
|
/* subtract 2 for starting & terminating quote */
|
|
|
|
f->fld_length = strlen(string) - 2;
|
|
if (gpreGlob.sw_cstring)
|
|
{
|
|
// add 1 back for the NULL byte
|
|
|
|
f->fld_length += 1;
|
|
f->fld_dtype = dtype_cstring;
|
|
}
|
|
else
|
|
f->fld_dtype = dtype_text;
|
|
}
|
|
}
|
|
return;
|
|
|
|
case nod_user_name:
|
|
f->fld_dtype = dtype_text;
|
|
f->fld_length = USER_LENGTH;
|
|
f->fld_ttype = ttype_ascii;
|
|
f->fld_charset_id = CS_ASCII;
|
|
return;
|
|
|
|
case nod_udf:
|
|
a_udf = (udf*) node->nod_arg[1];
|
|
f->fld_dtype = a_udf->udf_dtype;
|
|
f->fld_length = a_udf->udf_length;
|
|
f->fld_scale = a_udf->udf_scale;
|
|
f->fld_sub_type = a_udf->udf_sub_type;
|
|
f->fld_ttype = a_udf->udf_ttype;
|
|
f->fld_charset_id = a_udf->udf_charset_id;
|
|
return;
|
|
|
|
case nod_cast:
|
|
CME_get_dtype(node->nod_arg[0], &field1);
|
|
tmp_field = (gpre_fld*) node->nod_arg[1];
|
|
assign_dtype(f, tmp_field);
|
|
if (f->fld_length == 0)
|
|
f->fld_length = field1.fld_length;
|
|
return;
|
|
|
|
case nod_upcase:
|
|
case nod_lowcase:
|
|
CME_get_dtype(node->nod_arg[0], f);
|
|
if (f->fld_dtype <= dtype_any_text)
|
|
return;
|
|
|
|
/* User has specified UPPER(5) - while silly, we'll cast
|
|
the value into a string, and upcase it anyway */
|
|
|
|
f->fld_length = get_string_len(f) + sizeof(USHORT);
|
|
f->fld_dtype = dtype_varying;
|
|
f->fld_ttype = ttype_ascii;
|
|
f->fld_charset_id = CS_ASCII;
|
|
return;
|
|
|
|
default:
|
|
CPR_error("CME_get_dtype: node type not supported");
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Generate a relation reference.
|
|
//
|
|
|
|
void CME_relation(gpre_ctx* context, gpre_req* request)
|
|
{
|
|
CMP_check(request, 0);
|
|
|
|
gpre_rse* rs_stream = context->ctx_stream;
|
|
if (rs_stream)
|
|
{
|
|
CME_rse(rs_stream, request);
|
|
return;
|
|
}
|
|
|
|
gpre_prc* procedure;
|
|
gpre_rel* relation = context->ctx_relation;
|
|
if (relation)
|
|
{
|
|
if (gpreGlob.sw_ids)
|
|
{
|
|
if ((context->ctx_alias) &&
|
|
!(request->req_database->dbb_flags & DBB_v3))
|
|
{
|
|
request->add_byte(blr_rid2);
|
|
}
|
|
else
|
|
{
|
|
request->add_byte(blr_rid);
|
|
}
|
|
request->add_word(relation->rel_id);
|
|
}
|
|
else
|
|
{
|
|
if ((context->ctx_alias) &&
|
|
!(request->req_database->dbb_flags & DBB_v3))
|
|
{
|
|
request->add_byte(blr_relation2);
|
|
}
|
|
else
|
|
request->add_byte(blr_relation);
|
|
CMP_stuff_symbol(request, relation->rel_symbol);
|
|
}
|
|
|
|
if ((context->ctx_alias) &&
|
|
!(request->req_database->dbb_flags & DBB_v3))
|
|
{
|
|
request->add_cstring(context->ctx_alias);
|
|
}
|
|
request->add_byte(context->ctx_internal);
|
|
}
|
|
else if (procedure = context->ctx_procedure)
|
|
{
|
|
if (gpreGlob.sw_ids)
|
|
{
|
|
request->add_byte(blr_pid);
|
|
request->add_word(procedure->prc_id);
|
|
}
|
|
else
|
|
{
|
|
request->add_byte(blr_procedure);
|
|
CMP_stuff_symbol(request, procedure->prc_symbol);
|
|
}
|
|
request->add_byte(context->ctx_internal);
|
|
request->add_word(procedure->prc_in_count);
|
|
gpre_nod* inputs = context->ctx_prc_inputs;
|
|
if (inputs) {
|
|
gpre_nod** ptr = inputs->nod_arg;
|
|
for (const gpre_nod* const* const end = ptr + inputs->nod_count;
|
|
ptr < end; ptr++)
|
|
{
|
|
CME_expr(*ptr, request);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Generate blr for an rse node.
|
|
//
|
|
|
|
void CME_rse(gpre_rse* selection, gpre_req* request)
|
|
{
|
|
SSHORT i;
|
|
|
|
if (selection->rse_join_type == (NOD_T) 0)
|
|
{
|
|
if ((selection->rse_flags & RSE_singleton) &&
|
|
!(request->req_database->dbb_flags & DBB_v3))
|
|
{
|
|
request->add_byte(blr_singular);
|
|
}
|
|
request->add_byte(blr_rse);
|
|
}
|
|
else
|
|
request->add_byte(blr_rs_stream);
|
|
|
|
// Process unions, if any, otherwise process relations
|
|
|
|
gpre_rse* sub_rse = 0;
|
|
gpre_nod* union_node = selection->rse_union;
|
|
if (union_node)
|
|
{
|
|
request->add_byte(1);
|
|
request->add_byte(blr_union);
|
|
request->add_byte(selection->rse_context[0]->ctx_internal);
|
|
request->add_byte(union_node->nod_count);
|
|
gpre_nod** ptr = union_node->nod_arg;
|
|
for (const gpre_nod* const* const end = ptr + union_node->nod_count;
|
|
ptr < end; ptr++)
|
|
{
|
|
sub_rse = (gpre_rse*) * ptr;
|
|
CME_rse(sub_rse, request);
|
|
cmp_map(sub_rse->rse_map, request);
|
|
}
|
|
}
|
|
else if (sub_rse = selection->rse_aggregate)
|
|
{
|
|
request->add_byte(1);
|
|
request->add_byte(blr_aggregate);
|
|
request->add_byte(sub_rse->rse_map->map_context->ctx_internal);
|
|
CME_rse(sub_rse, request);
|
|
request->add_byte(blr_group_by);
|
|
gpre_nod* list = sub_rse->rse_group_by;
|
|
if (list)
|
|
{
|
|
request->add_byte(list->nod_count);
|
|
gpre_nod** ptr = list->nod_arg;
|
|
for (const gpre_nod* const* const end = ptr + list->nod_count;
|
|
ptr < end; ptr++)
|
|
{
|
|
CME_expr(*ptr, request);
|
|
}
|
|
}
|
|
else
|
|
request->add_byte(0);
|
|
cmp_map(sub_rse->rse_map, request);
|
|
}
|
|
else
|
|
{
|
|
request->add_byte(selection->rse_count);
|
|
for (i = 0; i < selection->rse_count; i++)
|
|
CME_relation(selection->rse_context[i], request);
|
|
}
|
|
|
|
// Process the clauses present
|
|
|
|
if (selection->rse_first)
|
|
{
|
|
request->add_byte(blr_first);
|
|
CME_expr(selection->rse_first, request);
|
|
}
|
|
|
|
if (selection->rse_boolean)
|
|
{
|
|
request->add_byte(blr_boolean);
|
|
CME_expr(selection->rse_boolean, request);
|
|
}
|
|
|
|
gpre_nod* temp = selection->rse_sort;
|
|
if (temp)
|
|
{
|
|
request->add_byte(blr_sort);
|
|
request->add_byte(temp->nod_count);
|
|
gpre_nod** ptr = temp->nod_arg;
|
|
for (i = 0; i < temp->nod_count; i++)
|
|
{
|
|
request->add_byte((*ptr++) ? blr_descending : blr_ascending);
|
|
CME_expr(*ptr++, request);
|
|
}
|
|
}
|
|
|
|
if (temp = selection->rse_reduced)
|
|
{
|
|
request->add_byte(blr_project);
|
|
request->add_byte(temp->nod_count);
|
|
gpre_nod** ptr = temp->nod_arg;
|
|
for (i = 0; i < temp->nod_count; i++)
|
|
CME_expr(*ptr++, request);
|
|
}
|
|
|
|
if (temp = selection->rse_plan)
|
|
{
|
|
request->add_byte(blr_plan);
|
|
cmp_plan(temp, request);
|
|
}
|
|
|
|
if (selection->rse_join_type != (NOD_T) 0
|
|
&& selection->rse_join_type != nod_join_inner)
|
|
{
|
|
request->add_byte(blr_join_type);
|
|
if (selection->rse_join_type == nod_join_left)
|
|
request->add_byte(blr_left);
|
|
else if (selection->rse_join_type == nod_join_right)
|
|
request->add_byte(blr_right);
|
|
else
|
|
request->add_byte(blr_full);
|
|
}
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
// generate a statement to be executed if the user scrolls
|
|
// in a direction other than forward; a message is sent outside
|
|
// the normal send/receive protocol to specify the direction
|
|
// and offset to scroll; note that we do this only on a SELECT
|
|
// type statement and only when talking to a 4.1 engine or greater
|
|
|
|
if (request->req_flags & REQ_sql_cursor &&
|
|
request->req_database->dbb_base_level >= 5)
|
|
{
|
|
request->add_byte(blr_receive);
|
|
request->add_byte(request->req_aport->por_msg_number);
|
|
request->add_byte(blr_seek);
|
|
request->add_byte(blr_parameter);
|
|
request->add_byte(request->req_aport->por_msg_number);
|
|
request->add_word(1);
|
|
request->add_byte(blr_parameter);
|
|
request->add_byte(request->req_aport->por_msg_number);
|
|
request->add_word(0);
|
|
}
|
|
#endif
|
|
|
|
// Finish up by making a BLR_END
|
|
|
|
request->add_byte(blr_end);
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Compile up an array reference putting
|
|
// out sdl (slice description language)
|
|
//
|
|
|
|
static void cmp_array( GPRE_NOD node, gpre_req* request)
|
|
{
|
|
CMP_check(request, 0);
|
|
|
|
ref* reference = (REF) node->nod_arg[0];
|
|
|
|
if (!reference->ref_context)
|
|
{
|
|
CPR_error("cmp_array: context missing");
|
|
return; //NULL;
|
|
}
|
|
|
|
const gpre_fld* field = reference->ref_field;
|
|
if (!field)
|
|
{
|
|
CPR_error("cmp_array: field missing");
|
|
return; // NULL;
|
|
}
|
|
else
|
|
{
|
|
// Header stuff
|
|
|
|
reference->ref_sdl = reference->ref_sdl_base =
|
|
reinterpret_cast<UCHAR*>(MSC_alloc(500));
|
|
reference->ref_sdl_length = 500;
|
|
reference->ref_sdl_ident = CMP_next_ident();
|
|
reference->add_byte(isc_sdl_version1);
|
|
reference->add_byte(isc_sdl_struct);
|
|
reference->add_byte(1);
|
|
|
|
// The datatype of the array elements
|
|
|
|
cmp_sdl_dtype(field->fld_array, reference);
|
|
|
|
// The relation and field identifiers or strings
|
|
|
|
if (gpreGlob.sw_ids)
|
|
{
|
|
reference->add_byte(isc_sdl_rid);
|
|
reference->add_byte(reference->ref_id);
|
|
reference->add_byte(isc_sdl_fid);
|
|
reference->add_byte(field->fld_id);
|
|
}
|
|
else
|
|
{
|
|
reference->add_byte(isc_sdl_relation);
|
|
reference->add_byte(strlen(field->fld_relation->rel_symbol->sym_string));
|
|
const TEXT* p;
|
|
for (p = field->fld_relation->rel_symbol->sym_string; *p; p++)
|
|
reference->add_byte(*p);
|
|
reference->add_byte(isc_sdl_field);
|
|
reference->add_byte(strlen(field->fld_symbol->sym_string));
|
|
for (p = field->fld_symbol->sym_string; *p; p++)
|
|
reference->add_byte(*p);
|
|
}
|
|
|
|
// The loops for the dimensions
|
|
|
|
stuff_sdl_loops(reference, field);
|
|
|
|
// The array element and its "subscripts"
|
|
|
|
stuff_sdl_element(reference, field);
|
|
|
|
reference->add_byte(isc_sdl_eoc);
|
|
}
|
|
|
|
reference->ref_sdl_length = reference->ref_sdl - reference->ref_sdl_base;
|
|
reference->ref_sdl = reference->ref_sdl_base;
|
|
|
|
if (debug_on)
|
|
PRETTY_print_sdl(reference->ref_sdl, 0, 0, 0);
|
|
|
|
//return node;
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Compile up a subscripted array reference
|
|
// from an gpre_rse and output blr for this reference
|
|
//
|
|
|
|
static void cmp_array_element( GPRE_NOD node, gpre_req* request)
|
|
{
|
|
request->add_byte(blr_index);
|
|
|
|
cmp_field(node, request);
|
|
|
|
request->add_byte(node->nod_count - 1);
|
|
|
|
for (USHORT index_count = 1; index_count < node->nod_count; index_count++)
|
|
CME_expr(node->nod_arg[index_count], request);
|
|
|
|
// return node;
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
//
|
|
|
|
static void cmp_cast( GPRE_NOD node, gpre_req* request)
|
|
{
|
|
|
|
request->add_byte(blr_cast);
|
|
CMP_external_field(request, (const gpre_fld*) node->nod_arg[1]);
|
|
CME_expr(node->nod_arg[0], request);
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Compile up a field reference.
|
|
//
|
|
|
|
static void cmp_field( const gpre_nod* node, gpre_req* request)
|
|
{
|
|
CMP_check(request, 0);
|
|
|
|
const ref* reference = (REF) node->nod_arg[0];
|
|
if (!reference)
|
|
{
|
|
CPR_error("cmp_field: reference missing");
|
|
return; // NULL;
|
|
}
|
|
|
|
const gpre_ctx* context = reference->ref_context;
|
|
if (!context)
|
|
{
|
|
CPR_error("cmp_field: context missing");
|
|
return; // NULL;
|
|
}
|
|
|
|
const gpre_fld* field = reference->ref_field;
|
|
if (!field)
|
|
{
|
|
CPR_error("cmp_field: field missing");
|
|
return; // NULL;
|
|
}
|
|
|
|
if (!field)
|
|
puts("cmp_field: symbol missing");
|
|
|
|
if (field->fld_flags & FLD_dbkey)
|
|
{
|
|
request->add_byte(blr_dbkey);
|
|
request->add_byte(context->ctx_internal);
|
|
}
|
|
else if (reference->ref_flags & REF_union)
|
|
{
|
|
request->add_byte(blr_fid);
|
|
request->add_byte(context->ctx_internal);
|
|
request->add_word(reference->ref_id);
|
|
}
|
|
else if (gpreGlob.sw_ids)
|
|
{
|
|
request->add_byte(blr_fid);
|
|
request->add_byte(context->ctx_internal);
|
|
request->add_word(field->fld_id);
|
|
}
|
|
else
|
|
{
|
|
request->add_byte(blr_field);
|
|
request->add_byte(context->ctx_internal);
|
|
CMP_stuff_symbol(request, field->fld_symbol);
|
|
}
|
|
|
|
// return node;
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Handle a literal expression.
|
|
//
|
|
|
|
static void cmp_literal( const gpre_nod* node, gpre_req* request)
|
|
{
|
|
bool negate = false;
|
|
|
|
if (node->nod_type == nod_negate)
|
|
{
|
|
node = node->nod_arg[0];
|
|
negate = true;
|
|
}
|
|
|
|
request->add_byte(blr_literal);
|
|
const ref* reference = (REF) node->nod_arg[0];
|
|
const char* string = reference->ref_value;
|
|
|
|
if (*string != '"' && *string != '\'')
|
|
{
|
|
/** If the numeric string contains an 'E' or 'e' in it
|
|
then the datatype is double.
|
|
**/
|
|
if (strpbrk(string, "Ee"))
|
|
{
|
|
string = reference->ref_value;
|
|
|
|
if (!(request->req_database->dbb_flags & DBB_v3))
|
|
request->add_byte(blr_double);
|
|
else if (gpreGlob.sw_know_interp)
|
|
{ // then must be using blr_version5
|
|
request->add_byte(blr_text2);
|
|
request->add_word(ttype_ascii);
|
|
}
|
|
else
|
|
request->add_byte(blr_text);
|
|
|
|
request->add_word(strlen(string));
|
|
while (*string)
|
|
request->add_byte(*string++);
|
|
}
|
|
else
|
|
{
|
|
/** The numeric string doesn't contain 'E' or 'e' in it.
|
|
Then this must be a scaled int. Figure out if there
|
|
is a '.' in it and calculate its scale.
|
|
**/
|
|
const char* s_ptr = string;
|
|
|
|
/** Get the scale **/
|
|
int scale;
|
|
const char* ptr = strpbrk(string, ".");
|
|
if (!ptr)
|
|
/** No '.' ?, Scale is 0 **/
|
|
scale = 0;
|
|
else
|
|
{
|
|
/** Aha!, there is a '.'. find the scale **/
|
|
scale = (string + (strlen(string) - 1)) - ptr;
|
|
scale = -scale;
|
|
}
|
|
|
|
UINT64 uint64_val = 0;
|
|
while (*s_ptr)
|
|
{
|
|
if (*s_ptr != '.')
|
|
uint64_val = (uint64_val * 10) + (*s_ptr - '0');
|
|
s_ptr++;
|
|
}
|
|
|
|
/** see if we can fit the value in a long or INT64. **/
|
|
if ((uint64_val <= MAX_SLONG) ||
|
|
((uint64_val == (MAX_SLONG + (UINT64) 1))
|
|
&& (negate == true)))
|
|
{
|
|
long long_val;
|
|
if (negate == true)
|
|
long_val = -((long) uint64_val);
|
|
else
|
|
long_val = (long) uint64_val;
|
|
request->add_byte(blr_long);
|
|
request->add_byte(scale); // scale factor
|
|
request->add_word(long_val);
|
|
request->add_word(long_val >> 16);
|
|
}
|
|
else if ((uint64_val <= MAX_SINT64) ||
|
|
((uint64_val == ((UINT64) MAX_SINT64 + 1))
|
|
&& (negate == true)))
|
|
{
|
|
SINT64 sint64_val;
|
|
if (negate == true)
|
|
sint64_val = -((SINT64) uint64_val);
|
|
else
|
|
sint64_val = (SINT64) uint64_val;
|
|
request->add_byte(blr_int64);
|
|
request->add_byte(scale); // scale factor
|
|
request->add_word(sint64_val);
|
|
request->add_word(sint64_val >> 16);
|
|
request->add_word(sint64_val >> 32);
|
|
request->add_word(sint64_val >> 48);
|
|
}
|
|
else
|
|
CPR_error("cmp_literal : Numeric Value too big");
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Remove surrounding quotes from string, etc.
|
|
char buffer[MAX_SYM_SIZE];
|
|
char* p = buffer;
|
|
|
|
// Skip introducing quote mark
|
|
if (*string)
|
|
string++;
|
|
|
|
while (*string)
|
|
*p++ = *string++;
|
|
|
|
// Zap out terminating quote mark
|
|
*--p = 0;
|
|
const SSHORT length = p - buffer;
|
|
|
|
dsc from;
|
|
from.dsc_sub_type = ttype_ascii;
|
|
from.dsc_flags = 0;
|
|
from.dsc_dtype = dtype_text;
|
|
from.dsc_length = length;
|
|
from.dsc_address = (UCHAR*) buffer;
|
|
dsc to;
|
|
to.dsc_sub_type = 0;
|
|
to.dsc_flags = 0;
|
|
if (reference->ref_flags & REF_sql_date)
|
|
{
|
|
ISC_DATE dt;
|
|
request->add_byte(blr_sql_date);
|
|
to.dsc_dtype = dtype_sql_date;
|
|
to.dsc_length = sizeof(ISC_DATE);
|
|
to.dsc_address = (UCHAR*) & dt;
|
|
MOVG_move(&from, &to);
|
|
request->add_word(dt);
|
|
request->add_word(dt >> 16);
|
|
return; // node;
|
|
}
|
|
else if (reference->ref_flags & REF_timestamp)
|
|
{
|
|
ISC_TIMESTAMP ts;
|
|
request->add_byte(blr_timestamp);
|
|
to.dsc_dtype = dtype_timestamp;
|
|
to.dsc_length = sizeof(ISC_TIMESTAMP);
|
|
to.dsc_address = (UCHAR*) & ts;
|
|
MOVG_move(&from, &to);
|
|
request->add_word(ts.timestamp_date);
|
|
request->add_word(ts.timestamp_date >> 16);
|
|
request->add_word(ts.timestamp_time);
|
|
request->add_word(ts.timestamp_time >> 16);
|
|
return; // node;
|
|
}
|
|
else if (reference->ref_flags & REF_sql_time)
|
|
{
|
|
ISC_TIME itim;
|
|
request->add_byte(blr_sql_time);
|
|
to.dsc_dtype = dtype_sql_time;
|
|
to.dsc_length = sizeof(ISC_DATE);
|
|
to.dsc_address = (UCHAR*) & itim;
|
|
MOVG_move(&from, &to);
|
|
request->add_word(itim);
|
|
request->add_word(itim >> 16);
|
|
return; // node;
|
|
}
|
|
else if (!(reference->ref_flags & REF_ttype))
|
|
request->add_byte(blr_text);
|
|
else
|
|
{
|
|
request->add_byte(blr_text2);
|
|
request->add_word(reference->ref_ttype);
|
|
}
|
|
|
|
request->add_word(length);
|
|
for (string = buffer; *string;)
|
|
request->add_byte(*string++);
|
|
}
|
|
|
|
// return node;
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Generate a map for a union or aggregate rse.
|
|
//
|
|
|
|
static void cmp_map(map* a_map, gpre_req* request)
|
|
{
|
|
request->add_byte(blr_map);
|
|
request->add_word(a_map->map_count);
|
|
|
|
for (MEL element = a_map->map_elements; element; element = element->mel_next)
|
|
{
|
|
request->add_word(element->mel_position);
|
|
CME_expr(element->mel_expr, request);
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Generate an access plan for a query.
|
|
//
|
|
|
|
static void cmp_plan(const gpre_nod* plan_expression, gpre_req* request)
|
|
{
|
|
// stuff the join type
|
|
|
|
const gpre_nod* list = plan_expression->nod_arg[1];
|
|
if (list->nod_count > 1)
|
|
{
|
|
const gpre_nod* node = plan_expression->nod_arg[0];
|
|
if (node)
|
|
request->add_byte(blr_merge);
|
|
else
|
|
request->add_byte(blr_join);
|
|
request->add_byte(list->nod_count);
|
|
}
|
|
|
|
// stuff one or more plan items
|
|
|
|
gpre_nod* const* ptr = list->nod_arg;
|
|
for (gpre_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++)
|
|
{
|
|
GPRE_NOD node = *ptr;
|
|
if (node->nod_type == nod_plan_expr)
|
|
{
|
|
cmp_plan(node, request);
|
|
continue;
|
|
}
|
|
|
|
// if we're here, it must be a nod_plan_item
|
|
|
|
request->add_byte(blr_retrieve);
|
|
|
|
/* stuff the relation--the relation id itself is redundant except
|
|
when there is a need to differentiate the base tables of views */
|
|
|
|
CME_relation((gpre_ctx*) node->nod_arg[2], request);
|
|
|
|
// now stuff the access method for this stream
|
|
|
|
const gpre_nod* arg = node->nod_arg[1];
|
|
switch (arg->nod_type)
|
|
{
|
|
case nod_natural:
|
|
request->add_byte(blr_sequential);
|
|
break;
|
|
|
|
case nod_index_order:
|
|
request->add_byte(blr_navigational);
|
|
request->add_cstring((TEXT*) arg->nod_arg[0]);
|
|
break;
|
|
|
|
case nod_index:
|
|
{
|
|
request->add_byte(blr_indices);
|
|
arg = arg->nod_arg[0];
|
|
request->add_byte(arg->nod_count);
|
|
const gpre_nod* const* ptr2 = arg->nod_arg;
|
|
for (const gpre_nod* const* const end2 = ptr2 + arg->nod_count;
|
|
ptr2 < end2; ptr2++)
|
|
{
|
|
request->add_cstring((TEXT*) *ptr2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Print out the correct blr for
|
|
// this datatype.
|
|
//
|
|
|
|
static void cmp_sdl_dtype( const gpre_fld* field, REF reference)
|
|
{
|
|
switch (field->fld_dtype)
|
|
{
|
|
case dtype_cstring:
|
|
// 3.2j has new, tagged blr intruction for cstring
|
|
|
|
if (gpreGlob.sw_know_interp)
|
|
{
|
|
reference->add_byte(blr_cstring2);
|
|
reference->add_word(gpreGlob.sw_interp);
|
|
reference->add_word(field->fld_length);
|
|
}
|
|
else
|
|
{
|
|
reference->add_byte(blr_cstring);
|
|
reference->add_word(field->fld_length);
|
|
}
|
|
break;
|
|
|
|
case dtype_text:
|
|
// 3.2j has new, tagged blr intruction for text too
|
|
|
|
if (gpreGlob.sw_know_interp)
|
|
{
|
|
reference->add_byte(blr_text2);
|
|
reference->add_word(gpreGlob.sw_interp);
|
|
reference->add_word(field->fld_length);
|
|
}
|
|
else
|
|
{
|
|
reference->add_byte(blr_text);
|
|
reference->add_word(field->fld_length);
|
|
}
|
|
break;
|
|
|
|
case dtype_varying:
|
|
// 3.2j has new, tagged blr intruction for varying also
|
|
|
|
if (gpreGlob.sw_know_interp)
|
|
{
|
|
reference->add_byte(blr_varying2);
|
|
reference->add_word(gpreGlob.sw_interp);
|
|
reference->add_word(field->fld_length);
|
|
}
|
|
else
|
|
{
|
|
reference->add_byte(blr_varying);
|
|
reference->add_word(field->fld_length);
|
|
}
|
|
break;
|
|
|
|
case dtype_short:
|
|
reference->add_byte(blr_short);
|
|
reference->add_byte(field->fld_scale);
|
|
break;
|
|
|
|
case dtype_long:
|
|
reference->add_byte(blr_long);
|
|
reference->add_byte(field->fld_scale);
|
|
break;
|
|
|
|
case dtype_quad:
|
|
reference->add_byte(blr_quad);
|
|
reference->add_byte(field->fld_scale);
|
|
break;
|
|
|
|
// ** Begin date/time/timestamp support *
|
|
case dtype_sql_date:
|
|
reference->add_byte(blr_sql_date);
|
|
break;
|
|
case dtype_sql_time:
|
|
reference->add_byte(blr_sql_time);
|
|
break;
|
|
|
|
case dtype_timestamp:
|
|
reference->add_byte(blr_timestamp);
|
|
break;
|
|
// ** End date/time/timestamp support *
|
|
|
|
case dtype_int64:
|
|
reference->add_byte(blr_int64);
|
|
break;
|
|
|
|
case dtype_real:
|
|
reference->add_byte(blr_float);
|
|
break;
|
|
|
|
case dtype_double:
|
|
if (gpreGlob.sw_d_float)
|
|
reference->add_byte(blr_d_float);
|
|
else
|
|
reference->add_byte(blr_double);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
TEXT s[50];
|
|
sprintf(s, "datatype %d not understood", field->fld_dtype);
|
|
CPR_error(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Compile a reference to a user defined function.
|
|
//
|
|
|
|
static void cmp_udf( GPRE_NOD node, gpre_req* request)
|
|
{
|
|
const udf* an_udf = (udf*) node->nod_arg[1];
|
|
request->add_byte(blr_function);
|
|
const TEXT* p = an_udf->udf_function;
|
|
request->add_byte(strlen(p));
|
|
|
|
while (*p)
|
|
request->add_byte(*p++);
|
|
|
|
gpre_nod* list = node->nod_arg[0];
|
|
if (list)
|
|
{
|
|
request->add_byte(list->nod_count);
|
|
|
|
gpre_nod** ptr = list->nod_arg;
|
|
for (gpre_nod** const end = ptr + list->nod_count;
|
|
ptr < end; ++ptr)
|
|
{
|
|
CME_expr(*ptr, request);
|
|
}
|
|
}
|
|
else
|
|
request->add_byte(0);
|
|
|
|
// return node;
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Process a random value expression.
|
|
//
|
|
|
|
static void cmp_value( const gpre_nod* node, gpre_req* request)
|
|
{
|
|
const ref* reference = (REF) node->nod_arg[0];
|
|
|
|
if (!reference)
|
|
puts("cmp_value: missing reference");
|
|
|
|
if (!reference->ref_port)
|
|
puts("cmp_value: port missing");
|
|
|
|
const ref* flag = reference->ref_null;
|
|
if (flag)
|
|
{
|
|
request->add_byte(blr_parameter2);
|
|
request->add_byte(reference->ref_port->por_msg_number);
|
|
request->add_word(reference->ref_parameter);
|
|
request->add_word(flag->ref_parameter);
|
|
}
|
|
else
|
|
{
|
|
request->add_byte(blr_parameter);
|
|
request->add_byte(reference->ref_port->por_msg_number);
|
|
request->add_word(reference->ref_parameter);
|
|
}
|
|
|
|
// return node;
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Figure out a text length from a datatype and a length
|
|
//
|
|
|
|
static USHORT get_string_len( const gpre_fld* field)
|
|
{
|
|
fb_assert(field->fld_dtype <= MAX_UCHAR);
|
|
|
|
dsc tmp_dsc;
|
|
tmp_dsc.dsc_dtype = (UCHAR) field->fld_dtype;
|
|
tmp_dsc.dsc_length = field->fld_length;
|
|
tmp_dsc.dsc_scale = 0;
|
|
tmp_dsc.dsc_sub_type = 0;
|
|
tmp_dsc.dsc_flags = 0;
|
|
tmp_dsc.dsc_address = NULL;
|
|
|
|
return DSC_string_length(&tmp_dsc);
|
|
}
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Write to the sdl string, the do
|
|
// loop for a particular dimension.
|
|
//
|
|
|
|
static void stuff_sdl_dimension(const dim* dimension,
|
|
ref* reference, SSHORT dimension_count)
|
|
{
|
|
|
|
// In the future, when we support slices, new code to handle the
|
|
// user-defined slice ranges will be here.
|
|
|
|
if (dimension->dim_lower == 1)
|
|
{
|
|
reference->add_byte(isc_sdl_do1);
|
|
reference->add_byte(dimension_count);
|
|
stuff_sdl_number(dimension->dim_upper, reference);
|
|
}
|
|
else
|
|
{
|
|
reference->add_byte(isc_sdl_do2);
|
|
reference->add_byte(dimension_count);
|
|
stuff_sdl_number(dimension->dim_lower, reference);
|
|
stuff_sdl_number(dimension->dim_upper, reference);
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Write the element information
|
|
// (including the subscripts) to
|
|
// the SDL string for the array.
|
|
//
|
|
|
|
static void stuff_sdl_element(ref* reference, const gpre_fld* field)
|
|
{
|
|
reference->add_byte(isc_sdl_element);
|
|
reference->add_byte(1);
|
|
reference->add_byte(isc_sdl_scalar);
|
|
reference->add_byte(0);
|
|
|
|
reference->add_byte(field->fld_array_info->ary_dimension_count);
|
|
|
|
// Fortran needs the array in column-major order
|
|
|
|
if (gpreGlob.sw_language == lang_fortran)
|
|
{
|
|
for (SSHORT i = field->fld_array_info->ary_dimension_count - 1;
|
|
i >= 0; i--)
|
|
{
|
|
reference->add_byte(isc_sdl_variable);
|
|
reference->add_byte(i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (SSHORT i = 0; i < field->fld_array_info->ary_dimension_count; i++)
|
|
{
|
|
reference->add_byte(isc_sdl_variable);
|
|
reference->add_byte(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Write loop information to the SDL
|
|
// string for the array dimensions.
|
|
//
|
|
|
|
static void stuff_sdl_loops(ref* reference, const gpre_fld* field)
|
|
{
|
|
// Fortran needs the array in column-major order
|
|
|
|
if (gpreGlob.sw_language == lang_fortran)
|
|
{
|
|
const dim* dimension;
|
|
for (dimension = field->fld_array_info->ary_dimension;
|
|
dimension->dim_next; dimension = dimension->dim_next);
|
|
|
|
for (SSHORT i = 0; i < field->fld_array_info->ary_dimension_count;
|
|
i++, dimension = dimension->dim_previous)
|
|
{
|
|
stuff_sdl_dimension(dimension, reference, i);
|
|
}
|
|
}
|
|
else {
|
|
SSHORT i = 0;
|
|
for (const dim* dimension = field->fld_array_info->ary_dimension;
|
|
i < field->fld_array_info->ary_dimension_count;
|
|
i++, dimension = dimension->dim_next)
|
|
{
|
|
stuff_sdl_dimension(dimension, reference, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Write the number in the 'smallest'
|
|
// form possible to the SDL string.
|
|
//
|
|
|
|
static void stuff_sdl_number(const SLONG number, REF reference)
|
|
{
|
|
|
|
if ((number > -16) && (number < 15))
|
|
{
|
|
reference->add_byte(isc_sdl_tiny_integer);
|
|
reference->add_byte(number);
|
|
}
|
|
else if ((number > -32768) && (number < 32767))
|
|
{
|
|
reference->add_byte(isc_sdl_short_integer);
|
|
reference->add_word(number);
|
|
}
|
|
else
|
|
{
|
|
reference->add_byte(isc_sdl_long_integer);
|
|
reference->add_long(number);
|
|
}
|
|
}
|
|
|