/* * PROGRAM: Dynamic SQL runtime support * MODULE: make.cpp * DESCRIPTION: Routines to make various blocks. * * 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): ______________________________________. * 2001.11.21 Claudio Valderrama: Finally solved the mystery of DSQL * not recognizing when a UDF returns NULL. This fixes SF bug #484399. * See case nod_udf in MAKE_desc(). * 2001.02.23 Claudio Valderrama: Fix SF bug #518350 with substring() * and text blobs containing charsets other than ASCII/NONE/BINARY. * 2002.07.30 Arno Brinkman: * COALESCE, CASE support added * procedure MAKE_desc_from_list added * 2003.01.25 Dmitry Yemanov: Fixed problem with concatenation which * trashed RDB$FIELD_LENGTH in the system tables. This change may * potentially interfere with the one made by Claudio one year ago. */ //This MUST be before any other includes #ifdef DARWIN #define _STLP_CCTYPE #endif #include "firebird.h" #include #include #include "../dsql/dsql.h" #include "../jrd/ibase.h" #include "../jrd/intl.h" #include "../jrd/constants.h" #include "../jrd/align.h" #include "../dsql/alld_proto.h" #include "../dsql/errd_proto.h" #include "../dsql/hsh_proto.h" #include "../dsql/make_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/misc_func.h" #include "../dsql/utld_proto.h" #include "../jrd/DataTypeUtil.h" #include "../jrd/ods.h" #include "../jrd/ini.h" #include "../jrd/thd.h" #include "../jrd/dsc_proto.h" #include "../jrd/cvt_proto.h" #include "../jrd/thread_proto.h" #include "../jrd/why_proto.h" #include "../common/config/config.h" /* InterBase provides transparent conversion from string to date in * contexts where it makes sense. This macro checks a descriptor to * see if it is something that *could* represent a date value */ static inline bool could_be_date(const dsc& d) { return DTYPE_IS_DATE(d.dsc_dtype) || (d.dsc_dtype <= dtype_any_text); } // One of d1, d2 is time, the other is date static inline bool is_date_and_time(const dsc& d1, const dsc& d2) { return ((d1.dsc_dtype == dtype_sql_time) && (d2.dsc_dtype == dtype_sql_date)) || ((d2.dsc_dtype == dtype_sql_time) && (d1.dsc_dtype == dtype_sql_date)); } static void make_null(dsc* const desc); static void make_placeholder_null(dsc* const desc); static void make_parameter_names(dsql_par*, const dsql_nod*); static const char* db_key_name = "DB_KEY"; /** MAKE_constant @brief Make a constant node. @param constant @param numeric_flag **/ dsql_nod* MAKE_constant(dsql_str* constant, dsql_constant_type numeric_flag) { tsql* tdsql = DSQL_get_thread_data(); dsql_nod* node = FB_NEW_RPT(*tdsql->getDefaultPool(), (numeric_flag == CONSTANT_TIMESTAMP || numeric_flag == CONSTANT_SINT64) ? 2 : 1) dsql_nod; node->nod_type = nod_constant; switch (numeric_flag) { case CONSTANT_SLONG: node->nod_desc.dsc_dtype = dtype_long; node->nod_desc.dsc_length = sizeof(SLONG); node->nod_desc.dsc_scale = 0; node->nod_desc.dsc_sub_type = 0; node->nod_desc.dsc_address = (UCHAR*) node->nod_arg; node->nod_arg[0] = (dsql_nod*) constant; break; case CONSTANT_DOUBLE: DEV_BLKCHK(constant, dsql_type_str); /* This is a numeric value which is transported to the engine as * a string. The engine will convert it. Use dtype_double so that the engine can distinguish it from an actual string. Note: Due to the size of dsc_scale we are limited to numeric constants of less than 256 bytes. */ node->nod_desc.dsc_dtype = dtype_double; // Scale has no use for double node->nod_desc.dsc_scale = static_cast(constant->str_length); node->nod_desc.dsc_sub_type = 0; node->nod_desc.dsc_length = sizeof(double); node->nod_desc.dsc_address = (UCHAR*) constant->str_data; node->nod_desc.dsc_ttype() = ttype_ascii; node->nod_arg[0] = (dsql_nod*) constant; break; case CONSTANT_SINT64: { /* We convert the string to an int64. We treat the two adjacent 32-bit words node->nod_arg[0] and node->nod_arg[1] as a 64-bit integer: if we ever port to a platform which requires 8-byte alignment of int64 data, we will have to force 8-byte alignment of node->nod_arg, which is now only guaranteed 4-byte alignment. -- ChrisJ 1999-02-20 */ node->nod_desc.dsc_dtype = dtype_int64; node->nod_desc.dsc_length = sizeof(SINT64); node->nod_desc.dsc_scale = 0; node->nod_desc.dsc_sub_type = 0; node->nod_desc.dsc_address = (UCHAR*) node->nod_arg; /* Now convert the string to an int64. We can omit testing for overflow, because we would never have gotten here if yylex hadn't recognized the string as a valid 64-bit integer value. We *might* have "9223372936854775808", which works an an int64 only if preceded by a '-', but that issue is handled in GEN_expr, and need not be addressed here. */ UINT64 value = 0; const char* p = constant->str_data; while (isdigit(*p)) value = 10 * value + (*(p++) - '0'); if (*p++ == '.') { while (isdigit(*p)) { value = 10 * value + (*p++ - '0'); node->nod_desc.dsc_scale--; } } *(UINT64 *) (node->nod_desc.dsc_address) = value; break; } case CONSTANT_DATE: case CONSTANT_TIME: case CONSTANT_TIMESTAMP: { // Setup the constant's descriptor switch (numeric_flag) { case CONSTANT_DATE: node->nod_desc.dsc_dtype = dtype_sql_date; break; case CONSTANT_TIME: node->nod_desc.dsc_dtype = dtype_sql_time; break; case CONSTANT_TIMESTAMP: node->nod_desc.dsc_dtype = dtype_timestamp; break; } node->nod_desc.dsc_sub_type = 0; node->nod_desc.dsc_scale = 0; node->nod_desc.dsc_length = type_lengths[node->nod_desc.dsc_dtype]; node->nod_desc.dsc_address = (UCHAR*) node->nod_arg; // Set up a descriptor to point to the string dsc tmp; tmp.dsc_dtype = dtype_text; tmp.dsc_scale = 0; tmp.dsc_flags = 0; tmp.dsc_ttype() = ttype_ascii; tmp.dsc_length = static_cast(constant->str_length); tmp.dsc_address = (UCHAR*) constant->str_data; // Now invoke the string_to_date/time/timestamp routines CVT_move(&tmp, &node->nod_desc, ERRD_post); break; } default: fb_assert(numeric_flag == CONSTANT_STRING); DEV_BLKCHK(constant, dsql_type_str); node->nod_desc.dsc_dtype = dtype_text; node->nod_desc.dsc_sub_type = 0; node->nod_desc.dsc_scale = 0; node->nod_desc.dsc_length = static_cast(constant->str_length); node->nod_desc.dsc_address = (UCHAR*) constant->str_data; node->nod_desc.dsc_ttype() = ttype_dynamic; // carry a pointer to the constant to resolve character set in pass1 node->nod_arg[0] = (dsql_nod*) constant; break; } return node; } /** MAKE_str_constant @brief Make a constant node when the character set ID is already known. @param constant @param character_set **/ dsql_nod* MAKE_str_constant(dsql_str* constant, SSHORT character_set) { tsql* tdsql = DSQL_get_thread_data(); dsql_nod* node = FB_NEW_RPT(*tdsql->getDefaultPool(), 1) dsql_nod; node->nod_type = nod_constant; DEV_BLKCHK(constant, dsql_type_str); node->nod_desc.dsc_dtype = dtype_text; node->nod_desc.dsc_sub_type = 0; node->nod_desc.dsc_scale = 0; node->nod_desc.dsc_length = static_cast(constant->str_length); node->nod_desc.dsc_address = (UCHAR*) constant->str_data; node->nod_desc.dsc_ttype() = character_set; // carry a pointer to the constant to resolve character set in pass1 node->nod_arg[0] = (dsql_nod*) constant; return node; } /** MAKE_cstring @brief Make a string node for a string whose length is not known, but is null-terminated. @param str **/ dsql_str* MAKE_cstring(const char* str) { return MAKE_string(str, strlen(str)); } /** MAKE_desc @brief Make a descriptor from input node. This function can modify node->nod_flags to add NOD_COMP_DIALECT @param desc @param node @param null_replacement **/ void MAKE_desc(dsql_req* request, dsc* desc, dsql_nod* node, dsql_nod* null_replacement) { dsc desc1, desc2, desc3; USHORT dtype, dtype1, dtype2; dsql_map* map; dsql_ctx* context; dsql_rel* relation; dsql_fld* field; DEV_BLKCHK(node, dsql_type_nod); // If we already know the datatype, don't worry about anything. // // dimitr: But let's re-evaluate descriptors for expression arguments // (when a NULL replacement node is provided) to always // choose the correct resulting datatype. Example: // NULLIF(NULL, 0) => CHAR(1), but // 1 + NULLIF(NULL, 0) => INT // This is required because of MAKE_desc() being called // from custom pass1 handlers for some node types and thus // causing an incorrect datatype (determined without // context) to be cached in nod_desc. if (node->nod_desc.dsc_dtype && !null_replacement) { *desc = node->nod_desc; return; } switch (node->nod_type) { case nod_constant: case nod_variable: *desc = node->nod_desc; return; case nod_agg_count: /* count2 case nod_agg_distinct: */ desc->dsc_dtype = dtype_long; desc->dsc_length = sizeof(SLONG); desc->dsc_sub_type = 0; desc->dsc_scale = 0; desc->dsc_flags = 0; return; case nod_map: map = (dsql_map*) node->nod_arg[e_map_map]; MAKE_desc(request, desc, map->map_node, null_replacement); return; case nod_agg_min: case nod_agg_max: MAKE_desc(request, desc, node->nod_arg[0], null_replacement); desc->dsc_flags = DSC_nullable; return; case nod_agg_average: MAKE_desc(request, desc, node->nod_arg[0], null_replacement); desc->dsc_flags = DSC_nullable; if (!DTYPE_IS_NUMERIC(desc->dsc_dtype) && !DTYPE_IS_TEXT(desc->dsc_dtype)) { ERRD_post(isc_expression_eval_err, 0); } else if (DTYPE_IS_TEXT(desc->dsc_dtype)) { desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); } return; case nod_agg_average2: MAKE_desc(request, desc, node->nod_arg[0], null_replacement); desc->dsc_flags = DSC_nullable; dtype = desc->dsc_dtype; if (!DTYPE_IS_NUMERIC(dtype)) { ERRD_post(isc_expression_eval_err, 0); } else if (DTYPE_IS_EXACT(dtype)) { desc->dsc_dtype = dtype_int64; desc->dsc_length = sizeof(SINT64); node->nod_flags |= NOD_COMP_DIALECT; } else { desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); } return; case nod_agg_total: MAKE_desc(request, desc, node->nod_arg[0], null_replacement); if (!DTYPE_IS_NUMERIC(desc->dsc_dtype) && !DTYPE_IS_TEXT(desc->dsc_dtype)) { ERRD_post(isc_expression_eval_err, 0); } else if (desc->dsc_dtype == dtype_short) { desc->dsc_dtype = dtype_long; desc->dsc_length = sizeof(SLONG); } else if (desc->dsc_dtype == dtype_int64) { desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); } else if (DTYPE_IS_TEXT(desc->dsc_dtype)) { desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); } desc->dsc_flags = DSC_nullable; return; case nod_agg_total2: MAKE_desc(request, desc, node->nod_arg[0], null_replacement); dtype = desc->dsc_dtype; if (!DTYPE_IS_NUMERIC(dtype)) { ERRD_post(isc_expression_eval_err, 0); } else if (DTYPE_IS_EXACT(dtype)) { desc->dsc_dtype = dtype_int64; desc->dsc_length = sizeof(SINT64); node->nod_flags |= NOD_COMP_DIALECT; } else { desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); } desc->dsc_flags = DSC_nullable; return; case nod_agg_list: MAKE_desc(request, desc, node->nod_arg[0], null_replacement); if (DTYPE_IS_BLOB(desc->dsc_dtype)) { ERRD_post(isc_expression_eval_err, 0); } if (!DTYPE_IS_TEXT(desc->dsc_dtype)) { desc->dsc_ttype() = ttype_ascii; } desc->dsc_dtype = dtype_varying; desc->dsc_length = MAX_COLUMN_SIZE; desc->dsc_scale = 0; desc->dsc_flags = DSC_nullable; return; case nod_concatenate: MAKE_desc(request, &desc1, node->nod_arg[0], node->nod_arg[1]); MAKE_desc(request, &desc2, node->nod_arg[1], node->nod_arg[0]); DSqlDataTypeUtil(request).makeConcatenate(desc, &desc1, &desc2); return; case nod_derived_field: MAKE_desc(request, desc, node->nod_arg[e_derived_field_value], null_replacement); return; case nod_upcase: case nod_lowcase: MAKE_desc(request, &desc1, node->nod_arg[0], null_replacement); if (desc1.dsc_dtype <= dtype_any_text || desc1.dsc_dtype == dtype_blob) { *desc = desc1; return; } desc->dsc_dtype = dtype_varying; desc->dsc_scale = 0; desc->dsc_ttype() = ttype_ascii; desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1); desc->dsc_flags = desc1.dsc_flags & DSC_nullable; return; case nod_substr: MAKE_desc(request, &desc1, node->nod_arg[0], null_replacement); MAKE_desc(request, &desc2, node->nod_arg[1], null_replacement); MAKE_desc(request, &desc3, node->nod_arg[2], null_replacement); DSqlDataTypeUtil(request).makeSubstr(desc, &desc1, &desc2, &desc3); return; case nod_trim: MAKE_desc(request, &desc1, node->nod_arg[e_trim_value], null_replacement); if (node->nod_arg[e_trim_characters]) MAKE_desc(request, &desc2, node->nod_arg[e_trim_characters], null_replacement); else desc2.dsc_flags = 0; if (desc1.dsc_dtype == dtype_blob) { *desc = desc1; desc->dsc_flags |= (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; } else if (desc1.dsc_dtype <= dtype_any_text) { *desc = desc1; desc->dsc_dtype = dtype_varying; desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1); desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; } else { desc->dsc_dtype = dtype_varying; desc->dsc_scale = 0; desc->dsc_ttype() = ttype_ascii; desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1); desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; } return; case nod_cast: field = (dsql_fld*) node->nod_arg[e_cast_target]; MAKE_desc_from_field(desc, field); MAKE_desc(request, &desc1, node->nod_arg[e_cast_source], NULL); desc->dsc_flags = desc1.dsc_flags & DSC_nullable; return; case nod_simple_case: MAKE_desc_from_list(request, &desc1, node->nod_arg[e_simple_case_results], null_replacement, "CASE"); *desc = desc1; return; case nod_searched_case: MAKE_desc_from_list(request, &desc1, node->nod_arg[e_searched_case_results], null_replacement, "CASE"); *desc = desc1; return; case nod_coalesce: MAKE_desc_from_list(request, &desc1, node->nod_arg[0], null_replacement, "COALESCE"); *desc = desc1; return; #ifdef DEV_BUILD case nod_collate: ERRD_bugcheck("Not expecting nod_collate in dsql/MAKE_desc"); return; #endif case nod_add: case nod_subtract: MAKE_desc(request, &desc1, node->nod_arg[0], node->nod_arg[1]); MAKE_desc(request, &desc2, node->nod_arg[1], node->nod_arg[0]); if (node->nod_arg[0]->nod_type == nod_null && node->nod_arg[1]->nod_type == nod_null) { // NULL + NULL = NULL of INT make_null(desc); return; } dtype1 = desc1.dsc_dtype; if (dtype_int64 == dtype1 || DTYPE_IS_TEXT(dtype1)) dtype1 = dtype_double; dtype2 = desc2.dsc_dtype; if (dtype_int64 == dtype2 || DTYPE_IS_TEXT(dtype2)) dtype2 = dtype_double; dtype = MAX(dtype1, dtype2); if (DTYPE_IS_BLOB(dtype)) { ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 607, isc_arg_gds, isc_dsql_no_blob_array, 0); } desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; switch (dtype) { case dtype_sql_time: case dtype_sql_date: /* Forbid +- */ if (DTYPE_IS_TEXT(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype)) { ERRD_post(isc_expression_eval_err, 0); } case dtype_timestamp: /* Allow +- (historical) */ if (could_be_date(desc1) && could_be_date(desc2)) { if (node->nod_type == nod_subtract) { /* - */ /* Legal permutations are: - - - -