//____________________________________________________________ // // 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 #include #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 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(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); } }