/* * PROGRAM: JRD Access Method * MODULE: evl.c * 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): ______________________________________. * $Id: evl.cpp,v 1.27 2003-02-13 10:10:59 dimitr Exp $ */ /* * 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 */ #include "firebird.h" #include #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/codes.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/constants.h" #include "../jrd/gdsassert.h" #include "../jrd/jrd_time.h" #include "../jrd/all_proto.h" #include "../jrd/bookmark.h" #include "../jrd/blb_proto.h" #include "../jrd/btr_proto.h" #include "../jrd/cvt_proto.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/rng_proto.h" #include "../jrd/rse_proto.h" #include "../jrd/sbm_proto.h" #include "../jrd/scl_proto.h" #include "../jrd/thd_proto.h" #include "../jrd/sort_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/align.h" #include "../jrd/met_proto.h" #include "../jrd/cvt_proto.h" #include "../jrd/misc_func_ids.h" //#include "../jrd/authenticate.h" #if defined(WIN_NT) && defined(_MSC_VER) #pragma warning(disable: 4244) #endif #define TEMP_LENGTH 128 #ifdef STACK_REDUCTION #define TEMP_SIZE(x) TEMP_LENGTH #else #define TEMP_SIZE(x) sizeof (x) #endif #define MAX_INT64_LIMIT (MAX_SINT64 / 10) #define MIN_INT64_LIMIT (MIN_SINT64 / 10) #ifdef VMS extern double MTH$CVT_D_G(), MTH$CVT_G_D(); #endif /* *** 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. */ static DSC *add(DSC *, JRD_NOD, VLU); static DSC *add2(DSC *, JRD_NOD, VLU); static DSC *add_datetime(DSC *, JRD_NOD, VLU); static DSC *add_sql_date(DSC *, JRD_NOD, VLU); static DSC *add_sql_time(DSC *, JRD_NOD, VLU); static DSC *add_timestamp(DSC *, JRD_NOD, VLU); static DSC *binary_value(TDBB, JRD_NOD, VLU); static DSC *cast(TDBB, DSC *, JRD_NOD, VLU); static SSHORT compute_agg_distinct(TDBB, JRD_NOD); static DSC *concatenate(TDBB, JRD_NOD, VLU); static DSC *dbkey(TDBB, JRD_NOD, VLU); static DSC *eval_statistical(TDBB, JRD_NOD, VLU); static SINT64 get_day_fraction(DSC * d); static DSC *get_mask(TDBB, JRD_NOD, VLU); static SINT64 get_timestamp_to_isc_ticks(DSC * d); static SSHORT init_agg_distinct(TDBB, JRD_NOD); #ifdef PC_ENGINE static DSC *lock_record(TDBB, JRD_NOD, VLU); static DSC *lock_relation(TDBB, JRD_NOD, VLU); #endif static DSC *lock_state(TDBB, JRD_NOD, VLU); static DSC *multiply(DSC *, VLU, JRD_NOD); static DSC *multiply2(DSC *, VLU, JRD_NOD); static DSC *divide2(DSC *, VLU, JRD_NOD); static DSC *negate_dsc(TDBB, DSC *, VLU); static DSC *record_version(TDBB, JRD_NOD, VLU); static BOOLEAN reject_duplicate(UCHAR *, UCHAR *, int); static DSC *scalar(TDBB, JRD_NOD, VLU); static SSHORT sleuth(TDBB, JRD_NOD, DSC *, DSC *); static BOOLEAN nc_sleuth_check(class TextType*, USHORT, UCHAR *, UCHAR *, UCHAR *, UCHAR *); static BOOLEAN nc_sleuth_class(class TextType*, USHORT, UCHAR *, UCHAR *, UCHAR); static BOOLEAN wc_sleuth_check(class TextType*, USHORT, WCHAR *, WCHAR *, WCHAR *, WCHAR *); static BOOLEAN wc_sleuth_class(class TextType*, USHORT, WCHAR *, WCHAR *, WCHAR); static SSHORT string_boolean(TDBB, JRD_NOD, DSC *, DSC *); static SSHORT string_function(TDBB, JRD_NOD, SSHORT, UCHAR *, SSHORT, UCHAR *, USHORT); static DSC *substring(TDBB, VLU, DSC *, USHORT, USHORT); static DSC *upcase(TDBB, DSC *, VLU); static DSC *internal_info(TDBB, DSC *, VLU); static const UCHAR special[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, /* $%*+- (dollar, percent, star, plus, minus) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* ? (question) */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ (at-sign) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* [ (open square) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* ~ (tilde) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; #define SECONDS_PER_DAY (24*60*60) #define ISC_TICKS_PER_DAY (((SINT64)SECONDS_PER_DAY)*ISC_TIME_SECONDS_PRECISION) #define DIALECT_3_TIMESTAMP_SCALE -9 #define DIALECT_1_TIMESTAMP_SCALE 0 DSC *DLL_EXPORT EVL_assign_to(TDBB 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. * **************************************/ JRD_REQ request; VLU impure; DSC *desc; FMT format; JRD_NOD message; REC record; SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); request = tdbb->tdbb_request; impure = (VLU) ((SCHAR *) request + node->nod_impure); /* The only nodes that can be assigned to are: argument, field and variable. */ switch (node->nod_type) { case nod_argument: message = node->nod_arg[e_arg_message]; format = (FMT) message->nod_arg[e_msg_format]; desc = &format->fmt_desc[(int) node->nod_arg[e_arg_number]]; impure->vlu_desc.dsc_address = (UCHAR *) request + message->nod_impure + (int) 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 (IS_DTYPE_ANY_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->tdbb_attachment->att_charset, COLLATE_NONE); } return &impure->vlu_desc; case nod_field: record = request->req_rpb[(int) node->nod_arg[e_fld_stream]].rpb_record; EVL_field(0, record, (USHORT) node->nod_arg[e_fld_id], &impure->vlu_desc); if (!impure->vlu_desc.dsc_address) ERR_post(gds_read_only_field, 0); return &impure->vlu_desc; case nod_null: return NULL; case nod_variable: node = node->nod_arg[e_var_variable]; impure = (VLU) ((SCHAR *) request + node->nod_impure); return &impure->vlu_desc; default: BUGCHECK(229); /* msg 229 EVL_assign_to: invalid operation */ } return NULL; } SBM *DLL_EXPORT EVL_bitmap(TDBB tdbb, JRD_NOD node) { /************************************** * * E V L _ b i t m a p * ************************************** * * Functional description * Evaluate bitmap valued expression. * **************************************/ SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); switch (node->nod_type) { case nod_bit_and: return SBM_and(EVL_bitmap(tdbb, node->nod_arg[0]), EVL_bitmap(tdbb, node->nod_arg[1])); case nod_bit_or: return SBM_or(EVL_bitmap(tdbb, node->nod_arg[0]), EVL_bitmap(tdbb, node->nod_arg[1])); case nod_bit_dbkey: { DSC *desc; USHORT id; UCHAR *numbers; SLONG rel_dbkey; INV impure; impure = (INV) ((SCHAR *) tdbb->tdbb_request + node->nod_impure); SBM_reset(&impure->inv_bitmap); desc = EVL_expr(tdbb, node->nod_arg[0]); id = 1 + 2 * (USHORT) node->nod_arg[1]; numbers = desc->dsc_address; numbers += id * sizeof(SLONG); MOVE_FAST(numbers, &rel_dbkey, sizeof(SLONG)); rel_dbkey -= 1; SBM_set(tdbb, &impure->inv_bitmap, rel_dbkey); return &impure->inv_bitmap; } case nod_index: { INV impure; impure = (INV) ((SCHAR *) tdbb->tdbb_request + node->nod_impure); BTR_evaluate(tdbb, reinterpret_cast < irb * >(node->nod_arg[e_idx_retrieval]), &impure->inv_bitmap); return &impure->inv_bitmap; } default: BUGCHECK(230); /* msg 230 EVL_bitmap: invalid operation */ } return NULL; } BOOLEAN DLL_EXPORT EVL_boolean(TDBB tdbb, JRD_NOD node) { /************************************** * * E V L _ b o o l e a n * ************************************** * * Functional description * Evaluate a boolean. * **************************************/ JRD_REQ request; JRD_NOD *ptr; DSC *desc[2]; USHORT value; SSHORT comparison; VLU impure; SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); /* Handle and pre-processing possible for various nodes. This includes evaluating argument and checking NULL flags */ request = tdbb->tdbb_request; ptr = node->nod_arg; switch (node->nod_type) { case nod_contains: case nod_starts: case nod_matches: case nod_like: case nod_eql: case nod_neq: case nod_gtr: case nod_geq: case nod_lss: case nod_leq: case nod_between: case nod_sleuth: { JRD_NOD rec_version; ULONG flags; SSHORT force_equal; request->req_flags &= ~req_same_tx_upd; 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 */ rec_version = *ptr; desc[0] = EVL_expr(tdbb, *ptr++); flags = request->req_flags; request->req_flags &= ~req_null; request->req_flags &= ~req_clone_data_from_default_clause; force_equal |= request->req_flags & req_same_tx_upd; desc[1] = EVL_expr(tdbb, *ptr++); /* restore preserved NULL state */ 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 */ switch (node->nod_type) { case nod_and: { USHORT firstnull, secondnull, value2; /* 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 */ firstnull = request->req_flags & req_null; request->req_flags &= ~req_null; value2 = EVL_boolean(tdbb, *ptr); secondnull = request->req_flags & req_null; request->req_flags &= ~req_null; if ((!value && !firstnull) || (!value2 && !secondnull)) return FALSE; /* at least one operand was FALSE */ else 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; RSB rsb; RSE rse; if (node->nod_flags & nod_invariant) { impure = (VLU) ((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; } } /* 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.c) */ rse = (RSE) (node->nod_arg[e_any_rse]); rsb = (RSB) (node->nod_arg[e_any_rsb]); if (node->nod_type != nod_any) { rsb->rsb_any_boolean = (JRD_NOD) rse->rse_boolean; if (node->nod_type == nod_ansi_any) request->req_flags |= req_ansi_any; else request->req_flags |= req_ansi_all; } RSE_open(tdbb, reinterpret_cast < struct Rsb *>(node->nod_arg[e_any_rsb])); value = RSE_get_record(tdbb, reinterpret_cast < struct Rsb *>(node->nod_arg[e_any_rsb]), #ifdef SCROLLABLE_CURSORS RSE_get_next); #else RSE_get_forward); #endif RSE_close(tdbb, reinterpret_cast < struct Rsb *>(node->nod_arg[e_any_rsb])); 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; } return value; } case nod_contains: case nod_starts: case nod_matches: case nod_like: return string_boolean(tdbb, node, desc[0], desc[1]); 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 { if (request->req_flags & req_clone_data_from_default_clause) { value = TRUE; request->req_flags &= ~req_clone_data_from_default_clause; } else value = FALSE; } return value; case nod_not: if (request->req_flags & req_null) return FALSE; return !value; case nod_or: { ULONG flags; USHORT value2; /* 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 */ flags = request->req_flags; request->req_flags &= ~req_null; 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 = (VLU) ((SCHAR *) request + node->nod_impure); invariant_flags = & impure->vlu_flags; if (*invariant_flags & VLU_computed) { /* An invariant node has already been computed. */ if (*invariant_flags & VLU_null) request->req_flags |= req_null; else request->req_flags &= ~req_null; return impure->vlu_misc.vlu_short; } } RSE_open(tdbb, reinterpret_cast < Rsb * >(node->nod_arg[e_any_rsb])); if ( (value = RSE_get_record(tdbb, reinterpret_cast < Rsb * >(node->nod_arg[e_any_rsb]), #ifdef SCROLLABLE_CURSORS RSE_get_next)) ) #else RSE_get_forward)) ) #endif value = !RSE_get_record(tdbb, reinterpret_cast < Rsb * >(node->nod_arg[e_any_rsb]), #ifdef SCROLLABLE_CURSORS RSE_get_next); #else RSE_get_forward); #endif RSE_close(tdbb, reinterpret_cast < Rsb * >(node->nod_arg[e_any_rsb])); /* If this is an invariant node, save the return value. */ if (node->nod_flags & nod_invariant) { *invariant_flags |= VLU_computed; if (request->req_flags & req_null) *invariant_flags |= VLU_null; impure->vlu_misc.vlu_short = value; } return value; } case nod_eql: return ((comparison == 0) ? TRUE : FALSE); case nod_neq: return ((comparison != 0) ? TRUE : FALSE); case nod_gtr: return ((comparison > 0) ? TRUE : FALSE); case nod_geq: return ((comparison >= 0) ? TRUE : FALSE); case nod_lss: return ((comparison < 0) ? TRUE : FALSE); case nod_leq: return ((comparison <= 0) ? TRUE : FALSE); case nod_between: return (comparison >= 0 && MOV_compare(desc[0], EVL_expr(tdbb, node->nod_arg[2])) <= 0) ? TRUE : FALSE; default: BUGCHECK(231); /* msg 231 EVL_bitmap: invalid operation */ } return FALSE; } DSC* DLL_EXPORT EVL_expr(TDBB tdbb, JRD_NOD node) { /************************************** * * E V L _ e x p r * ************************************** * * Functional description * Evaluate a value expression. * **************************************/ JRD_REQ request; VLU impure; DEV_BLKCHK(node, type_nod); if (!node) BUGCHECK(303); /* msg 303 Invalid expression for evaluation */ SET_TDBB(tdbb); request = tdbb->tdbb_request; impure = (VLU) ((SCHAR *) request + node->nod_impure); request->req_flags &= ~req_null; request->req_flags &= ~req_clone_data_from_default_clause; /* 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: return binary_value(tdbb, node, impure); case nod_argument: { FMT format; JRD_NOD message; 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; } } message = node->nod_arg[e_arg_message]; format = (FMT) message->nod_arg[e_msg_format]; desc = &format->fmt_desc[(int) node->nod_arg[e_arg_number]]; impure->vlu_desc.dsc_address = (UCHAR *) request + message->nod_impure + (int) 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; return &impure->vlu_desc; } case nod_concatenate: return concatenate(tdbb, node, impure); case nod_dbkey: return dbkey(tdbb, node, impure); case nod_rec_version: return record_version(tdbb, node, impure); case nod_field: { REC record = request->req_rpb[(int)node->nod_arg[e_fld_stream]].rpb_record; /* 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(request->req_rpb[(USHORT) node->nod_arg[e_fld_stream]].rpb_relation, record, (USHORT) node->nod_arg[e_fld_id], &impure->vlu_desc)) { request->req_flags |= req_null; } return &impure->vlu_desc; } case nod_function: FUN_evaluate(reinterpret_cast(node->nod_arg[e_fun_function]), node->nod_arg[e_fun_args], impure); /*request->req_flags |= req_null; THIS IS A TEST ONLY. return NULL;*/ return &impure->vlu_desc; case nod_literal: return &((LIT) node)->lit_desc; case nod_lock_state: return lock_state(tdbb, node, impure); #ifdef PC_ENGINE case nod_lock_record: return lock_record(tdbb, node, impure); case nod_lock_relation: return lock_relation(tdbb, node, impure); case nod_begin_range: return RNG_begin(node, impure); #endif 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: { ULONG clock; struct tm times; GDS_TIMESTAMP enc_times; /* Use the request timestamp, if there is one. Otherwise fetch the current clock in order to keep running */ if (request->req_timestamp) clock = request->req_timestamp; else { assert(FALSE); clock = time(0); } times = *localtime(reinterpret_cast < time_t * >(&clock)); 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) times.tm_year = times.tm_mon = times.tm_mday = 0; else if (node->nod_type == nod_current_date) times.tm_hour = times.tm_min = times.tm_sec = 0; isc_encode_timestamp(×, &enc_times); 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]; *((GDS_TIMESTAMP *) impure->vlu_desc.dsc_address) = enc_times; break; default: 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); if (tdbb->tdbb_attachment->att_user) impure->vlu_desc.dsc_address = (UCHAR *) tdbb->tdbb_attachment->att_user->usr_user_name; if (impure->vlu_desc.dsc_address != NULL) impure->vlu_desc.dsc_length = strlen(reinterpret_cast < const char *>(impure->vlu_desc.dsc_address)); 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); if (tdbb->tdbb_attachment->att_user) impure->vlu_desc.dsc_address = (UCHAR*) tdbb->tdbb_attachment->att_user->usr_sql_role_name; if (impure->vlu_desc.dsc_address != NULL) impure->vlu_desc.dsc_length = strlen(reinterpret_cast < const char *>(impure->vlu_desc.dsc_address)); else impure->vlu_desc.dsc_length = 0; return &impure->vlu_desc; case nod_extract: { DSC *value; struct tm times; USHORT part; GDS_TIMESTAMP timestamp; ULONG extract_part; impure = (VLU) ((SCHAR *) request + node->nod_impure); extract_part = (ULONG) node->nod_arg[e_extract_part]; value = EVL_expr(tdbb, node->nod_arg[e_extract_value]); impure->vlu_desc.dsc_dtype = dtype_short; impure->vlu_desc.dsc_scale = 0; impure->vlu_desc.dsc_address = reinterpret_cast(&impure->vlu_misc.vlu_short); impure->vlu_desc.dsc_length = sizeof(SSHORT); if (!value) { request->req_flags |= req_null; impure->vlu_misc.vlu_short = 0; return &impure->vlu_desc; } switch (value->dsc_dtype) { case dtype_sql_time: timestamp.timestamp_time = *(GDS_TIME *) value->dsc_address; timestamp.timestamp_date = 0; isc_decode_timestamp(×tamp, ×); if (extract_part != blr_extract_hour && extract_part != blr_extract_minute && extract_part != blr_extract_second) ERR_post(gds_expression_eval_err, 0); break; case dtype_sql_date: timestamp.timestamp_date = *(GDS_DATE *) value->dsc_address; timestamp.timestamp_time = 0; isc_decode_timestamp(×tamp, ×); if (extract_part == blr_extract_hour || extract_part == blr_extract_minute || extract_part == blr_extract_second) ERR_post(gds_expression_eval_err, 0); break; case dtype_timestamp: timestamp = *((GDS_TIMESTAMP *) value->dsc_address); isc_decode_timestamp(×tamp, ×); break; default: ERR_post(gds_expression_eval_err, 0); break; } switch (extract_part) { case blr_extract_year: part = times.tm_year + 1900; break; case blr_extract_month: part = times.tm_mon + 1; break; case blr_extract_day: part = times.tm_mday; break; case blr_extract_hour: part = times.tm_hour; break; case blr_extract_minute: part = times.tm_min; break; case blr_extract_second: impure->vlu_desc.dsc_dtype = dtype_long; impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE; impure->vlu_desc.dsc_address = reinterpret_cast < UCHAR * >(&impure->vlu_misc.vlu_long); impure->vlu_desc.dsc_length = sizeof(ULONG); *(ULONG *) impure->vlu_desc.dsc_address = times.tm_sec * ISC_TIME_SECONDS_PRECISION + (timestamp.timestamp_time % ISC_TIME_SECONDS_PRECISION); return &impure->vlu_desc; case blr_extract_yearday: part = times.tm_yday; break; case blr_extract_weekday: part = times.tm_wday; break; default: assert(FALSE); part = 0; } *(USHORT *) impure->vlu_desc.dsc_address = part; } return &impure->vlu_desc; 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: node = node->nod_arg[e_var_variable]; impure = (VLU) ((SCHAR *) request + node->nod_impure); if (impure->vlu_desc.dsc_flags & DSC_null) request->req_flags |= req_null; return &impure->vlu_desc; case nod_value_if: return EVL_expr(tdbb, (EVL_boolean(tdbb, node->nod_arg[0])) ? node->nod_arg[1] : node->nod_arg[2]); #ifdef PC_ENGINE case nod_crack: { RSB rsb; IRSB irsb; rsb = *(RSB *) node->nod_arg[1]; if (rsb->rsb_type == rsb_boolean) rsb = rsb->rsb_next; irsb = (IRSB) ((UCHAR *) request + rsb->rsb_impure); impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long; impure->vlu_desc.dsc_dtype = dtype_long; impure->vlu_desc.dsc_length = sizeof(ULONG); impure->vlu_desc.dsc_scale = 0; impure->vlu_misc.vlu_long = irsb->irsb_flags & (irsb_bof | irsb_eof | irsb_crack); return &impure->vlu_desc; } case nod_get_bookmark: { BKM bookmark; bookmark = RSE_get_bookmark(tdbb, *(RSB *) node->nod_arg[e_getmark_rsb]); return &bookmark->bkm_desc; } case nod_bookmark: { BKM bookmark; bookmark = BKM_lookup(node->nod_arg[e_bookmark_id]); return &bookmark->bkm_key_desc; } case nod_cardinality: impure->vlu_misc.vlu_long = (*(RSB *) node->nod_arg[e_card_rsb])->rsb_cardinality; impure->vlu_desc.dsc_dtype = dtype_long; impure->vlu_desc.dsc_length = sizeof(ULONG); impure->vlu_desc.dsc_scale = 0; impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long; return &impure->vlu_desc; #endif default: /* Shut up some compiler warnings */ break; } { /* Evaluate arguments */ DSC *values[3]; if (node->nod_count) { JRD_NOD *ptr, *end; DSC **v; for (ptr = node->nod_arg, end = ptr + node->nod_count, v = values; ptr < end;) { *v++ = EVL_expr(tdbb, *ptr++); if (request->req_flags & req_null) return NULL; } } switch (node->nod_type) { case nod_gen_id: /* return a 32-bit generator value */ impure->vlu_misc.vlu_long = (SLONG) DPM_gen_id(tdbb, (SLONG) node->nod_arg [e_gen_id], 0, MOV_get_int64 (values[0], 0)); impure->vlu_desc.dsc_dtype = dtype_long; impure->vlu_desc.dsc_length = sizeof(SLONG); impure->vlu_desc.dsc_scale = 0; impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long; return &impure->vlu_desc; case nod_gen_id2: impure->vlu_misc.vlu_int64 = DPM_gen_id(tdbb, (SLONG) node->nod_arg[e_gen_id], 0, MOV_get_int64(values[0], 0)); impure->vlu_desc.dsc_dtype = dtype_int64; impure->vlu_desc.dsc_length = sizeof(SINT64); impure->vlu_desc.dsc_scale = 0; impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_int64; return &impure->vlu_desc; case nod_negate: return negate_dsc(tdbb, values[0], impure); case nod_substr: return substring(tdbb, impure, values[0], (SSHORT) MOV_get_long(values[1], 0), (SSHORT) MOV_get_long(values[2], 0)); case nod_upcase: return upcase(tdbb, values[0], impure); 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; } BOOLEAN DLL_EXPORT EVL_field(JRD_REL relation, REC record, USHORT id, DSC * desc) { /************************************** * * E V L _ f i e l d * ************************************** * * Functional description * Evaluate a field by filling out a descriptor. * **************************************/ FMT format; DEV_BLKCHK(record, type_rec); if (!record) { ERR_warning(gds_no_cur_rec, 0); return FALSE; } if ( (format = record->rec_format) ) *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 */ JRD_FLD temp_field; /* 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) { TDBB tdbb = NULL; SET_TDBB(tdbb); MET_scan_relation(tdbb, relation); } if ( (temp_field = reinterpret_cast ((jrd_fld*)(*relation->rel_fields)[id])) ) if (temp_field->fld_default_value && temp_field->fld_not_null) { if (temp_field->fld_default_value->nod_type == nod_user_name || temp_field->fld_default_value->nod_type == nod_current_role) { desc->dsc_dtype = dtype_text; desc->dsc_sub_type = 0; desc->dsc_scale = 0; INTL_ASSIGN_TTYPE(desc, ttype_metadata); desc->dsc_address = (UCHAR *) relation->rel_owner_name; desc->dsc_length = strlen(reinterpret_cast < const char *>(desc->dsc_address)); return TRUE; } else if (temp_field->fld_default_value->nod_type == nod_current_date || temp_field->fld_default_value->nod_type == nod_current_time || temp_field->fld_default_value->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 < UCHAR * >(const_cast < ISC_TIMESTAMP * >(&temp_timestamp)); desc->dsc_length = sizeof(temp_timestamp); return TRUE; } else { LIT default_literal; DSC *default_desc = NULL; default_literal = reinterpret_cast < lit * >(temp_field->fld_default_value); if (default_literal->nod_type == nod_null) ERR_post(gds_not_valid, gds_arg_string, temp_field->fld_name, gds_arg_string, "*** null ***", 0); assert(default_literal->nod_type == nod_literal); default_desc = &default_literal->lit_desc; 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; return TRUE; } } else { 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; } } else { 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 + (int) desc->dsc_address; if (TEST_NULL(record, id)) { desc->dsc_flags |= DSC_null; return FALSE; } else { desc->dsc_flags &= ~DSC_null; return TRUE; } } USHORT DLL_EXPORT EVL_group(TDBB tdbb, BLK rsb, JRD_NOD 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) * **************************************/ JRD_REQ request; JRD_NOD group, map, *ptr, *end, from, field; DSC temp, *desc; VLUX impure; struct vlu vtemp; REC record; USHORT id; SLONG result; double d; SINT64 i; ASB asb; IASB asb_impure; UCHAR *data; SET_TDBB(tdbb); DEV_BLKCHK(node, type_nod); /* if we found the last record last time, we're all done */ if (state == 2) return 0; vtemp.vlu_string = NULL; request = tdbb->tdbb_request; map = node->nod_arg[e_agg_map]; group = node->nod_arg[e_agg_group]; /* Initialize the aggregate record */ for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++) { from = (*ptr)->nod_arg[e_asgn_from]; impure = (VLUX) ((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->vlu_desc.dsc_dtype = dtype_int64; impure->vlu_desc.dsc_length = sizeof(SINT64); impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = from->nod_scale; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_int64; impure->vlu_misc.vlu_int64 = 0; 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->vlu_desc.dsc_dtype = dtype_long; impure->vlu_desc.dsc_length = sizeof(SLONG); impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = 0; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long; impure->vlu_misc.vlu_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->vlu_desc.dsc_dtype = dtype_int64; impure->vlu_desc.dsc_length = sizeof(SINT64); impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = from->nod_scale; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_int64; impure->vlu_misc.vlu_int64 = 0; 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->vlu_desc.dsc_dtype = dtype_long; impure->vlu_desc.dsc_length = sizeof(SLONG); impure->vlu_desc.dsc_sub_type = 0; impure->vlu_desc.dsc_scale = 0; impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long; impure->vlu_misc.vlu_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_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, reinterpret_cast < struct Rsb *>(rsb)); #ifdef SCROLLABLE_CURSORS if (!RSE_get_record (tdbb, reinterpret_cast < struct Rsb *>(rsb), RSE_get_next)) #else if (!RSE_get_record (tdbb, reinterpret_cast < struct Rsb *>(rsb), RSE_get_forward)) #endif { if (group) return 0; state = 2; } } if (group) for (ptr = group->nod_arg, end = ptr + group->nod_count; ptr < end; ptr++) { from = *ptr; impure = (VLUX) ((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, reinterpret_cast < vlu * >(impure)); } /* Loop thru records until either a value change or EOF */ while (state != 2) { state = 1; /* 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++) { from = *ptr; impure = (VLUX) ((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, reinterpret_cast < vlu * >(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++) { from = (*ptr)->nod_arg[e_asgn_from]; impure = (VLUX) ((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; ++impure->vlux_count; if (!impure->vlu_desc.dsc_dtype) { EVL_make_value(tdbb, desc, reinterpret_cast < vlu * >(&impure->vlu_desc)); break; } result = MOV_compare(desc, reinterpret_cast < dsc * >(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, reinterpret_cast < vlu * >(impure)); /* 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; 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, reinterpret_cast < vlu * >(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, reinterpret_cast < vlu * >(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_count_distinct: case nod_agg_total_distinct: case nod_agg_average_distinct: case nod_agg_average_distinct2: case nod_agg_total_distinct2: desc = EVL_expr(tdbb, from->nod_arg[0]); if (request->req_flags & req_null) break; /* "Put" the value to sort. */ asb = (ASB) from->nod_arg[1]; asb_impure = (IASB) ((SCHAR *) request + asb->nod_impure); if (SORT_put(tdbb->tdbb_status_vector, reinterpret_cast < scb * >(asb_impure->iasb_sort_handle), reinterpret_cast < unsigned long **>(&data))) ERR_punt(); MOVE_CLEAR(data, ROUNDUP_LONG(asb->asb_key_desc->skd_length)); asb->asb_desc.dsc_address = data; MOV_move(desc, &asb->asb_desc); break; default: EXE_assignment(tdbb, *ptr); } } #ifdef SCROLLABLE_CURSORS if (!RSE_get_record(tdbb, rsb, RSE_get_next)) #else if (!RSE_get_record (tdbb, reinterpret_cast < struct Rsb *>(rsb), RSE_get_forward)) #endif state = 2; } break_out: /* Finish up any residual computations and get out */ if (vtemp.vlu_string) delete vtemp.vlu_string; for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++) { from = (*ptr)->nod_arg[e_asgn_from]; field = (*ptr)->nod_arg[e_asgn_to]; id = (USHORT) field->nod_arg[e_fld_id]; record = request->req_rpb[(int) field->nod_arg[e_fld_stream]].rpb_record; impure = (VLUX) ((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: if ((from->nod_type == nod_agg_total_distinct) || (from->nod_type == nod_agg_total_distinct2)) 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(&impure->vlu_desc, EVL_expr(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(&temp, EVL_expr(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(&temp, EVL_expr(tdbb, field)); break; default: /* Shut up some compiler warnings */ break; } } return state; } void DLL_EXPORT EVL_make_value(TDBB tdbb, DSC * desc, VLU 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. * **************************************/ DSC from; SET_TDBB(tdbb); /* Handle the fixed length data types first. They're easy. */ 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: #ifdef VMS case dtype_d_float: #endif 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; default: assert(FALSE); break; } { STR string; UCHAR temp[128], *address, *p; USHORT length; 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. */ length = MOV_get_string_ptr(&from, &ttype, &address, reinterpret_cast < vary * >(temp), sizeof(temp)); /* Allocate a string block of sufficient size. */ if ((string = value->vlu_string) && string->str_length < length) { delete string; string = NULL; } if (!string) { string = value->vlu_string = FB_NEW_RPT(*tdbb->tdbb_default, length) str(); string->str_length = length; } value->vlu_desc.dsc_dtype = dtype_text; value->vlu_desc.dsc_length = length; value->vlu_desc.dsc_address = p = string->str_data; value->vlu_desc.dsc_sub_type = 0; value->vlu_desc.dsc_scale = 0; INTL_ASSIGN_TTYPE(&value->vlu_desc, ttype); if (address && length) MOVE_FAST(address, p, length); } } USHORT DLL_EXPORT EVL_mb_contains(TDBB tdbb, class TextType* obj, UCHAR * p1, USHORT l1, UCHAR * p2, USHORT l2) { /************************************** * * E V L _ m b _ c o n t a i n s * ************************************** * * Functional description * **************************************/ USHORT buffer1[100], buffer2[100]; /* arbitrary size for optimization */ USHORT *pp1 = buffer1; USHORT *pp2 = buffer2; USHORT len1, len2; /* byte counts */ STR buf1, buf2; USHORT ret_val; SSHORT err_code; USHORT err_pos; SET_TDBB(tdbb); len1 = obj->to_wc(NULL, 0, p1, l1, &err_code, &err_pos); len2 = obj->to_wc(NULL, 0, p2, l2, &err_code, &err_pos); if (len1 > sizeof(buffer1)) { buf1 = FB_NEW_RPT(*tdbb->tdbb_default, len1) str(); pp1 = (USHORT *) buf1->str_data; } if (len2 > sizeof(buffer2)) { buf2 = FB_NEW_RPT(*tdbb->tdbb_default, len2) str(); pp2 = (USHORT *) buf2->str_data; } len1 = obj->to_wc((unsigned char*)pp1, len1, p1, l1, &err_code, &err_pos); len2 = obj->to_wc((unsigned char*)pp2, len2, p2, l2, &err_code, &err_pos); ret_val = EVL_wc_contains(tdbb, obj, pp1, len1, pp2, len2); if (pp1 != buffer1) delete buf1; if (pp2 != buffer2) delete buf2; return ret_val; } USHORT DLL_EXPORT EVL_mb_like(TDBB tdbb, class TextType* obj, UCHAR * p1, SSHORT l1, UCHAR * p2, SSHORT l2, WCHAR escape_char) { /************************************** * * E V L _ m b _ l i k e * ************************************** * * Functional description * Multi-Byte version of Like(). * Front-end of like() in Japanese version. * * Prepare buffer of short, then "copy" char-based data * into the new short-based buffer. Use the new buffer for * later processing with wc_like(). * **************************************/ USHORT buffer1[100], buffer2[100]; /* arbitrary size for optimization */ USHORT *pp1 = buffer1; USHORT *pp2 = buffer2; USHORT len1, len2; STR buf1, buf2; USHORT ret_val; SSHORT err_code; USHORT err_pos; SET_TDBB(tdbb); len1 = obj->to_wc(NULL, 0, p1, l1, &err_code, &err_pos); len2 = obj->to_wc(NULL, 0, p2, l2, &err_code, &err_pos); if (len1 > sizeof(buffer1)) { buf1 = FB_NEW_RPT(*tdbb->tdbb_default, len1) str(); pp1 = (USHORT *) buf1->str_data; } if (len2 > sizeof(buffer2)) { buf2 = FB_NEW_RPT(*tdbb->tdbb_default, len2) str(); pp2 = (USHORT *) buf2->str_data; } len1 = obj->to_wc((unsigned char*)pp1, len1, p1, l1, &err_code, &err_pos); len2 = obj->to_wc((unsigned char*)pp2, len2, p2, l2, &err_code, &err_pos); ret_val = EVL_wc_like(tdbb, obj, pp1, len1, pp2, len2, escape_char); if (pp1 != buffer1) delete buf1; if (pp2 != buffer2) delete buf2; return ret_val; } USHORT DLL_EXPORT EVL_mb_matches(TDBB tdbb, class TextType* obj, UCHAR * p1, SSHORT l1, UCHAR * p2, SSHORT l2) { /************************************** * * E V L _ m b _ m a t c h e s * ************************************** * * Functional description * Front-end of matches() in Japanese version. * Prepare buffer of short, then "copy" char-based data * into the new short-based buffer. Use the new buffer for * later processing with EVL_wc_matches(). * **************************************/ USHORT buffer1[100], buffer2[100]; /* arbitrary size for optimization */ USHORT *pp1 = buffer1; USHORT *pp2 = buffer2; USHORT len1, len2; STR buf1, buf2; USHORT ret_val; SSHORT err_code; USHORT err_pos; SET_TDBB(tdbb); len1 = obj->to_wc(NULL, 0, p1, l1, &err_code, &err_pos); len2 = obj->to_wc(NULL, 0, p2, l2, &err_code, &err_pos); if (len1 > sizeof(buffer1)) { buf1 = FB_NEW_RPT(*tdbb->tdbb_default, len1) str(); pp1 = (USHORT *) buf1->str_data; } if (len2 > sizeof(buffer2)) { buf2 = FB_NEW_RPT(*tdbb->tdbb_default, len2) str(); pp2 = (USHORT *) buf2->str_data; } len1 = obj->to_wc((unsigned char*)pp1, len1, p1, l1, &err_code, &err_pos); len2 = obj->to_wc((unsigned char*)pp2, len2, p2, l2, &err_code, &err_pos); ret_val = EVL_wc_matches(tdbb, obj, pp1, len1, pp2, len2); if (pp1 != buffer1) delete buf1; if (pp2 != buffer2) delete buf2; return ret_val; } USHORT DLL_EXPORT EVL_mb_sleuth_check(TDBB tdbb, class TextType* obj, USHORT flags, UCHAR * search, USHORT search_bytes, UCHAR * match, USHORT match_bytes) { /************************************** * * E V L _ m b _ s l e u t h _ c h e c k * ************************************** * * Functional description * Front-end of sleuth_check() in Japanese version. * Prepare buffer of short, then "copy" char-based data * into the new short-based buffer. Use the new buffer for * later processing with sleuth_check(). * **************************************/ USHORT buffer1[100]; /* arbitrary size for optimization */ USHORT *pp1 = buffer1; USHORT len1; STR buf1; USHORT ret_val; SSHORT err_code; USHORT err_pos; assert(search != NULL); assert(match != NULL); /* Note: search_merge has already converted the match string to wide character (see note in sleuth()) */ SET_TDBB(tdbb); len1 = obj->to_wc(NULL, 0, search, search_bytes, &err_code, &err_pos); if (len1 > sizeof(buffer1)) { buf1 = FB_NEW_RPT(*tdbb->tdbb_default, len1) str(); pp1 = (USHORT *) buf1->str_data; } len1 = obj->to_wc((unsigned char*)pp1, len1, search, search_bytes, &err_code, &err_pos); ret_val = EVL_wc_sleuth_check(tdbb, obj, 0, pp1, len1, reinterpret_cast < USHORT * >(match), match_bytes); if (pp1 != buffer1) delete buf1; return ret_val; } USHORT DLL_EXPORT EVL_mb_sleuth_merge(TDBB tdbb, class TextType* obj, UCHAR * match, USHORT match_bytes, UCHAR * control, USHORT control_bytes, UCHAR * combined, USHORT combined_bytes) { /************************************** * * E V L _ m b _ s l e u t h _ m e r g e * ************************************** * * Functional description * Front-end of sleuth_merge() in Japanese version. * **************************************/ USHORT buffer1[100], buffer2[100]; /* arbitrary size for optimization */ USHORT *pp1 = buffer1; USHORT *pp2 = buffer2; USHORT len1, len2; STR buf1, buf2; USHORT ret_val; SSHORT err_code; USHORT err_pos; assert(control != NULL); assert(match != NULL); assert(combined != NULL); SET_TDBB(tdbb); len1 = obj->to_wc(NULL, 0, match, match_bytes, &err_code, &err_pos); len2 = obj->to_wc(NULL, 0, control, control_bytes, &err_code, &err_pos); if (len1 > sizeof(buffer1)) { buf1 = FB_NEW_RPT(*tdbb->tdbb_default, len1) str(); pp1 = (USHORT *) buf1->str_data; } if (len2 > sizeof(buffer2)) { buf2 = FB_NEW_RPT(*tdbb->tdbb_default, len2) str(); pp2 = (USHORT *) buf2->str_data; } len1 = obj->to_wc((unsigned char*)pp1, len1, match, match_bytes, &err_code, &err_pos); len2 = obj->to_wc((unsigned char*)pp2, len2, control, control_bytes, &err_code, &err_pos); ret_val = EVL_wc_sleuth_merge(tdbb, obj, pp1, len1, pp2, len2, reinterpret_cast < USHORT * >(combined), combined_bytes); if (pp1 != buffer1) delete buf1; if (pp2 != buffer2) delete buf2; return ret_val; } USHORT DLL_EXPORT EVL_nc_contains(TDBB tdbb_dummy, class TextType* obj, UCHAR * p1, USHORT l1, UCHAR * p2, USHORT l2) { /************************************** * * E V L _ n c _ c o n t a i n s * ************************************** * * Functional description * **************************************/ UCHAR *q1, *q2, c1, c2; SSHORT l; while (l1 >= l2) { --l1; q1 = p1++; q2 = p2; l = l2; do { if (--l < 0) return TRUE; c1 = *q1++; c2 = *q2++; } while (obj->to_upper(c1) == obj->to_upper(c2)); } return FALSE; } /************************************** * * E V L _ n c _ l i k e * ************************************** */ /************************************** * * E V L _ n c _ m a t c h e s * ************************************** */ /************************************** * * E V L _ n c _ s l e u t h _ c h e c k * ************************************** */ #define LIKENAME EVL_nc_like #define LIKETYPE UCHAR #define MATCHESNAME EVL_nc_matches #define MATCHESTYPE UCHAR #define SLEUTHNAME EVL_nc_sleuth_check #define SLEUTH_MERGE_NAME EVL_nc_sleuth_merge #define SLEUTH_AUX nc_sleuth_check #define SLEUTH_CLASS_NAME nc_sleuth_class #define SLEUTHTYPE UCHAR #define EVL_LIKE_INCLUDED_BY_EVL_CPP #include "../jrd/evl_like.cpp" #undef EVL_LIKE_INCLUDED_BY_EVL_CPP #undef LIKENAME #undef LIKETYPE #undef MATCHESNAME #undef MATCHESTYPE #undef SLEUTHNAME #undef SLEUTH_MERGE_NAME #undef SLEUTH_AUX #undef SLEUTH_CLASS_NAME #undef SLEUTHTYPE USHORT DLL_EXPORT EVL_wc_contains(TDBB tdbb_dumm, class TextType* obj, WCHAR * p1, USHORT l1, /* byte count */ WCHAR * p2, USHORT l2) { /************************************** * * E V L _ w c _ c o n t a i n s * ************************************** * * Functional description * **************************************/ WCHAR *q1, *q2, c1, c2; SSHORT l; while (l1 >= l2) { l1 -= 2; q1 = p1++; q2 = p2; l = l2; do { l -= 2; if (l < 0) return TRUE; c1 = *q1++; c2 = *q2++; } while (obj->to_upper(c1) == obj->to_upper(c2)); } return FALSE; } /************************************** * * E V L _ w c _ l i k e * ************************************** */ /************************************** * * E V L _ w c _ m a t c h e s * ************************************** */ /************************************** * * E V L _ w c _ s l e u t h _ c h e c k * ************************************** */ #define LIKENAME EVL_wc_like #define LIKETYPE WCHAR #define MATCHESNAME EVL_wc_matches #define MATCHESTYPE WCHAR #define SLEUTHNAME EVL_wc_sleuth_check #define SLEUTH_MERGE_NAME EVL_wc_sleuth_merge #define SLEUTH_AUX wc_sleuth_check #define SLEUTH_CLASS_NAME wc_sleuth_class #define SLEUTHTYPE WCHAR #define EVL_LIKE_INCLUDED_BY_EVL_CPP #include "../jrd/evl_like.cpp" #undef EVL_LIKE_INCLUDED_BY_EVL_CPP #undef LIKENAME #undef LIKETYPE #undef MATCHESNAME #undef MATCHESTYPE #undef SLEUTHNAME #undef SLEUTH_MERGE_NAME #undef SLEUTH_AUX #undef SLEUTH_CLASS_NAME #undef SLEUTHTYPE static DSC *add(DSC * desc, JRD_NOD node, VLU 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?) * **************************************/ DSC *result; double d1, d2; SLONG l1, l2; DEV_BLKCHK(node, type_nod); 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); 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) { d1 = MOV_get_double(desc); 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) { SQUAD q1, q2; q1 = MOV_get_quad(desc, node->nod_scale); 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, (FPTR_VOID) ERR_post) : QUAD_ADD(q1, q2, (FPTR_VOID) ERR_post); result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad; return result; } /* Everything else defaults to longword */ l1 = MOV_get_long(desc, node->nod_scale); l2 = MOV_get_long(&value->vlu_desc, node->nod_scale); result->dsc_dtype = dtype_long; result->dsc_length = sizeof(SLONG); result->dsc_scale = node->nod_scale; value->vlu_misc.vlu_long = (node->nod_type == nod_subtract) ? l2 - l1 : l1 + l2; result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_long; result->dsc_sub_type = 0; return result; } static DSC *add2(DSC * desc, JRD_NOD node, VLU 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 * **************************************/ DSC *result; double d1, d2; SINT64 i1, i2; DEV_BLKCHK(node, type_nod); 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); 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) { d1 = MOV_get_double(desc); 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) { SQUAD q1, q2; q1 = MOV_get_quad(desc, node->nod_scale); 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, (FPTR_VOID) ERR_post) : QUAD_ADD(q1, q2, (FPTR_VOID) ERR_post); result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad; return result; } /* Everything else defaults to int64 */ i1 = MOV_get_int64(desc, node->nod_scale); 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(isc_exception_integer_overflow, 0); return result; } static DSC *add_datetime(DSC * desc, JRD_NOD node, VLU 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? */ 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 { 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: