2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: JRD Access Method
|
2003-10-08 10:42:48 +02:00
|
|
|
* MODULE: evl.cpp
|
2001-05-23 15:26:42 +02:00
|
|
|
* 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
|
2005-05-28 00:45:31 +02:00
|
|
|
* In the EVL_group processing, the internal assigment for
|
|
|
|
* the literal in the computation is being done on every
|
2001-05-23 15:26:42 +02:00
|
|
|
* 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.
|
2002-06-30 11:58:20 +02:00
|
|
|
*
|
2001-05-23 15:26:42 +02:00
|
|
|
* 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.
|
2002-06-30 11:58:20 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2005-05-28 00:45:31 +02:00
|
|
|
* 2002.04.16 Paul Beach HP10 Port - (UCHAR*) desc.dsc_address = p; modified for HP
|
2002-06-30 11:58:20 +02:00
|
|
|
* Compiler
|
2002-09-28 16:04:35 +02:00
|
|
|
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
|
|
|
|
* exception handling in SPs/triggers,
|
|
|
|
* implemented ROWS_AFFECTED system variable
|
2003-08-12 11:56:50 +02:00
|
|
|
* 2003.08.10 Claudio Valderrama: Fix SF bug# 784121.
|
2007-04-13 03:37:44 +02:00
|
|
|
* Adriano dos Santos Fernandes
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
|
2001-07-29 19:42:23 +02:00
|
|
|
#include "firebird.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include <string.h>
|
2004-04-09 00:29:50 +02:00
|
|
|
#include <math.h>
|
2004-03-22 12:38:23 +01:00
|
|
|
#include "../jrd/common.h"
|
2004-04-29 19:48:39 +02:00
|
|
|
#include "../jrd/ibase.h"
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/val.h"
|
|
|
|
#include "../jrd/req.h"
|
|
|
|
#include "../jrd/exe.h"
|
|
|
|
#include "../jrd/sbm.h"
|
|
|
|
#include "../jrd/blb.h"
|
2003-11-11 13:19:20 +01:00
|
|
|
#include "gen/iberror.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/scl.h"
|
|
|
|
#include "../jrd/lck.h"
|
|
|
|
#include "../jrd/lls.h"
|
|
|
|
#include "../jrd/intl.h"
|
2002-06-04 21:56:16 +02:00
|
|
|
#include "../jrd/intl_classes.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/rse.h"
|
|
|
|
#include "../jrd/quad.h"
|
|
|
|
#include "../jrd/sort.h"
|
|
|
|
#include "../jrd/blr.h"
|
|
|
|
#include "../jrd/tra.h"
|
|
|
|
#include "../jrd/gdsassert.h"
|
2008-01-16 09:54:50 +01:00
|
|
|
#include "../common/classes/auto.h"
|
2006-02-03 09:39:36 +01:00
|
|
|
#include "../common/classes/timestamp.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/blb_proto.h"
|
|
|
|
#include "../jrd/btr_proto.h"
|
|
|
|
#include "../jrd/cvt_proto.h"
|
2006-08-10 04:53:16 +02:00
|
|
|
#include "../jrd/DataTypeUtil.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#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"
|
2002-06-30 11:58:20 +02:00
|
|
|
#include "../jrd/met_proto.h"
|
|
|
|
#include "../jrd/cvt_proto.h"
|
2002-09-28 16:04:35 +02:00
|
|
|
#include "../jrd/misc_func_ids.h"
|
2003-02-21 10:17:03 +01:00
|
|
|
#include "../common/config/config.h"
|
2007-04-12 17:56:34 +02:00
|
|
|
#include "../jrd/SysFunction.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-03 23:43:56 +02:00
|
|
|
const int TEMP_LENGTH = 128;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-07 09:57:46 +02:00
|
|
|
const SINT64 MAX_INT64_LIMIT = MAX_SINT64 / 10;
|
|
|
|
const SINT64 MIN_INT64_LIMIT = MIN_SINT64 / 10;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* *** 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.
|
|
|
|
*/
|
|
|
|
|
2004-03-20 15:57:40 +01:00
|
|
|
using namespace Jrd;
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
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*);
|
2005-05-28 00:45:31 +02:00
|
|
|
static void adjust_text_descriptor(thread_db*, dsc*);
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* binary_value(thread_db*, const jrd_nod*, impure_value*);
|
2006-08-13 08:47:52 +02:00
|
|
|
static dsc* cast(thread_db*, dsc*, const jrd_nod*, impure_value*);
|
2004-03-11 06:04:26 +01:00
|
|
|
static void compute_agg_distinct(thread_db*, jrd_nod*);
|
2006-04-25 18:22:41 +02:00
|
|
|
static dsc* concatenate(thread_db*, const dsc*, const dsc*, impure_value*);
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* dbkey(thread_db*, const jrd_nod*, impure_value*);
|
|
|
|
static dsc* eval_statistical(thread_db*, jrd_nod*, impure_value*);
|
2005-06-11 02:14:55 +02:00
|
|
|
static dsc* extract(thread_db*, jrd_nod*, impure_value*);
|
2004-10-08 13:10:58 +02:00
|
|
|
static void fini_agg_distinct(thread_db* tdbb, const jrd_nod *const);
|
2003-12-11 11:33:30 +01:00
|
|
|
static SINT64 get_day_fraction(const dsc* d);
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* get_mask(thread_db*, jrd_nod*, impure_value*);
|
2003-12-11 11:33:30 +01:00
|
|
|
static SINT64 get_timestamp_to_isc_ticks(const dsc* d);
|
2004-03-11 06:04:26 +01:00
|
|
|
static void init_agg_distinct(thread_db*, const jrd_nod*);
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* lock_state(thread_db*, jrd_nod*, impure_value*);
|
2006-04-16 20:45:40 +02:00
|
|
|
static dsc* low_up_case(thread_db*, const dsc*, impure_value*,
|
2008-01-16 09:54:50 +01:00
|
|
|
ULONG (TextType::*tt_str_to_case)(ULONG, const UCHAR*, ULONG, UCHAR*));
|
2004-03-28 11:10:30 +02:00
|
|
|
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*);
|
2003-12-11 11:33:30 +01:00
|
|
|
static bool reject_duplicate(const UCHAR*, const UCHAR*, void*);
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* scalar(thread_db*, jrd_nod*, impure_value*);
|
2004-03-19 07:14:53 +01:00
|
|
|
static bool sleuth(thread_db*, jrd_nod*, const dsc*, const dsc*);
|
|
|
|
static bool string_boolean(thread_db*, jrd_nod*, dsc*, dsc*, bool);
|
2005-05-28 00:45:31 +02:00
|
|
|
static bool string_function(thread_db*, jrd_nod*, SLONG, const UCHAR*, SLONG, const UCHAR*, USHORT, bool);
|
2005-06-11 02:14:55 +02:00
|
|
|
static dsc* string_length(thread_db*, jrd_nod*, impure_value*);
|
2006-08-16 02:09:27 +02:00
|
|
|
static dsc* substring(thread_db*, impure_value*, dsc*, const dsc*, const dsc*);
|
2005-05-28 00:45:31 +02:00
|
|
|
static dsc* trim(thread_db*, jrd_nod*, impure_value*);
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* internal_info(thread_db*, const dsc*, impure_value*);
|
2002-06-14 14:09:37 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-11-30 07:18:39 +01:00
|
|
|
const SINT64 SECONDS_PER_DAY = 24 * 60 * 60;
|
2004-05-07 09:57:46 +02:00
|
|
|
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;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
#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
|
|
|
|
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
dsc* EVL_assign_to(thread_db* tdbb, jrd_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* E V L _ a s s i g n _ t o
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Evaluate the descriptor of the
|
|
|
|
* destination node of an assignment.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-22 11:00:59 +01:00
|
|
|
dsc* desc;
|
2004-03-30 06:10:52 +02:00
|
|
|
Format* format;
|
2004-01-13 10:52:19 +01:00
|
|
|
jrd_nod* message;
|
2004-03-18 06:56:06 +01:00
|
|
|
Record* record;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-01-15 13:08:59 +01:00
|
|
|
/* The only nodes that can be assigned to are: argument, field and variable. */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-23 21:17:30 +01:00
|
|
|
int arg_number;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
switch (node->nod_type) {
|
|
|
|
case nod_argument:
|
|
|
|
message = node->nod_arg[e_arg_message];
|
2004-03-30 06:10:52 +02:00
|
|
|
format = (Format*) message->nod_arg[e_msg_format];
|
2008-01-16 09:54:50 +01:00
|
|
|
arg_number = (int) (IPTR) node->nod_arg[e_arg_number];
|
2003-11-23 21:17:30 +01:00
|
|
|
desc = &format->fmt_desc[arg_number];
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_desc.dsc_address =
|
2004-01-21 08:18:30 +01:00
|
|
|
(UCHAR *) request + message->nod_impure + (IPTR) desc->dsc_address;
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2003-10-21 00:41:11 +02:00
|
|
|
if (DTYPE_IS_TEXT(desc->dsc_dtype) &&
|
2001-05-23 15:26:42 +02:00
|
|
|
((INTL_TTYPE(desc) == ttype_dynamic) ||
|
2004-02-24 06:34:44 +01:00
|
|
|
(INTL_GET_CHARSET(desc) == CS_dynamic)))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Value is a text value, we're assigning it back to the user
|
|
|
|
process, user process has not specified a subtype, user
|
2003-12-22 11:00:59 +01:00
|
|
|
process specified dynamic translation and the dsc isn't from
|
2001-05-23 15:26:42 +02:00
|
|
|
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,
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->getAttachment()->att_charset, COLLATE_NONE);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
|
|
|
case nod_field:
|
|
|
|
record =
|
2003-12-03 09:19:24 +01:00
|
|
|
request->req_rpb[(int) (IPTR) node->nod_arg[e_fld_stream]].rpb_record;
|
2006-03-28 11:55:45 +02:00
|
|
|
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(isc_field_disappeared, 0);
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!impure->vlu_desc.dsc_address)
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_post(isc_read_only_field, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
|
|
|
case nod_null:
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
case nod_variable:
|
2003-11-23 21:17:30 +01:00
|
|
|
// Calculate descriptor
|
2001-05-23 15:26:42 +02:00
|
|
|
node = node->nod_arg[e_var_variable];
|
2004-03-28 11:10:30 +02:00
|
|
|
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BUGCHECK(229); /* msg 229 EVL_assign_to: invalid operation */
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-27 22:01:02 +01:00
|
|
|
RecordBitmap** EVL_bitmap(thread_db* tdbb, jrd_nod* node, RecordBitmap* bitmap_and)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* E V L _ b i t m a p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Evaluate bitmap valued expression.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2004-10-03 14:10:19 +02:00
|
|
|
if (--tdbb->tdbb_quantum < 0)
|
|
|
|
JRD_reschedule(tdbb, 0, true);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
switch (node->nod_type) {
|
|
|
|
case nod_bit_and:
|
2006-12-27 22:01:02 +01:00
|
|
|
{
|
|
|
|
RecordBitmap** bitmap = EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and);
|
|
|
|
if (!(*bitmap) || !(*bitmap)->getFirst())
|
|
|
|
return bitmap;
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
return EVL_bitmap(tdbb, node->nod_arg[1], *bitmap);
|
2006-12-27 22:01:02 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_bit_or:
|
2004-09-28 08:28:38 +02:00
|
|
|
return RecordBitmap::bit_or(
|
2006-12-27 22:01:02 +01:00
|
|
|
EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and),
|
|
|
|
EVL_bitmap(tdbb, node->nod_arg[1], bitmap_and));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-08-17 19:52:19 +02:00
|
|
|
case nod_bit_in:
|
|
|
|
{
|
2006-12-27 22:01:02 +01:00
|
|
|
RecordBitmap** inv_bitmap = EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and);
|
2004-08-17 19:52:19 +02:00
|
|
|
BTR_evaluate(tdbb,
|
|
|
|
reinterpret_cast<IndexRetrieval*>(node->nod_arg[1]->nod_arg[e_idx_retrieval]),
|
2006-12-27 22:01:02 +01:00
|
|
|
inv_bitmap, bitmap_and);
|
2004-08-17 19:52:19 +02:00
|
|
|
return inv_bitmap;
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_bit_dbkey:
|
|
|
|
{
|
2007-12-05 01:03:15 +01:00
|
|
|
impure_inversion* impure = (impure_inversion*) ((SCHAR*) tdbb->getRequest() + node->nod_impure);
|
2004-09-28 08:28:38 +02:00
|
|
|
RecordBitmap::reset(impure->inv_bitmap);
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* desc = EVL_expr(tdbb, node->nod_arg[0]);
|
2007-07-02 01:13:09 +02:00
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
if (!(tdbb->getRequest()->req_flags & req_null) &&
|
2007-07-02 01:13:09 +02:00
|
|
|
desc->dsc_length == sizeof(RecordNumber::Packed))
|
|
|
|
{
|
|
|
|
const USHORT id = (USHORT)(IPTR) node->nod_arg[1];
|
|
|
|
RecordNumber::Packed* numbers = reinterpret_cast<RecordNumber::Packed*>(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());
|
|
|
|
}
|
|
|
|
|
2004-10-29 01:26:16 +02:00
|
|
|
return &impure->inv_bitmap;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case nod_index:
|
|
|
|
{
|
2007-12-05 01:03:15 +01:00
|
|
|
impure_inversion* impure = (impure_inversion*) ((SCHAR*) tdbb->getRequest() + node->nod_impure);
|
2004-09-28 08:28:38 +02:00
|
|
|
RecordBitmap::reset(impure->inv_bitmap);
|
2001-05-23 15:26:42 +02:00
|
|
|
BTR_evaluate(tdbb,
|
2004-03-19 07:14:53 +01:00
|
|
|
reinterpret_cast<IndexRetrieval*>(node->nod_arg[e_idx_retrieval]),
|
2006-12-27 22:01:02 +01:00
|
|
|
&impure->inv_bitmap, bitmap_and);
|
2004-10-29 01:26:16 +02:00
|
|
|
return &impure->inv_bitmap;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
BUGCHECK(230); /* msg 230 EVL_bitmap: invalid operation */
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-19 07:14:53 +01:00
|
|
|
bool EVL_boolean(thread_db* tdbb, jrd_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* E V L _ b o o l e a n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Evaluate a boolean.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-22 11:00:59 +01:00
|
|
|
dsc* desc[2];
|
2004-03-19 07:14:53 +01:00
|
|
|
bool value;
|
2001-05-23 15:26:42 +02:00
|
|
|
SSHORT comparison;
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value* impure;
|
2003-12-27 05:37:23 +01:00
|
|
|
bool computed_invariant = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* Handle and pre-processing possible for various nodes. This includes
|
|
|
|
evaluating argument and checking NULL flags */
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2003-12-22 18:51:43 +01:00
|
|
|
jrd_nod** ptr = node->nod_arg;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_contains:
|
|
|
|
case nod_starts:
|
|
|
|
case nod_matches:
|
|
|
|
case nod_like:
|
2004-10-14 21:09:19 +02:00
|
|
|
case nod_equiv:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_eql:
|
|
|
|
case nod_neq:
|
|
|
|
case nod_gtr:
|
|
|
|
case nod_geq:
|
|
|
|
case nod_lss:
|
|
|
|
case nod_leq:
|
|
|
|
case nod_between:
|
2008-01-16 09:54:50 +01:00
|
|
|
case nod_similar:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_sleuth:
|
|
|
|
{
|
|
|
|
request->req_flags &= ~req_same_tx_upd;
|
2003-12-11 11:33:30 +01:00
|
|
|
SSHORT force_equal = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
const jrd_nod* rec_version = *ptr;
|
2001-05-23 15:26:42 +02:00
|
|
|
desc[0] = EVL_expr(tdbb, *ptr++);
|
2003-12-11 11:33:30 +01:00
|
|
|
const ULONG flags = request->req_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
force_equal |= request->req_flags & req_same_tx_upd;
|
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
// Currently only nod_like, nod_contains, nod_starts and nod_similar may be marked invariant
|
2003-12-27 05:37:23 +01:00
|
|
|
if (node->nod_flags & nod_invariant) {
|
2004-03-28 11:10:30 +02:00
|
|
|
impure = reinterpret_cast<impure_value*>((SCHAR *)request + node->nod_impure);
|
2004-04-25 04:30:32 +02:00
|
|
|
|
|
|
|
// Check that data type of operand is still the same.
|
|
|
|
// It may change due to multiple formats present in stream
|
2004-05-09 07:48:33 +02:00
|
|
|
// System tables are the good example of such streams -
|
2004-04-25 04:30:32 +02:00
|
|
|
// data coming from ini.epp has ASCII ttype, user data is UNICODE_FSS
|
2004-07-21 00:56:32 +02:00
|
|
|
//
|
|
|
|
// Note that value descriptor may be NULL pointer if value is SQL NULL
|
|
|
|
if ((impure->vlu_flags & VLU_computed) && desc[0] &&
|
2004-04-25 04:30:32 +02:00
|
|
|
(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;
|
|
|
|
}
|
|
|
|
|
2003-12-27 05:37:23 +01:00
|
|
|
if (impure->vlu_flags & VLU_computed) {
|
|
|
|
if (impure->vlu_flags & VLU_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
else
|
|
|
|
computed_invariant = true;
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2003-12-31 17:09:07 +01:00
|
|
|
else {
|
2003-12-27 05:37:23 +01:00
|
|
|
desc[1] = EVL_expr(tdbb, *ptr++);
|
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
impure->vlu_flags |= VLU_computed;
|
|
|
|
impure->vlu_flags |= VLU_null;
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2004-04-25 04:30:32 +02:00
|
|
|
else {
|
2003-12-27 05:37:23 +01:00
|
|
|
impure->vlu_flags &= ~VLU_null;
|
2004-04-25 04:30:32 +02:00
|
|
|
|
|
|
|
// Search object depends on operand data type.
|
|
|
|
// Thus save data type which we use to compute invariant
|
2004-07-26 23:32:41 +02:00
|
|
|
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;
|
2006-04-06 10:18:53 +02:00
|
|
|
}
|
|
|
|
else {
|
2004-07-26 23:32:41 +02:00
|
|
|
// 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;
|
|
|
|
}
|
2004-04-25 04:30:32 +02:00
|
|
|
}
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
|
|
|
else
|
2003-12-27 05:37:23 +01:00
|
|
|
desc[1] = EVL_expr(tdbb, *ptr++);
|
2004-10-14 21:09:19 +02:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2006-09-09 10:32:32 +02:00
|
|
|
|
|
|
|
if ((flags & req_null) || (request->req_flags & req_null))
|
2004-10-14 21:09:19 +02:00
|
|
|
{
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// If either of expressions above returned NULL set req_null flag
|
2004-03-19 07:14:53 +01:00
|
|
|
// and return false
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (flags & req_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
2004-03-19 07:14:53 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
force_equal |= request->req_flags & req_same_tx_upd;
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_flags & nod_comparison) {
|
2001-05-23 15:26:42 +02:00
|
|
|
comparison = MOV_compare(desc[0], desc[1]);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* If we are checking equality of record_version
|
|
|
|
* and same transaction updated the record,
|
|
|
|
* force equality.
|
|
|
|
*/
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if ((rec_version->nod_type == nod_rec_version) && (force_equal)) {
|
2001-05-23 15:26:42 +02:00
|
|
|
comparison = 0;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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:
|
2003-02-13 18:28:38 +01:00
|
|
|
if ((*ptr)->nod_type == nod_ansi_any ||
|
|
|
|
(*ptr)->nod_type == nod_ansi_all)
|
|
|
|
{
|
|
|
|
request->req_flags |= req_ansi_not;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
value = EVL_boolean(tdbb, *ptr++);
|
2003-02-13 18:28:38 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* Shut up some compiler warnings */
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Evaluate node */
|
|
|
|
|
2003-02-21 10:17:03 +01:00
|
|
|
// 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();
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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 */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
const USHORT firstnull = request->req_flags & req_null;
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
2003-02-21 10:17:03 +01:00
|
|
|
|
|
|
|
if (!bEvalCompleteExpression && !value && !firstnull)
|
|
|
|
{
|
|
|
|
// First term is FALSE, why the whole expression is false.
|
|
|
|
// NULL flag is already turned off a few lines above.
|
2004-03-19 07:14:53 +01:00
|
|
|
return false;
|
2003-02-21 10:17:03 +01:00
|
|
|
}
|
|
|
|
|
2004-03-19 07:14:53 +01:00
|
|
|
const bool value2 = EVL_boolean(tdbb, *ptr);
|
2003-02-13 18:28:38 +01:00
|
|
|
const USHORT secondnull = request->req_flags & req_null;
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if ((!value && !firstnull) || (!value2 && !secondnull)) {
|
2004-03-19 07:14:53 +01:00
|
|
|
return false; /* at least one operand was FALSE */
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2006-09-09 10:32:32 +02:00
|
|
|
|
|
|
|
if (value && value2) {
|
2004-03-19 07:14:53 +01:00
|
|
|
return true; /* both true */
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags |= req_null;
|
2004-03-19 07:14:53 +01:00
|
|
|
return false; /* otherwise, return null */
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case nod_any:
|
|
|
|
case nod_ansi_any:
|
|
|
|
case nod_ansi_all:
|
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
USHORT* invariant_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2004-03-28 11:10:30 +02:00
|
|
|
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2002-11-24 14:47:17 +01:00
|
|
|
invariant_flags = & impure->vlu_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*invariant_flags & VLU_computed) {
|
|
|
|
/* An invariant node has already been computed. */
|
|
|
|
|
|
|
|
if ((node->nod_type == nod_ansi_any) &&
|
|
|
|
(*invariant_flags & VLU_null))
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags |= req_null;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2004-03-19 07:14:53 +01:00
|
|
|
return impure->vlu_misc.vlu_short != 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for ansi ANY clauses (and ALL's, which are negated ANY's)
|
|
|
|
the unoptimized boolean expression must be used, since the
|
2003-12-11 11:33:30 +01:00
|
|
|
processing of these clauses is order dependant (see rse.cpp) */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
RecordSource* select = (RecordSource*) (node->nod_arg[e_any_rsb]);
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_type != nod_any)
|
|
|
|
{
|
2006-09-12 13:36:18 +02:00
|
|
|
select->rsb_any_boolean =
|
|
|
|
((RecordSelExpr*) (node->nod_arg[e_any_rse]))->rse_boolean;
|
|
|
|
if (node->nod_type == nod_ansi_any) {
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags |= req_ansi_any;
|
2006-09-12 13:36:18 +02:00
|
|
|
}
|
|
|
|
else {
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags |= req_ansi_all;
|
2006-09-12 13:36:18 +02:00
|
|
|
// 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);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-11-04 12:35:51 +01:00
|
|
|
RSE_open(tdbb, select);
|
|
|
|
value = RSE_get_record(tdbb, select, g_RSE_get_mode);
|
|
|
|
RSE_close(tdbb, select);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (node->nod_type == nod_any)
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
|
|
|
/* If this is an invariant node, save the return value. */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
*invariant_flags |= VLU_computed;
|
|
|
|
if ((node->nod_type != nod_any) &&
|
2003-02-13 18:28:38 +01:00
|
|
|
(request->req_flags & req_null))
|
|
|
|
{
|
|
|
|
*invariant_flags |= VLU_null;
|
|
|
|
}
|
2004-03-19 07:14:53 +01:00
|
|
|
impure->vlu_misc.vlu_short = value ? TRUE : FALSE;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
case nod_contains:
|
|
|
|
case nod_starts:
|
|
|
|
case nod_matches:
|
|
|
|
case nod_like:
|
2008-01-16 09:54:50 +01:00
|
|
|
case nod_similar:
|
2003-12-27 05:37:23 +01:00
|
|
|
return string_boolean(tdbb, node, desc[0], desc[1], computed_invariant);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_sleuth:
|
|
|
|
return sleuth(tdbb, node, desc[0], desc[1]);
|
|
|
|
|
|
|
|
case nod_missing:
|
|
|
|
EVL_expr(tdbb, *ptr);
|
|
|
|
if (request->req_flags & req_null) {
|
2004-03-19 07:14:53 +01:00
|
|
|
value = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
}
|
|
|
|
else {
|
2005-03-11 09:34:20 +01:00
|
|
|
value = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
|
|
|
|
case nod_not:
|
|
|
|
if (request->req_flags & req_null)
|
2004-03-19 07:14:53 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
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 */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
const ULONG flags = request->req_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
2003-02-21 10:17:03 +01:00
|
|
|
|
|
|
|
if (!bEvalCompleteExpression && value)
|
|
|
|
{
|
|
|
|
// First term is TRUE, why the whole expression is true.
|
|
|
|
// NULL flag is already turned off a few lines above.
|
2004-03-19 07:14:53 +01:00
|
|
|
return true;
|
2003-02-21 10:17:03 +01:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2004-03-19 07:14:53 +01:00
|
|
|
const bool value2 = EVL_boolean(tdbb, *ptr);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (value || value2) {
|
|
|
|
request->req_flags &= ~req_null;
|
2004-03-19 07:14:53 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* restore saved NULL state */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (flags & req_null) {
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags |= req_null;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2004-03-19 07:14:53 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case nod_unique:
|
|
|
|
{
|
2003-02-13 18:28:38 +01:00
|
|
|
USHORT* invariant_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2004-03-28 11:10:30 +02:00
|
|
|
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2002-11-24 14:47:17 +01:00
|
|
|
invariant_flags = & impure->vlu_flags;
|
2003-02-13 18:28:38 +01:00
|
|
|
if (*invariant_flags & VLU_computed)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/* An invariant node has already been computed. */
|
|
|
|
|
2006-12-30 08:22:46 +01:00
|
|
|
request->req_flags &= ~req_null;
|
2004-03-19 07:14:53 +01:00
|
|
|
return impure->vlu_misc.vlu_short != 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-28 07:42:58 +01:00
|
|
|
RecordSource* urs = reinterpret_cast<RecordSource*>(node->nod_arg[e_any_rsb]);
|
|
|
|
RSE_open(tdbb, urs);
|
|
|
|
value = RSE_get_record(tdbb, urs, g_RSE_get_mode);
|
2003-02-13 18:28:38 +01:00
|
|
|
if (value)
|
|
|
|
{
|
2005-12-28 07:42:58 +01:00
|
|
|
value = !RSE_get_record(tdbb, urs, g_RSE_get_mode);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2005-12-28 07:42:58 +01:00
|
|
|
RSE_close(tdbb, urs);
|
2006-12-30 08:22:46 +01:00
|
|
|
request->req_flags &= ~req_null;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* If this is an invariant node, save the return value. */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
*invariant_flags |= VLU_computed;
|
2004-03-19 07:14:53 +01:00
|
|
|
impure->vlu_misc.vlu_short = value ? TRUE : FALSE;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2004-10-14 21:09:19 +02:00
|
|
|
case nod_equiv:
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_eql:
|
2004-03-19 07:14:53 +01:00
|
|
|
return (comparison == 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_neq:
|
2004-03-19 07:14:53 +01:00
|
|
|
return (comparison != 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_gtr:
|
2004-03-19 07:14:53 +01:00
|
|
|
return (comparison > 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_geq:
|
2004-03-19 07:14:53 +01:00
|
|
|
return (comparison >= 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_lss:
|
2004-03-19 07:14:53 +01:00
|
|
|
return (comparison < 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_leq:
|
2004-03-19 07:14:53 +01:00
|
|
|
return (comparison <= 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_between:
|
2003-12-22 18:51:43 +01:00
|
|
|
desc[1] = EVL_expr(tdbb, node->nod_arg[2]);
|
|
|
|
if (request->req_flags & req_null)
|
2004-03-19 07:14:53 +01:00
|
|
|
return false;
|
|
|
|
return (comparison >= 0 && MOV_compare(desc[0], desc[1]) <= 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
default:
|
2003-12-22 18:51:43 +01:00
|
|
|
BUGCHECK(231); /* msg 231 EVL_boolean: invalid operation */
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2004-03-19 07:14:53 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
dsc* EVL_expr(thread_db* tdbb, jrd_nod* const node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* 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);
|
|
|
|
|
2004-10-03 14:10:19 +02:00
|
|
|
if (--tdbb->tdbb_quantum < 0)
|
|
|
|
JRD_reschedule(tdbb, 0, true);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* const request = tdbb->getRequest();
|
2005-06-06 10:30:03 +02:00
|
|
|
impure_value* const impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
2006-04-24 18:56:58 +02:00
|
|
|
case nod_concatenate:
|
2001-05-23 15:26:42 +02:00
|
|
|
return binary_value(tdbb, node, impure);
|
|
|
|
|
|
|
|
case nod_argument:
|
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2003-12-11 11:33:30 +01:00
|
|
|
const jrd_nod* message = node->nod_arg[e_arg_message];
|
2004-03-30 06:10:52 +02:00
|
|
|
const Format* format = (Format*) message->nod_arg[e_msg_format];
|
2004-01-21 08:18:30 +01:00
|
|
|
desc = &format->fmt_desc[(int)(IPTR) node->nod_arg[e_arg_number]];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) request +
|
2004-01-21 08:18:30 +01:00
|
|
|
message->nod_impure + (IPTR) desc->dsc_address;
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
|
|
|
adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
|
|
|
|
2007-02-06 15:25:10 +01:00
|
|
|
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))
|
|
|
|
{
|
2007-12-08 14:41:26 +01:00
|
|
|
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<const ItemInfo*>(node->nod_arg[e_arg_info]),
|
|
|
|
&impure->vlu_desc, request->req_flags & req_null);
|
|
|
|
}
|
2007-02-06 15:25:10 +01:00
|
|
|
*impure_flags |= VLU_checked;
|
|
|
|
}
|
2007-01-17 02:19:01 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
|
|
|
{
|
2004-03-18 06:56:06 +01:00
|
|
|
Record* record =
|
2003-12-03 09:19:24 +01:00
|
|
|
request->req_rpb[(int) (IPTR)node->nod_arg[e_fld_stream]].rpb_record;
|
2005-05-28 00:45:31 +02:00
|
|
|
jrd_rel* relation = request->req_rpb[(USHORT)(IPTR) node->nod_arg[e_fld_stream]].rpb_relation;
|
|
|
|
/* In order to "map a null to a default" value (in EVL_field()),
|
|
|
|
* the relation block is referenced.
|
|
|
|
* Reference: Bug 10116, 10424
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
2005-05-28 00:45:31 +02:00
|
|
|
if (!EVL_field(relation,
|
2001-12-24 03:51:06 +01:00
|
|
|
record,
|
2004-01-21 08:18:30 +01:00
|
|
|
(USHORT)(IPTR) node->nod_arg[e_fld_id],
|
2001-12-24 03:51:06 +01:00
|
|
|
&impure->vlu_desc))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags |= req_null;
|
2001-12-24 03:51:06 +01:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
if (!relation || !(relation->rel_flags & REL_system))
|
2006-04-29 10:56:54 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
|
|
|
adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
2006-04-29 10:56:54 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
case nod_function:
|
2008-01-26 13:57:52 +01:00
|
|
|
FUN_evaluate(tdbb, reinterpret_cast<UserFunction*>(node->nod_arg[e_fun_function]),
|
2002-06-30 11:58:20 +02:00
|
|
|
node->nod_arg[e_fun_args], impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
2007-04-12 17:56:34 +02:00
|
|
|
case nod_sys_function:
|
|
|
|
{
|
2007-10-12 22:25:04 +02:00
|
|
|
const SysFunction* sysFunction = reinterpret_cast<SysFunction*>(node->nod_arg[e_sysfun_function]);
|
2007-04-12 17:56:34 +02:00
|
|
|
return sysFunction->evlFunc(tdbb, sysFunction, node->nod_arg[e_sysfun_args], impure);
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_literal:
|
2004-03-18 06:56:06 +01:00
|
|
|
return &((Literal*) node)->lit_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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:
|
|
|
|
{
|
2004-10-30 21:41:54 +02:00
|
|
|
// Use the request timestamp
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-11-12 06:32:13 +01:00
|
|
|
fb_assert(!request->req_timestamp.isEmpty());
|
2004-11-17 13:33:30 +01:00
|
|
|
ISC_TIMESTAMP enc_times = request->req_timestamp.value();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc));
|
|
|
|
impure->vlu_desc.dsc_address =
|
2004-10-30 21:41:54 +02:00
|
|
|
(UCHAR *) &impure->vlu_misc.vlu_timestamp;
|
2003-12-11 11:33:30 +01:00
|
|
|
|
2005-08-24 11:16:19 +02:00
|
|
|
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);
|
2008-03-26 11:21:04 +01:00
|
|
|
Firebird::TimeStamp::round_time(enc_times.timestamp_time, precision);
|
2005-08-24 11:16:19 +02:00
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
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 =
|
2005-08-24 11:16:19 +02:00
|
|
|
enc_times.timestamp_time;
|
2001-05-23 15:26:42 +02:00
|
|
|
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];
|
2004-10-30 21:41:54 +02:00
|
|
|
*((ISC_TIMESTAMP *) impure->vlu_desc.dsc_address) = enc_times;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
default:
|
2004-10-30 21:41:54 +02:00
|
|
|
fb_assert(false);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
|
|
|
case nod_user_name:
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2004-11-28 09:25:09 +01:00
|
|
|
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);
|
2005-05-12 20:28:04 +02:00
|
|
|
const char* cur_user = 0;
|
2007-12-03 16:46:39 +01:00
|
|
|
if (tdbb->getAttachment()->att_user)
|
2004-11-28 09:25:09 +01:00
|
|
|
{
|
2007-12-03 16:46:39 +01:00
|
|
|
cur_user = tdbb->getAttachment()->att_user->usr_user_name.c_str();
|
2005-05-17 20:20:07 +02:00
|
|
|
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>
|
|
|
|
(const_cast<char*>(cur_user));
|
2004-11-28 09:25:09 +01:00
|
|
|
}
|
|
|
|
if (cur_user)
|
|
|
|
impure->vlu_desc.dsc_length = strlen(cur_user);
|
|
|
|
else
|
|
|
|
impure->vlu_desc.dsc_length = 0;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
2003-08-12 11:56:50 +02:00
|
|
|
// CVC: Current role will get a validated role; IE one that exists.
|
2002-06-30 11:58:20 +02:00
|
|
|
case nod_current_role:
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2004-11-28 09:25:09 +01:00
|
|
|
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);
|
2007-06-08 12:24:57 +02:00
|
|
|
const char* cur_role = 0;
|
2007-12-03 16:46:39 +01:00
|
|
|
if (tdbb->getAttachment()->att_user)
|
2004-11-28 09:25:09 +01:00
|
|
|
{
|
2007-12-03 16:46:39 +01:00
|
|
|
cur_role = tdbb->getAttachment()->att_user->usr_sql_role_name.c_str();
|
2007-06-08 12:24:57 +02:00
|
|
|
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(const_cast<char*>(cur_role));
|
2004-11-28 09:25:09 +01:00
|
|
|
}
|
|
|
|
if (cur_role)
|
|
|
|
impure->vlu_desc.dsc_length = strlen(cur_role);
|
|
|
|
else
|
|
|
|
impure->vlu_desc.dsc_length = 0;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2002-06-30 11:58:20 +02:00
|
|
|
return &impure->vlu_desc;
|
2004-11-17 20:45:28 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_extract:
|
2005-06-11 02:14:55 +02:00
|
|
|
return extract(tdbb, node, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-06-06 20:14:10 +02:00
|
|
|
case nod_strlen:
|
2005-06-11 02:14:55 +02:00
|
|
|
return string_length(tdbb, node, impure);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_max:
|
|
|
|
case nod_min:
|
|
|
|
case nod_count:
|
2006-02-07 09:08:49 +01:00
|
|
|
//case nod_count2:
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
2005-06-06 10:30:03 +02:00
|
|
|
{
|
|
|
|
const jrd_nod* node2 = node->nod_arg[e_var_variable];
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
impure_value* impure2 = (impure_value*) ((SCHAR *) request + node2->nod_impure);
|
|
|
|
if (impure2->vlu_desc.dsc_flags & DSC_null)
|
|
|
|
request->req_flags |= req_null;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
impure->vlu_desc = impure2->vlu_desc;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
|
|
|
adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2007-02-06 15:25:10 +01:00
|
|
|
if (!(impure2->vlu_flags & VLU_checked))
|
|
|
|
{
|
2007-12-08 14:41:26 +01:00
|
|
|
if (node->nod_arg[e_var_info])
|
|
|
|
{
|
|
|
|
EVL_validate(tdbb, Item(nod_variable, (IPTR) node->nod_arg[e_var_id]),
|
|
|
|
reinterpret_cast<const ItemInfo*>(node->nod_arg[e_var_info]),
|
|
|
|
&impure->vlu_desc, impure->vlu_desc.dsc_flags & DSC_null);
|
|
|
|
}
|
2007-02-06 15:25:10 +01:00
|
|
|
impure2->vlu_flags |= VLU_checked;
|
|
|
|
}
|
2007-01-17 02:19:01 +01:00
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-01-17 02:19:01 +01:00
|
|
|
case nod_domain_validation:
|
2007-01-17 15:21:50 +01:00
|
|
|
if (request->req_domain_validation == NULL ||
|
|
|
|
(request->req_domain_validation->dsc_flags & DSC_null))
|
|
|
|
{
|
2007-01-17 02:19:01 +01:00
|
|
|
request->req_flags |= req_null;
|
2007-01-17 15:21:50 +01:00
|
|
|
}
|
2007-01-17 02:19:01 +01:00
|
|
|
return request->req_domain_validation;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_value_if:
|
|
|
|
return EVL_expr(tdbb, (EVL_boolean(tdbb, node->nod_arg[0])) ?
|
|
|
|
node->nod_arg[1] : node->nod_arg[2]);
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
|
|
|
return trim(tdbb, node, impure);
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
default: /* Shut up some compiler warnings */
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// Evaluate arguments
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
dsc* values[3];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
if (node->nod_count) {
|
|
|
|
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++);
|
2007-03-02 16:22:48 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2006-09-09 10:32:32 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
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)(IPTR) node->nod_arg[e_gen_id],
|
|
|
|
false, 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;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_gen_id2:
|
|
|
|
impure->vlu_misc.vlu_int64 = DPM_gen_id(tdbb,
|
|
|
|
(IPTR) node->nod_arg[e_gen_id], false, 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;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_negate:
|
|
|
|
return negate_dsc(tdbb, values[0], impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_substr:
|
|
|
|
return substring(tdbb, impure, values[0], values[1], values[2]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_upcase:
|
2008-01-16 09:54:50 +01:00
|
|
|
return low_up_case(tdbb, values[0], impure, &TextType::str_to_upper);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_lowcase:
|
2008-01-16 09:54:50 +01:00
|
|
|
return low_up_case(tdbb, values[0], impure, &TextType::str_to_lower);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_cast:
|
|
|
|
return cast(tdbb, values[0], node, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_internal_info:
|
|
|
|
return internal_info(tdbb, values[0], impure);
|
2002-06-14 14:09:37 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
default:
|
|
|
|
BUGCHECK(232); /* msg 232 EVL_expr: invalid operation */
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2006-09-09 10:32:32 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
bool EVL_field(jrd_rel* relation, Record* record, USHORT id, dsc* desc)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* 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) {
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_warning(isc_no_cur_rec, 0);
|
2003-12-22 11:00:59 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-03-30 06:10:52 +02:00
|
|
|
const Format* format = record->rec_format;
|
2003-02-13 18:28:38 +01:00
|
|
|
|
2004-09-25 22:29:51 +02:00
|
|
|
if (format && id < format->fmt_count) {
|
2001-05-23 15:26:42 +02:00
|
|
|
*desc = format->fmt_desc[id];
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2002-06-16 16:19:15 +02:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (!format || id >= format->fmt_count || !desc->dsc_length)
|
2002-06-16 16:19:15 +02:00
|
|
|
*/
|
2003-02-13 18:28:38 +01:00
|
|
|
if (!format || id >= format->fmt_count || !desc->dsc_dtype)
|
|
|
|
{
|
2002-06-16 16:19:15 +02:00
|
|
|
/* Map a non-existent field to a default value, if available.
|
2001-05-23 15:26:42 +02:00
|
|
|
* This enables automatic format upgrade for data rows.
|
|
|
|
* Handle Outer Joins and such specially!
|
|
|
|
* Reference: Bug 10424, 10116
|
|
|
|
*/
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
/* rec_format == NULL indicates we're performing a
|
2001-05-23 15:26:42 +02:00
|
|
|
join-to-null operation for outer joins */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (record && record->rec_format && relation)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/* A database sweep does not scan a relation's metadata. However
|
2005-05-28 00:45:31 +02:00
|
|
|
* the change to substitute a default value for a missing "not null"
|
|
|
|
* field makes it necessary to reference the field block.
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
2003-02-13 18:28:38 +01:00
|
|
|
if (!relation->rel_fields)
|
|
|
|
{
|
2004-03-11 06:04:26 +01:00
|
|
|
thread_db* tdbb = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
MET_scan_relation(tdbb, relation);
|
|
|
|
}
|
|
|
|
|
2006-07-25 07:08:47 +02:00
|
|
|
// CVC: With a corrupt db, the engine crashed doing backup.
|
2007-04-13 15:21:23 +02:00
|
|
|
jrd_fld* temp_field = NULL;
|
2006-07-25 07:08:47 +02:00
|
|
|
if (id < relation->rel_fields->count())
|
|
|
|
temp_field = (*relation->rel_fields)[id];
|
2003-02-13 18:28:38 +01:00
|
|
|
|
|
|
|
if (temp_field)
|
|
|
|
{
|
|
|
|
if (temp_field->fld_default_value && temp_field->fld_not_null)
|
|
|
|
{
|
2003-02-16 17:55:57 +01:00
|
|
|
const NOD_T temp_nod_type =
|
|
|
|
temp_field->fld_default_value->nod_type;
|
|
|
|
|
2004-11-28 09:25:09 +01:00
|
|
|
if (temp_nod_type == nod_user_name)
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2004-11-17 13:33:30 +01:00
|
|
|
desc->dsc_dtype = dtype_text;
|
2001-05-23 15:26:42 +02:00
|
|
|
desc->dsc_sub_type = 0;
|
2004-11-17 13:33:30 +01:00
|
|
|
desc->dsc_scale = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
INTL_ASSIGN_TTYPE(desc, ttype_metadata);
|
2005-05-16 11:15:31 +02:00
|
|
|
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();
|
2004-11-28 09:25:09 +01:00
|
|
|
}
|
|
|
|
else if (temp_nod_type == nod_current_role)
|
|
|
|
{
|
|
|
|
// CVC: Revisiting the current_role to fill default values:
|
2008-01-16 09:54:50 +01:00
|
|
|
// If the current user is the same as the table creator,
|
2004-11-28 09:25:09 +01:00
|
|
|
// 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);
|
2007-06-08 12:24:57 +02:00
|
|
|
const char* rc_role = 0;
|
2007-12-03 16:46:39 +01:00
|
|
|
const UserId* att_user = tdbb->getAttachment()->att_user;
|
2007-06-08 12:24:57 +02:00
|
|
|
const char* cur_user = att_user ? att_user->usr_user_name.c_str() : 0;
|
2005-05-12 20:28:04 +02:00
|
|
|
if (cur_user && relation->rel_owner_name == cur_user)
|
2007-06-08 12:24:57 +02:00
|
|
|
rc_role = att_user->usr_sql_role_name.c_str();
|
2004-11-28 09:25:09 +01:00
|
|
|
else
|
2007-06-08 12:24:57 +02:00
|
|
|
rc_role = NULL_ROLE;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2007-06-08 12:24:57 +02:00
|
|
|
desc->dsc_address = reinterpret_cast<UCHAR*>(const_cast<char*>(rc_role));
|
2004-11-28 09:25:09 +01:00
|
|
|
desc->dsc_length = strlen(rc_role);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2004-11-17 13:33:30 +01:00
|
|
|
else if (temp_nod_type == nod_current_date ||
|
2003-02-13 18:28:38 +01:00
|
|
|
temp_nod_type == nod_current_time ||
|
|
|
|
temp_nod_type == nod_current_timestamp)
|
|
|
|
{
|
2003-02-13 11:11:35 +01:00
|
|
|
static const GDS_TIMESTAMP temp_timestamp = { 0, 0 };
|
2001-05-23 15:26:42 +02:00
|
|
|
desc->dsc_dtype = dtype_timestamp;
|
|
|
|
desc->dsc_scale = 0;
|
|
|
|
desc->dsc_flags = 0;
|
|
|
|
desc->dsc_address =
|
2003-02-13 18:28:38 +01:00
|
|
|
reinterpret_cast<UCHAR*>(
|
|
|
|
const_cast<ISC_TIMESTAMP*>(&temp_timestamp));
|
2001-05-23 15:26:42 +02:00
|
|
|
desc->dsc_length = sizeof(temp_timestamp);
|
|
|
|
}
|
2004-11-17 13:33:30 +01:00
|
|
|
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<SLONG*>(&temp_long);
|
|
|
|
desc->dsc_length = sizeof(temp_long);
|
|
|
|
}
|
2003-02-13 18:28:38 +01:00
|
|
|
else
|
|
|
|
{
|
2004-03-18 06:56:06 +01:00
|
|
|
const Literal* default_literal =
|
|
|
|
reinterpret_cast<Literal*>(temp_field->fld_default_value);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (default_literal->nod_type == nod_null)
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_post(isc_not_valid,
|
2005-05-12 20:28:04 +02:00
|
|
|
isc_arg_string, temp_field->fld_name.c_str(),
|
2006-09-10 11:05:40 +02:00
|
|
|
isc_arg_string, NULL_STRING_MARK, 0);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(default_literal->nod_type == nod_literal);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* default_desc = &default_literal->lit_desc;
|
|
|
|
// CVC: This could be a bitwise copy in one line
|
2006-09-09 10:32:32 +02:00
|
|
|
/*
|
2003-02-13 18:28:38 +01:00
|
|
|
desc->dsc_dtype = default_desc->dsc_dtype;
|
|
|
|
desc->dsc_scale = default_desc->dsc_scale;
|
|
|
|
desc->dsc_length = default_desc->dsc_length;
|
2001-05-23 15:26:42 +02:00
|
|
|
desc->dsc_sub_type = default_desc->dsc_sub_type;
|
2003-02-13 18:28:38 +01:00
|
|
|
desc->dsc_flags = default_desc->dsc_flags;
|
|
|
|
desc->dsc_address = default_desc->dsc_address;
|
2006-09-09 10:32:32 +02:00
|
|
|
*/
|
|
|
|
*desc = *default_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2004-11-18 10:14:20 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-10-28 10:11:35 +02:00
|
|
|
|
|
|
|
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;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If the offset of the field is 0, the field can't possible exist */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (!desc->dsc_address) {
|
2003-12-22 11:00:59 +01:00
|
|
|
return false;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-01-21 08:18:30 +01:00
|
|
|
desc->dsc_address = record->rec_data + (IPTR) desc->dsc_address;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (TEST_NULL(record, id)) {
|
|
|
|
desc->dsc_flags |= DSC_null;
|
2003-12-22 11:00:59 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
desc->dsc_flags &= ~DSC_null;
|
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-10-09 03:40:32 +02:00
|
|
|
|
2004-10-11 19:18:42 +02:00
|
|
|
USHORT EVL_group(thread_db* tdbb, RecordSource* rsb, jrd_nod *const node, USHORT state)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* 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);
|
|
|
|
|
2004-10-03 14:10:19 +02:00
|
|
|
if (--tdbb->tdbb_quantum < 0)
|
|
|
|
JRD_reschedule(tdbb, 0, true);
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value vtemp;
|
2001-05-23 15:26:42 +02:00
|
|
|
vtemp.vlu_string = NULL;
|
2004-03-06 12:56:09 +01:00
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2003-12-11 11:33:30 +01:00
|
|
|
jrd_nod* map = node->nod_arg[e_agg_map];
|
|
|
|
jrd_nod* group = node->nod_arg[e_agg_group];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-07 08:58:55 +01:00
|
|
|
jrd_nod** ptr;
|
|
|
|
const jrd_nod* const* end;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-05-05 21:28:46 +02:00
|
|
|
// if we found the last record last time, we're all done
|
|
|
|
|
|
|
|
if (state == 2)
|
|
|
|
return 0;
|
|
|
|
|
2004-03-06 12:56:09 +01:00
|
|
|
try {
|
|
|
|
|
2007-05-05 21:28:46 +02:00
|
|
|
// Initialize the aggregate record
|
2004-03-06 12:56:09 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const jrd_nod* from = (*ptr)->nod_arg[e_asgn_from];
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
case nod_agg_list:
|
|
|
|
case nod_agg_list_distinct:
|
2007-05-05 21:28:46 +02:00
|
|
|
// 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;
|
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
if (from->nod_type == nod_agg_list_distinct)
|
|
|
|
/* Initialize a sort to reject duplicate values */
|
|
|
|
init_agg_distinct(tdbb, from);
|
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_literal: /* pjpg 20001124 */
|
|
|
|
EXE_assignment(tdbb, *ptr);
|
|
|
|
break;
|
2001-12-24 03:51:06 +01:00
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
default: // Shut up some compiler warnings
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there isn't a record pending, open the stream and get one */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if ((state == 0) || (state == 3))
|
|
|
|
{
|
2003-11-04 12:35:51 +01:00
|
|
|
RSE_open(tdbb, rsb);
|
|
|
|
if (!RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2003-02-13 18:28:38 +01:00
|
|
|
if (group) {
|
2004-10-08 12:18:41 +02:00
|
|
|
fini_agg_distinct(tdbb, node);
|
2001-05-23 15:26:42 +02:00
|
|
|
return 0;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
state = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* desc;
|
|
|
|
|
|
|
|
if (group) {
|
2001-05-23 15:26:42 +02:00
|
|
|
for (ptr = group->nod_arg, end = ptr + group->nod_count; ptr < end;
|
2005-05-28 00:45:31 +02:00
|
|
|
ptr++)
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
|
|
|
jrd_nod* from = *ptr;
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
desc = EVL_expr(tdbb, from);
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
impure->vlu_desc.dsc_address = NULL;
|
|
|
|
else
|
2004-02-20 07:43:27 +01:00
|
|
|
EVL_make_value(tdbb, desc, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2003-12-11 11:33:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Loop thru records until either a value change or EOF */
|
|
|
|
|
|
|
|
while (state != 2) {
|
|
|
|
state = 1;
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
/* in the case of a group by, look for a change in value of any of
|
2001-05-23 15:26:42 +02:00
|
|
|
the columns; if we find one, stop aggregating and return what we have */
|
|
|
|
|
|
|
|
if (group)
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
for (ptr = group->nod_arg, end = ptr + group->nod_count;
|
2003-02-13 18:28:38 +01:00
|
|
|
ptr < end; ptr++)
|
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
jrd_nod* from = *ptr;
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (impure->vlu_desc.dsc_address)
|
|
|
|
EVL_make_value(tdbb, &impure->vlu_desc, &vtemp);
|
|
|
|
else
|
|
|
|
vtemp.vlu_desc.dsc_address = NULL;
|
2006-09-09 10:32:32 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
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 {
|
2004-02-20 07:43:27 +01:00
|
|
|
EVL_make_value(tdbb, desc, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!vtemp.vlu_desc.dsc_address
|
|
|
|
|| MOV_compare(&vtemp.vlu_desc, desc))
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
goto break_out;
|
2003-12-11 11:33:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* go through and compute all the aggregates on this record */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
jrd_nod* from = (*ptr)->nod_arg[e_asgn_from];
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure);
|
2003-02-13 18:28:38 +01:00
|
|
|
switch (from->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_agg_min:
|
|
|
|
case nod_agg_min_indexed:
|
|
|
|
case nod_agg_max:
|
|
|
|
case nod_agg_max_indexed:
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
desc = EVL_expr(tdbb, from->nod_arg[0]);
|
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
break;
|
|
|
|
}
|
2007-07-31 15:06:11 +02:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
++impure->vlux_count;
|
|
|
|
if (!impure->vlu_desc.dsc_dtype)
|
|
|
|
{
|
2004-02-20 07:43:27 +01:00
|
|
|
EVL_make_value(tdbb, desc, impure);
|
2004-03-28 11:10:30 +02:00
|
|
|
// It was reinterpret_cast<impure_value*>(&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
|
2004-02-20 07:43:27 +01:00
|
|
|
// and it doesn't contain virtuals.
|
2004-03-28 11:10:30 +02:00
|
|
|
// Thus, &impure_value_ex->vlu_desc == &impure_value->vlu_desc == &impure_value_ex
|
2004-02-20 07:43:27 +01:00
|
|
|
// Delete this comment or restore the original code
|
|
|
|
// when this reasoning has been validated, please.
|
2003-12-11 11:33:30 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
const SLONG 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)))
|
|
|
|
{
|
2004-02-20 07:43:27 +01:00
|
|
|
EVL_make_value(tdbb, desc, impure);
|
2003-12-11 11:33:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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;
|
2004-02-20 07:43:27 +01:00
|
|
|
add(desc, from, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2004-02-20 07:43:27 +01:00
|
|
|
add2(desc, from, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
case nod_agg_list:
|
2007-05-05 21:28:46 +02:00
|
|
|
{
|
|
|
|
MoveBuffer buffer;
|
|
|
|
UCHAR* temp;
|
|
|
|
int len;
|
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
desc = EVL_expr(tdbb, from->nod_arg[0]);
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
break;
|
2007-05-05 21:28:46 +02:00
|
|
|
|
|
|
|
if (!impure->vlu_blob)
|
|
|
|
{
|
2007-12-03 16:46:39 +01:00
|
|
|
impure->vlu_blob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
|
2007-05-05 21:28:46 +02:00
|
|
|
&impure->vlu_misc.vlu_bid);
|
|
|
|
impure->vlu_desc.makeBlob(desc->getBlobSubType(), desc->getTextType(),
|
|
|
|
(ISC_QUAD* ) &impure->vlu_misc.vlu_bid);
|
|
|
|
}
|
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
if (impure->vlux_count) {
|
2006-09-09 10:32:32 +02:00
|
|
|
const dsc* const delimiter = EVL_expr(tdbb, from->nod_arg[1]);
|
2006-04-24 18:56:58 +02:00
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
// mark the result as NULL
|
|
|
|
impure->vlu_desc.dsc_dtype = 0;
|
|
|
|
break;
|
|
|
|
}
|
2007-05-05 21:28:46 +02:00
|
|
|
len = MOV_make_string2(tdbb, delimiter, impure->vlu_desc.getTextType(), &temp, buffer, false);
|
|
|
|
BLB_put_data(tdbb, impure->vlu_blob, temp, len);
|
2006-04-24 18:56:58 +02:00
|
|
|
}
|
|
|
|
++impure->vlux_count;
|
2007-05-05 21:28:46 +02:00
|
|
|
len = MOV_make_string2(tdbb, desc, impure->vlu_desc.getTextType(), &temp, buffer, false);
|
|
|
|
BLB_put_data(tdbb, impure->vlu_blob, temp, len);
|
2006-04-24 18:56:58 +02:00
|
|
|
break;
|
2007-05-05 21:28:46 +02:00
|
|
|
}
|
2006-04-24 18:56:58 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
2006-04-24 18:56:58 +02:00
|
|
|
case nod_agg_list_distinct:
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
|
|
|
desc = EVL_expr(tdbb, from->nod_arg[0]);
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
break;
|
|
|
|
/* "Put" the value to sort. */
|
2006-04-25 14:54:12 +02:00
|
|
|
const size_t asb_index =
|
|
|
|
(from->nod_type == nod_agg_list_distinct) ? 2 : 1;
|
|
|
|
AggregateSort* asb = (AggregateSort*) from->nod_arg[asb_index];
|
2006-10-07 12:53:01 +02:00
|
|
|
impure_agg_sort* asb_impure =
|
|
|
|
(impure_agg_sort*) ((SCHAR *) request + asb->nod_impure);
|
2003-12-11 11:33:30 +01:00
|
|
|
UCHAR* data;
|
2008-02-09 16:19:21 +01:00
|
|
|
SORT_put(tdbb, asb_impure->iasb_sort_handle, reinterpret_cast<ULONG**>(&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()),
|
2008-02-10 18:58:07 +01:00
|
|
|
desc, &to, INTL_KEY_UNIQUE);
|
2008-02-09 16:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
asb->asb_desc.dsc_address = data +
|
|
|
|
(asb->asb_intl ? asb->asb_key_desc[1].skd_offset : 0);
|
2007-03-02 01:46:03 +01:00
|
|
|
MOV_move(tdbb, desc, &asb->asb_desc);
|
2008-02-09 16:19:21 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2003-12-11 11:33:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
EXE_assignment(tdbb, *ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-31 15:06:11 +02:00
|
|
|
if (state == 2)
|
|
|
|
break;
|
|
|
|
|
2003-11-04 12:35:51 +01:00
|
|
|
if (!RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2007-07-31 15:06:11 +02:00
|
|
|
state = 2;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
break_out:
|
|
|
|
|
2008-02-06 15:29:57 +01:00
|
|
|
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);
|
|
|
|
|
2008-02-08 02:25:41 +01:00
|
|
|
if (from->nod_type == nod_agg_list && impure->vlu_blob)
|
2008-02-06 15:29:57 +01:00
|
|
|
{
|
|
|
|
BLB_close(tdbb, impure->vlu_blob);
|
2008-02-07 15:45:49 +01:00
|
|
|
impure->vlu_blob = NULL;
|
2008-02-06 15:29:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Finish up any residual computations and get out */
|
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
delete vtemp.vlu_string;
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc temp;
|
|
|
|
double d;
|
|
|
|
SINT64 i;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
jrd_nod* from = (*ptr)->nod_arg[e_asgn_from];
|
|
|
|
jrd_nod* field = (*ptr)->nod_arg[e_asgn_to];
|
2004-02-24 06:34:44 +01:00
|
|
|
const USHORT id = (USHORT)(IPTR) field->nod_arg[e_fld_id];
|
2004-03-18 06:56:06 +01:00
|
|
|
Record* record =
|
2003-12-03 09:19:24 +01:00
|
|
|
request->req_rpb[(int) (IPTR) field->nod_arg[e_fld_stream]].rpb_record;
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value_ex* impure = (impure_value_ex*) ((SCHAR *) request + from->nod_impure);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
switch (from->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
2006-04-24 18:56:58 +02:00
|
|
|
case nod_agg_list:
|
|
|
|
case nod_agg_list_distinct:
|
2001-05-23 15:26:42 +02:00
|
|
|
if ((from->nod_type == nod_agg_total_distinct) ||
|
2006-04-24 18:56:58 +02:00
|
|
|
(from->nod_type == nod_agg_total_distinct2) ||
|
|
|
|
(from->nod_type == nod_agg_list_distinct))
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
compute_agg_distinct(tdbb, from);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
|
|
|
if (!impure->vlux_count)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
2003-02-13 18:28:38 +01:00
|
|
|
if (from->nod_type == nod_agg_count_distinct) {
|
2001-05-23 15:26:42 +02:00
|
|
|
compute_agg_distinct(tdbb, from);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
|
|
|
if (!impure->vlu_desc.dsc_dtype) {
|
2001-05-23 15:26:42 +02:00
|
|
|
SET_NULL(record, id);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else {
|
2007-03-02 01:46:03 +01:00
|
|
|
MOV_move(tdbb, &impure->vlu_desc, EVL_expr(tdbb, field));
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2007-03-02 01:46:03 +01:00
|
|
|
MOV_move(tdbb, &temp, EVL_expr(tdbb, field));
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
2007-03-02 01:46:03 +01:00
|
|
|
MOV_move(tdbb, &temp, EVL_expr(tdbb, field));
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2001-12-24 03:51:06 +01:00
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
default: // Shut up some compiler warnings
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-06 12:56:09 +01:00
|
|
|
}
|
2006-05-19 17:17:02 +02:00
|
|
|
catch (const Firebird::Exception& ex) {
|
2004-03-06 12:56:09 +01:00
|
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
2004-10-08 12:18:41 +02:00
|
|
|
fini_agg_distinct(tdbb, node);
|
2004-03-06 12:56:09 +01:00
|
|
|
ERR_punt();
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* E V L _ m a k e _ v a l u e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2005-05-28 00:45:31 +02:00
|
|
|
* Make a value block reflect the value of a descriptor.
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
/* Handle the fixed length data types first. They're easy. */
|
|
|
|
|
2008-04-12 13:56:32 +02:00
|
|
|
const dsc from = *desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2006-04-16 20:45:40 +02:00
|
|
|
|
|
|
|
case dtype_blob:
|
|
|
|
value->vlu_misc.vlu_bid = *(bid*)from.dsc_address;
|
|
|
|
return;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2004-10-30 21:41:54 +02:00
|
|
|
fb_assert(false);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-04-14 17:22:52 +02:00
|
|
|
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<vary*>(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;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-04-14 17:22:52 +02:00
|
|
|
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);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-04-14 17:22:52 +02:00
|
|
|
if (address && length && target != address)
|
|
|
|
memcpy(target, address, length);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-17 02:19:01 +01:00
|
|
|
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;
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2007-01-18 00:59:23 +01:00
|
|
|
bool err = false;
|
2007-01-17 02:19:01 +01:00
|
|
|
|
|
|
|
if (null && !itemInfo->nullable)
|
2007-01-18 00:59:23 +01:00
|
|
|
err = true;
|
|
|
|
|
|
|
|
const char* value = NULL_STRING_MARK;
|
|
|
|
TEXT temp[128];
|
2007-01-17 02:19:01 +01:00
|
|
|
|
|
|
|
MapFieldInfo::ValueType fieldInfo;
|
2007-01-18 00:59:23 +01:00
|
|
|
if (!err && itemInfo->fullDomain &&
|
2007-01-17 02:19:01 +01:00
|
|
|
request->req_map_field_info.get(itemInfo->field, fieldInfo) &&
|
|
|
|
fieldInfo.validation)
|
|
|
|
{
|
|
|
|
request->req_domain_validation = desc;
|
2007-05-18 08:15:26 +02:00
|
|
|
const USHORT flags = request->req_flags;
|
2007-01-17 02:19:01 +01:00
|
|
|
|
|
|
|
if (!EVL_boolean(tdbb, fieldInfo.validation) &&
|
|
|
|
!(request->req_flags & req_null))
|
|
|
|
{
|
|
|
|
const USHORT length = desc && !(desc->dsc_flags & DSC_null) ?
|
|
|
|
MOV_make_string(desc, ttype_dynamic, &value,
|
|
|
|
reinterpret_cast<vary*>(temp),
|
|
|
|
sizeof(temp)) : 0;
|
|
|
|
|
|
|
|
if (desc == NULL || (desc->dsc_flags & DSC_null))
|
|
|
|
value = NULL_STRING_MARK;
|
|
|
|
else if (!length)
|
|
|
|
value = "";
|
|
|
|
else
|
|
|
|
value = ERR_string(value, length);
|
|
|
|
|
2007-01-18 00:59:23 +01:00
|
|
|
err = true;
|
|
|
|
}
|
2007-05-18 03:50:26 +02:00
|
|
|
|
|
|
|
request->req_flags = flags;
|
2007-01-18 00:59:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
Firebird::string s;
|
|
|
|
|
|
|
|
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 = ERR_cstring(s.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
arg = ERR_cstring(itemInfo->name);
|
2007-01-17 02:19:01 +01:00
|
|
|
}
|
2007-01-18 00:59:23 +01:00
|
|
|
|
|
|
|
ERR_post(status,
|
|
|
|
isc_arg_string, arg,
|
|
|
|
isc_arg_string, value,
|
|
|
|
0);
|
2007-01-17 02:19:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* add(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* 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);
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node->nod_type == nod_add ||
|
2001-05-23 15:26:42 +02:00
|
|
|
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);
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Handle date arithmetic */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_date) {
|
|
|
|
return add_datetime(desc, node, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle floating arithmetic */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_double) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const double d1 = MOV_get_double(desc);
|
|
|
|
const double d2 = MOV_get_double(&value->vlu_desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
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) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const SQUAD q1 = MOV_get_quad(desc, node->nod_scale);
|
|
|
|
const SQUAD q2 = MOV_get_quad(&value->vlu_desc, node->nod_scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
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) ?
|
2003-11-03 18:14:45 +01:00
|
|
|
QUAD_SUBTRACT(q2, q1, ERR_post) :
|
|
|
|
QUAD_ADD(q1, q2, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything else defaults to longword */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
const SLONG l1 = MOV_get_long(desc, node->nod_scale);
|
|
|
|
const SLONG l2 = MOV_get_long(&value->vlu_desc, node->nod_scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* add2(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* 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);
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node->nod_type == nod_add2 ||
|
2001-05-23 15:26:42 +02:00
|
|
|
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);
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Handle date arithmetic */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_date) {
|
|
|
|
return add_datetime(desc, node, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle floating arithmetic */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_double) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const double d1 = MOV_get_double(desc);
|
|
|
|
const double d2 = MOV_get_double(&value->vlu_desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
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) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const SQUAD q1 = MOV_get_quad(desc, node->nod_scale);
|
|
|
|
const SQUAD q2 = MOV_get_quad(&value->vlu_desc, node->nod_scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
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) ?
|
2003-11-03 18:14:45 +01:00
|
|
|
QUAD_SUBTRACT(q2, q1, ERR_post) :
|
|
|
|
QUAD_ADD(q1, q2, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything else defaults to int64 */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
SINT64 i1 = MOV_get_int64(desc, node->nod_scale);
|
|
|
|
const SINT64 i2 = MOV_get_int64(&value->vlu_desc, node->nod_scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* add_datetime(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* 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? */
|
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node->nod_flags & nod_date);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* 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 {
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert((node->nod_type == nod_subtract)
|
2001-05-23 15:26:42 +02:00
|
|
|
|| (node->nod_type == nod_subtract2));
|
|
|
|
dtype = DSC_sub_result[value->vlu_desc.dsc_dtype][desc->dsc_dtype];
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
/* Is this a <date type> - <date type> construct?
|
|
|
|
chose the proper routine to do the subtract from the
|
2001-05-23 15:26:42 +02:00
|
|
|
LHS of expression
|
|
|
|
Thus: <TIME> - <TIMESTAMP> uses TIME arithmetic
|
|
|
|
<DATE> - <TIMESTAMP> uses DATE arithmetic
|
|
|
|
<TIMESTAMP> - <DATE> uses TIMESTAMP arithmetic */
|
|
|
|
if (DTYPE_IS_NUMERIC(dtype))
|
|
|
|
dtype = value->vlu_desc.dsc_dtype;
|
|
|
|
|
|
|
|
/* Handle historical <timestamp> = <string> - <value> case */
|
|
|
|
if (!DTYPE_IS_DATE(dtype) &&
|
2003-10-21 00:41:11 +02:00
|
|
|
(DTYPE_IS_TEXT(value->vlu_desc.dsc_dtype) ||
|
2005-05-28 00:45:31 +02:00
|
|
|
DTYPE_IS_TEXT(desc->dsc_dtype)))
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
|
|
|
dtype = dtype_timestamp;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (dtype) {
|
|
|
|
case dtype_timestamp:
|
|
|
|
default:
|
|
|
|
/* This needs to handle a dtype_sql_date + dtype_sql_time */
|
2005-05-28 00:45:31 +02:00
|
|
|
/* For historical reasons prior to V6 - handle any types for
|
2001-05-23 15:26:42 +02:00
|
|
|
timestamp arithmetic */
|
|
|
|
return add_timestamp(desc, node, value);
|
|
|
|
|
|
|
|
case dtype_sql_time:
|
|
|
|
return add_sql_time(desc, node, value);
|
|
|
|
|
|
|
|
case dtype_sql_date:
|
|
|
|
return add_sql_date(desc, node, value);
|
|
|
|
|
|
|
|
case DTYPE_CANNOT:
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
return NULL;
|
2004-11-07 11:47:20 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* add_sql_date(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* a d d _ s q l _ d a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Perform date arithmetic
|
|
|
|
*
|
|
|
|
* DATE - DATE Result is SLONG
|
|
|
|
* DATE +/- NUMERIC Numeric is interpreted as days DECIMAL(*,0).
|
|
|
|
* NUMERIC +/- TIME Numeric is interpreted as days DECIMAL(*,0).
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node->nod_type == nod_add ||
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_type == nod_subtract ||
|
|
|
|
node->nod_type == nod_add2 || node->nod_type == nod_subtract2);
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(value->vlu_desc.dsc_dtype == dtype_sql_date ||
|
2001-05-23 15:26:42 +02:00
|
|
|
desc->dsc_dtype == dtype_sql_date);
|
|
|
|
|
2003-12-22 11:00:59 +01:00
|
|
|
SINT64 d1;
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Coerce operand1 to a count of days */
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op1_is_date = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (value->vlu_desc.dsc_dtype == dtype_sql_date) {
|
|
|
|
d1 = *((GDS_DATE *) value->vlu_desc.dsc_address);
|
2003-12-11 11:33:30 +01:00
|
|
|
op1_is_date = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
d1 = MOV_get_int64(&value->vlu_desc, 0);
|
|
|
|
|
2003-12-22 11:00:59 +01:00
|
|
|
SINT64 d2;
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Coerce operand2 to a count of days */
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op2_is_date = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (desc->dsc_dtype == dtype_sql_date) {
|
|
|
|
d2 = *((GDS_DATE *) desc->dsc_address);
|
2003-12-11 11:33:30 +01:00
|
|
|
op2_is_date = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
d2 = MOV_get_int64(desc, 0);
|
|
|
|
|
|
|
|
if (((node->nod_type == nod_subtract)
|
2003-12-11 11:33:30 +01:00
|
|
|
|| (node->nod_type == nod_subtract2)) && op1_is_date && op2_is_date)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
d2 = d1 - d2;
|
|
|
|
value->vlu_misc.vlu_int64 = d2;
|
|
|
|
result->dsc_dtype = dtype_int64;
|
|
|
|
result->dsc_length = sizeof(SINT64);
|
|
|
|
result->dsc_scale = 0;
|
|
|
|
result->dsc_sub_type = 0;
|
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_int64;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(op1_is_date || op2_is_date);
|
|
|
|
fb_assert(!(op1_is_date && op2_is_date));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
|
|
|
|
if ((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2)) {
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(op1_is_date);
|
2001-05-23 15:26:42 +02:00
|
|
|
d2 = d1 - d2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
d2 = d1 + d2;
|
|
|
|
|
|
|
|
value->vlu_misc.vlu_sql_date = d2;
|
|
|
|
|
2008-03-26 11:21:04 +01:00
|
|
|
if (!Firebird::TimeStamp::isValidDate(value->vlu_misc.vlu_sql_date)) {
|
2006-07-25 07:35:04 +02:00
|
|
|
ERR_post(isc_date_range_exceeded, 0);
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
result->dsc_dtype = dtype_sql_date;
|
|
|
|
result->dsc_length = type_lengths[result->dsc_dtype];
|
|
|
|
result->dsc_scale = 0;
|
|
|
|
result->dsc_sub_type = 0;
|
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_sql_date;
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* add_sql_time(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* a d d _ s q l _ t i m e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Perform time arithmetic
|
|
|
|
*
|
|
|
|
* TIME - TIME Result is SLONG, scale -4
|
|
|
|
* TIME +/- NUMERIC Numeric is interpreted as seconds DECIMAL(*,4).
|
|
|
|
* NUMERIC +/- TIME Numeric is interpreted as seconds DECIMAL(*,4).
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node->nod_type == nod_add ||
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_type == nod_subtract ||
|
|
|
|
node->nod_type == nod_add2 || node->nod_type == nod_subtract2);
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(value->vlu_desc.dsc_dtype == dtype_sql_time ||
|
2001-05-23 15:26:42 +02:00
|
|
|
desc->dsc_dtype == dtype_sql_time);
|
|
|
|
|
2003-12-22 11:00:59 +01:00
|
|
|
SINT64 d1;
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Coerce operand1 to a count of seconds */
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op1_is_time = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (value->vlu_desc.dsc_dtype == dtype_sql_time) {
|
|
|
|
d1 = *(GDS_TIME *) value->vlu_desc.dsc_address;
|
2003-12-11 11:33:30 +01:00
|
|
|
op1_is_time = true;
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(d1 >= 0 && d1 < ISC_TICKS_PER_DAY);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
2006-09-09 10:32:32 +02:00
|
|
|
d1 = MOV_get_int64(&value->vlu_desc, ISC_TIME_SECONDS_PRECISION_SCALE);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-22 11:00:59 +01:00
|
|
|
SINT64 d2;
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Coerce operand2 to a count of seconds */
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op2_is_time = false;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (desc->dsc_dtype == dtype_sql_time) {
|
|
|
|
d2 = *(GDS_TIME *) desc->dsc_address;
|
2003-12-11 11:33:30 +01:00
|
|
|
op2_is_time = true;
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(d2 >= 0 && d2 < ISC_TICKS_PER_DAY);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
d2 = MOV_get_int64(desc, ISC_TIME_SECONDS_PRECISION_SCALE);
|
|
|
|
|
|
|
|
if (((node->nod_type == nod_subtract)
|
2003-12-11 11:33:30 +01:00
|
|
|
|| (node->nod_type == nod_subtract2)) && op1_is_time && op2_is_time)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
d2 = d1 - d2;
|
|
|
|
/* Overflow cannot occur as the range of supported TIME values
|
|
|
|
is less than the range of INTEGER */
|
|
|
|
value->vlu_misc.vlu_long = d2;
|
|
|
|
result->dsc_dtype = dtype_long;
|
|
|
|
result->dsc_length = sizeof(SLONG);
|
|
|
|
result->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_long;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(op1_is_time || op2_is_time);
|
|
|
|
fb_assert(!(op1_is_time && op2_is_time));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Perform the operation */
|
|
|
|
|
|
|
|
if ((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2)) {
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(op1_is_time);
|
2001-05-23 15:26:42 +02:00
|
|
|
d2 = d1 - d2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
d2 = d1 + d2;
|
|
|
|
|
|
|
|
/* Make sure to use modulo 24 hour arithmetic */
|
|
|
|
|
|
|
|
/* Make the result positive */
|
|
|
|
while (d2 < 0)
|
|
|
|
d2 += (ISC_TICKS_PER_DAY);
|
|
|
|
|
|
|
|
/* And make it in the range of values for a day */
|
|
|
|
d2 %= (ISC_TICKS_PER_DAY);
|
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(d2 >= 0 && d2 < ISC_TICKS_PER_DAY);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
value->vlu_misc.vlu_sql_time = d2;
|
|
|
|
|
|
|
|
result->dsc_dtype = dtype_sql_time;
|
|
|
|
result->dsc_length = type_lengths[result->dsc_dtype];
|
|
|
|
result->dsc_scale = 0;
|
|
|
|
result->dsc_sub_type = 0;
|
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_sql_time;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* add_timestamp(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* a d d _ t i m e s t a m p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Perform date&time arithmetic
|
|
|
|
*
|
|
|
|
* TIMESTAMP - TIMESTAMP Result is INT64
|
|
|
|
* TIMESTAMP +/- NUMERIC Numeric is interpreted as days DECIMAL(*,*).
|
|
|
|
* NUMERIC +/- TIMESTAMP Numeric is interpreted as days DECIMAL(*,*).
|
|
|
|
* DATE + TIME
|
|
|
|
* TIME + DATE
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SINT64 d1, d2;
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node->nod_type == nod_add ||
|
2001-05-23 15:26:42 +02:00
|
|
|
node->nod_type == nod_subtract ||
|
|
|
|
node->nod_type == nod_add2 || node->nod_type == nod_subtract2);
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Operand 1 is Value -- Operand 2 is desc */
|
|
|
|
|
|
|
|
if (value->vlu_desc.dsc_dtype == dtype_sql_date) {
|
|
|
|
/* DATE + TIME */
|
|
|
|
if ((desc->dsc_dtype == dtype_sql_time) &&
|
2003-12-11 11:33:30 +01:00
|
|
|
((node->nod_type == nod_add) || (node->nod_type == nod_add2)))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_date =
|
|
|
|
value->vlu_misc.vlu_sql_date;
|
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_time =
|
|
|
|
*(GDS_TIME *) desc->dsc_address;
|
|
|
|
goto return_result;
|
|
|
|
}
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else if (desc->dsc_dtype == dtype_sql_date) {
|
|
|
|
/* TIME + DATE */
|
|
|
|
if ((value->vlu_desc.dsc_dtype == dtype_sql_time) &&
|
2003-12-22 11:00:59 +01:00
|
|
|
((node->nod_type == nod_add) || (node->nod_type == nod_add2)))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_time =
|
|
|
|
value->vlu_misc.vlu_sql_time;
|
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_date =
|
|
|
|
*(GDS_DATE *) desc->dsc_address;
|
|
|
|
goto return_result;
|
|
|
|
}
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
/* For historical reasons (behavior prior to V6),
|
|
|
|
there are times we will do timestamp arithmetic without a
|
|
|
|
timestamp being involved.
|
2001-05-23 15:26:42 +02:00
|
|
|
In such an event we need to convert a text type to a timestamp when
|
|
|
|
we don't already have one.
|
|
|
|
We assume any text string must represent a timestamp value. */
|
|
|
|
|
|
|
|
/* If we're subtracting, and the 2nd operand is a timestamp, or
|
|
|
|
something that looks & smells like it could be a timestamp, then
|
|
|
|
we must be doing <timestamp> - <timestamp> subtraction.
|
2005-05-28 00:45:31 +02:00
|
|
|
Notes that this COULD be as strange as <string> - <string>, but
|
|
|
|
because nod_date is set in the nod_flags we know we're supposed
|
2001-05-23 15:26:42 +02:00
|
|
|
to use some form of date arithmetic */
|
|
|
|
|
|
|
|
if (((node->nod_type == nod_subtract)
|
|
|
|
|| (node->nod_type == nod_subtract2))
|
|
|
|
&& ((desc->dsc_dtype == dtype_timestamp)
|
2003-12-11 11:33:30 +01:00
|
|
|
|| DTYPE_IS_TEXT(desc->dsc_dtype)))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Handle cases of
|
|
|
|
<string> - <string>
|
|
|
|
<string> - <timestamp>
|
|
|
|
<timestamp> - <string>
|
|
|
|
<timestamp> - <timestamp>
|
|
|
|
in which cases we assume the string represents a timestamp value */
|
|
|
|
|
|
|
|
/* If the first operand couldn't represent a timestamp, bomb out */
|
|
|
|
|
|
|
|
if (!((value->vlu_desc.dsc_dtype == dtype_timestamp) ||
|
|
|
|
DTYPE_IS_TEXT(value->vlu_desc.dsc_dtype)))
|
2003-09-12 03:58:51 +02:00
|
|
|
{
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
2003-09-12 03:58:51 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
d1 = get_timestamp_to_isc_ticks(&value->vlu_desc);
|
|
|
|
d2 = get_timestamp_to_isc_ticks(desc);
|
|
|
|
|
|
|
|
d2 = d1 - d2;
|
|
|
|
|
|
|
|
if (node->nod_type == nod_subtract2) {
|
|
|
|
|
2008-04-06 10:07:00 +02:00
|
|
|
/* multiply by 100,000 so that we can have the result as decimal (18,9)
|
2001-05-23 15:26:42 +02:00
|
|
|
* We have 10 ^-4; to convert this to 10^-9 we need to multiply by
|
|
|
|
* 100,000. Of course all this is true only because we are dividing
|
2005-05-28 00:45:31 +02:00
|
|
|
* by SECONDS_PER_DAY
|
2001-05-23 15:26:42 +02:00
|
|
|
* now divide by the number of seconds per day, this will give us the
|
|
|
|
* result as a int64 of type decimal (18, 9) in days instead of
|
|
|
|
* seconds.
|
|
|
|
*
|
|
|
|
* But SECONDS_PER_DAY has 2 trailing zeroes (because it is 24 * 60 *
|
|
|
|
* 60), so instead of calculating (X * 100000) / SECONDS_PER_DAY,
|
|
|
|
* use (X * (100000 / 100)) / (SECONDS_PER_DAY / 100), which can be
|
|
|
|
* simplified to (X * 1000) / (SECONDS_PER_DAY / 100)
|
|
|
|
* Since the largest possible difference in timestamps is about 3E11
|
|
|
|
* seconds or 3E15 isc_ticks, the product won't exceed approximately
|
2005-05-28 00:45:31 +02:00
|
|
|
* 3E18, which fits into an INT64.
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
2005-05-28 00:45:31 +02:00
|
|
|
// 09-Apr-2004, Nickolay Samofatov. Adjust number before division to
|
2004-04-10 02:25:22 +02:00
|
|
|
// make sure we don't lose a tick as a result of remainder truncation
|
2007-08-27 23:48:47 +02:00
|
|
|
if (d2 >= 0) {
|
|
|
|
d2 = (d2 * 1000 + (SECONDS_PER_DAY / 200)) / (SINT64) (SECONDS_PER_DAY / 100);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
d2 = (d2 * 1000 - (SECONDS_PER_DAY / 200)) / (SINT64) (SECONDS_PER_DAY / 100);
|
|
|
|
}
|
2004-04-10 02:25:22 +02:00
|
|
|
value->vlu_misc.vlu_int64 = d2;
|
2001-05-23 15:26:42 +02:00
|
|
|
result->dsc_dtype = dtype_int64;
|
|
|
|
result->dsc_length = sizeof(SINT64);
|
|
|
|
result->dsc_scale = DIALECT_3_TIMESTAMP_SCALE;
|
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_int64;
|
|
|
|
return result;
|
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
/* This is dialect 1 subtraction returning double as before */
|
|
|
|
value->vlu_misc.vlu_double =
|
|
|
|
(double) d2 / ((double) ISC_TICKS_PER_DAY);
|
|
|
|
result->dsc_dtype = dtype_double;
|
|
|
|
result->dsc_length = sizeof(double);
|
|
|
|
result->dsc_scale = DIALECT_1_TIMESTAMP_SCALE;
|
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_double;
|
|
|
|
return result;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* From here we know our result must be a <timestamp>. The only
|
|
|
|
legal cases are:
|
|
|
|
<timestamp> +/- <numeric>
|
|
|
|
<numeric> + <timestamp>
|
|
|
|
However, the nod_date flag might have been set on any type of
|
|
|
|
nod_add / nod_subtract equation -- so we must detect any invalid
|
|
|
|
operands. Any <string> value is assumed to be convertable to
|
|
|
|
a timestamp */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
|
|
|
|
{ // This block solves the goto v/s var initialization error
|
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// Coerce operand1 to a count of microseconds
|
2003-12-11 11:33:30 +01:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
bool op1_is_timestamp = false;
|
|
|
|
if ((value->vlu_desc.dsc_dtype == dtype_timestamp)
|
|
|
|
|| (DTYPE_IS_TEXT(value->vlu_desc.dsc_dtype)))
|
|
|
|
{
|
|
|
|
op1_is_timestamp = true;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// Coerce operand2 to a count of microseconds
|
2003-12-11 11:33:30 +01:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
bool op2_is_timestamp = false;
|
|
|
|
if ((desc->dsc_dtype == dtype_timestamp)
|
|
|
|
|| (DTYPE_IS_TEXT(desc->dsc_dtype)))
|
|
|
|
{
|
|
|
|
op2_is_timestamp = true;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-10 17:24:22 +02:00
|
|
|
// Exactly one of the operands must be a timestamp or
|
|
|
|
// convertable into a timestamp, otherwise it's one of
|
|
|
|
// <numeric> +/- <numeric>
|
|
|
|
// or <timestamp> +/- <timestamp>
|
|
|
|
// or <string> +/- <string>
|
|
|
|
// which are errors
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
if (op1_is_timestamp == op2_is_timestamp)
|
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
if (op1_is_timestamp) {
|
|
|
|
d1 = get_timestamp_to_isc_ticks(&value->vlu_desc);
|
|
|
|
d2 = get_day_fraction(desc);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fb_assert((node->nod_type == nod_add) || (node->nod_type == nod_add2));
|
|
|
|
fb_assert(op2_is_timestamp);
|
|
|
|
d1 = get_day_fraction(&value->vlu_desc);
|
|
|
|
d2 = get_timestamp_to_isc_ticks(desc);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// Perform the operation
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
if ((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2)) {
|
|
|
|
fb_assert(op1_is_timestamp);
|
|
|
|
d2 = d1 - d2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
d2 = d1 + d2;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// Convert the count of microseconds back to a date / time format
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_date = d2 / (ISC_TICKS_PER_DAY);
|
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_time = (d2 % ISC_TICKS_PER_DAY);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-26 11:21:04 +01:00
|
|
|
if (!Firebird::TimeStamp::isValidTimeStamp(value->vlu_misc.vlu_timestamp)) {
|
2008-03-28 14:21:59 +01:00
|
|
|
ERR_post(isc_datetime_range_exceeded, 0);
|
2006-09-09 10:32:32 +02:00
|
|
|
}
|
2006-07-25 07:35:04 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// Make sure the TIME portion is non-negative
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
if ((SLONG) value->vlu_misc.vlu_timestamp.timestamp_time < 0) {
|
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_time =
|
|
|
|
((SLONG) value->vlu_misc.vlu_timestamp.timestamp_time) +
|
|
|
|
ISC_TICKS_PER_DAY;
|
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_date -= 1;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
} // scope block for goto v/s var initialization error
|
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
return_result:
|
|
|
|
// Caution: target of GOTO
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(value->vlu_misc.vlu_timestamp.timestamp_time >= 0 &&
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_time < ISC_TICKS_PER_DAY);
|
|
|
|
|
|
|
|
result->dsc_dtype = dtype_timestamp;
|
|
|
|
result->dsc_length = type_lengths[result->dsc_dtype];
|
|
|
|
result->dsc_scale = 0;
|
|
|
|
result->dsc_sub_type = 0;
|
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_timestamp;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
static void adjust_text_descriptor(thread_db* tdbb, dsc* desc)
|
|
|
|
{
|
2005-06-24 17:38:31 +02:00
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* a d j u s t _ t e x t _ d e s c r i p t o r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* This function receives a text descriptor with
|
|
|
|
* dsc_length = numberOfCharacters * maxBytesPerChar
|
|
|
|
* and change dsc_length to number of bytes used by the string.
|
|
|
|
*
|
|
|
|
**************************************/
|
2005-05-28 00:45:31 +02:00
|
|
|
if (desc->dsc_dtype == dtype_text)
|
|
|
|
{
|
2005-07-08 05:27:07 +02:00
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
USHORT ttype = INTL_TTYPE(desc);
|
|
|
|
|
2005-06-14 05:16:54 +02:00
|
|
|
CharSet* charSet = INTL_charset_lookup(tdbb, ttype);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
if (charSet->isMultiByte())
|
|
|
|
{
|
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> buffer;
|
|
|
|
|
|
|
|
if (charSet->getFlags() & CHARSET_LEGACY_SEMANTICS)
|
|
|
|
{
|
2006-10-10 21:40:33 +02:00
|
|
|
desc->dsc_length = charSet->substring(TEXT_LEN(desc), desc->dsc_address, TEXT_LEN(desc),
|
2005-05-28 00:45:31 +02:00
|
|
|
buffer.getBuffer(TEXT_LEN(desc) * charSet->maxBytesPerChar()), 0,
|
|
|
|
TEXT_LEN(desc));
|
|
|
|
|
|
|
|
ULONG maxLength = TEXT_LEN(desc) / charSet->maxBytesPerChar();
|
2006-10-10 21:40:33 +02:00
|
|
|
ULONG charLength = charSet->length(desc->dsc_length, desc->dsc_address, true);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
while (charLength > maxLength)
|
|
|
|
{
|
|
|
|
if (desc->dsc_address[desc->dsc_length - 1] == *charSet->getSpace())
|
|
|
|
{
|
|
|
|
--desc->dsc_length;
|
|
|
|
--charLength;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-10-10 21:40:33 +02:00
|
|
|
desc->dsc_length = charSet->substring(TEXT_LEN(desc), desc->dsc_address,
|
2005-05-28 00:45:31 +02:00
|
|
|
TEXT_LEN(desc), buffer.getBuffer(TEXT_LEN(desc)), 0,
|
|
|
|
TEXT_LEN(desc) / charSet->maxBytesPerChar());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* binary_value(thread_db* tdbb, const jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* b i n a r y _ v a l u e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Evaluate a binary value expression.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2004-03-28 11:10:30 +02:00
|
|
|
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* desc1 = EVL_expr(tdbb, node->nod_arg[0]);
|
|
|
|
const ULONG flags = request->req_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* desc2 = EVL_expr(tdbb, node->nod_arg[1]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* restore saved NULL state */
|
|
|
|
|
|
|
|
if (flags & req_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
return NULL;
|
|
|
|
|
2006-04-25 18:22:41 +02:00
|
|
|
// special case: concatenation doesn't need its first argument
|
|
|
|
// being passed in the impure area, as it would double number
|
|
|
|
// of memory allocations
|
|
|
|
if (node->nod_type == nod_concatenate)
|
|
|
|
return concatenate(tdbb, desc1, desc2, impure);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
EVL_make_value(tdbb, desc1, impure);
|
|
|
|
|
|
|
|
switch (node->nod_type) {
|
|
|
|
case nod_add: /* with dialect-1 semantics */
|
|
|
|
case nod_subtract:
|
|
|
|
return add(desc2, node, impure);
|
|
|
|
|
|
|
|
case nod_divide: /* dialect-1 semantics */
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
|
|
|
const double divisor = MOV_get_double(desc2);
|
|
|
|
if (divisor == 0)
|
2008-04-01 14:04:08 +02:00
|
|
|
{
|
2008-04-04 12:13:44 +02:00
|
|
|
ERR_post(isc_arith_except, isc_arg_gds, isc_exception_float_divide_by_zero, 0);
|
2008-04-01 14:04:08 +02:00
|
|
|
}
|
2003-12-11 11:33:30 +01:00
|
|
|
impure->vlu_misc.vlu_double =
|
|
|
|
DOUBLE_DIVIDE(MOV_get_double(desc1), divisor);
|
|
|
|
impure->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(double);
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc;
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_multiply: /* dialect-1 semantics */
|
|
|
|
return multiply(desc2, impure, node);
|
|
|
|
|
|
|
|
case nod_add2: /* with dialect-3 semantics */
|
|
|
|
case nod_subtract2:
|
|
|
|
return add2(desc2, node, impure);
|
|
|
|
|
|
|
|
case nod_multiply2: /* dialect-3 semantics */
|
|
|
|
return multiply2(desc2, impure, node);
|
|
|
|
|
|
|
|
case nod_divide2: /* dialect-3 semantics */
|
|
|
|
return divide2(desc2, impure, node);
|
|
|
|
|
|
|
|
default:
|
|
|
|
BUGCHECK(232); /* msg 232 EVL_expr: invalid operation */
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-13 08:47:52 +02:00
|
|
|
static dsc* cast(thread_db* tdbb, dsc* value, const jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* c a s t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Cast from one datatype to another.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* value is present; make the conversion */
|
|
|
|
|
2004-03-30 06:10:52 +02:00
|
|
|
const Format* format = (Format*) node->nod_arg[e_cast_fmt];
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_desc = format->fmt_desc[0];
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc;
|
|
|
|
if (DTYPE_IS_TEXT(impure->vlu_desc.dsc_dtype)) {
|
2003-12-11 11:33:30 +01:00
|
|
|
USHORT length = DSC_string_length(&impure->vlu_desc);
|
2007-11-02 15:43:11 +01:00
|
|
|
if (length <= 0 && value) {
|
2001-05-23 15:26:42 +02:00
|
|
|
/* cast is a subtype cast only */
|
|
|
|
|
|
|
|
length = DSC_string_length(value);
|
|
|
|
if (impure->vlu_desc.dsc_dtype == dtype_cstring)
|
|
|
|
length++; /* for NULL byte */
|
|
|
|
else if (impure->vlu_desc.dsc_dtype == dtype_varying)
|
|
|
|
length += sizeof(USHORT);
|
|
|
|
impure->vlu_desc.dsc_length = length;
|
|
|
|
}
|
|
|
|
length = impure->vlu_desc.dsc_length;
|
|
|
|
|
|
|
|
/* Allocate a string block of sufficient size. */
|
|
|
|
|
2005-05-12 20:28:04 +02:00
|
|
|
VaryingString* string = impure->vlu_string;
|
2003-12-11 11:33:30 +01:00
|
|
|
if (string && string->str_length < length) {
|
2001-12-24 03:51:06 +01:00
|
|
|
delete string;
|
2001-05-23 15:26:42 +02:00
|
|
|
string = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!string) {
|
2005-05-12 20:28:04 +02:00
|
|
|
string = impure->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(), length) VaryingString();
|
2001-05-23 15:26:42 +02:00
|
|
|
string->str_length = length;
|
|
|
|
}
|
|
|
|
|
|
|
|
impure->vlu_desc.dsc_address = string->str_data;
|
|
|
|
}
|
|
|
|
|
2007-01-17 02:19:01 +01:00
|
|
|
EVL_validate(tdbb, Item(nod_cast), (ItemInfo*) node->nod_arg[e_cast_iteminfo],
|
|
|
|
value, value == NULL || (value->dsc_flags & DSC_null));
|
|
|
|
|
|
|
|
if (value == NULL)
|
|
|
|
{
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->getRequest()->req_flags |= req_null;
|
2007-01-17 02:19:01 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-08-05 21:52:26 +02:00
|
|
|
if (DTYPE_IS_BLOB(value->dsc_dtype) || DTYPE_IS_BLOB(impure->vlu_desc.dsc_dtype))
|
2006-08-13 08:47:52 +02:00
|
|
|
BLB_move(tdbb, value, &impure->vlu_desc, NULL);
|
2006-08-05 21:52:26 +02:00
|
|
|
else
|
2007-03-02 01:46:03 +01:00
|
|
|
MOV_move(tdbb, value, &impure->vlu_desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-12-06 02:02:22 +01:00
|
|
|
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
|
|
|
adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
static void compute_agg_distinct(thread_db* tdbb, jrd_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* c o m p u t e _ a g g _ d i s t i n c t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2005-05-28 00:45:31 +02:00
|
|
|
* Sort/project the values and compute
|
2001-05-23 15:26:42 +02:00
|
|
|
* the aggregate.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2006-04-25 14:54:12 +02:00
|
|
|
const size_t asb_index =
|
|
|
|
(node->nod_type == nod_agg_list_distinct) ? 2 : 1;
|
|
|
|
AggregateSort* asb = (AggregateSort*) node->nod_arg[asb_index];
|
2006-10-07 12:53:01 +02:00
|
|
|
impure_agg_sort* asb_impure =
|
|
|
|
(impure_agg_sort*) ((SCHAR *) request + asb->nod_impure);
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* desc = &asb->asb_desc;
|
2006-10-07 12:53:01 +02:00
|
|
|
impure_value_ex* impure =
|
|
|
|
(impure_value_ex*) ((SCHAR *) request + node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Sort the values already "put" to sort */
|
|
|
|
|
2006-10-07 12:53:01 +02:00
|
|
|
SORT_sort(tdbb, asb_impure->iasb_sort_handle);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Now get the sorted/projected values and compute the aggregate */
|
|
|
|
|
2003-10-08 10:42:48 +02:00
|
|
|
while (true) {
|
2003-12-11 11:33:30 +01:00
|
|
|
UCHAR* data;
|
2006-10-07 12:53:01 +02:00
|
|
|
SORT_get(tdbb, asb_impure->iasb_sort_handle,
|
2004-02-20 07:43:27 +01:00
|
|
|
reinterpret_cast<ULONG**>(&data)
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
|
|
, RSE_get_forward
|
|
|
|
#endif
|
|
|
|
);
|
|
|
|
|
|
|
|
if (data == NULL) {
|
|
|
|
/* we are done, close the sort */
|
2007-12-03 16:46:39 +01:00
|
|
|
SORT_fini(asb_impure->iasb_sort_handle, tdbb->getAttachment());
|
2001-05-23 15:26:42 +02:00
|
|
|
asb_impure->iasb_sort_handle = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-02-09 16:19:21 +01:00
|
|
|
desc->dsc_address = data + (asb->asb_intl ? asb->asb_key_desc[1].skd_offset : 0);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
switch (node->nod_type) {
|
|
|
|
case nod_agg_total_distinct:
|
|
|
|
case nod_agg_average_distinct:
|
|
|
|
++impure->vlux_count;
|
2004-02-20 07:43:27 +01:00
|
|
|
add(desc, node, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_agg_total_distinct2:
|
|
|
|
case nod_agg_average_distinct2:
|
|
|
|
++impure->vlux_count;
|
2004-02-20 07:43:27 +01:00
|
|
|
add2(desc, node, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_agg_count_distinct:
|
|
|
|
++impure->vlux_count;
|
|
|
|
++impure->vlu_misc.vlu_long;
|
|
|
|
break;
|
2001-12-24 03:51:06 +01:00
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
case nod_agg_list_distinct:
|
2007-05-05 21:28:46 +02:00
|
|
|
{
|
|
|
|
if (!impure->vlu_blob)
|
|
|
|
{
|
2007-12-03 16:46:39 +01:00
|
|
|
impure->vlu_blob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
|
2007-05-05 21:28:46 +02:00
|
|
|
&impure->vlu_misc.vlu_bid);
|
|
|
|
impure->vlu_desc.makeBlob(desc->getBlobSubType(), desc->getTextType(),
|
|
|
|
(ISC_QUAD* ) &impure->vlu_misc.vlu_bid);
|
|
|
|
}
|
|
|
|
|
|
|
|
MoveBuffer buffer;
|
|
|
|
UCHAR* temp;
|
|
|
|
int len;
|
|
|
|
|
2006-04-24 18:56:58 +02:00
|
|
|
if (impure->vlux_count) {
|
|
|
|
const dsc* const delimiter = EVL_expr(tdbb, node->nod_arg[1]);
|
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
// mark the result as NULL
|
|
|
|
impure->vlu_desc.dsc_dtype = 0;
|
|
|
|
break;
|
|
|
|
}
|
2007-05-05 21:28:46 +02:00
|
|
|
len = MOV_make_string2(tdbb, delimiter, impure->vlu_desc.getTextType(), &temp, buffer, false);
|
|
|
|
BLB_put_data(tdbb, impure->vlu_blob, temp, len);
|
2006-04-24 18:56:58 +02:00
|
|
|
}
|
|
|
|
++impure->vlux_count;
|
2007-05-05 21:28:46 +02:00
|
|
|
len = MOV_make_string2(tdbb, desc, impure->vlu_desc.getTextType(), &temp, buffer, false);
|
|
|
|
BLB_put_data(tdbb, impure->vlu_blob, temp, len);
|
2006-04-24 18:56:58 +02:00
|
|
|
break;
|
2007-05-05 21:28:46 +02:00
|
|
|
}
|
2006-04-24 18:56:58 +02:00
|
|
|
|
|
|
|
default: // Shut up some warnings
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
2008-02-07 15:45:49 +01:00
|
|
|
|
|
|
|
if (node->nod_type == nod_agg_list_distinct && impure->vlu_blob)
|
|
|
|
{
|
|
|
|
BLB_close(tdbb, impure->vlu_blob);
|
|
|
|
impure->vlu_blob = NULL;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-04-25 18:22:41 +02:00
|
|
|
static dsc* concatenate(thread_db* tdbb,
|
|
|
|
const dsc* value1,
|
|
|
|
const dsc* value2,
|
|
|
|
impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* c o n c a t e n a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2006-08-10 04:53:16 +02:00
|
|
|
* Concatenate two values.
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
dsc desc;
|
2006-08-12 05:17:01 +02:00
|
|
|
DataTypeUtil(tdbb).makeConcatenate(&desc, value1, value2);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
// Both values are present; build the concatenation
|
2006-04-30 20:13:27 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
MoveBuffer temp1;
|
|
|
|
UCHAR* address1 = NULL;
|
|
|
|
USHORT length1 = 0;
|
|
|
|
|
|
|
|
if (!value1->isBlob())
|
2007-02-27 03:36:20 +01:00
|
|
|
length1 = MOV_make_string2(tdbb, value1, desc.getTextType(), &address1, temp1);
|
2006-08-10 04:53:16 +02:00
|
|
|
|
|
|
|
MoveBuffer temp2;
|
|
|
|
UCHAR* address2 = NULL;
|
|
|
|
USHORT length2 = 0;
|
|
|
|
|
|
|
|
if (!value2->isBlob())
|
2007-02-27 03:36:20 +01:00
|
|
|
length2 = MOV_make_string2(tdbb, value2, desc.getTextType(), &address2, temp2);
|
2006-08-10 04:53:16 +02:00
|
|
|
|
|
|
|
if (address1 && address2)
|
2006-04-30 20:13:27 +02:00
|
|
|
{
|
2006-08-10 04:53:16 +02:00
|
|
|
fb_assert(desc.dsc_dtype == dtype_varying);
|
|
|
|
|
|
|
|
if ((ULONG) length1 + (ULONG) length2 > MAX_COLUMN_SIZE - sizeof(USHORT))
|
|
|
|
{
|
|
|
|
ERR_post(isc_concat_overflow, 0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
desc.dsc_dtype = dtype_text;
|
|
|
|
desc.dsc_length = length1 + length2;
|
|
|
|
desc.dsc_address = NULL;
|
|
|
|
|
|
|
|
VaryingString* string = NULL;
|
|
|
|
if (value1->dsc_address == impure->vlu_desc.dsc_address ||
|
|
|
|
value2->dsc_address == impure->vlu_desc.dsc_address)
|
|
|
|
{
|
|
|
|
string = impure->vlu_string;
|
|
|
|
impure->vlu_string = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVL_make_value(tdbb, &desc, impure);
|
|
|
|
UCHAR* p = impure->vlu_desc.dsc_address;
|
|
|
|
|
|
|
|
if (length1) {
|
|
|
|
memcpy(p, address1, length1);
|
|
|
|
p += length1;
|
|
|
|
}
|
|
|
|
if (length2) {
|
|
|
|
memcpy(p, address2, length2);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete string;
|
2006-04-30 20:13:27 +02:00
|
|
|
}
|
|
|
|
else
|
2006-04-24 18:56:58 +02:00
|
|
|
{
|
2006-08-10 04:53:16 +02:00
|
|
|
fb_assert(desc.dsc_dtype == dtype_blob);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
desc.dsc_address = (UCHAR*)&impure->vlu_misc.vlu_bid;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
|
2006-08-10 04:53:16 +02:00
|
|
|
&impure->vlu_misc.vlu_bid);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> buffer;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
if (address1)
|
|
|
|
BLB_put_data(tdbb, newBlob, address1, length1); // first value is not a blob
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Firebird::UCharBuffer bpb;
|
|
|
|
BLB_gen_bpb_from_descs(value1, &desc, bpb);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* blob = BLB_open2(tdbb, tdbb->getRequest()->req_transaction,
|
2006-08-10 04:53:16 +02:00
|
|
|
reinterpret_cast<bid*>(value1->dsc_address), bpb.getCount(), bpb.begin());
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
|
|
|
SLONG len = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCapacity(), false);
|
2006-04-30 20:13:27 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
if (len)
|
|
|
|
BLB_put_data(tdbb, newBlob, buffer.begin(), len);
|
|
|
|
}
|
2004-08-02 21:49:12 +02:00
|
|
|
|
2006-08-10 04:53:16 +02:00
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (address2)
|
|
|
|
BLB_put_data(tdbb, newBlob, address2, length2); // second value is not a blob
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Firebird::UCharBuffer bpb;
|
|
|
|
BLB_gen_bpb_from_descs(value2, &desc, bpb);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* blob = BLB_open2(tdbb, tdbb->getRequest()->req_transaction,
|
2006-08-10 04:53:16 +02:00
|
|
|
reinterpret_cast<bid*>(value2->dsc_address), bpb.getCount(), bpb.begin());
|
|
|
|
|
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
|
|
|
SLONG len = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCapacity(), false);
|
|
|
|
|
|
|
|
if (len)
|
|
|
|
BLB_put_data(tdbb, newBlob, buffer.begin(), len);
|
|
|
|
}
|
|
|
|
|
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
}
|
|
|
|
|
|
|
|
BLB_close(tdbb, newBlob);
|
|
|
|
|
|
|
|
EVL_make_value(tdbb, &desc, impure);
|
2006-04-24 18:56:58 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* dbkey(thread_db* tdbb, const jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* d b k e y ( J R D )
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Make up a dbkey for a record stream. A dbkey is expressed
|
|
|
|
* as an 8 byte character string.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2006-09-07 03:55:49 +02:00
|
|
|
// Get request, record parameter block, and relation for stream
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2004-03-28 11:10:30 +02:00
|
|
|
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2004-03-18 06:56:06 +01:00
|
|
|
const record_param* rpb = &request->req_rpb[(int) (IPTR) node->nod_arg[0]];
|
2006-09-07 03:55:49 +02:00
|
|
|
|
2007-09-27 17:52:47 +02:00
|
|
|
const jrd_rel* relation = rpb->rpb_relation;
|
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// If it doesn't point to a valid record, return NULL
|
2007-09-27 17:52:47 +02:00
|
|
|
if (!rpb->rpb_number.isValid() || !relation)
|
2006-09-07 03:55:49 +02:00
|
|
|
{
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Format dbkey as vector of relation id, record number
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (relation->rel_file) {
|
|
|
|
impure->vlu_misc.vlu_dbkey[0] = rpb->rpb_b_page;
|
|
|
|
impure->vlu_misc.vlu_dbkey[1] = rpb->rpb_b_line;
|
|
|
|
}
|
|
|
|
else {
|
2004-09-28 08:28:38 +02:00
|
|
|
// Initialize first 32 bits of DB_KEY
|
|
|
|
impure->vlu_misc.vlu_dbkey[0] = 0;
|
|
|
|
|
|
|
|
// Now, put relation ID into first 16 bits of DB_KEY
|
|
|
|
// We do not assign it as SLONG because of big-endian machines.
|
|
|
|
*(USHORT*)impure->vlu_misc.vlu_dbkey = relation->rel_id;
|
|
|
|
|
|
|
|
// NS: Encode 40-bit record number. Again, I have no idea why we
|
|
|
|
// increment it by one, but retain algorithm as it were before
|
|
|
|
RecordNumber temp(rpb->rpb_number.getValue() + 1);
|
2007-03-25 18:09:00 +02:00
|
|
|
temp.bid_encode(reinterpret_cast<RecordNumber::Packed*>(impure->vlu_misc.vlu_dbkey));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2006-09-07 03:55:49 +02:00
|
|
|
// Initialize descriptor
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) impure->vlu_misc.vlu_dbkey;
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_text;
|
|
|
|
impure->vlu_desc.dsc_length = 8;
|
2004-05-21 08:16:17 +02:00
|
|
|
impure->vlu_desc.dsc_ttype() = ttype_binary;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* eval_statistical(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* e v a l _ s t a t i s t i c a l
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Evaluate a statistical expression.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-11 11:33:30 +01:00
|
|
|
USHORT* invariant_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* Get started by opening stream */
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* desc = &impure->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (node->nod_flags & nod_invariant) {
|
2002-11-24 14:47:17 +01:00
|
|
|
invariant_flags = & impure->vlu_flags;
|
2001-05-23 15:26:42 +02:00
|
|
|
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 desc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((nod_average2 == node->nod_type)) {
|
|
|
|
impure->vlu_misc.vlu_int64 = 0;
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_int64;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(SINT64);
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_int64;
|
|
|
|
impure->vlu_desc.dsc_scale = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
impure->vlu_misc.vlu_long = 0;
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(SLONG);
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long;
|
|
|
|
}
|
|
|
|
|
2005-12-02 08:35:34 +01:00
|
|
|
RecordSource* rsb = (RecordSource*) node->nod_arg[e_stat_rsb];
|
2004-02-20 07:43:27 +01:00
|
|
|
RSE_open(tdbb, rsb);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
SLONG count = 0;
|
|
|
|
ULONG flag = req_null;
|
|
|
|
double d;
|
|
|
|
|
|
|
|
// Handle each variety separately
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_count:
|
|
|
|
flag = 0;
|
2004-02-20 07:43:27 +01:00
|
|
|
while (RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
++impure->vlu_misc.vlu_long;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2006-02-07 09:08:49 +01:00
|
|
|
/*
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_count2:
|
|
|
|
flag = 0;
|
2004-02-20 07:43:27 +01:00
|
|
|
while (RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
2003-02-13 18:28:38 +01:00
|
|
|
if (!(request->req_flags & req_null)) {
|
2001-05-23 15:26:42 +02:00
|
|
|
++impure->vlu_misc.vlu_long;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
break;
|
2006-02-07 09:08:49 +01:00
|
|
|
*/
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case nod_min:
|
|
|
|
case nod_max:
|
2004-02-20 07:43:27 +01:00
|
|
|
while (RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
dsc* value = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
2003-02-13 18:28:38 +01:00
|
|
|
if (request->req_flags & req_null) {
|
2001-05-23 15:26:42 +02:00
|
|
|
continue;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
int result;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (flag ||
|
|
|
|
((result = MOV_compare(value, desc)) < 0 &&
|
|
|
|
node->nod_type == nod_min) ||
|
2003-02-13 18:28:38 +01:00
|
|
|
(node->nod_type != nod_min && result > 0))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
flag = 0;
|
|
|
|
EVL_make_value(tdbb, value, impure);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_from:
|
2004-02-20 07:43:27 +01:00
|
|
|
if (RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (node->nod_arg[e_stat_default])
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_default]);
|
|
|
|
else
|
2003-11-11 13:19:20 +01:00
|
|
|
ERR_post(isc_from_no_match, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
flag = request->req_flags;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_average: /* total or average with dialect-1 semantics */
|
|
|
|
case nod_total:
|
2004-02-20 07:43:27 +01:00
|
|
|
while (RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
2003-02-13 18:28:38 +01:00
|
|
|
if (request->req_flags & req_null) {
|
2001-05-23 15:26:42 +02:00
|
|
|
continue;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Note: if the field being SUMed or AVERAGEd is short or long,
|
|
|
|
impure will stay long, and the first add() will
|
|
|
|
set the correct scale; if it is approximate numeric,
|
|
|
|
the first add() will convert impure to double. */
|
|
|
|
add(desc, node, impure);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
desc = &impure->vlu_desc;
|
|
|
|
if (node->nod_type == nod_total) {
|
|
|
|
flag = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!count)
|
|
|
|
break;
|
|
|
|
d = MOV_get_double(&impure->vlu_desc);
|
|
|
|
impure->vlu_misc.vlu_double = d / count;
|
|
|
|
impure->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(double);
|
|
|
|
impure->vlu_desc.dsc_scale = 0;
|
|
|
|
flag = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_average2: /* average with dialect-3 semantics */
|
2004-02-20 07:43:27 +01:00
|
|
|
while (RSE_get_record(tdbb, rsb, g_RSE_get_mode))
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
continue;
|
|
|
|
/* Note: if the field being SUMed or AVERAGEd is exact
|
|
|
|
numeric, impure will stay int64, and the first add() will
|
|
|
|
set the correct scale; if it is approximate numeric,
|
|
|
|
the first add() will convert impure to double. */
|
|
|
|
add(desc, node, impure);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
desc = &impure->vlu_desc;
|
|
|
|
if (!count)
|
|
|
|
break;
|
|
|
|
/* We know the sum, but we want the average. To get it, divide
|
|
|
|
the sum by the count. Since count is exact, dividing an int64
|
|
|
|
sum by count should leave an int64 average, while dividing a
|
|
|
|
double sum by count should leave a double average. */
|
|
|
|
if (dtype_int64 == impure->vlu_desc.dsc_dtype)
|
|
|
|
impure->vlu_misc.vlu_int64 /= count;
|
|
|
|
else
|
|
|
|
impure->vlu_misc.vlu_double /= count;
|
|
|
|
flag = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BUGCHECK(233); /* msg 233 eval_statistical: invalid operation */
|
2003-12-11 11:33:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Close stream and return value */
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
RSE_close(tdbb, rsb);
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
request->req_flags |= flag;
|
|
|
|
|
|
|
|
/* If this is an invariant node, save the return value. If the
|
|
|
|
descriptor does not point to the impure area for this node then
|
|
|
|
point this node's descriptor to the correct place; copy the whole
|
|
|
|
structure to be absolutely sure */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_invariant) {
|
|
|
|
*invariant_flags |= VLU_computed;
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
*invariant_flags |= VLU_null;
|
|
|
|
if (desc && (desc != &impure->vlu_desc))
|
|
|
|
impure->vlu_desc = *desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return desc;
|
|
|
|
}
|
|
|
|
|
2004-10-09 03:40:32 +02:00
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
// *************
|
|
|
|
// e x t r a c t
|
|
|
|
// *************
|
|
|
|
// Handles EXTRACT(part FROM date/time/timestamp)
|
|
|
|
static dsc* extract(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
|
|
|
{
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
const ULONG extract_part = (IPTR) node->nod_arg[e_extract_part];
|
|
|
|
const dsc* 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<UCHAR*>(&impure->vlu_misc.vlu_short);
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(SSHORT);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2005-06-11 02:14:55 +02:00
|
|
|
// CVC: Borland used special signaling for nulls in outer joins.
|
|
|
|
if (!value || (request->req_flags & req_null)) {
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
impure->vlu_misc.vlu_short = 0;
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
2008-03-26 11:21:04 +01:00
|
|
|
tm times = {0};
|
|
|
|
int fractions;
|
|
|
|
|
|
|
|
switch (value->dsc_dtype)
|
|
|
|
{
|
2005-06-11 02:14:55 +02:00
|
|
|
case dtype_sql_time:
|
|
|
|
if (extract_part != blr_extract_hour &&
|
|
|
|
extract_part != blr_extract_minute &&
|
2007-07-31 03:22:59 +02:00
|
|
|
extract_part != blr_extract_second &&
|
|
|
|
extract_part != blr_extract_millisecond)
|
2005-06-11 02:14:55 +02:00
|
|
|
{
|
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
|
|
|
}
|
2008-03-26 11:21:04 +01:00
|
|
|
Firebird::TimeStamp::decode_time(*(GDS_TIME *) value->dsc_address,
|
|
|
|
×.tm_hour, ×.tm_min, ×.tm_sec,
|
|
|
|
&fractions);
|
2005-06-11 02:14:55 +02:00
|
|
|
break;
|
2008-03-26 11:21:04 +01:00
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
case dtype_sql_date:
|
|
|
|
if (extract_part == blr_extract_hour ||
|
|
|
|
extract_part == blr_extract_minute ||
|
2007-07-31 03:22:59 +02:00
|
|
|
extract_part == blr_extract_second ||
|
|
|
|
extract_part == blr_extract_millisecond)
|
2005-06-11 02:14:55 +02:00
|
|
|
{
|
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
|
|
|
}
|
2008-03-26 11:21:04 +01:00
|
|
|
Firebird::TimeStamp::decode_date(*(GDS_DATE *) value->dsc_address, ×);
|
2005-06-11 02:14:55 +02:00
|
|
|
break;
|
2008-03-26 11:21:04 +01:00
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
case dtype_timestamp:
|
2008-03-26 11:21:04 +01:00
|
|
|
Firebird::TimeStamp::decode_timestamp(*(GDS_TIMESTAMP *) value->dsc_address,
|
|
|
|
×, &fractions);
|
2005-06-11 02:14:55 +02:00
|
|
|
break;
|
2008-03-26 11:21:04 +01:00
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
default:
|
|
|
|
ERR_post(isc_expression_eval_err, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
USHORT part;
|
|
|
|
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_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
2007-07-31 03:22:59 +02:00
|
|
|
// fall through
|
|
|
|
case blr_extract_millisecond:
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
2005-06-11 02:14:55 +02:00
|
|
|
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 =
|
2008-03-26 11:21:04 +01:00
|
|
|
times.tm_sec * ISC_TIME_SECONDS_PRECISION + fractions;
|
2007-07-31 03:22:59 +02:00
|
|
|
|
|
|
|
if (extract_part == blr_extract_millisecond)
|
|
|
|
(*(ULONG *) impure->vlu_desc.dsc_address) /= ISC_TIME_SECONDS_PRECISION / 1000;
|
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
return &impure->vlu_desc;
|
2007-07-31 03:22:59 +02:00
|
|
|
|
|
|
|
case blr_extract_week:
|
2007-08-01 11:00:20 +02:00
|
|
|
{
|
|
|
|
// Algorithm for Converting Gregorian Dates to ISO 8601 Week Date by Rick McCarty, 1999
|
|
|
|
// http://personal.ecu.edu/mccartyr/ISOwdALG.txt
|
2007-07-31 03:22:59 +02:00
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
const int y = times.tm_year + 1900;
|
|
|
|
const int dayOfYearNumber = times.tm_yday + 1;
|
2007-07-31 03:22:59 +02:00
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
// Find the jan1Weekday for y (Monday=1, Sunday=7)
|
|
|
|
const int yy = (y - 1) % 100;
|
|
|
|
const int c = (y - 1) - yy;
|
|
|
|
const int g = yy + yy / 4;
|
|
|
|
const int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
|
2007-07-31 03:22:59 +02:00
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
// Find the weekday for y m d
|
|
|
|
const int h = dayOfYearNumber + (jan1Weekday - 1);
|
|
|
|
const int weekday = 1 + ((h - 1) % 7);
|
2007-07-31 03:22:59 +02:00
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
// Find if y m d falls in yearNumber y-1, weekNumber 52 or 53
|
|
|
|
int yearNumber, weekNumber;
|
2007-07-31 03:22:59 +02:00
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
if ((dayOfYearNumber <= (8 - jan1Weekday)) && (jan1Weekday > 4))
|
|
|
|
{
|
|
|
|
yearNumber = y - 1;
|
|
|
|
weekNumber = ((jan1Weekday == 5) || ((jan1Weekday == 6) &&
|
|
|
|
Firebird::TimeStamp::isLeapYear(yearNumber))) ? 53 : 52;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
yearNumber = y;
|
2007-07-31 03:22:59 +02:00
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
// Find if y m d falls in yearNumber y+1, weekNumber 1
|
|
|
|
int i = Firebird::TimeStamp::isLeapYear(y) ? 366 : 365;
|
2007-07-31 03:22:59 +02:00
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
if ((i - dayOfYearNumber) < (4 - weekday))
|
|
|
|
{
|
|
|
|
yearNumber = y + 1;
|
|
|
|
weekNumber = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find if y m d falls in yearNumber y, weekNumber 1 through 53
|
|
|
|
if (yearNumber == y)
|
2007-07-31 03:22:59 +02:00
|
|
|
{
|
2007-08-01 11:00:20 +02:00
|
|
|
int j = dayOfYearNumber + (7 - weekday) + (jan1Weekday - 1);
|
|
|
|
weekNumber = j / 7;
|
|
|
|
if (jan1Weekday > 4)
|
|
|
|
weekNumber--;
|
2007-07-31 03:22:59 +02:00
|
|
|
}
|
|
|
|
|
2007-08-01 11:00:20 +02:00
|
|
|
part = weekNumber;
|
|
|
|
}
|
2007-07-31 03:22:59 +02:00
|
|
|
break;
|
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
case blr_extract_yearday:
|
|
|
|
part = times.tm_yday;
|
|
|
|
break;
|
|
|
|
case blr_extract_weekday:
|
|
|
|
part = times.tm_wday;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
part = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*(USHORT *) impure->vlu_desc.dsc_address = part;
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-08 13:10:58 +02:00
|
|
|
static void fini_agg_distinct(thread_db* tdbb, const jrd_nod *const node)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* f i n i _ a g g _ d i s t i n c t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Finalize a sort for distinct aggregate.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2004-10-08 13:10:58 +02:00
|
|
|
jrd_nod* map = node->nod_arg[e_agg_map];
|
|
|
|
|
|
|
|
jrd_nod** ptr;
|
|
|
|
const jrd_nod* const* end;
|
|
|
|
|
|
|
|
for (ptr = map->nod_arg, end = ptr + map->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
const jrd_nod* from = (*ptr)->nod_arg[e_asgn_from];
|
|
|
|
switch (from->nod_type)
|
|
|
|
{
|
|
|
|
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:
|
2006-04-24 18:56:58 +02:00
|
|
|
case nod_agg_list_distinct:
|
2004-10-08 13:10:58 +02:00
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
const size_t asb_index =
|
|
|
|
(from->nod_type == nod_agg_list_distinct) ? 2 : 1;
|
|
|
|
const AggregateSort* asb = (AggregateSort*) from->nod_arg[asb_index];
|
|
|
|
impure_agg_sort* asb_impure = (impure_agg_sort*) ((SCHAR *) request + asb->nod_impure);
|
2007-12-03 16:46:39 +01:00
|
|
|
SORT_fini(asb_impure->iasb_sort_handle, tdbb->getAttachment());
|
2006-09-09 10:32:32 +02:00
|
|
|
asb_impure->iasb_sort_handle = NULL;
|
2004-10-08 13:10:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-10-09 03:40:32 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
static SINT64 get_day_fraction(const dsc* d)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* g e t _ d a y _ f r a c t i o n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Take the input number, assume it represents a fractional
|
|
|
|
* count of days. Convert it to a count of microseconds.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-22 11:00:59 +01:00
|
|
|
dsc result;
|
2001-05-23 15:26:42 +02:00
|
|
|
double result_days;
|
|
|
|
|
|
|
|
result.dsc_dtype = dtype_double;
|
|
|
|
result.dsc_scale = 0;
|
|
|
|
result.dsc_flags = 0;
|
|
|
|
result.dsc_sub_type = 0;
|
|
|
|
result.dsc_length = sizeof(double);
|
2004-02-20 07:43:27 +01:00
|
|
|
result.dsc_address = reinterpret_cast<UCHAR*>(&result_days);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Convert the input number to a double */
|
2003-11-03 18:14:45 +01:00
|
|
|
CVT_move(d, &result, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* There's likely some loss of precision here due to rounding of number */
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// 08-Apr-2004, Nickolay Samofatov. Loss of precision manifested itself as bad
|
2004-04-09 00:29:50 +02:00
|
|
|
// result returned by the following query:
|
|
|
|
//
|
|
|
|
// select (cast('01.01.2004 10:01:00' as timestamp)
|
|
|
|
// -cast('01.01.2004 10:00:00' as timestamp))
|
|
|
|
// +cast('01.01.2004 10:00:00' as timestamp) from rdb$database
|
|
|
|
//
|
2005-05-28 00:45:31 +02:00
|
|
|
// Let's use llrint where it is supported and offset number for other platforms
|
2004-04-09 00:29:50 +02:00
|
|
|
// in hope that compiler rounding mode doesn't get in.
|
|
|
|
|
|
|
|
#ifdef HAVE_LLRINT
|
|
|
|
return llrint(result_days * ISC_TICKS_PER_DAY);
|
|
|
|
#else
|
2007-05-07 15:23:22 +02:00
|
|
|
const double eps = 0.49999999999999;
|
|
|
|
if (result_days >= 0)
|
|
|
|
return (SINT64)(result_days * ISC_TICKS_PER_DAY + eps);
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
return (SINT64)(result_days * ISC_TICKS_PER_DAY - eps);
|
2004-04-09 00:29:50 +02:00
|
|
|
#endif
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* get_mask(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* g e t _ m a s k
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Compute protection mask.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2003-12-11 11:33:30 +01:00
|
|
|
TEXT* p1 = NULL;
|
|
|
|
TEXT* p2 = NULL;
|
|
|
|
const dsc* value = EVL_expr(tdbb, node->nod_arg[0]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
SqlIdentifier relation_name, field_name;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!(request->req_flags & req_null)) {
|
|
|
|
p1 = relation_name;
|
|
|
|
MOV_get_name(value, p1);
|
|
|
|
value = EVL_expr(tdbb, node->nod_arg[1]);
|
|
|
|
if (!(request->req_flags & req_null)) {
|
|
|
|
p2 = field_name;
|
|
|
|
MOV_get_name(value, p2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
2004-06-29 06:37:59 +02:00
|
|
|
// SecurityClass::flags_t is USHORT for now, so it fits in vlu_long.
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_misc.vlu_long = SCL_get_mask(p1, p2);
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(SLONG);
|
|
|
|
impure->vlu_desc.dsc_scale = 0;
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long;
|
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
static SINT64 get_timestamp_to_isc_ticks(const dsc* d)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* g e t _ t i m e s t a m p _ t o _ i s c _ t i c k s
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Take the input value, which is either a timestamp
|
|
|
|
* or a string representing a timestamp. Convert it to
|
|
|
|
* a timestamp, and then return that timestamp as a
|
|
|
|
* count of isc_ticks since the base date and time
|
|
|
|
* in MJD time arithmetic.
|
|
|
|
* ISC_TICKS or isc_ticks are actually deci - milli seconds or
|
2005-05-28 00:45:31 +02:00
|
|
|
* tenthousandth of seconds per day. This is derived from the
|
2001-05-23 15:26:42 +02:00
|
|
|
* ISC_TIME_SECONDS_PRECISION.
|
|
|
|
*
|
|
|
|
**************************************/
|
2003-12-22 11:00:59 +01:00
|
|
|
dsc result;
|
2001-05-23 15:26:42 +02:00
|
|
|
GDS_TIMESTAMP result_timestamp;
|
|
|
|
|
|
|
|
result.dsc_dtype = dtype_timestamp;
|
|
|
|
result.dsc_scale = 0;
|
|
|
|
result.dsc_flags = 0;
|
|
|
|
result.dsc_sub_type = 0;
|
|
|
|
result.dsc_length = sizeof(GDS_TIMESTAMP);
|
2004-02-20 07:43:27 +01:00
|
|
|
result.dsc_address = reinterpret_cast<UCHAR*>(&result_timestamp);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-03 18:14:45 +01:00
|
|
|
CVT_move(d, &result, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return ((SINT64) result_timestamp.timestamp_date) * ISC_TICKS_PER_DAY
|
|
|
|
+ (SINT64) result_timestamp.timestamp_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
static void init_agg_distinct(thread_db* tdbb, const jrd_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* i n i t _ a g g _ d i s t i n c t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Initialize a sort for distinct aggregate.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-04-25 14:54:12 +02:00
|
|
|
const size_t asb_index =
|
|
|
|
(node->nod_type == nod_agg_list_distinct) ? 2 : 1;
|
|
|
|
const AggregateSort* asb = (AggregateSort*) node->nod_arg[asb_index];
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_agg_sort* asb_impure = (impure_agg_sort*) ((char*) request + asb->nod_impure);
|
|
|
|
const sort_key_def* sort_key = asb->asb_key_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-31 10:53:00 +02:00
|
|
|
asb_impure->iasb_sort_handle =
|
2007-05-11 04:07:36 +02:00
|
|
|
SORT_init(tdbb, ROUNDUP_LONG(asb->asb_length),
|
2008-02-09 16:19:21 +01:00
|
|
|
(asb->asb_intl ? 2 : 1), 1, sort_key, reject_duplicate, 0, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* lock_state(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* l o c k _ s t a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Compute state of an attachment id. The values
|
|
|
|
* returned are:
|
|
|
|
*
|
|
|
|
* 0 - value is null
|
|
|
|
* 1 - attachment is gone
|
|
|
|
* 2 - we are that attachment
|
|
|
|
* 3 - attachment is active
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* Initialize descriptor */
|
|
|
|
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long;
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(SLONG);
|
|
|
|
impure->vlu_desc.dsc_scale = 0;
|
|
|
|
|
|
|
|
/* Evaluate attachment id */
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* desc = EVL_expr(tdbb, node->nod_arg[0]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
impure->vlu_misc.vlu_long = 0;
|
|
|
|
else {
|
2003-12-11 11:33:30 +01:00
|
|
|
const SLONG id = MOV_get_long(desc, 0);
|
2006-05-22 00:07:35 +02:00
|
|
|
if (id == PAG_attachment_id(tdbb))
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_misc.vlu_long = 2;
|
|
|
|
else {
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock temp_lock;
|
2001-05-23 15:26:42 +02:00
|
|
|
/* fill out a lock block, zeroing it out first */
|
|
|
|
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_type = LCK_attachment;
|
|
|
|
temp_lock.lck_owner_handle =
|
|
|
|
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
|
|
|
temp_lock.lck_length = sizeof(SLONG);
|
|
|
|
temp_lock.lck_key.lck_long = id;
|
2006-06-20 06:16:50 +02:00
|
|
|
temp_lock.lck_dbb = dbb;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-10-07 11:15:32 +02:00
|
|
|
if (LCK_lock(tdbb, &temp_lock, LCK_write, LCK_NO_WAIT)) {
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_misc.vlu_long = 1;
|
|
|
|
LCK_release(tdbb, &temp_lock);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
impure->vlu_misc.vlu_long = 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-04-16 20:45:40 +02:00
|
|
|
static dsc* low_up_case(thread_db* tdbb, const dsc* value, impure_value* impure,
|
2008-01-16 09:54:50 +01:00
|
|
|
ULONG (TextType::*tt_str_to_case)(ULONG, const UCHAR*, ULONG, UCHAR*))
|
2006-04-16 20:45:40 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* l o w _ u p _ c a s e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Low/up case a string.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
TextType* textType = INTL_texttype_lookup(tdbb, value->getTextType());
|
|
|
|
|
2006-04-16 20:45:40 +02:00
|
|
|
if (value->dsc_dtype == dtype_blob)
|
|
|
|
{
|
|
|
|
EVL_make_value(tdbb, value, impure);
|
|
|
|
|
|
|
|
if (value->dsc_sub_type != isc_blob_text)
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
|
|
|
CharSet* charSet = textType->getCharSet();
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
2006-04-16 20:45:40 +02:00
|
|
|
reinterpret_cast<bid*>(value->dsc_address));
|
|
|
|
|
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> buffer;
|
|
|
|
|
|
|
|
if (charSet->isMultiByte())
|
|
|
|
buffer.getBuffer(blob->blb_length); // alloc space to put entire blob in memory
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
|
2006-04-18 14:40:52 +02:00
|
|
|
&impure->vlu_misc.vlu_bid);
|
2006-04-16 20:45:40 +02:00
|
|
|
|
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
|
|
|
SLONG len = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCapacity(), false);
|
|
|
|
|
|
|
|
if (len)
|
|
|
|
{
|
|
|
|
len = (textType->*tt_str_to_case)(len, buffer.begin(), len, buffer.begin());
|
2006-05-13 20:35:13 +02:00
|
|
|
BLB_put_data(tdbb, newBlob, buffer.begin(), len);
|
2006-04-16 20:45:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BLB_close(tdbb, newBlob);
|
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
USHORT temp[16];
|
|
|
|
USHORT ttype;
|
|
|
|
dsc desc;
|
|
|
|
|
|
|
|
desc.dsc_length =
|
|
|
|
MOV_get_string_ptr(value, &ttype, &desc.dsc_address,
|
|
|
|
reinterpret_cast<vary*>(temp), sizeof(temp));
|
|
|
|
desc.dsc_dtype = dtype_text;
|
|
|
|
INTL_ASSIGN_TTYPE(&desc, ttype);
|
|
|
|
EVL_make_value(tdbb, &desc, impure);
|
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
if (value->isText())
|
|
|
|
{
|
|
|
|
(textType->*tt_str_to_case)(desc.dsc_length, impure->vlu_desc.dsc_address,
|
|
|
|
desc.dsc_length, impure->vlu_desc.dsc_address);
|
|
|
|
}
|
2006-04-16 20:45:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* multiply(const dsc* desc, impure_value* value, const jrd_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* m u l t i p l y
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Multiply two numbers, with SQL dialect-1 semantics
|
|
|
|
* This function can be removed when dialect-3 becomes
|
|
|
|
* the lowest supported dialect. (Version 7.0?)
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* Handle floating arithmetic */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_double) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const double d1 = MOV_get_double(desc);
|
|
|
|
const double d2 = MOV_get_double(&value->vlu_desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_misc.vlu_double = DOUBLE_MULTIPLY(d1, d2);
|
|
|
|
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(double);
|
|
|
|
value->vlu_desc.dsc_scale = 0;
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_double;
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle (oh, ugh) quad arithmetic */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_quad) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const SSHORT scale = NUMERIC_SCALE(value->vlu_desc);
|
|
|
|
const SQUAD q1 = MOV_get_quad(desc, node->nod_scale - scale);
|
|
|
|
const SQUAD q2 = MOV_get_quad(&value->vlu_desc, scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_desc.dsc_dtype = dtype_quad;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(SQUAD);
|
|
|
|
value->vlu_desc.dsc_scale = node->nod_scale;
|
|
|
|
value->vlu_misc.vlu_quad =
|
2003-11-03 18:14:45 +01:00
|
|
|
QUAD_MULTIPLY(q1, q2, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad;
|
|
|
|
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything else defaults to longword */
|
|
|
|
|
2002-06-30 11:58:20 +02:00
|
|
|
/* CVC: With so many problems cropping with dialect 1 and multiplication,
|
|
|
|
I decided to close this Pandora box by incurring in INT64 performance
|
|
|
|
overhead (if noticeable) and try to get the best result. When I read it,
|
|
|
|
this function didn't bother even to check for overflow! */
|
|
|
|
|
|
|
|
#define FIREBIRD_AVOID_DIALECT1_OVERFLOW
|
2003-12-11 11:33:30 +01:00
|
|
|
// SLONG l1, l2;
|
2002-06-30 11:58:20 +02:00
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
const SSHORT scale = NUMERIC_SCALE(value->vlu_desc);
|
|
|
|
const SINT64 i1 = MOV_get_long(desc, node->nod_scale - scale);
|
|
|
|
const SINT64 i2 = MOV_get_long(&value->vlu_desc, scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_desc.dsc_dtype = dtype_long;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(SLONG);
|
|
|
|
value->vlu_desc.dsc_scale = node->nod_scale;
|
2003-12-11 11:33:30 +01:00
|
|
|
const SINT64 rc = i1 * i2;
|
2002-06-30 11:58:20 +02:00
|
|
|
if (rc < MIN_SLONG || rc > MAX_SLONG)
|
|
|
|
{
|
|
|
|
#ifdef FIREBIRD_AVOID_DIALECT1_OVERFLOW
|
|
|
|
value->vlu_misc.vlu_int64 = rc;
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_int64;
|
|
|
|
value->vlu_desc.dsc_dtype = dtype_int64;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(SINT64);
|
|
|
|
value->vlu_misc.vlu_double = MOV_get_double(&value->vlu_desc);
|
|
|
|
/* This is the Borland solution instead of the five lines above.
|
2005-05-28 00:45:31 +02:00
|
|
|
d1 = MOV_get_double (desc);
|
|
|
|
d2 = MOV_get_double (&value->vlu_desc);
|
2002-06-30 11:58:20 +02:00
|
|
|
value->vlu_misc.vlu_double = DOUBLE_MULTIPLY (d1, d2); */
|
|
|
|
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(double);
|
|
|
|
value->vlu_desc.dsc_scale = 0;
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_double;
|
|
|
|
#else
|
|
|
|
ERR_post(isc_exception_integer_overflow, 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value->vlu_misc.vlu_long = (SLONG) rc; /* l1 * l2;*/
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_long;
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* multiply2(const dsc* desc, impure_value* value, const jrd_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* m u l t i p l y 2
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Multiply two numbers, with dialect-3 semantics,
|
|
|
|
* implementing blr_version5 ... blr_multiply.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* Handle floating arithmetic */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_flags & nod_double)
|
|
|
|
{
|
2003-12-22 11:00:59 +01:00
|
|
|
const double d1 = MOV_get_double(desc);
|
|
|
|
const double d2 = MOV_get_double(&value->vlu_desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_misc.vlu_double = DOUBLE_MULTIPLY(d1, d2);
|
|
|
|
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(double);
|
|
|
|
value->vlu_desc.dsc_scale = 0;
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_double;
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle (oh, ugh) quad arithmetic */
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (node->nod_flags & nod_quad)
|
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
const SSHORT scale = NUMERIC_SCALE(value->vlu_desc);
|
2003-12-22 11:00:59 +01:00
|
|
|
const SQUAD q1 = MOV_get_quad(desc, node->nod_scale - scale);
|
|
|
|
const SQUAD q2 = MOV_get_quad(&value->vlu_desc, scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_desc.dsc_dtype = dtype_quad;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(SQUAD);
|
|
|
|
value->vlu_desc.dsc_scale = node->nod_scale;
|
|
|
|
value->vlu_misc.vlu_quad =
|
2003-11-03 18:14:45 +01:00
|
|
|
QUAD_MULTIPLY(q1, q2, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_quad;
|
|
|
|
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything else defaults to int64 */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
const SSHORT scale = NUMERIC_SCALE(value->vlu_desc);
|
2003-02-13 18:28:38 +01:00
|
|
|
const SINT64 i1 = MOV_get_int64(desc, node->nod_scale - scale);
|
|
|
|
const SINT64 i2 = MOV_get_int64(&value->vlu_desc, scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
We need to report an overflow if
|
|
|
|
(i1 * i2 < MIN_SINT64) || (i1 * i2 > MAX_SINT64)
|
|
|
|
which is equivalent to
|
|
|
|
(i1 < MIN_SINT64 / i2) || (i1 > MAX_SINT64 / i2)
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
Unfortunately, a trial division to see whether the multiplication will
|
|
|
|
overflow is expensive: fortunately, we only need perform one division and
|
|
|
|
test for one of the two cases, depending on whether the factors have the
|
|
|
|
same or opposite signs.
|
|
|
|
|
|
|
|
Unfortunately, in C it is unspecified which way division rounds
|
|
|
|
when one or both arguments are negative. (ldiv() is guaranteed to
|
|
|
|
round towards 0, but the standard does not yet require an lldiv()
|
|
|
|
or whatever for 64-bit operands. This makes the problem messy.
|
2007-11-12 15:26:44 +01:00
|
|
|
We use FB_UINT64s for the checking, thus ensuring that our division rounds
|
2001-05-23 15:26:42 +02:00
|
|
|
down. This means that we have to check the sign of the product first
|
|
|
|
in order to know whether the maximum abs(i1*i2) is MAX_SINT64 or
|
|
|
|
(MAX_SINT64+1).
|
|
|
|
|
|
|
|
Of course, if a factor is 0, the product will also be 0, and we don't
|
|
|
|
need a trial-division to be sure the multiply won't overflow.
|
|
|
|
*/
|
|
|
|
|
2007-11-12 15:26:44 +01:00
|
|
|
const FB_UINT64 u1 = (i1 >= 0) ? i1 : -i1; // abs(i1)
|
|
|
|
const FB_UINT64 u2 = (i2 >= 0) ? i2 : -i2; // abs(i2)
|
2003-08-12 11:56:50 +02:00
|
|
|
// largest product
|
2007-11-12 15:26:44 +01:00
|
|
|
const FB_UINT64 u_limit = ((i1 ^ i2) >= 0) ? MAX_SINT64 : (FB_UINT64) MAX_SINT64 + 1;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if ((u1 != 0) && ((u_limit / u1) < u2)) {
|
2001-05-23 15:26:42 +02:00
|
|
|
ERR_post(isc_exception_integer_overflow, 0);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
value->vlu_desc.dsc_dtype = dtype_int64;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(SINT64);
|
|
|
|
value->vlu_desc.dsc_scale = node->nod_scale;
|
|
|
|
value->vlu_misc.vlu_int64 = i1 * i2;
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_int64;
|
|
|
|
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* divide2(const dsc* desc, impure_value* value, const jrd_nod* node)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* d i v i d e 2
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Divide two numbers, with SQL dialect-3 semantics,
|
|
|
|
* as in the blr_version5 ... blr_divide or
|
|
|
|
* blr_version5 ... blr_average ....
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* Handle floating arithmetic */
|
|
|
|
|
|
|
|
if (node->nod_flags & nod_double) {
|
2003-12-11 11:33:30 +01:00
|
|
|
const double d2 = MOV_get_double(desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (d2 == 0.0)
|
2008-04-01 14:04:08 +02:00
|
|
|
{
|
2008-04-04 12:13:44 +02:00
|
|
|
ERR_post(isc_arith_except, isc_arg_gds, isc_exception_float_divide_by_zero, 0);
|
2008-04-01 14:04:08 +02:00
|
|
|
}
|
2003-12-11 11:33:30 +01:00
|
|
|
const double d1 = MOV_get_double(&value->vlu_desc);
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_misc.vlu_double = DOUBLE_DIVIDE(d1, d2);
|
|
|
|
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(double);
|
|
|
|
value->vlu_desc.dsc_scale = 0;
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_double;
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything else defaults to int64 */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the SQL standard, the precision and scale of the quotient of exact
|
|
|
|
* numeric dividend and divisor are implementation-defined: we have defined
|
|
|
|
* the precision as 18 (in other words, an SINT64), and the scale as the
|
|
|
|
* sum of the scales of the two operands. To make this work, we have to
|
|
|
|
* multiply by pow(10, -2* (scale of divisor)).
|
|
|
|
*
|
|
|
|
* To see this, consider the operation n1 / n2, and represent the numbers
|
|
|
|
* by ordered pairs (v1, s1) and (v2, s2), representing respectively the
|
|
|
|
* integer value and the scale of each operation, so that
|
|
|
|
* n1 = v1 * pow(10, s1), and
|
|
|
|
* n2 = v2 * pow(10, s2)
|
|
|
|
* Then the quotient is ...
|
2005-05-28 00:45:31 +02:00
|
|
|
*
|
2001-05-23 15:26:42 +02:00
|
|
|
* v1 * pow(10,s1)
|
|
|
|
* ----------------- = (v1/v2) * pow(10, s1-s2)
|
|
|
|
* v2 * pow(10,s2)
|
2005-05-28 00:45:31 +02:00
|
|
|
*
|
2001-05-23 15:26:42 +02:00
|
|
|
* But we want the scale of the result to be (s1+s2), not (s1-s2)
|
|
|
|
* so we need to multiply by 1 in the form
|
|
|
|
* pow(10, -2 * s2) * pow(20, 2 * s2)
|
|
|
|
* which, after regrouping, gives us ...
|
|
|
|
* = ((v1 * pow(10, -2*s2))/v2) * pow(10, 2*s2) * pow(10, s1-s2)
|
|
|
|
* = ((v1 * pow(10, -2*s2))/v2) * pow(10, 2*s2 + s1 - s2)
|
|
|
|
* = ((v1 * pow(10, -2*s2))/v2) * pow(10, s1 + s2)
|
2005-05-28 00:45:31 +02:00
|
|
|
* or in our ordered-pair notation,
|
2001-05-23 15:26:42 +02:00
|
|
|
* ( v1 * pow(10, -2*s2) / v2, s1 + s2 )
|
|
|
|
*
|
2008-04-06 10:07:00 +02:00
|
|
|
* To maximize the amount of information in the result, we scale up
|
2001-05-23 15:26:42 +02:00
|
|
|
* the dividend as far as we can without causing overflow, then we perform
|
|
|
|
* the division, then do any additional required scaling.
|
|
|
|
*
|
|
|
|
* Who'da thunk that 9th-grade algebra would prove so useful.
|
|
|
|
* -- Chris Jewell, December 1998
|
|
|
|
*/
|
2003-12-11 11:33:30 +01:00
|
|
|
SINT64 i2 = MOV_get_int64(desc, desc->dsc_scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (i2 == 0)
|
2008-04-01 14:04:08 +02:00
|
|
|
{
|
2008-04-04 12:13:44 +02:00
|
|
|
ERR_post(isc_arith_except, isc_arg_gds, isc_exception_integer_divide_by_zero, 0);
|
2008-04-01 14:04:08 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-08 16:24:01 +01:00
|
|
|
SINT64 i1 = MOV_get_int64(&value->vlu_desc, node->nod_scale - desc->dsc_scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* MIN_SINT64 / -1 = (MAX_SINT64 + 1), which overflows in SINT64. */
|
|
|
|
if ((i1 == MIN_SINT64) && (i2 == -1))
|
|
|
|
ERR_post(isc_exception_integer_overflow, 0);
|
|
|
|
|
|
|
|
/* Scale the dividend by as many of the needed powers of 10 as possible
|
|
|
|
without causing an overflow. */
|
2003-12-11 11:33:30 +01:00
|
|
|
int addl_scale = 2 * desc->dsc_scale;
|
2002-06-30 11:58:20 +02:00
|
|
|
if (i1 >= 0)
|
|
|
|
{
|
|
|
|
while ((addl_scale < 0) && (i1 <= MAX_INT64_LIMIT))
|
|
|
|
{
|
|
|
|
i1 *= 10;
|
|
|
|
++addl_scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while ((addl_scale < 0) && (i1 >= MIN_INT64_LIMIT))
|
|
|
|
{
|
|
|
|
i1 *= 10;
|
|
|
|
++addl_scale;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If we couldn't use up all the additional scaling by multiplying the
|
|
|
|
dividend by 10, but there are trailing zeroes in the divisor, we can
|
|
|
|
get the same effect by dividing the divisor by 10 instead. */
|
|
|
|
while ((addl_scale < 0) && (0 == (i2 % 10))) {
|
|
|
|
i2 /= 10;
|
|
|
|
++addl_scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
value->vlu_desc.dsc_dtype = dtype_int64;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(SINT64);
|
|
|
|
value->vlu_desc.dsc_scale = node->nod_scale;
|
|
|
|
value->vlu_misc.vlu_int64 = i1 / i2;
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_int64;
|
|
|
|
|
|
|
|
/* If we couldn't do all the required scaling beforehand without causing
|
|
|
|
an overflow, do the rest of it now. If we get an overflow now, then
|
|
|
|
the result is really too big to store in a properly-scaled SINT64,
|
|
|
|
so report the error. For example, MAX_SINT64 / 1.00 overflows. */
|
2002-06-30 11:58:20 +02:00
|
|
|
if (value->vlu_misc.vlu_int64 >= 0)
|
|
|
|
{
|
|
|
|
while ((addl_scale < 0) && (value->vlu_misc.vlu_int64 <= MAX_INT64_LIMIT))
|
|
|
|
{
|
|
|
|
value->vlu_misc.vlu_int64 *= 10;
|
|
|
|
addl_scale++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while ((addl_scale < 0) && (value->vlu_misc.vlu_int64 >= MIN_INT64_LIMIT))
|
|
|
|
{
|
|
|
|
value->vlu_misc.vlu_int64 *= 10;
|
|
|
|
addl_scale++;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-04-04 12:13:44 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (addl_scale < 0)
|
2008-04-04 12:13:44 +02:00
|
|
|
{
|
|
|
|
ERR_post(isc_arith_except, isc_arg_gds, isc_numeric_out_of_range, 0);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* negate_dsc(thread_db* tdbb, const dsc* desc, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* n e g a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Negate a single value
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
EVL_make_value(tdbb, desc, value);
|
|
|
|
|
|
|
|
switch (value->vlu_desc.dsc_dtype) {
|
|
|
|
case dtype_short:
|
|
|
|
if (value->vlu_misc.vlu_short == MIN_SSHORT)
|
|
|
|
ERR_post(isc_exception_integer_overflow, 0);
|
|
|
|
value->vlu_misc.vlu_short = -value->vlu_misc.vlu_short;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_long:
|
|
|
|
if (value->vlu_misc.vlu_long == MIN_SLONG)
|
|
|
|
ERR_post(isc_exception_integer_overflow, 0);
|
|
|
|
value->vlu_misc.vlu_long = -value->vlu_misc.vlu_long;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_real:
|
|
|
|
value->vlu_misc.vlu_float = -value->vlu_misc.vlu_float;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DEFAULT_DOUBLE:
|
|
|
|
value->vlu_misc.vlu_double = -value->vlu_misc.vlu_double;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_quad:
|
|
|
|
value->vlu_misc.vlu_quad =
|
2003-11-03 18:14:45 +01:00
|
|
|
QUAD_NEGATE(value->vlu_misc.vlu_quad, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_int64:
|
|
|
|
if (value->vlu_misc.vlu_int64 == MIN_SINT64)
|
|
|
|
ERR_post(isc_exception_integer_overflow, 0);
|
|
|
|
value->vlu_misc.vlu_int64 = -value->vlu_misc.vlu_int64;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
value->vlu_misc.vlu_double = -MOV_get_double(&value->vlu_desc);
|
|
|
|
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
|
|
|
value->vlu_desc.dsc_length = sizeof(double);
|
|
|
|
value->vlu_desc.dsc_scale = 0;
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc.vlu_double;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &value->vlu_desc;
|
|
|
|
}
|
2003-02-13 18:28:38 +01:00
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* record_version(thread_db* tdbb, const jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* r e c o r d _ v e r s i o n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Make up a record version for a record stream.
|
2005-05-28 00:45:31 +02:00
|
|
|
* The tid of the record will be used. This will be returned
|
2001-05-23 15:26:42 +02:00
|
|
|
* as a 4 byte character string.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
// Get request, record parameter block for stream
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2006-09-09 10:32:32 +02:00
|
|
|
// Already set by the caller
|
|
|
|
//impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2004-03-18 06:56:06 +01:00
|
|
|
const record_param* rpb = &request->req_rpb[(int) (IPTR) node->nod_arg[0]];
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* If the current transaction has updated the record, the record version
|
|
|
|
* coming in from DSQL will have the original transaction # (or current
|
|
|
|
* transaction if the current transaction updated the record in a different
|
|
|
|
* request.). In these cases, mark the request so that the boolean
|
|
|
|
* to check equality of record version will be forced to evaluate to true.
|
|
|
|
*/
|
|
|
|
|
2007-12-05 01:03:15 +01:00
|
|
|
if (tdbb->getRequest()->req_transaction->tra_number == rpb->rpb_transaction_nr)
|
2003-02-13 18:28:38 +01:00
|
|
|
request->req_flags |= req_same_tx_upd;
|
|
|
|
else
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/* If the transaction is a commit retain, check if the record was
|
|
|
|
* last updated in one of its own prior transactions
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (request->req_transaction->tra_commit_sub_trans)
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2004-09-28 08:28:38 +02:00
|
|
|
if (request->req_transaction->tra_commit_sub_trans->test(rpb->rpb_transaction_nr))
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
|
|
|
request->req_flags |= req_same_tx_upd;
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize descriptor */
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
impure->vlu_misc.vlu_long = rpb->rpb_transaction_nr;
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long;
|
2003-02-13 18:28:38 +01:00
|
|
|
impure->vlu_desc.dsc_dtype = dtype_text;
|
|
|
|
impure->vlu_desc.dsc_length = 4;
|
2004-05-21 08:16:17 +02:00
|
|
|
impure->vlu_desc.dsc_ttype() = ttype_binary;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
static bool reject_duplicate(const UCHAR* data1, const UCHAR* data2, void* user_arg)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* r e j e c t _ d u p l i c a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Callback routine used by sort/project to reject duplicate values.
|
2003-12-11 11:33:30 +01:00
|
|
|
* Particularly dumb routine -- always returns true;
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* scalar(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* s c a l a r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Evaluate a scalar item from an array.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* desc = EVL_expr(tdbb, node->nod_arg[e_scl_field]);
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (desc->dsc_dtype != dtype_array)
|
2006-09-09 10:32:32 +02:00
|
|
|
IBERROR(261); // msg 261 scalar operator used on field which is not an array
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
jrd_nod* list = node->nod_arg[e_scl_subscripts];
|
2006-09-10 11:05:40 +02:00
|
|
|
if (list->nod_count > MAX_ARRAY_DIMENSIONS)
|
|
|
|
ERR_post(isc_array_max_dimensions, isc_arg_number, SLONG(MAX_ARRAY_DIMENSIONS), 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-10 11:05:40 +02:00
|
|
|
SLONG subscripts[MAX_ARRAY_DIMENSIONS];
|
|
|
|
int iter = 0;
|
2003-12-11 11:33:30 +01:00
|
|
|
jrd_nod** ptr = list->nod_arg;
|
|
|
|
for (const jrd_nod* const* const end = ptr + list->nod_count; ptr < end;)
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2004-08-17 14:28:57 +02:00
|
|
|
const dsc* temp = EVL_expr(tdbb, *ptr++);
|
|
|
|
if (temp && !(request->req_flags & req_null))
|
2006-09-09 10:32:32 +02:00
|
|
|
subscripts[iter++] = MOV_get_long(temp, 0);
|
2004-08-17 14:28:57 +02:00
|
|
|
else
|
2001-05-23 15:26:42 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BLB_scalar(tdbb,
|
|
|
|
request->req_transaction,
|
2004-02-20 07:43:27 +01:00
|
|
|
reinterpret_cast<bid*>(desc->dsc_address),
|
2001-05-23 15:26:42 +02:00
|
|
|
list->nod_count,
|
2004-02-20 07:43:27 +01:00
|
|
|
subscripts, impure);
|
2004-03-28 11:10:30 +02:00
|
|
|
// It was subscripts, reinterpret_cast<impure_value*>(&impure->vlu_desc));
|
|
|
|
// but vlu_desc is the first member of impure_value and impure is already
|
|
|
|
// of type impure_value*, so this cast seems nonsense.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-19 07:14:53 +01:00
|
|
|
static bool sleuth(thread_db* tdbb, jrd_nod* node, const dsc* desc1, const dsc* desc2)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* s l e u t h
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Execute SLEUTH operator.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
/* Choose interpretation for the operation */
|
|
|
|
|
2004-03-19 07:14:53 +01:00
|
|
|
USHORT ttype;
|
2005-11-25 16:20:26 +01:00
|
|
|
if (desc1->dsc_dtype == dtype_blob ||
|
|
|
|
desc1->dsc_dtype == dtype_quad)
|
|
|
|
{
|
2004-04-29 19:48:39 +02:00
|
|
|
if (desc1->dsc_sub_type == isc_blob_text)
|
2005-05-28 00:45:31 +02:00
|
|
|
ttype = desc1->dsc_blob_ttype(); /* Load blob character set and collation */
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
|
|
|
ttype = INTL_TTYPE(desc2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ttype = INTL_TTYPE(desc1);
|
|
|
|
|
2006-10-10 21:40:33 +02:00
|
|
|
Collation* obj = INTL_texttype_lookup(tdbb, ttype);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Get operator definition string (control string) */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* desc3 = EVL_expr(tdbb, node->nod_arg[2]);
|
2004-03-19 07:14:53 +01:00
|
|
|
|
|
|
|
UCHAR* p1;
|
2005-05-12 20:28:04 +02:00
|
|
|
MoveBuffer sleuth_str;
|
2007-02-27 03:36:20 +01:00
|
|
|
USHORT l1 = MOV_make_string2(tdbb, desc3, ttype, &p1, sleuth_str);
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Get address and length of search string */
|
2004-03-19 07:14:53 +01:00
|
|
|
UCHAR* p2;
|
2005-05-12 20:28:04 +02:00
|
|
|
MoveBuffer match_str;
|
2007-02-27 03:36:20 +01:00
|
|
|
USHORT l2 = MOV_make_string2(tdbb, desc2, ttype, &p2, match_str);
|
2004-03-19 07:14:53 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Merge search and control strings */
|
2004-03-19 07:14:53 +01:00
|
|
|
UCHAR control[BUFFER_SMALL];
|
2008-01-16 09:54:50 +01:00
|
|
|
SLONG control_length = obj->sleuthMerge(*tdbb->getDefaultPool(), p2, l2, p1, l1, control, BUFFER_SMALL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
/* Note: resulting string from sleuthMerge is either USHORT or UCHAR
|
|
|
|
and never Multibyte (see note in EVL_mb_sleuthCheck) */
|
2004-03-19 07:14:53 +01:00
|
|
|
bool ret_val;
|
2005-05-12 20:28:04 +02:00
|
|
|
MoveBuffer data_str;
|
2005-11-25 16:20:26 +01:00
|
|
|
if (desc1->dsc_dtype != dtype_blob &&
|
|
|
|
desc1->dsc_dtype != dtype_quad)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Source is not a blob, do a simple search */
|
|
|
|
|
2007-02-27 03:36:20 +01:00
|
|
|
l1 = MOV_make_string2(tdbb, desc1, ttype, &p1, data_str);
|
2008-01-16 09:54:50 +01:00
|
|
|
ret_val = obj->sleuthCheck(*tdbb->getDefaultPool(), 0, p1, l1, control, control_length);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Source string is a blob, things get interesting */
|
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
blb* blob =
|
2007-12-03 16:46:39 +01:00
|
|
|
BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
2004-02-20 07:43:27 +01:00
|
|
|
reinterpret_cast<bid*>(desc1->dsc_address));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-19 07:14:53 +01:00
|
|
|
UCHAR buffer[BUFFER_LARGE];
|
|
|
|
ret_val = false;
|
2003-02-13 18:28:38 +01:00
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
l1 = BLB_get_segment(tdbb, blob, buffer, sizeof(buffer));
|
2008-01-16 09:54:50 +01:00
|
|
|
if (obj->sleuthCheck(*tdbb->getDefaultPool(), 0, buffer, l1, control, control_length))
|
2002-06-04 21:56:16 +02:00
|
|
|
{
|
2004-03-19 07:14:53 +01:00
|
|
|
ret_val = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
static bool string_boolean(thread_db* tdbb, jrd_nod* node, dsc* desc1,
|
2004-03-19 07:14:53 +01:00
|
|
|
dsc* desc2, bool computed_invariant)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* s t r i n g _ b o o l e a n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Perform one of the complex string functions CONTAINING, MATCHES,
|
|
|
|
* or STARTS WITH.
|
|
|
|
*
|
|
|
|
**************************************/
|
2008-03-18 14:02:52 +01:00
|
|
|
UCHAR* p1 = NULL;
|
|
|
|
UCHAR* p2 = NULL;
|
2005-05-28 00:45:31 +02:00
|
|
|
SLONG l2 = 0;
|
2004-03-19 07:14:53 +01:00
|
|
|
USHORT type1;
|
2005-05-12 20:28:04 +02:00
|
|
|
MoveBuffer match_str;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2003-12-27 05:37:23 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (desc1->dsc_dtype != dtype_blob && desc1->dsc_dtype != dtype_quad)
|
2005-11-25 16:20:26 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Source is not a blob, do a simple search */
|
|
|
|
|
|
|
|
/* Get text type of data string */
|
|
|
|
|
|
|
|
type1 = INTL_TEXT_TYPE(*desc1);
|
|
|
|
|
|
|
|
/* Get address and length of search string - convert to datatype of data */
|
|
|
|
|
2003-12-27 05:37:23 +01:00
|
|
|
if (!computed_invariant) {
|
2007-02-27 03:36:20 +01:00
|
|
|
l2 = MOV_make_string2(tdbb, desc2, type1, &p2, match_str);
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
UCHAR temp1[256];
|
2004-03-19 07:14:53 +01:00
|
|
|
USHORT xtype1;
|
2007-01-29 04:00:09 +01:00
|
|
|
const USHORT l1 =
|
2001-05-23 15:26:42 +02:00
|
|
|
MOV_get_string_ptr(desc1, &xtype1, &p1,
|
2004-02-20 07:43:27 +01:00
|
|
|
reinterpret_cast<vary*>(temp1),
|
2004-05-03 23:43:56 +02:00
|
|
|
sizeof(temp1));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(xtype1 == type1);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
return string_function(tdbb, node, l1, p1, l2, p2, type1, computed_invariant);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Source string is a blob, things get interesting */
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> buffer;
|
2004-05-22 01:26:38 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (desc1->dsc_sub_type == isc_blob_text)
|
|
|
|
type1 = desc1->dsc_blob_ttype(); /* pick up character set and collation of blob */
|
|
|
|
else
|
|
|
|
type1 = ttype_none; /* Do byte matching */
|
|
|
|
|
|
|
|
Collation* obj = INTL_texttype_lookup(tdbb, type1);
|
|
|
|
CharSet* charset = obj->getCharSet();
|
|
|
|
|
|
|
|
// Get address and length of search string - make it string if necessary
|
|
|
|
// but don't transliterate character set if the source blob is binary
|
|
|
|
UCHAR temp2[256];
|
|
|
|
if (!computed_invariant) {
|
|
|
|
if (type1 == ttype_none) {
|
|
|
|
l2 =
|
|
|
|
MOV_get_string(desc2, &p2, reinterpret_cast<vary*>(temp2),
|
|
|
|
sizeof(temp2));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
else {
|
|
|
|
l2 =
|
|
|
|
MOV_make_string2(tdbb, desc2, type1, &p2, match_str);
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
blb* blob = BLB_open(tdbb, request->req_transaction,
|
|
|
|
reinterpret_cast<bid*>(desc1->dsc_address));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (charset->isMultiByte() &&
|
|
|
|
(node->nod_type != nod_starts || !(obj->getFlags() & TEXTTYPE_DIRECT_MATCH)))
|
|
|
|
{
|
|
|
|
buffer.getBuffer(blob->blb_length); // alloc space to put entire blob in memory
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
/* Performs the string_function on each segment of the blob until
|
|
|
|
a positive result is obtained */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
bool ret_val = false;
|
|
|
|
|
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_like:
|
|
|
|
case nod_similar:
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
UCHAR temp3[TEMP_LENGTH];
|
|
|
|
const UCHAR* escape_str = NULL;
|
|
|
|
USHORT escape_length = 0;
|
|
|
|
|
|
|
|
/* ensure 3rd argument (escape char) is in operation text type */
|
|
|
|
if (node->nod_count == 3 && !computed_invariant) {
|
|
|
|
/* Convert ESCAPE to operation character set */
|
|
|
|
DSC* dsc = EVL_expr(tdbb, node->nod_arg[2]);
|
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
if (node->nod_flags & nod_invariant) {
|
|
|
|
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
|
|
|
impure->vlu_flags |= VLU_computed;
|
|
|
|
impure->vlu_flags |= VLU_null;
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
ret_val = false;
|
|
|
|
break;
|
|
|
|
}
|
2004-03-19 07:14:53 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
escape_length = MOV_make_string(dsc,
|
|
|
|
type1, reinterpret_cast<const char**>(&escape_str), (vary*) temp3,
|
|
|
|
sizeof(temp3));
|
|
|
|
if (!escape_length || charset->length(escape_length, escape_str, true) != 1)
|
|
|
|
{
|
|
|
|
/* If characters left, or null byte character, return error */
|
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
ERR_post(isc_escape_invalid, 0);
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
USHORT escape[2] = {0, 0};
|
2003-12-27 05:37:23 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
charset->getConvToUnicode().convert(escape_length, escape_str, sizeof(escape), escape);
|
|
|
|
if (!escape[0])
|
|
|
|
{
|
|
|
|
/* If or null byte character, return error */
|
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
ERR_post(isc_escape_invalid, 0);
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PatternMatcher* evaluator;
|
2003-12-27 05:37:23 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
|
|
|
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2008-01-16 09:54:50 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
delete reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
|
|
|
impure->vlu_flags |= VLU_computed;
|
2008-01-16 09:54:50 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (node->nod_type == nod_like)
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
impure->vlu_misc.vlu_invariant = evaluator =
|
|
|
|
obj->createLikeMatcher(*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length);
|
2003-12-31 17:09:07 +01:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
else // nod_similar
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
impure->vlu_misc.vlu_invariant = evaluator =
|
|
|
|
obj->createSimilarToMatcher(*tdbb->getDefaultPool(),
|
|
|
|
p2, l2, escape_str, escape_length);
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
else
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
evaluator = reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
|
|
|
evaluator->reset();
|
2008-01-16 09:54:50 +01:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
}
|
|
|
|
else if (node->nod_type == nod_like)
|
|
|
|
{
|
|
|
|
evaluator = obj->createLikeMatcher(*tdbb->getDefaultPool(),
|
|
|
|
p2, l2, escape_str, escape_length);
|
|
|
|
}
|
|
|
|
else // nod_similar
|
|
|
|
{
|
|
|
|
evaluator = obj->createSimilarToMatcher(*tdbb->getDefaultPool(),
|
|
|
|
p2, l2, escape_str, escape_length);
|
|
|
|
}
|
2003-12-27 05:37:23 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
|
|
|
const SLONG l1 = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCapacity(), false);
|
|
|
|
if (!evaluator->process(buffer.begin(), l1))
|
|
|
|
break;
|
|
|
|
}
|
2003-12-27 05:37:23 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
ret_val = evaluator->result();
|
2008-01-16 09:54:50 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (!(node->nod_flags & nod_invariant))
|
|
|
|
delete evaluator;
|
|
|
|
}
|
|
|
|
break;
|
2008-01-16 09:54:50 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
case nod_contains:
|
|
|
|
case nod_starts:
|
|
|
|
{
|
|
|
|
PatternMatcher* evaluator;
|
|
|
|
if (node->nod_flags & nod_invariant)
|
2003-12-27 05:37:23 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
delete reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
2008-01-16 09:54:50 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
if (node->nod_type == nod_contains)
|
|
|
|
{
|
|
|
|
impure->vlu_misc.vlu_invariant = evaluator = obj->createContainsMatcher(
|
|
|
|
*tdbb->getDefaultPool(), p2, l2);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
else // nod_starts
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
impure->vlu_misc.vlu_invariant = evaluator = obj->createStartsMatcher(
|
|
|
|
*tdbb->getDefaultPool(), p2, l2);
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
|
|
|
|
impure->vlu_flags |= VLU_computed;
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
|
|
|
else
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
evaluator = reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
|
|
|
evaluator->reset();
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (node->nod_type == nod_contains)
|
|
|
|
evaluator = obj->createContainsMatcher(*tdbb->getDefaultPool(), p2, l2);
|
|
|
|
else // nod_starts
|
|
|
|
evaluator = obj->createStartsMatcher(*tdbb->getDefaultPool(), p2, l2);
|
|
|
|
}
|
2003-12-27 05:37:23 +01:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
|
|
|
const SLONG l1 = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCapacity(), false);
|
|
|
|
if (!evaluator->process(buffer.begin(), l1))
|
|
|
|
break;
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
ret_val = evaluator->result();
|
|
|
|
if (!(node->nod_flags & nod_invariant))
|
|
|
|
delete evaluator;
|
|
|
|
}
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
static bool string_function(thread_db* tdbb,
|
|
|
|
jrd_nod* node,
|
|
|
|
SLONG l1, const UCHAR* p1,
|
|
|
|
SLONG l2, const UCHAR* p2,
|
|
|
|
USHORT ttype, bool computed_invariant)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* s t r i n g _ f u n c t i o n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2008-01-16 09:54:50 +01:00
|
|
|
* Perform one of the pattern matching string functions.
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2003-12-27 05:37:23 +01:00
|
|
|
|
2006-10-10 21:40:33 +02:00
|
|
|
Collation* obj = INTL_texttype_lookup(tdbb, ttype);
|
2005-05-28 00:45:31 +02:00
|
|
|
CharSet* charset = obj->getCharSet();
|
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
// Handle contains and starts
|
|
|
|
if (node->nod_type == nod_contains || node->nod_type == nod_starts)
|
|
|
|
{
|
|
|
|
if (node->nod_flags & nod_invariant)
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
2008-01-16 09:54:50 +01:00
|
|
|
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
|
|
|
PatternMatcher* evaluator;
|
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
|
|
|
{
|
|
|
|
delete reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
if (node->nod_type == nod_contains)
|
|
|
|
{
|
|
|
|
impure->vlu_misc.vlu_invariant = evaluator = obj->createContainsMatcher(
|
|
|
|
*tdbb->getDefaultPool(), p2, l2);
|
|
|
|
}
|
|
|
|
else // nod_starts
|
|
|
|
{
|
|
|
|
impure->vlu_misc.vlu_invariant = evaluator = obj->createStartsMatcher(
|
|
|
|
*tdbb->getDefaultPool(), p2, l2);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-27 05:37:23 +01:00
|
|
|
impure->vlu_flags |= VLU_computed;
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
evaluator = reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
2004-04-25 04:30:32 +02:00
|
|
|
evaluator->reset();
|
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
evaluator->process(p1, l1);
|
2004-04-25 04:30:32 +02:00
|
|
|
return evaluator->result();
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
if (node->nod_type == nod_contains)
|
|
|
|
return obj->contains(*tdbb->getDefaultPool(), p1, l1, p2, l2);
|
|
|
|
|
|
|
|
// nod_starts
|
|
|
|
return obj->starts(*tdbb->getDefaultPool(), p1, l1, p2, l2);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
// Handle LIKE and SIMILAR
|
|
|
|
if (node->nod_type == nod_like || node->nod_type == nod_similar)
|
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
UCHAR temp3[TEMP_LENGTH];
|
|
|
|
const UCHAR* escape_str = NULL;
|
|
|
|
USHORT escape_length = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
/* ensure 3rd argument (escape char) is in operation text type */
|
2003-12-27 05:37:23 +01:00
|
|
|
if (node->nod_count == 3 && !computed_invariant) {
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Convert ESCAPE to operation character set */
|
2003-12-27 05:37:23 +01:00
|
|
|
DSC* dsc = EVL_expr(tdbb, node->nod_arg[2]);
|
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
if (node->nod_flags & nod_invariant) {
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2003-12-27 05:37:23 +01:00
|
|
|
impure->vlu_flags |= VLU_computed;
|
|
|
|
impure->vlu_flags |= VLU_null;
|
|
|
|
}
|
2004-03-19 07:14:53 +01:00
|
|
|
return false;
|
2003-12-27 05:37:23 +01:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
escape_length = MOV_make_string(dsc,
|
|
|
|
ttype, reinterpret_cast<const char**>(&escape_str), (vary*) temp3,
|
2004-05-03 23:43:56 +02:00
|
|
|
sizeof(temp3));
|
2006-10-10 21:40:33 +02:00
|
|
|
if (!escape_length || charset->length(escape_length, escape_str, true) != 1)
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
|
|
|
/* If characters left, or null byte character, return error */
|
2008-01-16 09:54:50 +01:00
|
|
|
ERR_post(isc_escape_invalid, 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
USHORT escape[2] = {0, 0};
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-10-10 21:40:33 +02:00
|
|
|
charset->getConvToUnicode().convert(escape_length, escape_str, sizeof(escape), escape);
|
2005-05-28 00:45:31 +02:00
|
|
|
if (!escape[0])
|
|
|
|
{
|
|
|
|
/* If or null byte character, return error */
|
2008-01-16 09:54:50 +01:00
|
|
|
ERR_post(isc_escape_invalid, 0);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2004-03-28 11:10:30 +02:00
|
|
|
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
|
2008-01-16 09:54:50 +01:00
|
|
|
PatternMatcher* evaluator;
|
|
|
|
|
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
|
|
|
{
|
|
|
|
delete reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
2003-12-27 05:37:23 +01:00
|
|
|
impure->vlu_flags |= VLU_computed;
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
if (node->nod_type == nod_like)
|
|
|
|
{
|
|
|
|
impure->vlu_misc.vlu_invariant = evaluator = obj->createLikeMatcher(
|
|
|
|
*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length);
|
|
|
|
}
|
|
|
|
else // nod_similar
|
|
|
|
{
|
|
|
|
impure->vlu_misc.vlu_invariant = evaluator = obj->createSimilarToMatcher(
|
|
|
|
*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length);
|
|
|
|
}
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
evaluator = reinterpret_cast<PatternMatcher*>(impure->vlu_misc.vlu_invariant);
|
2004-04-25 04:30:32 +02:00
|
|
|
evaluator->reset();
|
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
evaluator->process(p1, l1);
|
|
|
|
|
2004-04-25 04:30:32 +02:00
|
|
|
return evaluator->result();
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
|
|
|
|
if (node->nod_type == nod_like)
|
|
|
|
return obj->like(*tdbb->getDefaultPool(), p1, l1, p2, l2, escape_str, escape_length);
|
|
|
|
|
|
|
|
// nod_similar
|
|
|
|
return obj->similarTo(*tdbb->getDefaultPool(), p1, l1, p2, l2, escape_str, escape_length);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2008-01-16 09:54:50 +01:00
|
|
|
// Handle MATCHES
|
|
|
|
return obj->matches(*tdbb->getDefaultPool(), p1, l1, p2, l2);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
// *************************
|
|
|
|
// s t r i n g _ l e n g t h
|
|
|
|
// *************************
|
|
|
|
// Handles BIT_LENGTH(s), OCTET_LENGTH(s) and CHAR[ACTER]_LENGTH(s)
|
|
|
|
static dsc* string_length(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
|
|
|
{
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
DEV_BLKCHK(node, type_nod);
|
|
|
|
|
|
|
|
const ULONG length_type = (IPTR) node->nod_arg[e_strlen_type];
|
|
|
|
const dsc* value = EVL_expr(tdbb, node->nod_arg[e_strlen_value]);
|
|
|
|
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
|
|
|
impure->vlu_desc.dsc_scale = 0;
|
|
|
|
impure->vlu_desc.dsc_address =
|
|
|
|
reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2005-06-11 02:14:55 +02:00
|
|
|
// CVC: Borland used special signaling for nulls in outer joins.
|
|
|
|
if (!value || (request->req_flags & req_null)) {
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
impure->vlu_misc.vlu_long = 0;
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG length;
|
|
|
|
|
2005-11-25 16:20:26 +01:00
|
|
|
if (value->dsc_dtype == dtype_blob ||
|
|
|
|
value->dsc_dtype == dtype_quad)
|
2005-06-11 02:14:55 +02:00
|
|
|
{
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
2005-06-11 02:14:55 +02:00
|
|
|
reinterpret_cast<bid*>(value->dsc_address));
|
|
|
|
|
|
|
|
switch (length_type)
|
|
|
|
{
|
|
|
|
case blr_strlen_bit:
|
|
|
|
{
|
2007-11-12 15:26:44 +01:00
|
|
|
FB_UINT64 l = (FB_UINT64) blob->blb_length * 8;
|
2005-06-11 02:14:55 +02:00
|
|
|
if (l > MAX_SINT64)
|
2008-04-04 12:13:44 +02:00
|
|
|
{
|
|
|
|
ERR_post(isc_arith_except, isc_arg_gds, isc_numeric_out_of_range, 0);
|
|
|
|
}
|
2005-06-11 02:14:55 +02:00
|
|
|
|
|
|
|
length = l;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_strlen_octet:
|
|
|
|
length = blob->blb_length;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_strlen_char:
|
|
|
|
{
|
2005-06-14 05:16:54 +02:00
|
|
|
CharSet* charSet = INTL_charset_lookup(tdbb, value->dsc_blob_ttype());
|
2005-06-11 02:14:55 +02:00
|
|
|
|
|
|
|
if (charSet->isMultiByte())
|
|
|
|
{
|
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> buffer;
|
|
|
|
|
|
|
|
length = BLB_get_data(tdbb, blob, buffer.getBuffer(blob->blb_length), blob->blb_length, false);
|
2006-10-10 21:40:33 +02:00
|
|
|
length = charSet->length(length, buffer.begin(), true);
|
2005-06-11 02:14:55 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
length = blob->blb_length / charSet->maxBytesPerChar();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*(ULONG*)impure->vlu_desc.dsc_address = length;
|
|
|
|
|
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
UCHAR temp[32];
|
|
|
|
USHORT ttype;
|
|
|
|
UCHAR* p;
|
|
|
|
|
|
|
|
length = MOV_get_string_ptr(value, &ttype, &p, reinterpret_cast<vary*>(temp), sizeof(temp));
|
|
|
|
|
|
|
|
switch (length_type)
|
|
|
|
{
|
|
|
|
case blr_strlen_bit:
|
|
|
|
{
|
2007-11-12 15:26:44 +01:00
|
|
|
FB_UINT64 l = (FB_UINT64) length * 8;
|
2005-06-11 02:14:55 +02:00
|
|
|
if (l > MAX_SINT64)
|
2008-04-04 12:13:44 +02:00
|
|
|
{
|
|
|
|
ERR_post(isc_arith_except, isc_arg_gds, isc_numeric_out_of_range, 0);
|
|
|
|
}
|
2005-06-11 02:14:55 +02:00
|
|
|
|
|
|
|
length = l;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_strlen_octet:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_strlen_char:
|
|
|
|
{
|
2005-06-14 05:16:54 +02:00
|
|
|
CharSet* charSet = INTL_charset_lookup(tdbb, ttype);
|
2006-10-10 21:40:33 +02:00
|
|
|
length = charSet->length(length, p, true);
|
2005-06-11 02:14:55 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*(ULONG *) impure->vlu_desc.dsc_address = length;
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-22 09:36:55 +02:00
|
|
|
static dsc* substring(thread_db* tdbb, impure_value* impure,
|
2006-08-16 02:09:27 +02:00
|
|
|
dsc* value, const dsc* offset_value, const dsc* length_value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* s u b s t r i n g
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Perform substring function.
|
|
|
|
*
|
|
|
|
**************************************/
|
2007-04-12 17:56:34 +02:00
|
|
|
return SysFunction::substring(tdbb, impure, value, offset_value, length_value);
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static dsc* trim(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* t r i m
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2005-06-11 02:14:55 +02:00
|
|
|
* Perform trim function = TRIM([where what FROM] string)
|
2005-05-28 00:45:31 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
const ULONG specification = (IPTR) node->nod_arg[e_trim_specification];
|
|
|
|
|
2005-06-12 23:20:12 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
dsc* characters = (node->nod_arg[e_trim_characters] ? EVL_expr(tdbb, node->nod_arg[e_trim_characters]) : NULL);
|
|
|
|
if (request->req_flags & req_null)
|
2005-05-28 00:45:31 +02:00
|
|
|
return characters;
|
|
|
|
|
2005-06-12 23:20:12 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
dsc* value = EVL_expr(tdbb, node->nod_arg[e_trim_value]);
|
2005-05-28 00:45:31 +02:00
|
|
|
if (request->req_flags & req_null)
|
|
|
|
return value;
|
|
|
|
|
2006-05-13 03:27:35 +02:00
|
|
|
USHORT ttype = INTL_TEXT_TYPE(*value);
|
2005-06-14 05:16:54 +02:00
|
|
|
TextType* tt = INTL_texttype_lookup(tdbb, ttype);
|
2006-05-13 03:27:35 +02:00
|
|
|
CharSet* cs = tt->getCharSet();
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
const UCHAR* charactersAddress;
|
2005-05-28 00:45:31 +02:00
|
|
|
MoveBuffer charactersBuffer;
|
|
|
|
USHORT charactersLength;
|
|
|
|
|
|
|
|
if (characters)
|
|
|
|
{
|
2005-06-06 10:30:03 +02:00
|
|
|
UCHAR* tempAddress = 0;
|
2007-02-27 03:36:20 +01:00
|
|
|
charactersLength = MOV_make_string2(tdbb, characters, ttype, &tempAddress,
|
2005-05-28 00:45:31 +02:00
|
|
|
charactersBuffer);
|
2005-06-06 10:30:03 +02:00
|
|
|
charactersAddress = tempAddress;
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
charactersLength = tt->getCharSet()->getSpaceLength();
|
2005-06-06 10:30:03 +02:00
|
|
|
charactersAddress = tt->getCharSet()->getSpace();
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> charactersCanonical;
|
|
|
|
charactersCanonical.getBuffer(charactersLength / tt->getCharSet()->minBytesPerChar() * tt->getCanonicalWidth());
|
2005-06-06 10:30:03 +02:00
|
|
|
const SLONG charactersCanonicalLen = tt->canonical(charactersLength, charactersAddress,
|
2005-05-28 00:45:31 +02:00
|
|
|
charactersCanonical.getCount(), charactersCanonical.begin()) * tt->getCanonicalWidth();
|
|
|
|
|
2006-05-13 03:27:35 +02:00
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> blobBuffer;
|
|
|
|
MoveBuffer valueBuffer;
|
|
|
|
UCHAR* valueAddress;
|
|
|
|
ULONG valueLength;
|
|
|
|
|
|
|
|
if (value->dsc_dtype == dtype_blob)
|
|
|
|
{
|
|
|
|
// Source string is a blob, things get interesting.
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
2006-05-13 03:27:35 +02:00
|
|
|
reinterpret_cast<bid*>(value->dsc_address));
|
|
|
|
|
|
|
|
// It's very difficult (and probably not very efficient) to trim a blob in chunks.
|
|
|
|
// So go simple way and always read entire blob in memory.
|
|
|
|
valueAddress = blobBuffer.getBuffer(blob->blb_length);
|
|
|
|
valueLength = BLB_get_data(tdbb, blob, valueAddress, blob->blb_length, true);
|
|
|
|
}
|
|
|
|
else
|
2007-02-27 03:36:20 +01:00
|
|
|
valueLength = MOV_make_string2(tdbb, value, ttype, &valueAddress, valueBuffer);
|
2006-05-13 03:27:35 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> valueCanonical;
|
2006-05-13 03:27:35 +02:00
|
|
|
valueCanonical.getBuffer(valueLength / cs->minBytesPerChar() * tt->getCanonicalWidth());
|
2005-06-06 10:30:03 +02:00
|
|
|
const SLONG valueCanonicalLen = tt->canonical(valueLength, valueAddress,
|
2005-05-28 00:45:31 +02:00
|
|
|
valueCanonical.getCount(), valueCanonical.begin()) * tt->getCanonicalWidth();
|
|
|
|
|
|
|
|
SLONG offsetLead = 0;
|
|
|
|
SLONG offsetTrail = valueCanonicalLen;
|
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
// CVC: Avoid endless loop with zero length trim chars.
|
|
|
|
if (charactersCanonicalLen)
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
2005-06-06 10:30:03 +02:00
|
|
|
if (specification == blr_trim_both || specification == blr_trim_leading)
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
// CVC: Prevent surprises with offsetLead < valueCanonicalLen; it may fail.
|
2005-06-06 10:30:03 +02:00
|
|
|
for (; offsetLead + charactersCanonicalLen <= valueCanonicalLen; offsetLead += charactersCanonicalLen)
|
|
|
|
{
|
|
|
|
if (memcmp(charactersCanonical.begin(), &valueCanonical[offsetLead], charactersCanonicalLen) != 0)
|
|
|
|
break;
|
|
|
|
}
|
2002-06-30 11:58:20 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2005-06-06 10:30:03 +02:00
|
|
|
if (specification == blr_trim_both || specification == blr_trim_trailing)
|
2005-05-28 00:45:31 +02:00
|
|
|
{
|
2005-06-06 10:30:03 +02:00
|
|
|
for (; offsetTrail - charactersCanonicalLen >= offsetLead; offsetTrail -= charactersCanonicalLen)
|
2002-06-30 11:58:20 +02:00
|
|
|
{
|
2005-06-06 10:30:03 +02:00
|
|
|
if (memcmp(charactersCanonical.begin(), &valueCanonical[offsetTrail - charactersCanonicalLen],
|
|
|
|
charactersCanonicalLen) != 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2002-06-30 11:58:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2006-05-13 03:27:35 +02:00
|
|
|
if (value->dsc_dtype == dtype_blob)
|
|
|
|
{
|
|
|
|
// We have valueCanonical already allocated.
|
|
|
|
// Use it to get the substring that will be written to the new blob.
|
2006-10-10 21:40:33 +02:00
|
|
|
ULONG len = cs->substring(valueLength, valueAddress,
|
2006-05-13 03:27:35 +02:00
|
|
|
valueCanonical.getCapacity(), valueCanonical.begin(),
|
|
|
|
offsetLead / tt->getCanonicalWidth(),
|
|
|
|
(offsetTrail - offsetLead) / tt->getCanonicalWidth());
|
|
|
|
|
|
|
|
EVL_make_value(tdbb, value, impure);
|
|
|
|
|
2007-12-03 16:46:39 +01:00
|
|
|
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
|
2006-05-13 03:27:35 +02:00
|
|
|
&impure->vlu_misc.vlu_bid);
|
|
|
|
|
2006-05-13 20:35:13 +02:00
|
|
|
BLB_put_data(tdbb, newBlob, valueCanonical.begin(), len);
|
2006-05-13 03:27:35 +02:00
|
|
|
|
|
|
|
BLB_close(tdbb, newBlob);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dsc desc;
|
|
|
|
desc.dsc_dtype = dtype_text;
|
|
|
|
desc.dsc_sub_type = 0;
|
|
|
|
desc.dsc_scale = 0;
|
|
|
|
desc.dsc_length = valueLength;
|
|
|
|
desc.dsc_address = NULL;
|
|
|
|
INTL_ASSIGN_TTYPE(&desc, ttype);
|
|
|
|
EVL_make_value(tdbb, &desc, impure);
|
|
|
|
|
2006-10-10 21:40:33 +02:00
|
|
|
impure->vlu_desc.dsc_length = cs->substring(valueLength, valueAddress,
|
2006-05-13 03:27:35 +02:00
|
|
|
impure->vlu_desc.dsc_length, impure->vlu_desc.dsc_address,
|
|
|
|
offsetLead / tt->getCanonicalWidth(),
|
|
|
|
(offsetTrail - offsetLead) / tt->getCanonicalWidth());
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* internal_info(thread_db* tdbb, const dsc* value, impure_value* impure)
|
2002-06-14 14:09:37 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* i n t e r n a l _ i n f o
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Return a given element
|
|
|
|
* of the internal engine data.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
EVL_make_value(tdbb, value, impure);
|
|
|
|
|
2004-08-21 11:29:46 +02:00
|
|
|
const internal_info_id id =
|
2002-09-28 16:04:35 +02:00
|
|
|
*reinterpret_cast<internal_info_id*>(impure->vlu_desc.dsc_address);
|
2002-06-14 14:09:37 +02:00
|
|
|
|
2002-09-28 16:04:35 +02:00
|
|
|
switch (id)
|
2002-06-14 14:09:37 +02:00
|
|
|
{
|
2002-09-28 16:04:35 +02:00
|
|
|
case internal_connection_id:
|
2006-05-22 00:07:35 +02:00
|
|
|
impure->vlu_misc.vlu_long = PAG_attachment_id(tdbb);
|
2002-06-14 14:09:37 +02:00
|
|
|
break;
|
2002-09-28 16:04:35 +02:00
|
|
|
case internal_transaction_id:
|
2007-12-03 16:46:39 +01:00
|
|
|
impure->vlu_misc.vlu_long = tdbb->getTransaction()->tra_number;
|
2002-06-14 14:09:37 +02:00
|
|
|
break;
|
2002-09-28 16:04:35 +02:00
|
|
|
case internal_gdscode:
|
|
|
|
impure->vlu_misc.vlu_long =
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->getRequest()->req_last_xcp.as_gdscode();
|
2002-09-28 16:04:35 +02:00
|
|
|
break;
|
|
|
|
case internal_sqlcode:
|
|
|
|
impure->vlu_misc.vlu_long =
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->getRequest()->req_last_xcp.as_sqlcode();
|
2002-09-28 16:04:35 +02:00
|
|
|
break;
|
|
|
|
case internal_rows_affected:
|
2005-06-24 14:56:34 +02:00
|
|
|
impure->vlu_misc.vlu_long =
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->getRequest()->req_records_affected.getCount();
|
2002-09-28 16:04:35 +02:00
|
|
|
break;
|
2003-01-15 13:08:59 +01:00
|
|
|
case internal_trigger_action:
|
2005-06-24 14:56:34 +02:00
|
|
|
impure->vlu_misc.vlu_long =
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->getRequest()->req_trigger_action;
|
2003-01-15 13:08:59 +01:00
|
|
|
break;
|
2002-06-14 14:09:37 +02:00
|
|
|
default:
|
|
|
|
BUGCHECK(232); /* msg 232 EVL_expr: invalid operation */
|
|
|
|
}
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) &impure->vlu_misc.vlu_long;
|
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|