/* * PROGRAM: JRD Access Method * MODULE: evl.cpp * DESCRIPTION: Expression evaluation * * 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): ______________________________________. */ /* * Modified by: Patrick J. P. Griffin * Date: 11/24/2000 * Problem: select count(0)+1 from rdb$relations where 0=1; returns 0 * In the EVL_group processing, the internal assigment for * the literal in the computation is being done on every * statement fetch, so if there are no statements fetched * then the internal field never gets set. * Change: Added an assignment process for the literal * before the first fetch. * * Modified by: Neil McCalden * Date: 05 Jan 2001 * Problem: Firebird bug: 127375 * Group by on a calculated expression would cause segv * when it encountered a NULL value as the calculation * was trying reference a null pointer. * Change: Test the null flag before trying to expand the value. * * 2001.6.17 Claudio Valderrama: Fix the annoying behavior that causes silent * overflow in dialect 1. If you define the macro FIREBIRD_AVOID_DIALECT1_OVERFLOW * it will work with double should an overflow happen. Otherwise, an error will be * issued to the user if the overflow happens. The multiplication is done using * SINT64 quantities. I had the impression that casting this SINT64 result to double * when we detect overflow was faster than achieving the failed full multiplication * with double operands again. Usage will tell the truth. * For now, the aforementioned macro is enabled. * 2001.6.18 Claudio Valderrama: substring() is working with international charsets, * thanks to Dave Schnepper's directions. * 2002.2.15 Claudio Valderrama: divide2() should not mangle negative values. * 2002.04.16 Paul Beach HP10 Port - (UCHAR*) desc.dsc_address = p; modified for HP * Compiler * 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced * exception handling in SPs/triggers, * implemented ROWS_AFFECTED system variable * 2003.08.10 Claudio Valderrama: Fix SF bug# 784121. * Adriano dos Santos Fernandes */ #include "firebird.h" #include #include #include "../jrd/common.h" #include "../jrd/ibase.h" #include "../jrd/jrd.h" #include "../jrd/val.h" #include "../jrd/req.h" #include "../jrd/exe.h" #include "../jrd/sbm.h" #include "../jrd/blb.h" #include "gen/iberror.h" #include "../jrd/scl.h" #include "../jrd/lck.h" #include "../jrd/lls.h" #include "../jrd/intl.h" #include "../jrd/intl_classes.h" #include "../jrd/rse.h" #include "../jrd/quad.h" #include "../jrd/sort.h" #include "../jrd/blr.h" #include "../jrd/tra.h" #include "../jrd/gdsassert.h" #include "../common/classes/auto.h" #include "../common/classes/timestamp.h" #include "../jrd/blb_proto.h" #include "../jrd/btr_proto.h" #include "../jrd/cvt_proto.h" #include "../jrd/DataTypeUtil.h" #include "../jrd/dpm_proto.h" #include "../jrd/dsc_proto.h" #include "../jrd/err_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/fun_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/rlck_proto.h" #include "../jrd/rse_proto.h" #include "../jrd/scl_proto.h" #include "../jrd/sort_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/align.h" #include "../jrd/met_proto.h" #include "../jrd/misc_func_ids.h" #include "../common/config/config.h" #include "../jrd/SysFunction.h" const int TEMP_LENGTH = 128; const SINT64 MAX_INT64_LIMIT = MAX_SINT64 / 10; const SINT64 MIN_INT64_LIMIT = MIN_SINT64 / 10; /* *** DANGER DANGER WILL ROBINSON *** * add(), multiply(), and divide() all take the same three arguments, but * they don't all take them in the same order. Be careful out there. * The order should be made to agree as part of the next code cleanup. */ using namespace Jrd; using namespace Firebird; static dsc* add(const dsc*, const jrd_nod*, impure_value*); static dsc* add2(const dsc*, const jrd_nod*, impure_value*); static dsc* add_datetime(const dsc*, const jrd_nod*, impure_value*); static dsc* add_sql_date(const dsc*, const jrd_nod*, impure_value*); static dsc* add_sql_time(const dsc*, const jrd_nod*, impure_value*); static dsc* add_timestamp(const dsc*, const jrd_nod*, impure_value*); static void adjust_text_descriptor(thread_db*, dsc*); static dsc* binary_value(thread_db*, const jrd_nod*, impure_value*); static dsc* cast(thread_db*, dsc*, const jrd_nod*, impure_value*); static void compute_agg_distinct(thread_db*, jrd_nod*); static dsc* concatenate(thread_db*, const dsc*, const dsc*, impure_value*); static dsc* dbkey(thread_db*, const jrd_nod*, impure_value*); static dsc* eval_statistical(thread_db*, jrd_nod*, impure_value*); static dsc* extract(thread_db*, jrd_nod*, impure_value*); static void fini_agg_distinct(thread_db* tdbb, const jrd_nod *const); static SINT64 get_day_fraction(const dsc* d); static dsc* get_mask(thread_db*, jrd_nod*, impure_value*); static SINT64 get_timestamp_to_isc_ticks(const dsc* d); static void init_agg_distinct(thread_db*, const jrd_nod*); static dsc* lock_state(thread_db*, jrd_nod*, impure_value*); static dsc* low_up_case(thread_db*, const dsc*, impure_value*, ULONG (TextType::*tt_str_to_case)(ULONG, const UCHAR*, ULONG, UCHAR*)); static dsc* multiply(const dsc*, impure_value*, const jrd_nod*); static dsc* multiply2(const dsc*, impure_value*, const jrd_nod*); static dsc* divide2(const dsc*, impure_value*, const jrd_nod*); static dsc* negate_dsc(thread_db*, const dsc*, impure_value*); static dsc* record_version(thread_db*, const jrd_nod*, impure_value*); static bool reject_duplicate(const UCHAR*, const UCHAR*, void*); static dsc* scalar(thread_db*, jrd_nod*, impure_value*); static bool sleuth(thread_db*, jrd_nod*, const dsc*, const dsc*); static bool string_boolean(thread_db*, jrd_nod*, dsc*, dsc*, bool); static bool string_function(thread_db*, jrd_nod*, SLONG, const UCHAR*, SLONG, const UCHAR*, USHORT, bool); static dsc* string_length(thread_db*, jrd_nod*, impure_value*); static dsc* substring(thread_db*, impure_value*, dsc*, const dsc*, const dsc*); static dsc* trim(thread_db*, jrd_nod*, impure_value*); static dsc* internal_info(thread_db*, const dsc*, impure_value*); const SINT64 SECONDS_PER_DAY = 24 * 60 * 60; const SINT64 ISC_TICKS_PER_DAY = SECONDS_PER_DAY * ISC_TIME_SECONDS_PRECISION; const SCHAR DIALECT_3_TIMESTAMP_SCALE = -9; const SCHAR DIALECT_1_TIMESTAMP_SCALE = 0; #ifdef SCROLLABLE_CURSORS static const RSE_GET_MODE g_RSE_get_mode = RSE_get_next; #else static const RSE_GET_MODE g_RSE_get_mode = RSE_get_forward; #endif dsc* EVL_assign_to(thread_db* tdbb, jrd_nod* node) { /************************************** * * E V L _ a s s i g n _ t o * ************************************** * * Functional description * Evaluate the descriptor of the * destination node of an assignment. * **************************************/ dsc* desc; Format* format; jrd_nod* message; Record* record; SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); jrd_req* request = tdbb->getRequest(); impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure); /* The only nodes that can be assigned to are: argument, field and variable. */ int arg_number; switch (node->nod_type) { case nod_argument: message = node->nod_arg[e_arg_message]; format = (Format*) message->nod_arg[e_msg_format]; arg_number = (int) (IPTR) node->nod_arg[e_arg_number]; desc = &format->fmt_desc[arg_number]; impure->vlu_desc.dsc_address = (UCHAR *) request + message->nod_impure + (IPTR) desc->dsc_address; impure->vlu_desc.dsc_dtype = desc->dsc_dtype; impure->vlu_desc.dsc_length = desc->dsc_length; impure->vlu_desc.dsc_scale = desc->dsc_scale; impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type; if (DTYPE_IS_TEXT(desc->dsc_dtype) && ((INTL_TTYPE(desc) == ttype_dynamic) || (INTL_GET_CHARSET(desc) == CS_dynamic))) { /* Value is a text value, we're assigning it back to the user process, user process has not specified a subtype, user process specified dynamic translation and the dsc isn't from a 3.3 type request (blr_cstring2 instead of blr_cstring) so convert the charset to the declared charset of the process. */ INTL_ASSIGN_DSC(&impure->vlu_desc, tdbb->getAttachment()->att_charset, COLLATE_NONE); } return &impure->vlu_desc; case nod_field: record = request->req_rpb[(int) (IPTR) node->nod_arg[e_fld_stream]].rpb_record; if (!EVL_field(0, record, (USHORT)(IPTR) node->nod_arg[e_fld_id], &impure->vlu_desc)) { // The below condition means that EVL_field() returned // a read-only dummy value which cannot be assigned to. // The usual reason is a field being unexpectedly dropped. if (impure->vlu_desc.dsc_address && !(impure->vlu_desc.dsc_flags & DSC_null)) { ERR_post(Arg::Gds(isc_field_disappeared)); } } if (!impure->vlu_desc.dsc_address) ERR_post(Arg::Gds(isc_read_only_field)); return &impure->vlu_desc; case nod_null: return NULL; case nod_variable: // Calculate descriptor node = node->nod_arg[e_var_variable]; impure = (impure_value*) ((SCHAR *) request + node->nod_impure); return &impure->vlu_desc; default: BUGCHECK(229); /* msg 229 EVL_assign_to: invalid operation */ } return NULL; } RecordBitmap** EVL_bitmap(thread_db* tdbb, jrd_nod* node, RecordBitmap* bitmap_and) { /************************************** * * E V L _ b i t m a p * ************************************** * * Functional description * Evaluate bitmap valued expression. * **************************************/ SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); if (--tdbb->tdbb_quantum < 0) JRD_reschedule(tdbb, 0, true); switch (node->nod_type) { case nod_bit_and: { RecordBitmap** bitmap = EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and); if (!(*bitmap) || !(*bitmap)->getFirst()) return bitmap; return EVL_bitmap(tdbb, node->nod_arg[1], *bitmap); } case nod_bit_or: return RecordBitmap::bit_or( EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and), EVL_bitmap(tdbb, node->nod_arg[1], bitmap_and)); case nod_bit_in: { RecordBitmap** inv_bitmap = EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and); BTR_evaluate(tdbb, reinterpret_cast(node->nod_arg[1]->nod_arg[e_idx_retrieval]), inv_bitmap, bitmap_and); return inv_bitmap; } case nod_bit_dbkey: { impure_inversion* impure = (impure_inversion*) ((SCHAR*) tdbb->getRequest() + node->nod_impure); RecordBitmap::reset(impure->inv_bitmap); const dsc* desc = EVL_expr(tdbb, node->nod_arg[0]); if (!(tdbb->getRequest()->req_flags & req_null) && desc->dsc_length == sizeof(RecordNumber::Packed)) { const USHORT id = (USHORT)(IPTR) node->nod_arg[1]; RecordNumber::Packed* numbers = reinterpret_cast(desc->dsc_address); RecordNumber rel_dbkey; rel_dbkey.bid_decode(&numbers[id]); // NS: Why the heck we decrement record number here? I have no idea, but retain the algorithm for now. // hvlad: because from the user point of view db_key's begins from 1 rel_dbkey.decrement(); if (!bitmap_and || bitmap_and->test(rel_dbkey.getValue())) RBM_SET(tdbb->getDefaultPool(), &impure->inv_bitmap, rel_dbkey.getValue()); } return &impure->inv_bitmap; } case nod_index: { impure_inversion* impure = (impure_inversion*) ((SCHAR*) tdbb->getRequest() + node->nod_impure); RecordBitmap::reset(impure->inv_bitmap); BTR_evaluate(tdbb, reinterpret_cast(node->nod_arg[e_idx_retrieval]), &impure->inv_bitmap, bitmap_and); return &impure->inv_bitmap; } default: BUGCHECK(230); /* msg 230 EVL_bitmap: invalid operation */ } return NULL; } bool EVL_boolean(thread_db* tdbb, jrd_nod* node) { /************************************** * * E V L _ b o o l e a n * ************************************** * * Functional description * Evaluate a boolean. * **************************************/ dsc* desc[2]; bool value; SSHORT comparison; impure_value* impure; bool computed_invariant = false; SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); /* Handle and pre-processing possible for various nodes. This includes evaluating argument and checking NULL flags */ jrd_req* request = tdbb->getRequest(); jrd_nod** ptr = node->nod_arg; switch (node->nod_type) { case nod_contains: case nod_starts: case nod_matches: case nod_like: case nod_equiv: case nod_eql: case nod_neq: case nod_gtr: case nod_geq: case nod_lss: case nod_leq: case nod_between: case nod_similar: case nod_sleuth: { request->req_flags &= ~req_same_tx_upd; SSHORT force_equal = 0; /* Evaluate arguments. If either is null, result is null, but in any case, evaluate both, since some expressions may later depend on mappings which are developed here */ const jrd_nod* rec_version = *ptr; desc[0] = EVL_expr(tdbb, *ptr++); const ULONG flags = request->req_flags; request->req_flags &= ~req_null; force_equal |= request->req_flags & req_same_tx_upd; // Currently only nod_like, nod_contains, nod_starts and nod_similar may be marked invariant if (node->nod_flags & nod_invariant) { impure = reinterpret_cast((SCHAR *)request + node->nod_impure); // Check that data type of operand is still the same. // It may change due to multiple formats present in stream // System tables are the good example of such streams - // data coming from ini.epp has ASCII ttype, user data is UNICODE_FSS // // Note that value descriptor may be NULL pointer if value is SQL NULL if ((impure->vlu_flags & VLU_computed) && desc[0] && (impure->vlu_desc.dsc_dtype != desc[0]->dsc_dtype || impure->vlu_desc.dsc_sub_type != desc[0]->dsc_sub_type || impure->vlu_desc.dsc_scale != desc[0]->dsc_scale) ) { impure->vlu_flags &= ~VLU_computed; } if (impure->vlu_flags & VLU_computed) { if (impure->vlu_flags & VLU_null) request->req_flags |= req_null; else computed_invariant = true; } else { desc[1] = EVL_expr(tdbb, *ptr++); if (request->req_flags & req_null) { impure->vlu_flags |= VLU_computed; impure->vlu_flags |= VLU_null; } else { impure->vlu_flags &= ~VLU_null; // Search object depends on operand data type. // Thus save data type which we use to compute invariant if (desc[0]) { impure->vlu_desc.dsc_dtype = desc[0]->dsc_dtype; impure->vlu_desc.dsc_sub_type = desc[0]->dsc_sub_type; impure->vlu_desc.dsc_scale = desc[0]->dsc_scale; } else { // Indicate we do not know type of expression. // This code will force pattern recompile for the next non-null value impure->vlu_desc.dsc_dtype = 0; impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = 0; } } } } else desc[1] = EVL_expr(tdbb, *ptr++); // An equivalence operator evaluates to true when both operands // are NULL and behaves like an equality operator otherwise. // Note that this operator never sets req_null flag if (node->nod_type == nod_equiv) { if ((flags & req_null) && (request->req_flags & req_null)) { request->req_flags &= ~req_null; return true; } if ((flags & req_null) || (request->req_flags & req_null)) { request->req_flags &= ~req_null; return false; } } // If either of expressions above returned NULL set req_null flag // and return false if (flags & req_null) request->req_flags |= req_null; if (request->req_flags & req_null) return false; force_equal |= request->req_flags & req_same_tx_upd; if (node->nod_flags & nod_comparison) comparison = MOV_compare(desc[0], desc[1]); /* If we are checking equality of record_version * and same transaction updated the record, * force equality. */ if (rec_version->nod_type == nod_rec_version && force_equal) comparison = 0; request->req_flags &= ~(req_null | req_same_tx_upd); } break; case nod_and: value = EVL_boolean(tdbb, *ptr++); break; case nod_or: value = EVL_boolean(tdbb, *ptr++); break; case nod_not: if ((*ptr)->nod_type == nod_ansi_any || (*ptr)->nod_type == nod_ansi_all) { request->req_flags |= req_ansi_not; } value = EVL_boolean(tdbb, *ptr++); break; default: /* Shut up some compiler warnings */ break; } /* Evaluate node */ // TODO: Verify and remove this flag once FB1.5beta3 is out. // Default to not eval complete expression (i.e. do short-circuit // optimizied evaluation). Both to get possible early warnings from // users, and to default to the faster of the two options. static bool bEvalCompleteExpression = Config::getCompleteBooleanEvaluation(); switch (node->nod_type) { case nod_and: { /* for and, if either operand is FALSE, then the result is FALSE; if both are TRUE, the result is TRUE; otherwise, the result is NULL op 1 op 2 result ---- ---- ------ F F F F T F F N F T F F T T T T N N N F F N T N N N N */ /* save null state and get other operand */ const USHORT firstnull = request->req_flags & req_null; request->req_flags &= ~req_null; if (!bEvalCompleteExpression && !value && !firstnull) { // First term is FALSE, why the whole expression is false. // NULL flag is already turned off a few lines above. return false; } const bool value2 = EVL_boolean(tdbb, *ptr); const USHORT secondnull = request->req_flags & req_null; request->req_flags &= ~req_null; if ((!value && !firstnull) || (!value2 && !secondnull)) { return false; /* at least one operand was FALSE */ } if (value && value2) { return true; /* both true */ } request->req_flags |= req_null; return false; /* otherwise, return null */ } case nod_any: case nod_ansi_any: case nod_ansi_all: { USHORT* invariant_flags; if (node->nod_flags & nod_invariant) { impure = (impure_value*) ((SCHAR *) request + node->nod_impure); invariant_flags = & impure->vlu_flags; if (*invariant_flags & VLU_computed) { /* An invariant node has already been computed. */ if ((node->nod_type == nod_ansi_any) && (*invariant_flags & VLU_null)) { request->req_flags |= req_null; } else { request->req_flags &= ~req_null; } return impure->vlu_misc.vlu_short != 0; } } /* for ansi ANY clauses (and ALL's, which are negated ANY's) the unoptimized boolean expression must be used, since the processing of these clauses is order dependant (see rse.cpp) */ RecordSource* select = (RecordSource*) (node->nod_arg[e_any_rsb]); if (node->nod_type != nod_any) { select->rsb_any_boolean = ((RecordSelExpr*) (node->nod_arg[e_any_rse]))->rse_boolean; if (node->nod_type == nod_ansi_any) { request->req_flags |= req_ansi_any; } else { request->req_flags |= req_ansi_all; // dimitr: Even if we can evaluate ANY without a residual // boolean (what I still doubt), it's impossible // for ALL. Hence this assertion. fb_assert(select->rsb_type == rsb_boolean); } } RSE_open(tdbb, select); value = RSE_get_record(tdbb, select, g_RSE_get_mode); RSE_close(tdbb, select); if (node->nod_type == nod_any) request->req_flags &= ~req_null; /* If this is an invariant node, save the return value. */ if (node->nod_flags & nod_invariant) { *invariant_flags |= VLU_computed; if ((node->nod_type != nod_any) && (request->req_flags & req_null)) { *invariant_flags |= VLU_null; } impure->vlu_misc.vlu_short = value ? TRUE : FALSE; } return value; } case nod_contains: case nod_starts: case nod_matches: case nod_like: case nod_similar: return string_boolean(tdbb, node, desc[0], desc[1], computed_invariant); case nod_sleuth: return sleuth(tdbb, node, desc[0], desc[1]); case nod_missing: EVL_expr(tdbb, *ptr); if (request->req_flags & req_null) { value = true; request->req_flags &= ~req_null; } else { value = false; } return value; case nod_not: if (request->req_flags & req_null) return false; return !value; case nod_or: { /* for or, if either operand is TRUE, then the result is TRUE; if both are FALSE, the result is FALSE; otherwise, the result is NULL op 1 op 2 result ---- ---- ------ F F F F T T F N N T F T T T T T N T N F N N T T N N N also, preserve first operand's value and null state, but still evaluate second operand, since latter field mappings may depend on the evaluation */ const ULONG flags = request->req_flags; request->req_flags &= ~req_null; if (!bEvalCompleteExpression && value) { // First term is TRUE, why the whole expression is true. // NULL flag is already turned off a few lines above. return true; } const bool value2 = EVL_boolean(tdbb, *ptr); if (value || value2) { request->req_flags &= ~req_null; return true; } /* restore saved NULL state */ if (flags & req_null) { request->req_flags |= req_null; } return false; } case nod_unique: { USHORT* invariant_flags; if (node->nod_flags & nod_invariant) { impure = (impure_value*) ((SCHAR *) request + node->nod_impure); invariant_flags = & impure->vlu_flags; if (*invariant_flags & VLU_computed) { /* An invariant node has already been computed. */ request->req_flags &= ~req_null; return impure->vlu_misc.vlu_short != 0; } } RecordSource* urs = reinterpret_cast(node->nod_arg[e_any_rsb]); RSE_open(tdbb, urs); value = RSE_get_record(tdbb, urs, g_RSE_get_mode); if (value) { value = !RSE_get_record(tdbb, urs, g_RSE_get_mode); } RSE_close(tdbb, urs); request->req_flags &= ~req_null; /* If this is an invariant node, save the return value. */ if (node->nod_flags & nod_invariant) { *invariant_flags |= VLU_computed; impure->vlu_misc.vlu_short = value ? TRUE : FALSE; } return value; } case nod_equiv: case nod_eql: return (comparison == 0); case nod_neq: return (comparison != 0); case nod_gtr: return (comparison > 0); case nod_geq: return (comparison >= 0); case nod_lss: return (comparison < 0); case nod_leq: return (comparison <= 0); case nod_between: desc[1] = EVL_expr(tdbb, node->nod_arg[2]); if (request->req_flags & req_null) return false; return (comparison >= 0 && MOV_compare(desc[0], desc[1]) <= 0); case nod_stmt_expr: EXE_looper(tdbb, request, node); return EVL_boolean(tdbb, node->nod_arg[e_stmt_expr_expr]); default: BUGCHECK(231); /* msg 231 EVL_boolean: invalid operation */ } return false; } dsc* EVL_expr(thread_db* tdbb, jrd_nod* const node) { /************************************** * * E V L _ e x p r * ************************************** * * Functional description * Evaluate a value expression. * **************************************/ DEV_BLKCHK(node, type_nod); if (!node) BUGCHECK(303); /* msg 303 Invalid expression for evaluation */ SET_TDBB(tdbb); if (--tdbb->tdbb_quantum < 0) JRD_reschedule(tdbb, 0, true); jrd_req* const request = tdbb->getRequest(); impure_value* const impure = (impure_value*) ((SCHAR *) request + node->nod_impure); request->req_flags &= ~req_null; /* Do a preliminary screen for either simple nodes or nodes that are special cased elsewhere */ switch (node->nod_type) { case nod_add: case nod_subtract: case nod_divide: case nod_multiply: case nod_add2: case nod_subtract2: case nod_divide2: case nod_multiply2: case nod_concatenate: return binary_value(tdbb, node, impure); case nod_argument: { const dsc* desc; if (node->nod_arg[e_arg_flag]) { desc = EVL_expr(tdbb, node->nod_arg[e_arg_flag]); if (MOV_get_long(desc, 0)) { request->req_flags |= req_null; } } const jrd_nod* message = node->nod_arg[e_arg_message]; const Format* format = (Format*) message->nod_arg[e_msg_format]; desc = &format->fmt_desc[(int)(IPTR) node->nod_arg[e_arg_number]]; impure->vlu_desc.dsc_address = (UCHAR *) request + message->nod_impure + (IPTR) desc->dsc_address; impure->vlu_desc.dsc_dtype = desc->dsc_dtype; impure->vlu_desc.dsc_length = desc->dsc_length; impure->vlu_desc.dsc_scale = desc->dsc_scale; impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type; if (impure->vlu_desc.dsc_dtype == dtype_text) adjust_text_descriptor(tdbb, &impure->vlu_desc); USHORT* impure_flags = (USHORT*) ((UCHAR *) request + (IPTR) message->nod_arg[e_msg_impure_flags] + (sizeof(USHORT) * (IPTR) node->nod_arg[e_arg_number])); if (!(*impure_flags & VLU_checked)) { if (node->nod_arg[e_arg_info]) { EVL_validate(tdbb, Item(nod_argument, (IPTR) node->nod_arg[e_arg_message]->nod_arg[e_msg_number], (IPTR) node->nod_arg[e_arg_number]), reinterpret_cast(node->nod_arg[e_arg_info]), &impure->vlu_desc, request->req_flags & req_null); } *impure_flags |= VLU_checked; } return &impure->vlu_desc; } case nod_dbkey: return dbkey(tdbb, node, impure); case nod_rec_version: return record_version(tdbb, node, impure); case nod_field: { const USHORT id = (USHORT)(IPTR) node->nod_arg[e_fld_id]; record_param& rpb = request->req_rpb[(USHORT)(IPTR) node->nod_arg[e_fld_stream]]; Record* record = rpb.rpb_record; jrd_rel* relation = rpb.rpb_relation; // In order to "map a null to a default" value (in EVL_field()), // the relation block is referenced. // Reference: Bug 10116, 10424 if (!EVL_field(relation, record, id, &impure->vlu_desc)) request->req_flags |= req_null; else { const Format* compileFormat = (Format*) node->nod_arg[e_fld_format]; // ASF: CORE-1432 - If the the record is not on the latest format, upgrade it. if (compileFormat && record->rec_format->fmt_version != compileFormat->fmt_version && !DSC_EQUIV(&impure->vlu_desc, &compileFormat->fmt_desc[id], true)) { dsc desc = impure->vlu_desc; impure->vlu_desc = compileFormat->fmt_desc[id]; if (impure->vlu_desc.isText()) { // Allocate a string block of sufficient size. VaryingString* string = impure->vlu_string; if (string && string->str_length < impure->vlu_desc.dsc_length) { delete string; string = NULL; } if (!string) { string = impure->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(), impure->vlu_desc.dsc_length) VaryingString(); string->str_length = impure->vlu_desc.dsc_length; } impure->vlu_desc.dsc_address = string->str_data; } else impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc; MOV_move(tdbb, &desc, &impure->vlu_desc); } } if (!relation || !(relation->rel_flags & REL_system)) { if (impure->vlu_desc.dsc_dtype == dtype_text) adjust_text_descriptor(tdbb, &impure->vlu_desc); } return &impure->vlu_desc; } case nod_derived_expr: { const UCHAR streamCount = (UCHAR)(IPTR) node->nod_arg[e_derived_expr_stream_count]; const USHORT* streamList = (USHORT*) node->nod_arg[e_derived_expr_stream_list]; for (UCHAR i = 0; i < streamCount; ++i) { if (request->req_rpb[streamList[i]].rpb_number.isValid()) return EVL_expr(tdbb, node->nod_arg[e_derived_expr_expr]); } request->req_flags |= req_null; return NULL; } case nod_function: FUN_evaluate(tdbb, reinterpret_cast(node->nod_arg[e_fun_function]), node->nod_arg[e_fun_args], impure); return &impure->vlu_desc; case nod_sys_function: { const SysFunction* sysFunction = reinterpret_cast(node->nod_arg[e_sysfun_function]); return sysFunction->evlFunc(tdbb, sysFunction, node->nod_arg[e_sysfun_args], impure); } case nod_literal: return &((Literal*) node)->lit_desc; case nod_lock_state: return lock_state(tdbb, node, impure); case nod_null: request->req_flags |= req_null; return NULL; case nod_prot_mask: return get_mask(tdbb, node, impure); case nod_current_time: case nod_current_date: case nod_current_timestamp: { // Use the request timestamp fb_assert(!request->req_timestamp.isEmpty()); ISC_TIMESTAMP enc_times = request->req_timestamp.value(); memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); impure->vlu_desc.dsc_address = (UCHAR *) &impure->vlu_misc.vlu_timestamp; if (node->nod_type == nod_current_time || node->nod_type == nod_current_timestamp) { const int precision = (int)(IPTR) node->nod_arg[0]; fb_assert(precision >= 0); Firebird::TimeStamp::round_time(enc_times.timestamp_time, precision); } switch (node->nod_type) { case nod_current_time: impure->vlu_desc.dsc_dtype = dtype_sql_time; impure->vlu_desc.dsc_length = type_lengths[dtype_sql_time]; *(ULONG *) impure->vlu_desc.dsc_address = enc_times.timestamp_time; break; case nod_current_date: impure->vlu_desc.dsc_dtype = dtype_sql_date; impure->vlu_desc.dsc_length = type_lengths[dtype_sql_date]; *(ULONG *) impure->vlu_desc.dsc_address = enc_times.timestamp_date; break; case nod_current_timestamp: impure->vlu_desc.dsc_dtype = dtype_timestamp; impure->vlu_desc.dsc_length = type_lengths[dtype_timestamp]; *((ISC_TIMESTAMP *) impure->vlu_desc.dsc_address) = enc_times; break; default: fb_assert(false); break; } } return &impure->vlu_desc; case nod_user_name: { impure->vlu_desc.dsc_dtype = dtype_text; impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = 0; INTL_ASSIGN_TTYPE(&impure->vlu_desc, ttype_metadata); const char* cur_user = 0; if (tdbb->getAttachment()->att_user) { cur_user = tdbb->getAttachment()->att_user->usr_user_name.c_str(); impure->vlu_desc.dsc_address = reinterpret_cast (const_cast(cur_user)); } if (cur_user) impure->vlu_desc.dsc_length = strlen(cur_user); else impure->vlu_desc.dsc_length = 0; } return &impure->vlu_desc; // CVC: Current role will get a validated role; IE one that exists. case nod_current_role: { impure->vlu_desc.dsc_dtype = dtype_text; impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = 0; INTL_ASSIGN_TTYPE(&impure->vlu_desc, ttype_metadata); const char* cur_role = 0; if (tdbb->getAttachment()->att_user) { cur_role = tdbb->getAttachment()->att_user->usr_sql_role_name.c_str(); impure->vlu_desc.dsc_address = reinterpret_cast(const_cast(cur_role)); } if (cur_role) impure->vlu_desc.dsc_length = strlen(cur_role); else impure->vlu_desc.dsc_length = 0; } return &impure->vlu_desc; case nod_extract: return extract(tdbb, node, impure); case nod_strlen: return string_length(tdbb, node, impure); case nod_max: case nod_min: case nod_count: //case nod_count2: case nod_average: case nod_average2: case nod_total: case nod_from: return eval_statistical(tdbb, node, impure); case nod_scalar: return scalar(tdbb, node, impure); case nod_variable: { const jrd_nod* node2 = node->nod_arg[e_var_variable]; impure_value* impure2 = (impure_value*) ((SCHAR *) request + node2->nod_impure); if (impure2->vlu_desc.dsc_flags & DSC_null) request->req_flags |= req_null; impure->vlu_desc = impure2->vlu_desc; if (impure->vlu_desc.dsc_dtype == dtype_text) adjust_text_descriptor(tdbb, &impure->vlu_desc); if (!(impure2->vlu_flags & VLU_checked)) { if (node->nod_arg[e_var_info]) { EVL_validate(tdbb, Item(nod_variable, (IPTR) node->nod_arg[e_var_id]), reinterpret_cast(node->nod_arg[e_var_info]), &impure->vlu_desc, impure->vlu_desc.dsc_flags & DSC_null); } impure2->vlu_flags |= VLU_checked; } return &impure->vlu_desc; } case nod_domain_validation: if (request->req_domain_validation == NULL || (request->req_domain_validation->dsc_flags & DSC_null)) { request->req_flags |= req_null; } return request->req_domain_validation; case nod_value_if: return EVL_expr(tdbb, (EVL_boolean(tdbb, node->nod_arg[0])) ? node->nod_arg[1] : node->nod_arg[2]); case nod_trim: return trim(tdbb, node, impure); default: /* Shut up some compiler warnings */ break; case nod_stmt_expr: EXE_looper(tdbb, request, node); return EVL_expr(tdbb, node->nod_arg[e_stmt_expr_expr]); } // Evaluate arguments dsc* values[3]; if (node->nod_count) { fb_assert(node->nod_count <= 3); dsc** v = values; jrd_nod** ptr = node->nod_arg; for (const jrd_nod* const* const end = ptr + node->nod_count; ptr < end;) { *v++ = EVL_expr(tdbb, *ptr++); if (request->req_flags & req_null) { // ASF: CAST target type may be constrained if (node->nod_type == nod_cast) *(v - 1) = NULL; else return NULL; } } } switch (node->nod_type) { case nod_gen_id: // return a 32-bit generator value { SLONG temp = (SLONG) DPM_gen_id(tdbb, (SLONG)(IPTR) node->nod_arg[e_gen_id], false, MOV_get_int64(values[0], 0)); impure->make_long(temp); } return &impure->vlu_desc; case nod_gen_id2: { SINT64 temp = DPM_gen_id(tdbb, (IPTR) node->nod_arg[e_gen_id], false, MOV_get_int64(values[0], 0)); impure->make_int64(temp); } return &impure->vlu_desc; case nod_negate: return negate_dsc(tdbb, values[0], impure); case nod_substr: return substring(tdbb, impure, values[0], values[1], values[2]); case nod_upcase: return low_up_case(tdbb, values[0], impure, &TextType::str_to_upper); case nod_lowcase: return low_up_case(tdbb, values[0], impure, &TextType::str_to_lower); case nod_cast: return cast(tdbb, values[0], node, impure); case nod_internal_info: return internal_info(tdbb, values[0], impure); default: BUGCHECK(232); /* msg 232 EVL_expr: invalid operation */ } return NULL; } bool EVL_field(jrd_rel* relation, Record* record, USHORT id, dsc* desc) { /************************************** * * E V L _ f i e l d * ************************************** * * Functional description * Evaluate a field by filling out a descriptor. * **************************************/ DEV_BLKCHK(record, type_rec); if (!record) { ERR_warning(Arg::Gds(isc_no_cur_rec)); return false; } const Format* format = record->rec_format; if (format && id < format->fmt_count) { *desc = format->fmt_desc[id]; } /* dimitr: fixed bug SF #562417 AFAIU, there was an assumption here that if a field descriptor is not filled, then a field doesn't exist. This is not true, because in fact an empty string has dsc_length = 0, and being part of an aggregate it becomes nod_field with zero length, hence we had NULL as a result. if (!format || id >= format->fmt_count || !desc->dsc_length) */ if (!format || id >= format->fmt_count || !desc->dsc_dtype) { /* Map a non-existent field to a default value, if available. * This enables automatic format upgrade for data rows. * Handle Outer Joins and such specially! * Reference: Bug 10424, 10116 */ /* rec_format == NULL indicates we're performing a join-to-null operation for outer joins */ if (record && record->rec_format && relation) { /* A database sweep does not scan a relation's metadata. However * the change to substitute a default value for a missing "not null" * field makes it necessary to reference the field block. */ if (!relation->rel_fields) { thread_db* tdbb = NULL; SET_TDBB(tdbb); MET_scan_relation(tdbb, relation); } // CVC: With a corrupt db, the engine crashed doing backup. jrd_fld* temp_field = NULL; if (id < relation->rel_fields->count()) temp_field = (*relation->rel_fields)[id]; if (temp_field) { if (temp_field->fld_default_value && temp_field->fld_not_null) { const NOD_T temp_nod_type = temp_field->fld_default_value->nod_type; if (temp_nod_type == nod_user_name) { desc->dsc_dtype = dtype_text; desc->dsc_sub_type = 0; desc->dsc_scale = 0; INTL_ASSIGN_TTYPE(desc, ttype_metadata); Firebird::MetaName& owner_name = relation->rel_owner_name; desc->dsc_address = (UCHAR*) owner_name.c_str(); // throwing away const. desc->dsc_length = owner_name.length(); } else if (temp_nod_type == nod_current_role) { // CVC: Revisiting the current_role to fill default values: // If the current user is the same as the table creator, // return the current role for that user, otherwise return NONE. desc->dsc_dtype = dtype_text; desc->dsc_sub_type = 0; desc->dsc_scale = 0; INTL_ASSIGN_TTYPE(desc, ttype_metadata); thread_db* tdbb = NULL; SET_TDBB(tdbb); const char* rc_role = 0; const UserId* att_user = tdbb->getAttachment()->att_user; const char* cur_user = att_user ? att_user->usr_user_name.c_str() : 0; if (cur_user && relation->rel_owner_name == cur_user) rc_role = att_user->usr_sql_role_name.c_str(); else rc_role = NULL_ROLE; desc->dsc_address = reinterpret_cast(const_cast(rc_role)); desc->dsc_length = strlen(rc_role); } else if (temp_nod_type == nod_current_date || temp_nod_type == nod_current_time || temp_nod_type == nod_current_timestamp) { static const GDS_TIMESTAMP temp_timestamp = { 0, 0 }; desc->dsc_dtype = dtype_timestamp; desc->dsc_scale = 0; desc->dsc_flags = 0; desc->dsc_address = reinterpret_cast( const_cast(&temp_timestamp)); desc->dsc_length = sizeof(temp_timestamp); } else if (temp_nod_type == nod_internal_info) { static const SLONG temp_long = 0; desc->dsc_dtype = dtype_long; desc->dsc_scale = 0; desc->dsc_flags = 0; desc->dsc_address = (UCHAR*) const_cast(&temp_long); desc->dsc_length = sizeof(temp_long); } else { const Literal* default_literal = reinterpret_cast(temp_field->fld_default_value); if (default_literal->nod_type == nod_null) { ERR_post(Arg::Gds(isc_not_valid) << Arg::Str(temp_field->fld_name) << Arg::Str(NULL_STRING_MARK)); } fb_assert(default_literal->nod_type == nod_literal); const dsc* default_desc = &default_literal->lit_desc; // CVC: This could be a bitwise copy in one line /* desc->dsc_dtype = default_desc->dsc_dtype; desc->dsc_scale = default_desc->dsc_scale; desc->dsc_length = default_desc->dsc_length; desc->dsc_sub_type = default_desc->dsc_sub_type; desc->dsc_flags = default_desc->dsc_flags; desc->dsc_address = default_desc->dsc_address; */ *desc = *default_desc; } return true; } } } desc->dsc_dtype = dtype_text; desc->dsc_length = 1; desc->dsc_sub_type = 0; desc->dsc_scale = 0; desc->dsc_ttype() = ttype_ascii; desc->dsc_address = (UCHAR *) " "; return false; } /* If the offset of the field is 0, the field can't possible exist */ if (!desc->dsc_address) { return false; } desc->dsc_address = record->rec_data + (IPTR) desc->dsc_address; if (TEST_NULL(record, id)) { desc->dsc_flags |= DSC_null; return false; } desc->dsc_flags &= ~DSC_null; return true; } USHORT EVL_group(thread_db* tdbb, RecordSource* rsb, jrd_nod *const node, USHORT state) { /************************************** * * E V L _ g r o u p * ************************************** * * Functional description * Compute the next aggregated record of a value group. EVL_group * is driven by, and returns, a state variable. The values of the * state are: * * 3 Entering EVL group beforing fetching the first record. * 1 Values are pending from a prior fetch * 2 We encountered EOF from the last attempted fetch * 0 We processed everything now process (EOF) * **************************************/ SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); if (--tdbb->tdbb_quantum < 0) JRD_reschedule(tdbb, 0, true); impure_value vtemp; vtemp.vlu_string = NULL; jrd_req* request = tdbb->getRequest(); jrd_nod* map = node->nod_arg[e_agg_map]; jrd_nod* group = node->nod_arg[e_agg_group]; jrd_nod** ptr; const jrd_nod* const* end; // if we found the last record last time, we're all done if (state == 2) return 0; try { // Initialize the aggregate record for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++) { const jrd_nod* from = (*ptr)->nod_arg[e_asgn_from]; impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure); impure->vlux_count = 0; switch (from->nod_type) { case nod_agg_average: case nod_agg_average_distinct: impure->vlu_desc.dsc_dtype = DEFAULT_DOUBLE; impure->vlu_desc.dsc_length = sizeof(double); impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = 0; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_double; impure->vlu_misc.vlu_double = 0; if (from->nod_type == nod_agg_average_distinct) /* Initialize a sort to reject duplicate values */ init_agg_distinct(tdbb, from); break; case nod_agg_average2: case nod_agg_average_distinct2: /* Initialize the result area as an int64. If the field being averaged is approximate numeric, the first call to add2 will convert the descriptor to double. */ impure->make_int64(0, from->nod_scale); if (from->nod_type == nod_agg_average_distinct2) /* Initialize a sort to reject duplicate values */ init_agg_distinct(tdbb, from); break; case nod_agg_total: case nod_agg_total_distinct: impure->make_long(0); if (from->nod_type == nod_agg_total_distinct) /* Initialize a sort to reject duplicate values */ init_agg_distinct(tdbb, from); break; case nod_agg_total2: case nod_agg_total_distinct2: /* Initialize the result area as an int64. If the field being averaged is approximate numeric, the first call to add2 will convert the descriptor to double. */ impure->make_int64(0, from->nod_scale); if (from->nod_type == nod_agg_total_distinct2) /* Initialize a sort to reject duplicate values */ init_agg_distinct(tdbb, from); break; case nod_agg_min: case nod_agg_min_indexed: case nod_agg_max: case nod_agg_max_indexed: impure->vlu_desc.dsc_dtype = 0; break; case nod_agg_count: case nod_agg_count2: case nod_agg_count_distinct: impure->make_long(0); if (from->nod_type == nod_agg_count_distinct) /* Initialize a sort to reject duplicate values */ init_agg_distinct(tdbb, from); break; case nod_agg_list: case nod_agg_list_distinct: // We don't know here what should be the sub-type and text-type. // Defer blob creation for when first record is found. impure->vlu_blob = NULL; impure->vlu_desc.dsc_dtype = 0; if (from->nod_type == nod_agg_list_distinct) /* Initialize a sort to reject duplicate values */ init_agg_distinct(tdbb, from); break; case nod_literal: /* pjpg 20001124 */ EXE_assignment(tdbb, *ptr); break; default: // Shut up some compiler warnings break; } } /* If there isn't a record pending, open the stream and get one */ if ((state == 0) || (state == 3)) { RSE_open(tdbb, rsb); if (!RSE_get_record(tdbb, rsb, g_RSE_get_mode)) { if (group) { fini_agg_distinct(tdbb, node); return 0; } state = 2; } } dsc* desc; if (group) { for (ptr = group->nod_arg, end = ptr + group->nod_count; ptr < end; ptr++) { jrd_nod* from = *ptr; impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure); desc = EVL_expr(tdbb, from); if (request->req_flags & req_null) impure->vlu_desc.dsc_address = NULL; else EVL_make_value(tdbb, desc, impure); } } // Loop thru records until either a value change or EOF bool first = true; while (state != 2) { state = 1; if (first) first = false; else { // In the case of a group by, look for a change in value of any of // the columns; if we find one, stop aggregating and return what we have. if (group) { for (ptr = group->nod_arg, end = ptr + group->nod_count; ptr < end; ptr++) { jrd_nod* from = *ptr; impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure); if (impure->vlu_desc.dsc_address) EVL_make_value(tdbb, &impure->vlu_desc, &vtemp); else vtemp.vlu_desc.dsc_address = NULL; desc = EVL_expr(tdbb, from); if (request->req_flags & req_null) { impure->vlu_desc.dsc_address = NULL; if (vtemp.vlu_desc.dsc_address) goto break_out; } else { EVL_make_value(tdbb, desc, impure); if (!vtemp.vlu_desc.dsc_address || MOV_compare(&vtemp.vlu_desc, desc)) { goto break_out; } } } } } // go through and compute all the aggregates on this record for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++) { jrd_nod* from = (*ptr)->nod_arg[e_asgn_from]; impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure); switch (from->nod_type) { case nod_agg_min: case nod_agg_min_indexed: case nod_agg_max: case nod_agg_max_indexed: { desc = EVL_expr(tdbb, from->nod_arg[0]); if (request->req_flags & req_null) { break; } // if a max or min has been mapped to an index, // then the first record is the EOF if (from->nod_type == nod_agg_max_indexed || from->nod_type == nod_agg_min_indexed) { state = 2; } ++impure->vlux_count; if (!impure->vlu_desc.dsc_dtype) { EVL_make_value(tdbb, desc, impure); // It was reinterpret_cast(&impure->vlu_desc)); // but vlu_desc is the first member of impure_value and impure_value_ex // derives from impure_value and impure_value doesn't derive from anything // and it doesn't contain virtuals. // Thus, &impure_value_ex->vlu_desc == &impure_value->vlu_desc == &impure_value_ex // Delete this comment or restore the original code // when this reasoning has been validated, please. break; } const SLONG result = MOV_compare(desc, reinterpret_cast(impure)); if ((result > 0 && (from->nod_type == nod_agg_max || from->nod_type == nod_agg_max_indexed)) || (result < 0 && (from->nod_type == nod_agg_min || from->nod_type == nod_agg_min_indexed))) { EVL_make_value(tdbb, desc, impure); } break; } case nod_agg_total: case nod_agg_average: desc = EVL_expr(tdbb, from->nod_arg[0]); if (request->req_flags & req_null) break; ++impure->vlux_count; add(desc, from, impure); break; case nod_agg_total2: case nod_agg_average2: desc = EVL_expr(tdbb, from->nod_arg[0]); if (request->req_flags & req_null) break; ++impure->vlux_count; add2(desc, from, impure); break; case nod_agg_count2: ++impure->vlux_count; desc = EVL_expr(tdbb, from->nod_arg[0]); if (request->req_flags & req_null) break; case nod_agg_count: ++impure->vlux_count; ++impure->vlu_misc.vlu_long; break; case nod_agg_list: { MoveBuffer buffer; UCHAR* temp; int len; desc = EVL_expr(tdbb, from->nod_arg[0]); if (request->req_flags & req_null) break; if (!impure->vlu_blob) { impure->vlu_blob = BLB_create(tdbb, tdbb->getRequest()->req_transaction, &impure->vlu_misc.vlu_bid); impure->vlu_desc.makeBlob(desc->getBlobSubType(), desc->getTextType(), (ISC_QUAD* ) &impure->vlu_misc.vlu_bid); } if (impure->vlux_count) { const dsc* const delimiter = EVL_expr(tdbb, from->nod_arg[1]); if (request->req_flags & req_null) { // mark the result as NULL impure->vlu_desc.dsc_dtype = 0; break; } len = MOV_make_string2(tdbb, delimiter, impure->vlu_desc.getTextType(), &temp, buffer, false); BLB_put_data(tdbb, impure->vlu_blob, temp, len); } ++impure->vlux_count; len = MOV_make_string2(tdbb, desc, impure->vlu_desc.getTextType(), &temp, buffer, false); BLB_put_data(tdbb, impure->vlu_blob, temp, len); break; } case nod_agg_count_distinct: case nod_agg_total_distinct: case nod_agg_average_distinct: case nod_agg_average_distinct2: case nod_agg_total_distinct2: case nod_agg_list_distinct: { desc = EVL_expr(tdbb, from->nod_arg[0]); if (request->req_flags & req_null) break; /* "Put" the value to sort. */ const size_t asb_index = (from->nod_type == nod_agg_list_distinct) ? 2 : 1; AggregateSort* asb = (AggregateSort*) from->nod_arg[asb_index]; impure_agg_sort* asb_impure = (impure_agg_sort*) ((SCHAR *) request + asb->nod_impure); UCHAR* data; SORT_put(tdbb, asb_impure->iasb_sort_handle, reinterpret_cast(&data)); MOVE_CLEAR(data, asb->asb_length); if (asb->asb_intl) { // convert to an international byte array dsc to; to.dsc_dtype = dtype_text; to.dsc_flags = 0; to.dsc_sub_type = 0; to.dsc_scale = 0; to.dsc_ttype() = ttype_sort_key; to.dsc_length = asb->asb_key_desc[0].skd_length; to.dsc_address = data; INTL_string_to_key(tdbb, INTL_TEXT_TO_INDEX(desc->getTextType()), desc, &to, INTL_KEY_UNIQUE); } asb->asb_desc.dsc_address = data + (asb->asb_intl ? asb->asb_key_desc[1].skd_offset : 0); MOV_move(tdbb, desc, &asb->asb_desc); break; } default: EXE_assignment(tdbb, *ptr); } } if (state == 2) break; if (!RSE_get_record(tdbb, rsb, g_RSE_get_mode)) { state = 2; } } break_out: for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++) { const jrd_nod* from = (*ptr)->nod_arg[e_asgn_from]; impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure); if (from->nod_type == nod_agg_list && impure->vlu_blob) { BLB_close(tdbb, impure->vlu_blob); impure->vlu_blob = NULL; } } /* Finish up any residual computations and get out */ delete vtemp.vlu_string; dsc temp; double d; SINT64 i; for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++) { jrd_nod* from = (*ptr)->nod_arg[e_asgn_from]; jrd_nod* field = (*ptr)->nod_arg[e_asgn_to]; const USHORT id = (USHORT)(IPTR) field->nod_arg[e_fld_id]; Record* record = request->req_rpb[(int) (IPTR) field->nod_arg[e_fld_stream]].rpb_record; impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure); switch (from->nod_type) { case nod_agg_min: case nod_agg_min_indexed: case nod_agg_max: case nod_agg_max_indexed: case nod_agg_total: case nod_agg_total_distinct: case nod_agg_total2: case nod_agg_total_distinct2: case nod_agg_list: case nod_agg_list_distinct: if ((from->nod_type == nod_agg_total_distinct) || (from->nod_type == nod_agg_total_distinct2) || (from->nod_type == nod_agg_list_distinct)) { compute_agg_distinct(tdbb, from); } if (!impure->vlux_count) { SET_NULL(record, id); break; } /* If vlux_count is non-zero, we need to fall through. */ case nod_agg_count: case nod_agg_count2: case nod_agg_count_distinct: if (from->nod_type == nod_agg_count_distinct) { compute_agg_distinct(tdbb, from); } if (!impure->vlu_desc.dsc_dtype) { SET_NULL(record, id); } else { MOV_move(tdbb, &impure->vlu_desc, EVL_assign_to(tdbb, field)); CLEAR_NULL(record, id); } break; case nod_agg_average_distinct: compute_agg_distinct(tdbb, from); /* fall through */ case nod_agg_average: if (!impure->vlux_count) { SET_NULL(record, id); break; } CLEAR_NULL(record, id); temp.dsc_dtype = DEFAULT_DOUBLE; temp.dsc_length = sizeof(double); temp.dsc_scale = 0; temp.dsc_sub_type = 0; temp.dsc_address = (UCHAR *) & d; d = MOV_get_double(&impure->vlu_desc) / impure->vlux_count; MOV_move(tdbb, &temp, EVL_assign_to(tdbb, field)); break; case nod_agg_average_distinct2: compute_agg_distinct(tdbb, from); /* fall through */ case nod_agg_average2: if (!impure->vlux_count) { SET_NULL(record, id); break; } CLEAR_NULL(record, id); temp.dsc_sub_type = 0; if (dtype_int64 == impure->vlu_desc.dsc_dtype) { temp.dsc_dtype = dtype_int64; temp.dsc_length = sizeof(SINT64); temp.dsc_scale = impure->vlu_desc.dsc_scale; temp.dsc_address = (UCHAR *) & i; i = *((SINT64 *) impure->vlu_desc.dsc_address) / impure->vlux_count; } else { temp.dsc_dtype = DEFAULT_DOUBLE; temp.dsc_length = sizeof(double); temp.dsc_scale = 0; temp.dsc_address = (UCHAR *) & d; d = MOV_get_double(&impure->vlu_desc) / impure->vlux_count; } MOV_move(tdbb, &temp, EVL_assign_to(tdbb, field)); break; default: // Shut up some compiler warnings break; } } } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); fini_agg_distinct(tdbb, node); ERR_punt(); } return state; } void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value) { /************************************** * * E V L _ m a k e _ v a l u e * ************************************** * * Functional description * Make a value block reflect the value of a descriptor. * **************************************/ SET_TDBB(tdbb); /* Handle the fixed length data types first. They're easy. */ const dsc from = *desc; value->vlu_desc = *desc; value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc; switch (from.dsc_dtype) { case dtype_short: value->vlu_misc.vlu_short = *((SSHORT *) from.dsc_address); return; case dtype_long: case dtype_real: case dtype_sql_time: case dtype_sql_date: value->vlu_misc.vlu_long = *((SLONG *) from.dsc_address); return; case dtype_int64: value->vlu_misc.vlu_int64 = *((SINT64 *) from.dsc_address); return; case dtype_double: value->vlu_misc.vlu_double = *((double *) from.dsc_address); return; case dtype_timestamp: case dtype_quad: value->vlu_misc.vlu_dbkey[0] = ((SLONG *) from.dsc_address)[0]; value->vlu_misc.vlu_dbkey[1] = ((SLONG *) from.dsc_address)[1]; return; case dtype_text: case dtype_varying: case dtype_cstring: break; case dtype_blob: value->vlu_misc.vlu_bid = *(bid*)from.dsc_address; return; default: fb_assert(false); break; } UCHAR temp[128]; UCHAR* address; USHORT ttype; // Get string. If necessary, get_string will convert the string into a // temporary buffer. Since this will always be the result of a conversion, // this isn't a serious problem. const USHORT length = MOV_get_string_ptr(&from, &ttype, &address, reinterpret_cast(temp), sizeof(temp)); // Allocate a string block of sufficient size. VaryingString* string = value->vlu_string; if (string && string->str_length < length) { delete string; string = NULL; } if (!string) { string = value->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(), length) VaryingString(); string->str_length = length; } value->vlu_desc.dsc_dtype = dtype_text; value->vlu_desc.dsc_length = length; UCHAR* target = string->str_data; value->vlu_desc.dsc_address = target; value->vlu_desc.dsc_sub_type = 0; value->vlu_desc.dsc_scale = 0; INTL_ASSIGN_TTYPE(&value->vlu_desc, ttype); if (address && length && target != address) memcpy(target, address, length); } void EVL_validate(thread_db* tdbb, const Item& item, const ItemInfo* itemInfo, dsc* desc, bool null) { /************************************** * * E V L _ v a l i d a t e * ************************************** * * Functional description * Validate argument/variable for not null and check constraint * **************************************/ if (itemInfo == NULL) return; jrd_req* request = tdbb->getRequest(); bool err = false; if (null && !itemInfo->nullable) err = true; const char* value = NULL_STRING_MARK; TEXT temp[128]; MapFieldInfo::ValueType fieldInfo; if (!err && itemInfo->fullDomain && request->req_map_field_info.get(itemInfo->field, fieldInfo) && fieldInfo.validation) { if (desc && null) { desc->dsc_flags |= DSC_null; } const bool desc_is_null = !desc || (desc->dsc_flags & DSC_null); request->req_domain_validation = desc; const USHORT flags = request->req_flags; if (!EVL_boolean(tdbb, fieldInfo.validation) && !(request->req_flags & req_null)) { const USHORT length = desc_is_null ? 0 : MOV_make_string(desc, ttype_dynamic, &value, reinterpret_cast(temp), sizeof(temp) - 1); if (desc_is_null) value = NULL_STRING_MARK; else if (!length) value = ""; else const_cast(value)[length] = 0; // safe cast - data is on our local stack err = true; } request->req_flags = flags; } Firebird::string s; if (err) { ISC_STATUS status = isc_not_valid_for_var; const char* arg; if (item.type == nod_cast) { status = isc_not_valid_for; arg = "CAST"; } else { if (itemInfo->name.isEmpty()) { int index = item.index + 1; status = isc_not_valid_for; if (item.type == nod_variable) { if (request->req_procedure) { if (index <= request->req_procedure->prc_outputs) s.printf("output parameter number %d", index); else { s.printf("variable number %d", index - request->req_procedure->prc_outputs); } } else s.printf("variable number %d", index); } else if (item.type == nod_argument && item.subType == 0) s.printf("input parameter number %d", (index - 1) / 2 + 1); else if (item.type == nod_argument && item.subType == 1) s.printf("output parameter number %d", index); if (s.isEmpty()) arg = UNKNOWN_STRING_MARK; else arg = s.c_str(); } else arg = itemInfo->name.c_str(); } ERR_post(Arg::Gds(status) << Arg::Str(arg) << Arg::Str(value)); } } static dsc* add(const dsc* desc, const jrd_nod* node, impure_value* value) { /************************************** * * a d d * ************************************** * * Functional description * Add (or subtract) the contents of a descriptor to value * block, with dialect-1 semantics. * This function can be removed when dialect-3 becomes * the lowest supported dialect. (Version 7.0?) * **************************************/ DEV_BLKCHK(node, type_nod); fb_assert(node->nod_type == nod_add || node->nod_type == nod_subtract || node->nod_type == nod_total || node->nod_type == nod_average || node->nod_type == nod_agg_total || node->nod_type == nod_agg_average || node->nod_type == nod_agg_total_distinct || node->nod_type == nod_agg_average_distinct); dsc* result = &value->vlu_desc; /* Handle date arithmetic */ if (node->nod_flags & nod_date) { return add_datetime(desc, node, value); } /* Handle floating arithmetic */ if (node->nod_flags & nod_double) { const double d1 = MOV_get_double(desc); const double d2 = MOV_get_double(&value->vlu_desc); value->vlu_misc.vlu_double = (node->nod_type == nod_subtract) ? d2 - d1 : d1 + d2; result->dsc_dtype = DEFAULT_DOUBLE; result->dsc_length = sizeof(double); result->dsc_scale = 0; result->dsc_sub_type = 0; result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_double; return result; } /* Handle (oh, ugh) quad arithmetic */ if (node->nod_flags & nod_quad) { const SQUAD q1 = MOV_get_quad(desc, node->nod_scale); const SQUAD q2 = MOV_get_quad(&value->vlu_desc, node->nod_scale); result->dsc_dtype = dtype_quad; result->dsc_length = sizeof(SQUAD); result->dsc_scale = node->nod_scale; value->vlu_misc.vlu_quad = (node->nod_type == nod_subtract) ? QUAD_SUBTRACT(q2, q1, ERR_post) : QUAD_ADD(q1, q2, ERR_post); result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad; return result; } /* Everything else defaults to longword */ const SLONG l1 = MOV_get_long(desc, node->nod_scale); const SLONG l2 = MOV_get_long(&value->vlu_desc, node->nod_scale); value->make_long(((node->nod_type == nod_subtract) ? l2 - l1 : l1 + l2), node->nod_scale); return result; } static dsc* add2(const dsc* desc, const jrd_nod* node, impure_value* value) { /************************************** * * a d d 2 * ************************************** * * Functional description * Add (or subtract) the contents of a descriptor to value * block, with dialect-3 semantics, as in the blr_add, * blr_subtract, and blr_agg_total verbs following a * blr_version5 * **************************************/ DEV_BLKCHK(node, type_nod); fb_assert(node->nod_type == nod_add2 || node->nod_type == nod_subtract2 || node->nod_type == nod_average2 || node->nod_type == nod_agg_total2 || node->nod_type == nod_agg_average2 || node->nod_type == nod_agg_total_distinct2 || node->nod_type == nod_agg_average_distinct2); dsc* result = &value->vlu_desc; /* Handle date arithmetic */ if (node->nod_flags & nod_date) { return add_datetime(desc, node, value); } /* Handle floating arithmetic */ if (node->nod_flags & nod_double) { const double d1 = MOV_get_double(desc); const double d2 = MOV_get_double(&value->vlu_desc); value->vlu_misc.vlu_double = (node->nod_type == nod_subtract2) ? d2 - d1 : d1 + d2; result->dsc_dtype = DEFAULT_DOUBLE; result->dsc_length = sizeof(double); result->dsc_scale = 0; result->dsc_sub_type = 0; result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_double; return result; } /* Handle (oh, ugh) quad arithmetic */ if (node->nod_flags & nod_quad) { const SQUAD q1 = MOV_get_quad(desc, node->nod_scale); const SQUAD q2 = MOV_get_quad(&value->vlu_desc, node->nod_scale); result->dsc_dtype = dtype_quad; result->dsc_length = sizeof(SQUAD); result->dsc_scale = node->nod_scale; value->vlu_misc.vlu_quad = (node->nod_type == nod_subtract2) ? QUAD_SUBTRACT(q2, q1, ERR_post) : QUAD_ADD(q1, q2, ERR_post); result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad; return result; } /* Everything else defaults to int64 */ SINT64 i1 = MOV_get_int64(desc, node->nod_scale); const SINT64 i2 = MOV_get_int64(&value->vlu_desc, node->nod_scale); result->dsc_dtype = dtype_int64; result->dsc_length = sizeof(SINT64); result->dsc_scale = node->nod_scale; value->vlu_misc.vlu_int64 = (node->nod_type == nod_subtract2) ? i2 - i1 : i1 + i2; result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_int64; result->dsc_sub_type = MAX(desc->dsc_sub_type, value->vlu_desc.dsc_sub_type); /* If the operands of an addition have the same sign, and their sum has the opposite sign, then overflow occurred. If the two addends have opposite signs, then the result will lie between the two addends, and overflow cannot occur. If this is a subtraction, note that we invert the sign bit, rather than negating the argument, so that subtraction of MIN_SINT64, which is unchanged by negation, will be correctly treated like the addition of a positive number for the purposes of this test. Test cases for a Gedankenexperiment, considering the sign bits of the operands and result after the inversion below: L Rt Sum MIN_SINT64 - MIN_SINT64 == 0, with no overflow 1 0 0 -MAX_SINT64 - MIN_SINT64 == 1, with no overflow 1 0 0 1 - MIN_SINT64 == overflow 0 0 1 -1 - MIN_SINT64 == MAX_SINT64, no overflow 1 0 0 */ if (node->nod_type == nod_subtract2) i1 ^= MIN_SINT64; /* invert the sign bit */ if (((i1 ^ i2) >= 0) && ((i1 ^ value->vlu_misc.vlu_int64) < 0)) ERR_post(Arg::Gds(isc_exception_integer_overflow)); return result; } static dsc* add_datetime(const dsc* desc, const jrd_nod* node, impure_value* value) { /************************************** * * a d d _ d a t e t i m e * ************************************** * * Functional description * Vector out to one of the actual datetime addition routines * **************************************/ BYTE dtype; /* Which addition routine to use? */ fb_assert(node->nod_flags & nod_date); /* Value is the LHS of the operand. desc is the RHS */ if ((node->nod_type == nod_add) || (node->nod_type == nod_add2)) { dtype = DSC_add_result[value->vlu_desc.dsc_dtype][desc->dsc_dtype]; } else { fb_assert((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2)); dtype = DSC_sub_result[value->vlu_desc.dsc_dtype][desc->dsc_dtype]; /* Is this a - construct? chose the proper routine to do the subtract from the LHS of expression Thus: