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"
|
2010-02-13 21:29:29 +01:00
|
|
|
#include "../dsql/Nodes.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"
|
2009-04-29 16:00:32 +02:00
|
|
|
#include "../common/classes/VaryStr.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/scl_proto.h"
|
|
|
|
#include "../jrd/gds_proto.h"
|
|
|
|
#include "../jrd/align.h"
|
2002-06-30 11:58:20 +02:00
|
|
|
#include "../jrd/met_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"
|
2009-05-27 09:57:06 +02:00
|
|
|
#include "../common/classes/FpeControl.h"
|
2009-12-09 19:45:44 +01:00
|
|
|
#include "../jrd/recsrc/RecordSource.h"
|
|
|
|
#include "../jrd/recsrc/Cursor.h"
|
2009-11-05 18:04:30 +01:00
|
|
|
#include "../common/classes/Aligner.h"
|
2009-12-21 18:43:01 +01:00
|
|
|
#include "../jrd/Function.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 ***
|
2009-10-31 20:03:30 +01:00
|
|
|
* EVL_add(), multiply(), and divide() all take the same three arguments, but
|
2001-05-23 15:26:42 +02:00
|
|
|
* 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;
|
2008-08-27 14:20:47 +02:00
|
|
|
using namespace Firebird;
|
2004-03-20 15:57:40 +01:00
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
static dsc* add_datetime(const dsc*, const jrd_nod*, impure_value*);
|
|
|
|
static dsc* add_sql_date(const dsc*, const jrd_nod*, impure_value*);
|
|
|
|
static dsc* add_sql_time(const dsc*, const jrd_nod*, impure_value*);
|
|
|
|
static dsc* add_timestamp(const dsc*, const jrd_nod*, impure_value*);
|
|
|
|
static 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-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*);
|
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-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*);
|
|
|
|
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
|
|
|
|
|
|
|
|
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();
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +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;
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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];
|
2010-04-05 23:20:08 +02:00
|
|
|
impure->vlu_desc.dsc_address = request->getImpure<UCHAR>(
|
|
|
|
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) &&
|
2008-12-20 09:12:19 +01:00
|
|
|
((INTL_TTYPE(desc) == ttype_dynamic) || (INTL_GET_CHARSET(desc) == CS_dynamic)))
|
2004-02-24 06:34:44 +01:00
|
|
|
{
|
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. */
|
|
|
|
|
2009-11-28 20:39:23 +01:00
|
|
|
impure->vlu_desc.setTextType(tdbb->getCharSet());
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
|
|
|
case nod_field:
|
2008-11-30 03:23:01 +01:00
|
|
|
record = request->req_rpb[(int) (IPTR) node->nod_arg[e_fld_stream]].rpb_record;
|
|
|
|
if (!EVL_field(0, record, (USHORT)(IPTR) node->nod_arg[e_fld_id], &impure->vlu_desc))
|
2006-03-28 11:55:45 +02:00
|
|
|
{
|
|
|
|
// 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))
|
|
|
|
{
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_field_disappeared));
|
2006-03-28 11:55:45 +02:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!impure->vlu_desc.dsc_address)
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_read_only_field));
|
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];
|
2010-04-05 23:20:08 +02:00
|
|
|
impure = request->getImpure<impure_value>(node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
|
|
|
default:
|
2009-11-22 04:56:20 +01:00
|
|
|
BUGCHECK(229); // msg 229 EVL_assign_to: invalid operation
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_inversion* impure = tdbb->getRequest()->getImpure<impure_inversion>(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];
|
2009-11-05 18:04:30 +01:00
|
|
|
Firebird::Aligner<RecordNumber::Packed> alignedNumbers(desc->dsc_address, desc->dsc_length);
|
|
|
|
const RecordNumber::Packed* numbers = alignedNumbers;
|
2007-07-02 01:13:09 +02:00
|
|
|
RecordNumber rel_dbkey;
|
|
|
|
rel_dbkey.bid_decode(&numbers[id]);
|
2009-08-27 11:45:05 +02:00
|
|
|
// Decrement the value in order to switch back to the zero based numbering
|
|
|
|
// (from the user point of view the DB_KEY numbering starts from one)
|
2007-07-02 01:13:09 +02:00
|
|
|
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:
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_inversion* impure = tdbb->getRequest()->getImpure<impure_inversion>(node->nod_impure);
|
2004-09-28 08:28:38 +02:00
|
|
|
RecordBitmap::reset(impure->inv_bitmap);
|
2010-04-05 23:20:08 +02:00
|
|
|
BTR_evaluate(tdbb, reinterpret_cast<IndexRetrieval*>(node->nod_arg[e_idx_retrieval]),
|
|
|
|
&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:
|
2009-11-22 04:56:20 +01:00
|
|
|
BUGCHECK(230); // msg 230 EVL_bitmap: invalid operation
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
**************************************/
|
2008-11-30 03:23:01 +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;
|
2010-04-05 23:20:08 +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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle and pre-processing possible for various nodes. This includes
|
|
|
|
// evaluating argument and checking NULL flags
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01: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
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
2008-11-30 03:23:01 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure = request->getImpure<impure_value>(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;
|
|
|
|
}
|
|
|
|
|
2008-11-30 03:23:01 +01:00
|
|
|
if (impure->vlu_flags & VLU_computed)
|
|
|
|
{
|
2003-12-27 05:37:23 +01:00
|
|
|
if (impure->vlu_flags & VLU_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
else
|
|
|
|
computed_invariant = true;
|
2005-05-28 00:45:31 +02:00
|
|
|
}
|
2008-11-30 03:23:01 +01:00
|
|
|
else
|
|
|
|
{
|
2003-12-27 05:37:23 +01:00
|
|
|
desc[1] = EVL_expr(tdbb, *ptr++);
|
2008-11-30 03:23:01 +01:00
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
2003-12-27 05:37:23 +01:00
|
|
|
impure->vlu_flags |= VLU_computed;
|
|
|
|
impure->vlu_flags |= VLU_null;
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2008-11-30 03:23:01 +01: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
|
2008-11-30 03:23:01 +01:00
|
|
|
if (desc[0])
|
|
|
|
{
|
2004-07-26 23:32:41 +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;
|
2008-10-03 07:41:16 +02:00
|
|
|
}
|
2008-11-30 03:23:01 +01: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;
|
|
|
|
|
2008-11-30 03:23:01 +01:00
|
|
|
if (node->nod_flags & nod_comparison)
|
2001-05-23 15:26:42 +02:00
|
|
|
comparison = MOV_compare(desc[0], desc[1]);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// If we are checking equality of record_version
|
|
|
|
// and same transaction updated the record, force equality.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-11-30 03:23:01 +01:00
|
|
|
if (rec_version->nod_type == nod_rec_version && force_equal)
|
2001-05-23 15:26:42 +02:00
|
|
|
comparison = 0;
|
|
|
|
|
|
|
|
request->req_flags &= ~(req_null | req_same_tx_upd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_and:
|
|
|
|
value = EVL_boolean(tdbb, *ptr++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_or:
|
|
|
|
value = EVL_boolean(tdbb, *ptr++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nod_not:
|
|
|
|
value = EVL_boolean(tdbb, *ptr++);
|
2003-02-13 18:28:38 +01:00
|
|
|
break;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
default: // Shut up some compiler warnings
|
2003-02-13 18:28:38 +01:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Evaluate node
|
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_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
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// save null state and get other operand
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2009-12-21 08:01:24 +01:00
|
|
|
if (!value && !firstnull)
|
2003-02-21 10:17:03 +01:00
|
|
|
{
|
|
|
|
// 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)) {
|
2009-11-22 04:56:20 +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) {
|
2009-11-22 04:56:20 +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;
|
2009-11-22 04:56:20 +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)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure = request->getImpure<impure_value>(node->nod_impure);
|
2002-11-24 14:47:17 +01:00
|
|
|
invariant_flags = & impure->vlu_flags;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (*invariant_flags & VLU_computed)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// An invariant node has already been computed.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-15 18:10:51 +02:00
|
|
|
RecordSource* const rsb = reinterpret_cast<RecordSource*>(node->nod_arg[e_any_rsb]);
|
|
|
|
rsb->open(tdbb);
|
|
|
|
value = rsb->getRecord(tdbb);
|
|
|
|
rsb->close(tdbb);
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (node->nod_type == nod_any)
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// If this is an invariant node, save the return value.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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;
|
2008-12-20 09:12:19 +01:00
|
|
|
if ((node->nod_type != nod_any) && (request->req_flags & req_null))
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
|
|
|
*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);
|
2009-11-23 06:24:29 +01:00
|
|
|
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
|
|
|
|
2009-12-21 08:01:24 +01:00
|
|
|
if (value)
|
2003-02-21 10:17:03 +01:00
|
|
|
{
|
|
|
|
// 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);
|
2009-11-23 06:24:29 +01:00
|
|
|
if (value || value2)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
2004-03-19 07:14:53 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// restore saved NULL state
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure = request->getImpure<impure_value>(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)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// An invariant node has already been computed.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-15 18:10:51 +02:00
|
|
|
RecordSource* const rsb = reinterpret_cast<RecordSource*>(node->nod_arg[e_any_rsb]);
|
|
|
|
rsb->open(tdbb);
|
|
|
|
|
|
|
|
value = rsb->getRecord(tdbb);
|
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (value)
|
|
|
|
{
|
2010-04-15 18:10:51 +02:00
|
|
|
value = !rsb->getRecord(tdbb);
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2010-04-15 18:10:51 +02:00
|
|
|
|
|
|
|
rsb->close(tdbb);
|
2006-12-30 08:22:46 +01:00
|
|
|
request->req_flags &= ~req_null;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// If this is an invariant node, save the return value.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2008-09-01 15:18:02 +02:00
|
|
|
case nod_stmt_expr:
|
|
|
|
EXE_looper(tdbb, request, node);
|
|
|
|
return EVL_boolean(tdbb, node->nod_arg[e_stmt_expr_expr]);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2009-11-22 04:56:20 +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)
|
2009-11-22 04:56:20 +01:00
|
|
|
BUGCHECK(303); // msg 303 Invalid expression for evaluation
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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();
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* const impure = request->getImpure<impure_value>(node->nod_impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Do a preliminary screen for either simple nodes or nodes that are special cased elsewhere
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
case nod_class_exprnode_jrd:
|
|
|
|
{
|
|
|
|
ExprNode* exprNode = reinterpret_cast<ExprNode*>(node->nod_arg[0]);
|
|
|
|
dsc* desc = exprNode->execute(tdbb, request);
|
|
|
|
|
|
|
|
if (desc)
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
else
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
|
|
|
|
return desc;
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_add:
|
|
|
|
case nod_subtract:
|
|
|
|
case nod_divide:
|
|
|
|
case nod_multiply:
|
|
|
|
case nod_add2:
|
|
|
|
case nod_subtract2:
|
|
|
|
case nod_divide2:
|
|
|
|
case nod_multiply2:
|
|
|
|
return binary_value(tdbb, node, impure);
|
|
|
|
|
|
|
|
case nod_argument:
|
|
|
|
{
|
2003-12-11 11:33:30 +01:00
|
|
|
const dsc* desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_arg[e_arg_flag])
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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
|
|
|
|
2010-04-07 04:04:22 +02:00
|
|
|
impure->vlu_desc.dsc_address = request->getImpure<UCHAR>(
|
|
|
|
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)
|
2009-10-21 02:42:38 +02:00
|
|
|
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2010-04-07 04:04:22 +02:00
|
|
|
USHORT* impure_flags = request->getImpure<USHORT>(
|
2007-02-06 15:25:10 +01:00
|
|
|
(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])
|
|
|
|
{
|
2008-10-03 07:41:16 +02:00
|
|
|
EVL_validate(tdbb,
|
2007-12-08 14:41:26 +01:00
|
|
|
Item(nod_argument, (IPTR) node->nod_arg[e_arg_message]->nod_arg[e_msg_number],
|
|
|
|
(IPTR) node->nod_arg[e_arg_number]),
|
2008-10-03 07:41:16 +02:00
|
|
|
reinterpret_cast<const ItemInfo*>(node->nod_arg[e_arg_info]),
|
2007-12-08 14:41:26 +01:00
|
|
|
&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:
|
|
|
|
{
|
2008-12-01 10:21:31 +01:00
|
|
|
const USHORT id = (USHORT)(IPTR) node->nod_arg[e_fld_id];
|
2008-11-30 19:55:52 +01:00
|
|
|
record_param& rpb = request->req_rpb[(USHORT)(IPTR) node->nod_arg[e_fld_stream]];
|
|
|
|
Record* record = rpb.rpb_record;
|
|
|
|
jrd_rel* relation = rpb.rpb_relation;
|
|
|
|
// In order to "map a null to a default" value (in EVL_field()),
|
|
|
|
// the relation block is referenced.
|
|
|
|
// Reference: Bug 10116, 10424
|
|
|
|
|
|
|
|
if (!EVL_field(relation, record, id, &impure->vlu_desc))
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags |= req_null;
|
2008-11-30 19:55:52 +01:00
|
|
|
else
|
|
|
|
{
|
2008-12-01 10:21:31 +01:00
|
|
|
const Format* compileFormat = (Format*) node->nod_arg[e_fld_format];
|
2008-11-30 19:55:52 +01:00
|
|
|
|
|
|
|
// ASF: CORE-1432 - If the the record is not on the latest format, upgrade it.
|
2010-06-15 13:57:13 +02:00
|
|
|
// AP: for fields that are missing in original format use record's one.
|
2008-11-30 19:55:52 +01:00
|
|
|
if (compileFormat &&
|
|
|
|
record->rec_format->fmt_version != compileFormat->fmt_version &&
|
2010-06-15 13:57:13 +02:00
|
|
|
id < compileFormat->fmt_desc.getCount() &&
|
2008-11-30 19:55:52 +01:00
|
|
|
!DSC_EQUIV(&impure->vlu_desc, &compileFormat->fmt_desc[id], true))
|
|
|
|
{
|
|
|
|
dsc desc = impure->vlu_desc;
|
|
|
|
impure->vlu_desc = compileFormat->fmt_desc[id];
|
|
|
|
|
|
|
|
if (impure->vlu_desc.isText())
|
|
|
|
{
|
|
|
|
// Allocate a string block of sufficient size.
|
|
|
|
VaryingString* string = impure->vlu_string;
|
|
|
|
if (string && string->str_length < impure->vlu_desc.dsc_length)
|
|
|
|
{
|
|
|
|
delete string;
|
|
|
|
string = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!string)
|
|
|
|
{
|
|
|
|
string = impure->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(),
|
|
|
|
impure->vlu_desc.dsc_length) VaryingString();
|
|
|
|
string->str_length = impure->vlu_desc.dsc_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
impure->vlu_desc.dsc_address = string->str_data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc;
|
|
|
|
|
|
|
|
MOV_move(tdbb, &desc, &impure->vlu_desc);
|
|
|
|
}
|
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)
|
2009-10-21 02:42:38 +02:00
|
|
|
INTL_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;
|
|
|
|
}
|
|
|
|
|
2008-09-07 21:44:48 +02:00
|
|
|
case nod_derived_expr:
|
|
|
|
{
|
2009-11-23 06:24:29 +01:00
|
|
|
const UCHAR streamCount = (UCHAR)(IPTR) node->nod_arg[e_derived_expr_stream_count];
|
|
|
|
const USHORT* streamList = (USHORT*) node->nod_arg[e_derived_expr_stream_list];
|
2008-09-07 21:44:48 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
for (UCHAR i = 0; i < streamCount; ++i)
|
|
|
|
{
|
|
|
|
if (request->req_rpb[streamList[i]].rpb_number.isValid())
|
|
|
|
return EVL_expr(tdbb, node->nod_arg[e_derived_expr_expr]);
|
|
|
|
}
|
|
|
|
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-09-07 21:44:48 +02:00
|
|
|
|
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));
|
2008-12-20 09:12:19 +01:00
|
|
|
impure->vlu_desc.dsc_address = (UCHAR *) &impure->vlu_misc.vlu_timestamp;
|
2003-12-11 11:33:30 +01:00
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
if (node->nod_type == nod_current_time || node->nod_type == nod_current_timestamp)
|
2005-08-24 11:16:19 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_current_time:
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_sql_time;
|
|
|
|
impure->vlu_desc.dsc_length = type_lengths[dtype_sql_time];
|
2008-12-20 09:12:19 +01:00
|
|
|
*(ULONG *) impure->vlu_desc.dsc_address = 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];
|
2008-12-20 09:12:19 +01:00
|
|
|
*(ULONG *) impure->vlu_desc.dsc_address = enc_times.timestamp_date;
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2009-11-28 20:39:23 +01:00
|
|
|
impure->vlu_desc.setTextType(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();
|
2008-12-20 09:12:19 +01: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;
|
2009-11-28 20:39:23 +01:00
|
|
|
impure->vlu_desc.setTextType(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:
|
|
|
|
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
|
|
|
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure2 = request->getImpure<impure_value>(node2->nod_impure);
|
2005-06-06 10:30:03 +02:00
|
|
|
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)
|
2009-10-21 02:42:38 +02:00
|
|
|
INTL_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]),
|
2008-10-03 07:41:16 +02:00
|
|
|
reinterpret_cast<const ItemInfo*>(node->nod_arg[e_var_info]),
|
2007-12-08 14:41:26 +01:00
|
|
|
&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])) ?
|
2008-12-20 09:12:19 +01:00
|
|
|
node->nod_arg[1] : node->nod_arg[2]);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
case nod_trim:
|
|
|
|
return trim(tdbb, node, impure);
|
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
default: // Shut up some compiler warnings
|
|
|
|
break;
|
2008-09-01 15:18:02 +02:00
|
|
|
|
|
|
|
case nod_stmt_expr:
|
|
|
|
EXE_looper(tdbb, request, node);
|
|
|
|
return EVL_expr(tdbb, node->nod_arg[e_stmt_expr_expr]);
|
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
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_count)
|
|
|
|
{
|
2008-04-18 16:30:45 +02:00
|
|
|
fb_assert(node->nod_count <= 3);
|
2006-09-09 10:32:32 +02:00
|
|
|
dsc** v = values;
|
|
|
|
jrd_nod** ptr = node->nod_arg;
|
2008-12-20 09:12:19 +01:00
|
|
|
for (const jrd_nod* const* const end = ptr + node->nod_count; ptr < end;)
|
2006-09-09 10:32:32 +02:00
|
|
|
{
|
|
|
|
*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
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_gen_id: // return a 32-bit generator value
|
2008-04-24 12:56:57 +02:00
|
|
|
{
|
|
|
|
SLONG temp = (SLONG) DPM_gen_id(tdbb, (SLONG)(IPTR) node->nod_arg[e_gen_id],
|
2006-09-09 10:32:32 +02:00
|
|
|
false, MOV_get_int64(values[0], 0));
|
2008-04-24 12:56:57 +02:00
|
|
|
impure->make_long(temp);
|
|
|
|
}
|
2006-09-09 10:32:32 +02:00
|
|
|
return &impure->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
case nod_gen_id2:
|
2008-04-24 12:56:57 +02:00
|
|
|
{
|
|
|
|
SINT64 temp = DPM_gen_id(tdbb, (IPTR) node->nod_arg[e_gen_id],
|
2008-12-20 09:12:19 +01:00
|
|
|
false, MOV_get_int64(values[0], 0));
|
2008-04-24 12:56:57 +02:00
|
|
|
impure->make_int64(temp);
|
|
|
|
}
|
2006-09-09 10:32:32 +02:00
|
|
|
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);
|
2008-08-20 03:54:45 +02:00
|
|
|
|
2006-09-09 10:32:32 +02:00
|
|
|
default:
|
2009-11-22 04:56:20 +01:00
|
|
|
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);
|
|
|
|
|
2009-07-16 15:09:00 +02:00
|
|
|
if (!record)
|
|
|
|
{
|
|
|
|
// ASF: Usage of ERR_warning with Arg::Gds (instead of Arg::Warning) is correct here.
|
|
|
|
// Maybe not all code paths are prepared for throwing an exception here,
|
|
|
|
// but it will leave the engine as an error (when testing for req_warning).
|
|
|
|
ERR_warning(Arg::Gds(isc_no_cur_rec));
|
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)
|
|
|
|
{
|
2008-11-30 03:23:01 +01: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
|
|
|
|
*/
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// rec_format == NULL indicates we're performing a
|
|
|
|
// join-to-null operation for outer joins
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-02-13 18:28:38 +01:00
|
|
|
if (record && record->rec_format && relation)
|
|
|
|
{
|
2009-11-18 15:24:47 +01:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
|
2009-11-27 03:33:40 +01:00
|
|
|
while (format &&
|
|
|
|
(id >= format->fmt_defaults.getCount() ||
|
|
|
|
format->fmt_defaults[id].vlu_desc.isUnknown()))
|
2009-11-18 15:24:47 +01:00
|
|
|
{
|
2009-11-27 03:33:40 +01:00
|
|
|
if (format->fmt_version >= relation->rel_current_format->fmt_version)
|
2009-11-18 15:24:47 +01:00
|
|
|
{
|
2009-11-27 03:33:40 +01:00
|
|
|
format = NULL;
|
|
|
|
break;
|
2009-11-18 15:24:47 +01:00
|
|
|
}
|
|
|
|
|
2009-11-27 03:33:40 +01:00
|
|
|
format = MET_format(tdbb, relation, format->fmt_version + 1);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-11-27 03:33:40 +01:00
|
|
|
return format && !(*desc = format->fmt_defaults[id].vlu_desc).isUnknown();
|
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;
|
2009-11-27 03:33:40 +01:00
|
|
|
desc->dsc_address = (UCHAR*) " ";
|
2005-10-28 10:11:35 +02:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// If the offset of the field is 0, the field can't possible exist
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (TEST_NULL(record, id))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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-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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle the fixed length data types first. They're easy.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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;
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (from.dsc_dtype)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
2009-07-09 16:04:42 +02:00
|
|
|
case dtype_dbkey:
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<128> temp;
|
2008-04-14 17:22:52 +02:00
|
|
|
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.
|
|
|
|
|
2009-04-29 16:00:32 +02:00
|
|
|
const USHORT length = MOV_get_string_ptr(&from, &ttype, &address, &temp, sizeof(temp));
|
2008-04-14 17:22:52 +02:00
|
|
|
|
|
|
|
// Allocate a string block of sufficient size.
|
|
|
|
VaryingString* string = value->vlu_string;
|
2009-07-14 03:18:17 +02:00
|
|
|
if (string && string->str_length < length)
|
2009-07-09 16:04:42 +02:00
|
|
|
{
|
2008-04-14 17:22:52 +02:00
|
|
|
delete string;
|
|
|
|
string = NULL;
|
|
|
|
}
|
2009-07-14 03:18:17 +02:00
|
|
|
if (!string)
|
2009-07-09 16:04:42 +02:00
|
|
|
{
|
2008-04-14 17:22:52 +02:00
|
|
|
string = value->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(), length) VaryingString();
|
|
|
|
string->str_length = length;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2009-07-09 16:04:42 +02:00
|
|
|
if (from.dsc_dtype == dtype_dbkey)
|
|
|
|
{
|
|
|
|
value->vlu_desc.dsc_dtype = dtype_dbkey;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value->vlu_desc.dsc_dtype = dtype_text;
|
2009-11-28 20:39:23 +01:00
|
|
|
value->vlu_desc.setTextType(ttype);
|
2009-07-09 16:04:42 +02:00
|
|
|
}
|
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;
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<128> temp;
|
2007-01-17 02:19:01 +01:00
|
|
|
|
|
|
|
MapFieldInfo::ValueType fieldInfo;
|
2007-01-18 00:59:23 +01:00
|
|
|
if (!err && itemInfo->fullDomain &&
|
2010-04-19 00:19:11 +02:00
|
|
|
request->getStatement()->mapFieldInfo.get(itemInfo->field, fieldInfo) &&
|
|
|
|
fieldInfo.validation)
|
2007-01-17 02:19:01 +01:00
|
|
|
{
|
2008-08-12 09:22:12 +02:00
|
|
|
if (desc && null)
|
|
|
|
{
|
|
|
|
desc->dsc_flags |= DSC_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool desc_is_null = !desc || (desc->dsc_flags & DSC_null);
|
|
|
|
|
2007-01-17 02:19:01 +01:00
|
|
|
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
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
if (!EVL_boolean(tdbb, fieldInfo.validation) && !(request->req_flags & req_null))
|
2007-01-17 02:19:01 +01:00
|
|
|
{
|
2008-08-12 09:22:12 +02:00
|
|
|
const USHORT length = desc_is_null ? 0 :
|
2009-04-29 16:00:32 +02:00
|
|
|
MOV_make_string(desc, ttype_dynamic, &value, &temp, sizeof(temp) - 1);
|
2007-01-17 02:19:01 +01:00
|
|
|
|
2008-08-12 09:22:12 +02:00
|
|
|
if (desc_is_null)
|
2007-01-17 02:19:01 +01:00
|
|
|
value = NULL_STRING_MARK;
|
|
|
|
else if (!length)
|
|
|
|
value = "";
|
|
|
|
else
|
2008-08-27 14:20:47 +02:00
|
|
|
const_cast<char*>(value)[length] = 0; // safe cast - data is on our local stack
|
2007-01-17 02:19:01 +01:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2008-08-30 05:07:35 +02:00
|
|
|
Firebird::string s;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (item.type == nod_variable)
|
|
|
|
{
|
2010-08-05 17:55:49 +02:00
|
|
|
const jrd_prc* procedure = request->getStatement()->procedure;
|
2010-04-19 00:19:11 +02:00
|
|
|
|
|
|
|
if (procedure)
|
2007-01-18 00:59:23 +01:00
|
|
|
{
|
2010-04-19 00:19:11 +02:00
|
|
|
if (index <= int(procedure->prc_output_fields.getCount()))
|
2007-01-18 00:59:23 +01:00
|
|
|
s.printf("output parameter number %d", index);
|
|
|
|
else
|
|
|
|
{
|
2009-12-27 23:05:22 +01:00
|
|
|
s.printf("variable number %d",
|
2010-04-19 00:19:11 +02:00
|
|
|
index - int(procedure->prc_output_fields.getCount()));
|
2007-01-18 00:59:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
2008-08-27 14:20:47 +02:00
|
|
|
arg = s.c_str();
|
2007-01-18 00:59:23 +01:00
|
|
|
}
|
|
|
|
else
|
2008-08-27 14:20:47 +02:00
|
|
|
arg = itemInfo->name.c_str();
|
2007-01-17 02:19:01 +01:00
|
|
|
}
|
2007-01-18 00:59:23 +01:00
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
ERR_post(Arg::Gds(status) << Arg::Str(arg) << Arg::Str(value));
|
2007-01-17 02:19:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-31 20:03:30 +01:00
|
|
|
dsc* EVL_add(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
2009-10-31 20:03:30 +01:00
|
|
|
* E V L _ a d d
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* 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);
|
2010-02-13 21:29:29 +01:00
|
|
|
fb_assert(ExprNode::is<AggNode>(node) ||
|
|
|
|
node->nod_type == nod_add || node->nod_type == nod_subtract ||
|
|
|
|
node->nod_type == nod_total || node->nod_type == nod_average);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-08-04 15:01:53 +02:00
|
|
|
dsc* const result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle date arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (node->nod_flags & nod_date) {
|
|
|
|
return add_datetime(desc, node, value);
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle floating arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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);
|
2008-12-20 09:12:19 +01:00
|
|
|
value->vlu_misc.vlu_double = (node->nod_type == nod_subtract) ? d2 - d1 : d1 + d2;
|
2009-08-04 15:01:53 +02:00
|
|
|
if (isinf(value->vlu_misc.vlu_double))
|
|
|
|
{
|
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_exception_float_overflow));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle (oh, ugh) quad arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Everything else defaults to longword
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-08-04 15:01:53 +02:00
|
|
|
// CVC: Maybe we should upgrade the sum to double if it doesn't fit?
|
|
|
|
// This is what was done for multiplicaton in dialect 1.
|
2003-12-11 11:33:30 +01:00
|
|
|
const SLONG l1 = MOV_get_long(desc, node->nod_scale);
|
2009-08-04 15:01:53 +02:00
|
|
|
const SINT64 l2 = MOV_get_long(&value->vlu_desc, node->nod_scale);
|
|
|
|
SINT64 rc = (node->nod_type == nod_subtract) ? l2 - l1 : l2 + l1;
|
|
|
|
if (rc < MIN_SLONG || rc > MAX_SLONG)
|
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
|
|
|
value->make_long(rc, node->nod_scale);
|
2001-05-23 15:26:42 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-31 20:03:30 +01:00
|
|
|
dsc* EVL_add2(const dsc* desc, const jrd_nod* node, impure_value* value)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
2009-10-31 20:03:30 +01:00
|
|
|
* E V L _ a d d 2
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* 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);
|
2010-02-13 21:29:29 +01:00
|
|
|
fb_assert(ExprNode::is<AggNode>(node) ||
|
|
|
|
node->nod_type == nod_add2 || node->nod_type == nod_subtract2 ||
|
|
|
|
node->nod_type == nod_average2);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle date arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (node->nod_flags & nod_date) {
|
|
|
|
return add_datetime(desc, node, value);
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle floating arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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);
|
2008-12-20 09:12:19 +01:00
|
|
|
value->vlu_misc.vlu_double = (node->nod_type == nod_subtract2) ? d2 - d1 : d1 + d2;
|
2009-08-04 15:01:53 +02:00
|
|
|
if (isinf(value->vlu_misc.vlu_double))
|
|
|
|
{
|
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_exception_float_overflow));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle (oh, ugh) quad arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Everything else defaults to int64
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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;
|
2008-12-20 09:12:19 +01:00
|
|
|
value->vlu_misc.vlu_int64 = (node->nod_type == nod_subtract2) ? i2 - i1 : i1 + i2;
|
2001-05-23 15:26:42 +02:00
|
|
|
result->dsc_address = (UCHAR *) & value->vlu_misc.vlu_int64;
|
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
result->dsc_sub_type = MAX(desc->dsc_sub_type, value->vlu_desc.dsc_sub_type);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-25 10:34:43 +01:00
|
|
|
/* 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
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
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
|
2009-11-25 10:34:43 +01:00
|
|
|
*/
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (node->nod_type == nod_subtract2)
|
2009-11-22 04:56:20 +01:00
|
|
|
i1 ^= MIN_SINT64; // invert the sign bit
|
2001-05-23 15:26:42 +02:00
|
|
|
if (((i1 ^ i2) >= 0) && ((i1 ^ value->vlu_misc.vlu_int64) < 0))
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
*
|
|
|
|
**************************************/
|
2009-11-22 04:56:20 +01:00
|
|
|
BYTE dtype; // Which addition routine to use?
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(node->nod_flags & nod_date);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Value is the LHS of the operand. desc is the RHS
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if ((node->nod_type == nod_add) || (node->nod_type == nod_add2)) {
|
|
|
|
dtype = DSC_add_result[value->vlu_desc.dsc_dtype][desc->dsc_dtype];
|
|
|
|
}
|
2009-11-23 06:24:29 +01:00
|
|
|
else
|
|
|
|
{
|
2008-12-20 09:12:19 +01:00
|
|
|
fb_assert((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2));
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle historical <timestamp> = <string> - <value> case
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!DTYPE_IS_DATE(dtype) &&
|
2008-12-20 09:12:19 +01:00
|
|
|
(DTYPE_IS_TEXT(value->vlu_desc.dsc_dtype) || 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
|
|
|
}
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (dtype)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_timestamp:
|
|
|
|
default:
|
2009-11-22 04:56:20 +01:00
|
|
|
// This needs to handle a dtype_sql_date + dtype_sql_time
|
|
|
|
// For historical reasons prior to V6 - handle any types for timestamp arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
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:
|
2008-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_invalid_type_datetime_op));
|
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);
|
2008-12-20 09:12:19 +01:00
|
|
|
fb_assert(node->nod_type == nod_add || node->nod_type == nod_subtract ||
|
|
|
|
node->nod_type == nod_add2 || node->nod_type == nod_subtract2);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
fb_assert(value->vlu_desc.dsc_dtype == dtype_sql_date || desc->dsc_dtype == dtype_sql_date);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-22 11:00:59 +01:00
|
|
|
SINT64 d1;
|
2009-11-22 04:56:20 +01:00
|
|
|
// Coerce operand1 to a count of days
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op1_is_date = false;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (value->vlu_desc.dsc_dtype == dtype_sql_date)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2009-11-22 04:56:20 +01:00
|
|
|
// Coerce operand2 to a count of days
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op2_is_date = false;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (desc->dsc_dtype == dtype_sql_date)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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);
|
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
if (((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2)) &&
|
|
|
|
op1_is_date && op2_is_date)
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
d2 = d1 - d2;
|
2008-04-24 12:56:57 +02:00
|
|
|
value->make_int64(d2);
|
2001-05-23 15:26:42 +02:00
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Perform the operation
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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)) {
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_date_range_exceeded));
|
2006-07-25 07:35:04 +02:00
|
|
|
}
|
|
|
|
|
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);
|
2008-12-20 09:12:19 +01:00
|
|
|
fb_assert(node->nod_type == nod_add || node->nod_type == nod_subtract ||
|
|
|
|
node->nod_type == nod_add2 || node->nod_type == nod_subtract2);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
fb_assert(value->vlu_desc.dsc_dtype == dtype_sql_time || desc->dsc_dtype == dtype_sql_time);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-22 11:00:59 +01:00
|
|
|
SINT64 d1;
|
2009-11-22 04:56:20 +01:00
|
|
|
// Coerce operand1 to a count of seconds
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op1_is_time = false;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (value->vlu_desc.dsc_dtype == dtype_sql_time)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
2009-11-22 04:56:20 +01:00
|
|
|
// Coerce operand2 to a count of seconds
|
2003-12-11 11:33:30 +01:00
|
|
|
bool op2_is_time = false;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (desc->dsc_dtype == dtype_sql_time)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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);
|
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
if (((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2)) &&
|
|
|
|
op1_is_time && op2_is_time)
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
d2 = d1 - d2;
|
2009-11-22 04:56:20 +01:00
|
|
|
// Overflow cannot occur as the range of supported TIME values
|
|
|
|
// is less than the range of INTEGER
|
2001-05-23 15:26:42 +02:00
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Perform the operation
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Make sure to use modulo 24 hour arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Make the result positive
|
2001-05-23 15:26:42 +02:00
|
|
|
while (d2 < 0)
|
|
|
|
d2 += (ISC_TICKS_PER_DAY);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// And make it in the range of values for a day
|
2001-05-23 15:26:42 +02:00
|
|
|
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);
|
2008-12-20 09:12:19 +01:00
|
|
|
fb_assert(node->nod_type == nod_add || node->nod_type == nod_subtract ||
|
|
|
|
node->nod_type == nod_add2 || node->nod_type == nod_subtract2);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-11 11:33:30 +01:00
|
|
|
dsc* result = &value->vlu_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-10-03 07:41:16 +02:00
|
|
|
// Operand 1 is Value -- Operand 2 is desc
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (value->vlu_desc.dsc_dtype == dtype_sql_date)
|
|
|
|
{
|
2008-10-03 07:41:16 +02:00
|
|
|
// DATE + TIME
|
2001-05-23 15:26:42 +02:00
|
|
|
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)))
|
|
|
|
{
|
2008-12-20 09:12:19 +01: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;
|
2001-05-23 15:26:42 +02:00
|
|
|
goto return_result;
|
|
|
|
}
|
2008-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_onlycan_add_timetodate));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2009-11-23 06:24:29 +01:00
|
|
|
else if (desc->dsc_dtype == dtype_sql_date)
|
|
|
|
{
|
2008-10-03 07:41:16 +02:00
|
|
|
// TIME + DATE
|
2001-05-23 15:26:42 +02:00
|
|
|
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)))
|
|
|
|
{
|
2008-12-20 09:12:19 +01: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;
|
2001-05-23 15:26:42 +02:00
|
|
|
goto return_result;
|
|
|
|
}
|
2009-01-03 10:14:29 +01:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_onlycan_add_datetotime));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
/* For historical reasons (behavior prior to V6),
|
|
|
|
there are times we will do timestamp arithmetic without a
|
|
|
|
timestamp being involved.
|
|
|
|
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. */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
/* 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.
|
|
|
|
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
|
|
|
|
to use some form of date arithmetic */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-18 11:57:12 +01:00
|
|
|
if (((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2)) &&
|
|
|
|
((desc->dsc_dtype == dtype_timestamp) || DTYPE_IS_TEXT(desc->dsc_dtype)))
|
2003-12-11 11:33:30 +01:00
|
|
|
{
|
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 */
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// If the first operand couldn't represent a timestamp, bomb out
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (!((value->vlu_desc.dsc_dtype == dtype_timestamp) ||
|
2008-12-20 09:12:19 +01:00
|
|
|
DTYPE_IS_TEXT(value->vlu_desc.dsc_dtype)))
|
2003-09-12 03:58:51 +02:00
|
|
|
{
|
2008-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_onlycansub_tstampfromtstamp));
|
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;
|
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_type == nod_subtract2)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// This is dialect 1 subtraction returning double as before
|
2008-12-20 09:12:19 +01:00
|
|
|
value->vlu_misc.vlu_double = (double) d2 / ((double) ISC_TICKS_PER_DAY);
|
2008-01-16 09:54:50 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2009-11-25 10:34:43 +01:00
|
|
|
/* From here we know our result must be a <timestamp>. The only
|
|
|
|
legal cases are:
|
2001-05-23 15:26:42 +02:00
|
|
|
<timestamp> +/- <numeric>
|
|
|
|
<numeric> + <timestamp>
|
2009-11-25 10:34:43 +01:00
|
|
|
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 */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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;
|
2008-12-18 11:57:12 +01:00
|
|
|
if ((value->vlu_desc.dsc_dtype == dtype_timestamp) ||
|
|
|
|
(DTYPE_IS_TEXT(value->vlu_desc.dsc_dtype)))
|
2006-09-09 10:32:32 +02:00
|
|
|
{
|
|
|
|
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;
|
2008-12-18 11:57:12 +01:00
|
|
|
if ((desc->dsc_dtype == dtype_timestamp) || (DTYPE_IS_TEXT(desc->dsc_dtype)))
|
2006-09-09 10:32:32 +02:00
|
|
|
{
|
|
|
|
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)
|
2008-10-08 04:24:51 +02:00
|
|
|
{
|
2008-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_onlyoneop_mustbe_tstamp));
|
2008-10-08 04:24:51 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (op1_is_timestamp)
|
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
d1 = get_timestamp_to_isc_ticks(&value->vlu_desc);
|
|
|
|
d2 = get_day_fraction(desc);
|
|
|
|
}
|
2009-11-23 06:24:29 +01:00
|
|
|
else
|
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
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
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if ((node->nod_type == nod_subtract) || (node->nod_type == nod_subtract2))
|
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
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-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_datetime_range_exceeded));
|
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
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if ((SLONG) value->vlu_misc.vlu_timestamp.timestamp_time < 0)
|
|
|
|
{
|
2006-09-09 10:32:32 +02:00
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_time =
|
2008-12-20 09:12:19 +01:00
|
|
|
((SLONG) value->vlu_misc.vlu_timestamp.timestamp_time) + ISC_TICKS_PER_DAY;
|
2006-09-09 10:32:32 +02:00
|
|
|
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 &&
|
2008-12-20 09:12:19 +01:00
|
|
|
value->vlu_misc.vlu_timestamp.timestamp_time < ISC_TICKS_PER_DAY);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01: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
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// restore saved NULL state
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (flags & req_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
EVL_make_value(tdbb, desc1, impure);
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
case nod_add: // with dialect-1 semantics
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_subtract:
|
2009-10-31 20:03:30 +01:00
|
|
|
return EVL_add(desc2, node, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
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-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
2008-08-27 14:20:47 +02:00
|
|
|
Arg::Gds(isc_exception_float_divide_by_zero));
|
2008-04-01 14:04:08 +02:00
|
|
|
}
|
2009-08-04 15:01:53 +02:00
|
|
|
impure->vlu_misc.vlu_double = MOV_get_double(desc1) / divisor;
|
|
|
|
if (isinf(impure->vlu_misc.vlu_double))
|
|
|
|
{
|
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_exception_float_overflow));
|
|
|
|
}
|
2003-12-11 11:33:30 +01:00
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
case nod_multiply: // dialect-1 semantics
|
2001-05-23 15:26:42 +02:00
|
|
|
return multiply(desc2, impure, node);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
case nod_add2: // with dialect-3 semantics
|
2001-05-23 15:26:42 +02:00
|
|
|
case nod_subtract2:
|
2009-10-31 20:03:30 +01:00
|
|
|
return EVL_add2(desc2, node, impure);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-26 01:20:59 +01:00
|
|
|
case nod_multiply2: // dialect-3 semantics
|
2001-05-23 15:26:42 +02:00
|
|
|
return multiply2(desc2, impure, node);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
case nod_divide2: // dialect-3 semantics
|
2001-05-23 15:26:42 +02:00
|
|
|
return divide2(desc2, impure, node);
|
|
|
|
|
|
|
|
default:
|
2009-11-22 04:56:20 +01:00
|
|
|
BUGCHECK(232); // msg 232 EVL_expr: invalid operation
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// value is present; make the conversion
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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;
|
2009-11-23 06:24:29 +01:00
|
|
|
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);
|
2009-11-23 06:24:29 +01:00
|
|
|
if (length <= 0 && value)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// cast is a subtype cast only
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
length = DSC_string_length(value);
|
|
|
|
if (impure->vlu_desc.dsc_dtype == dtype_cstring)
|
2009-11-22 04:56:20 +01:00
|
|
|
length++; // for NULL byte
|
2001-05-23 15:26:42 +02:00
|
|
|
else if (impure->vlu_desc.dsc_dtype == dtype_varying)
|
|
|
|
length += sizeof(USHORT);
|
|
|
|
impure->vlu_desc.dsc_length = length;
|
|
|
|
}
|
|
|
|
length = impure->vlu_desc.dsc_length;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Allocate a string block of sufficient size.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-12 20:28:04 +02:00
|
|
|
VaryingString* string = impure->vlu_string;
|
2009-11-23 06:24:29 +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;
|
|
|
|
}
|
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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)
|
2009-10-21 02:42:38 +02:00
|
|
|
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
2005-12-06 02:02:22 +01: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-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
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
// Initialize first 32 bits of DB_KEY
|
|
|
|
impure->vlu_misc.vlu_dbkey[0] = 0;
|
2004-09-28 08:28:38 +02:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
// Now, put relation ID into first 16 bits of DB_KEY
|
|
|
|
// We do not assign it as SLONG because of big-endian machines.
|
2009-12-10 01:02:01 +01:00
|
|
|
*(USHORT*) impure->vlu_misc.vlu_dbkey = relation->rel_id;
|
2004-09-28 08:28:38 +02:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
// Encode 40-bit record number. Before that, increment the value
|
|
|
|
// because users expect the numbering to start with one.
|
|
|
|
RecordNumber temp(rpb->rpb_number.getValue() + 1);
|
|
|
|
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;
|
2009-07-09 16:04:42 +02:00
|
|
|
impure->vlu_desc.dsc_dtype = dtype_dbkey;
|
|
|
|
impure->vlu_desc.dsc_length = type_lengths[dtype_dbkey];
|
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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Get started by opening stream
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
invariant_flags = &impure->vlu_flags;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (*invariant_flags & VLU_computed)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// An invariant node has already been computed.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (*invariant_flags & VLU_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
else
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
return desc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-10 01:02:01 +01:00
|
|
|
if (nod_average2 == node->nod_type)
|
2009-11-23 06:24:29 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_misc.vlu_int64 = 0;
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_int64;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(SINT64);
|
2010-04-05 23:20:08 +02:00
|
|
|
impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_int64;
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_desc.dsc_scale = 0;
|
|
|
|
}
|
2009-11-23 06:24:29 +01:00
|
|
|
else
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
impure->vlu_misc.vlu_long = 0;
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(SLONG);
|
2010-04-05 23:20:08 +02:00
|
|
|
impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_long;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2010-04-15 18:10:51 +02:00
|
|
|
RecordSource* const rsb = reinterpret_cast<RecordSource*>(node->nod_arg[e_stat_rsb]);
|
2009-12-09 19:45:44 +01:00
|
|
|
rsb->open(tdbb);
|
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;
|
2008-10-03 07:41:16 +02:00
|
|
|
|
2009-05-10 17:23:31 +02:00
|
|
|
try
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2009-05-07 18:48:48 +02:00
|
|
|
// Handle each variety separately
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-05-07 18:48:48 +02:00
|
|
|
switch (node->nod_type)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-05-07 18:48:48 +02:00
|
|
|
case nod_count:
|
|
|
|
flag = 0;
|
2010-04-15 18:10:51 +02:00
|
|
|
while (rsb->getRecord(tdbb))
|
2009-05-07 18:48:48 +02:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
++impure->vlu_misc.vlu_long;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2009-05-07 18:48:48 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-05-07 18:48:48 +02:00
|
|
|
case nod_min:
|
|
|
|
case nod_max:
|
2010-04-15 18:10:51 +02:00
|
|
|
while (rsb->getRecord(tdbb))
|
2003-02-13 18:28:38 +01:00
|
|
|
{
|
2009-05-07 18:48:48 +02:00
|
|
|
dsc* value = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int result;
|
|
|
|
if (flag || ((result = MOV_compare(value, desc)) < 0 && node->nod_type == nod_min) ||
|
|
|
|
(node->nod_type != nod_min && result > 0))
|
|
|
|
{
|
|
|
|
flag = 0;
|
|
|
|
EVL_make_value(tdbb, value, impure);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2009-05-07 18:48:48 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-05-07 18:48:48 +02:00
|
|
|
case nod_from:
|
2010-04-15 18:10:51 +02:00
|
|
|
if (rsb->getRecord(tdbb))
|
2009-05-07 18:48:48 +02:00
|
|
|
{
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
else
|
2009-05-07 18:48:48 +02:00
|
|
|
{
|
|
|
|
if (node->nod_arg[e_stat_default])
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_default]);
|
|
|
|
else
|
|
|
|
ERR_post(Arg::Gds(isc_from_no_match));
|
|
|
|
}
|
|
|
|
flag = request->req_flags;
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
case nod_average: // total or average with dialect-1 semantics
|
2009-05-07 18:48:48 +02:00
|
|
|
case nod_total:
|
2010-04-15 18:10:51 +02:00
|
|
|
while (rsb->getRecord(tdbb))
|
2009-05-07 18:48:48 +02:00
|
|
|
{
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
|
|
|
if (request->req_flags & req_null) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-11-22 04:56:20 +01:00
|
|
|
// Note: if the field being SUMed or AVERAGEd is short or long,
|
|
|
|
// impure will stay long, and the first EVL_add() will
|
|
|
|
// set the correct scale; if it is approximate numeric,
|
|
|
|
// the first EVL_add() will convert impure to double.
|
2009-10-31 20:03:30 +01:00
|
|
|
EVL_add(desc, node, impure);
|
2009-05-07 18:48:48 +02:00
|
|
|
count++;
|
2003-02-13 18:28:38 +01:00
|
|
|
}
|
2009-05-07 18:48:48 +02:00
|
|
|
desc = &impure->vlu_desc;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_type == nod_total)
|
|
|
|
{
|
2009-05-07 18:48:48 +02:00
|
|
|
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;
|
2001-05-23 15:26:42 +02:00
|
|
|
flag = 0;
|
|
|
|
break;
|
2009-05-07 18:48:48 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
case nod_average2: // average with dialect-3 semantics
|
2010-04-15 18:10:51 +02:00
|
|
|
while (rsb->getRecord(tdbb))
|
2009-05-07 18:48:48 +02:00
|
|
|
{
|
|
|
|
desc = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
continue;
|
2009-11-22 04:56:20 +01:00
|
|
|
// Note: if the field being SUMed or AVERAGEd is exact
|
|
|
|
// numeric, impure will stay int64, and the first EVL_add() will
|
|
|
|
// set the correct scale; if it is approximate numeric,
|
|
|
|
// the first EVL_add() will convert impure to double.
|
2009-10-31 20:03:30 +01:00
|
|
|
EVL_add(desc, node, impure);
|
2009-05-07 18:48:48 +02:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
desc = &impure->vlu_desc;
|
|
|
|
if (!count)
|
|
|
|
break;
|
2009-11-22 04:56:20 +01:00
|
|
|
// 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.
|
2009-05-07 18:48:48 +02:00
|
|
|
if (dtype_int64 == impure->vlu_desc.dsc_dtype)
|
|
|
|
impure->vlu_misc.vlu_int64 /= count;
|
|
|
|
else
|
|
|
|
impure->vlu_misc.vlu_double /= count;
|
|
|
|
flag = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2009-05-07 18:48:48 +02:00
|
|
|
default:
|
2009-11-22 04:56:20 +01:00
|
|
|
BUGCHECK(233); // msg 233 eval_statistical: invalid operation
|
2009-05-07 18:48:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{
|
2009-05-10 17:23:31 +02:00
|
|
|
// close stream
|
2009-05-07 18:48:48 +02:00
|
|
|
// ignore any error during it to keep original
|
|
|
|
try
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2009-12-09 19:45:44 +01:00
|
|
|
rsb->close(tdbb);
|
2009-05-07 18:48:48 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
request->req_flags |= flag;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2009-05-10 17:23:31 +02:00
|
|
|
catch (const Firebird::Exception&)
|
2010-04-15 18:10:51 +02:00
|
|
|
{} // no-op
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-05-07 18:48:48 +02:00
|
|
|
throw;
|
2003-12-11 11:33:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Close stream and return value
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
rsb->close(tdbb);
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
request->req_flags |= flag;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// 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
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
*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;
|
2008-12-20 09:12:19 +01:00
|
|
|
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_short);
|
2005-06-11 02:14:55 +02:00
|
|
|
impure->vlu_desc.dsc_length = sizeof(SSHORT);
|
2008-10-03 07:41:16 +02:00
|
|
|
|
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.
|
2009-11-23 06:24:29 +01:00
|
|
|
if (!value || (request->req_flags & req_null))
|
|
|
|
{
|
2005-06-11 02:14:55 +02:00
|
|
|
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:
|
2008-12-20 09:12:19 +01:00
|
|
|
switch (extract_part)
|
2005-06-11 02:14:55 +02:00
|
|
|
{
|
2008-12-20 09:12:19 +01:00
|
|
|
case blr_extract_hour:
|
|
|
|
case blr_extract_minute:
|
|
|
|
case blr_extract_second:
|
|
|
|
case blr_extract_millisecond:
|
2008-12-20 20:57:43 +01:00
|
|
|
Firebird::TimeStamp::decode_time(*(GDS_TIME*) value->dsc_address,
|
2008-12-20 09:12:19 +01:00
|
|
|
×.tm_hour, ×.tm_min, ×.tm_sec, &fractions);
|
|
|
|
break;
|
|
|
|
default:
|
2008-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_invalid_extractpart_time));
|
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:
|
2008-12-20 09:12:19 +01:00
|
|
|
switch (extract_part)
|
2005-06-11 02:14:55 +02:00
|
|
|
{
|
2008-12-20 09:12:19 +01:00
|
|
|
case blr_extract_hour:
|
|
|
|
case blr_extract_minute:
|
|
|
|
case blr_extract_second:
|
|
|
|
case blr_extract_millisecond:
|
2008-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_invalid_extractpart_date));
|
2008-12-20 09:12:19 +01:00
|
|
|
break;
|
|
|
|
default:
|
2008-12-20 20:57:43 +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-12-20 20:57:43 +01:00
|
|
|
Firebird::TimeStamp::decode_timestamp(*(GDS_TIMESTAMP*) value->dsc_address,
|
2008-03-26 11:21:04 +01:00
|
|
|
×, &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:
|
2008-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
|
|
|
Arg::Gds(isc_invalidarg_extract));
|
2005-06-11 02:14:55 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
USHORT part;
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (extract_part)
|
|
|
|
{
|
2005-06-11 02:14:55 +02:00
|
|
|
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;
|
2008-06-25 17:58:03 +02:00
|
|
|
|
2005-06-11 02:14:55 +02:00
|
|
|
case blr_extract_second:
|
2008-06-25 17:58:03 +02:00
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
|
|
|
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
2005-06-11 02:14:55 +02:00
|
|
|
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
2008-06-25 17:58:03 +02:00
|
|
|
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
2008-10-08 04:24:51 +02:00
|
|
|
*(ULONG*) impure->vlu_desc.dsc_address = times.tm_sec * ISC_TIME_SECONDS_PRECISION + fractions;
|
2008-06-25 17:58:03 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
|
2007-07-31 03:22:59 +02:00
|
|
|
case blr_extract_millisecond:
|
|
|
|
impure->vlu_desc.dsc_dtype = dtype_long;
|
2005-06-11 02:14:55 +02:00
|
|
|
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
2008-06-25 17:58:03 +02:00
|
|
|
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE + 3;
|
|
|
|
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
2008-10-08 04:24:51 +02:00
|
|
|
(*(ULONG*) impure->vlu_desc.dsc_address) = fractions;
|
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;
|
|
|
|
}
|
2008-10-03 07:41:16 +02:00
|
|
|
|
2010-04-05 23:20:08 +02:00
|
|
|
*(USHORT*) impure->vlu_desc.dsc_address = part;
|
2005-06-11 02:14:55 +02:00
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Convert the input number to a double
|
2008-07-10 17:57:33 +02:00
|
|
|
CVT_move(d, &result);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// There's likely some loss of precision here due to rounding of number
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// 08-Apr-2004, Nickolay Samofatov. Loss of precision manifested itself as bad
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
// Let's use llrint where it is supported and offset number for other platforms
|
|
|
|
// in hope that compiler rounding mode doesn't get in.
|
2004-04-09 00:29:50 +02:00
|
|
|
|
|
|
|
#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;
|
2004-03-28 11:10:30 +02:00
|
|
|
SqlIdentifier relation_name, field_name;
|
2009-05-02 16:56:43 +02:00
|
|
|
|
|
|
|
const dsc* value = EVL_expr(tdbb, node->nod_arg[0]);
|
2009-11-23 06:24:29 +01:00
|
|
|
if (!(request->req_flags & req_null))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
p1 = relation_name;
|
|
|
|
MOV_get_name(value, p1);
|
|
|
|
value = EVL_expr(tdbb, node->nod_arg[1]);
|
2009-11-23 06:24:29 +01:00
|
|
|
if (!(request->req_flags & req_null))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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.
|
2008-12-14 10:28:25 +01:00
|
|
|
impure->make_long(SCL_get_mask(tdbb, p1, p2));
|
2001-05-23 15:26:42 +02:00
|
|
|
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
|
|
|
|
2008-07-10 17:57:33 +02:00
|
|
|
CVT_move(d, &result);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-07-11 04:37:23 +02:00
|
|
|
return ((SINT64) result_timestamp.timestamp_date) * ISC_TICKS_PER_DAY +
|
|
|
|
(SINT64) result_timestamp.timestamp_time;
|
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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Initialize descriptor
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Evaluate attachment id
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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;
|
2009-11-23 06:24:29 +01:00
|
|
|
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;
|
2009-11-23 06:24:29 +01:00
|
|
|
else
|
|
|
|
{
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock temp_lock;
|
2009-11-22 04:56:20 +01:00
|
|
|
// fill out a lock block, zeroing it out first
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_type = LCK_attachment;
|
2008-12-20 09:12:19 +01:00
|
|
|
temp_lock.lck_owner_handle = LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
2001-05-23 15:26:42 +02:00
|
|
|
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
|
|
|
|
2009-11-23 06:24:29 +01: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());
|
|
|
|
|
2008-12-25 16:05:06 +01:00
|
|
|
if (value->isBlob())
|
2006-04-16 20:45:40 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2009-11-17 14:06:14 +01:00
|
|
|
UCHAR* ptr;
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<32> temp;
|
2006-04-16 20:45:40 +02:00
|
|
|
USHORT ttype;
|
|
|
|
|
2009-11-17 14:06:14 +01:00
|
|
|
dsc desc;
|
|
|
|
desc.dsc_length = MOV_get_string_ptr(value, &ttype, &ptr, &temp, sizeof(temp));
|
2006-04-16 20:45:40 +02:00
|
|
|
desc.dsc_dtype = dtype_text;
|
2009-11-17 14:06:14 +01:00
|
|
|
desc.dsc_address = NULL;
|
|
|
|
desc.setTextType(ttype);
|
2006-04-16 20:45:40 +02:00
|
|
|
EVL_make_value(tdbb, &desc, impure);
|
|
|
|
|
2010-03-09 16:43:59 +01:00
|
|
|
ULONG len = (textType->*tt_str_to_case)(desc.dsc_length,
|
2009-11-17 14:06:14 +01:00
|
|
|
ptr, desc.dsc_length, impure->vlu_desc.dsc_address);
|
2010-03-09 16:43:59 +01:00
|
|
|
|
|
|
|
if (len == INTL_BAD_STR_LENGTH)
|
|
|
|
status_exception::raise(Arg::Gds(isc_arith_except));
|
|
|
|
|
|
|
|
impure->vlu_desc.dsc_length = (USHORT) len;
|
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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle floating arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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);
|
2009-08-04 15:01:53 +02:00
|
|
|
value->vlu_misc.vlu_double = d1 * d2;
|
2009-05-27 09:57:06 +02:00
|
|
|
if (isinf(value->vlu_misc.vlu_double))
|
|
|
|
{
|
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_exception_float_overflow));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle (oh, ugh) quad arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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;
|
2008-12-20 09:12:19 +01:00
|
|
|
value->vlu_misc.vlu_quad = 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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Everything else defaults to longword
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
2008-12-20 09:12:19 +01:00
|
|
|
// SLONG l1, l2;
|
|
|
|
//{
|
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);
|
2008-12-20 09:12:19 +01:00
|
|
|
d2 = MOV_get_double (&value->vlu_desc);
|
2009-08-04 15:01:53 +02:00
|
|
|
value->vlu_misc.vlu_double = d1 * d2; */
|
2002-06-30 11:58:20 +02:00
|
|
|
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
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
2002-06-30 11:58:20 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
value->vlu_misc.vlu_long = (SLONG) rc; // l1 * l2;
|
2002-06-30 11:58:20 +02:00
|
|
|
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_long;
|
|
|
|
}
|
2008-12-20 09:12:19 +01:00
|
|
|
//}
|
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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle floating arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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);
|
2009-08-04 15:01:53 +02:00
|
|
|
value->vlu_misc.vlu_double = d1 * d2;
|
2009-05-27 09:57:06 +02:00
|
|
|
if (isinf(value->vlu_misc.vlu_double))
|
|
|
|
{
|
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_exception_float_overflow));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle (oh, ugh) quad arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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;
|
2008-12-20 09:12:19 +01:00
|
|
|
value->vlu_misc.vlu_quad = 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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Everything else defaults to int64
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
2009-11-22 04:56:20 +01: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)
|
|
|
|
|
|
|
|
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.
|
|
|
|
We use FB_UINT64s for the checking, thus ensuring that our division rounds
|
|
|
|
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.
|
|
|
|
*/
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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)) {
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Handle floating arithmetic
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-23 06:24:29 +01:00
|
|
|
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-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
2008-08-27 14:20:47 +02:00
|
|
|
Arg::Gds(isc_exception_float_divide_by_zero));
|
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);
|
2009-08-04 15:01:53 +02:00
|
|
|
value->vlu_misc.vlu_double = d1 / d2;
|
2009-05-27 09:57:06 +02:00
|
|
|
if (isinf(value->vlu_misc.vlu_double))
|
|
|
|
{
|
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_exception_float_overflow));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// 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 ...
|
|
|
|
*
|
|
|
|
* v1 * pow(10,s1)
|
|
|
|
* ----------------- = (v1/v2) * pow(10, s1-s2)
|
|
|
|
* v2 * pow(10,s2)
|
|
|
|
*
|
|
|
|
* 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)
|
|
|
|
* or in our ordered-pair notation,
|
|
|
|
* ( v1 * pow(10, -2*s2) / v2, s1 + s2 )
|
|
|
|
*
|
|
|
|
* To maximize the amount of information in the result, we scale up
|
|
|
|
* 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-10-03 07:41:16 +02:00
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
2008-08-27 14:20:47 +02:00
|
|
|
Arg::Gds(isc_exception_integer_divide_by_zero));
|
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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// MIN_SINT64 / -1 = (MAX_SINT64 + 1), which overflows in SINT64.
|
2001-05-23 15:26:42 +02:00
|
|
|
if ((i1 == MIN_SINT64) && (i2 == -1))
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01: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.
|
2009-11-23 06:24:29 +01:00
|
|
|
while ((addl_scale < 0) && (0 == (i2 % 10)))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
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;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// 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
|
|
|
{
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_numeric_out_of_range));
|
2008-04-04 12:13:44 +02:00
|
|
|
}
|
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);
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
switch (value->vlu_desc.dsc_dtype)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_short:
|
|
|
|
if (value->vlu_misc.vlu_short == MIN_SSHORT)
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
2001-05-23 15:26:42 +02:00
|
|
|
value->vlu_misc.vlu_short = -value->vlu_misc.vlu_short;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_long:
|
|
|
|
if (value->vlu_misc.vlu_long == MIN_SLONG)
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
2001-05-23 15:26:42 +02:00
|
|
|
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)
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_exception_integer_overflow));
|
2001-05-23 15:26:42 +02:00
|
|
|
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();
|
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
|
|
|
|
2009-11-22 04:56:20 +01: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.
|
|
|
|
*/
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// If the transaction is a commit retain, check if the record was
|
|
|
|
// last updated in one of its own prior transactions
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Initialize descriptor
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2010-04-05 23:20:08 +02:00
|
|
|
impure->vlu_misc.vlu_long = rpb->rpb_transaction_nr;
|
2010-04-07 04:04:22 +02:00
|
|
|
impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_long;
|
2010-04-05 23:20:08 +02:00
|
|
|
impure->vlu_desc.dsc_dtype = dtype_text;
|
|
|
|
impure->vlu_desc.dsc_length = 4;
|
|
|
|
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* 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)
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_array_max_dimensions) << Arg::Num(MAX_ARRAY_DIMENSIONS));
|
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);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Choose interpretation for the operation
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-19 07:14:53 +01:00
|
|
|
USHORT ttype;
|
2008-12-25 16:05:06 +01:00
|
|
|
if (desc1->isBlob())
|
2005-11-25 16:20:26 +01:00
|
|
|
{
|
2004-04-29 19:48:39 +02:00
|
|
|
if (desc1->dsc_sub_type == isc_blob_text)
|
2009-11-22 04:56:20 +01: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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Get operator definition string (control string)
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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);
|
2009-11-22 04:56:20 +01: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
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Merge search and control strings
|
2004-03-19 07:14:53 +01:00
|
|
|
UCHAR control[BUFFER_SMALL];
|
2009-05-01 19:21:36 +02:00
|
|
|
SLONG control_length = obj->sleuthMerge(*tdbb->getDefaultPool(), p2, l2, p1, l1, control); //, BUFFER_SMALL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +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;
|
2008-12-25 16:05:06 +01:00
|
|
|
if (!desc1->isBlob())
|
2005-11-25 16:20:26 +01:00
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// Source is not a blob, do a simple search
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
}
|
2009-11-23 06:24:29 +01:00
|
|
|
else
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// Source string is a blob, things get interesting
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
|
|
|
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-12-25 16:05:06 +01:00
|
|
|
if (!desc1->isBlob())
|
2005-11-25 16:20:26 +01:00
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// Source is not a blob, do a simple search
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Get text type of data string
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
type1 = INTL_TEXT_TYPE(*desc1);
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// Get address and length of search string - convert to datatype of data
|
2001-05-23 15:26:42 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<256> temp1;
|
2004-03-19 07:14:53 +01:00
|
|
|
USHORT xtype1;
|
2009-04-29 16:00:32 +02:00
|
|
|
const USHORT l1 = MOV_get_string_ptr(desc1, &xtype1, &p1, &temp1, 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);
|
|
|
|
}
|
2008-10-03 07:41:16 +02:00
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// 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)
|
2009-11-22 04:56:20 +01:00
|
|
|
type1 = desc1->dsc_blob_ttype(); // pick up character set and collation of blob
|
2008-03-18 14:02:52 +01:00
|
|
|
else
|
2009-11-22 04:56:20 +01:00
|
|
|
type1 = ttype_none; // Do byte matching
|
2008-03-18 14:02:52 +01:00
|
|
|
|
|
|
|
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
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<256> temp2;
|
2009-11-23 06:24:29 +01:00
|
|
|
if (!computed_invariant)
|
|
|
|
{
|
2008-03-18 14:02:52 +01:00
|
|
|
if (type1 == ttype_none) {
|
2009-04-29 16:00:32 +02:00
|
|
|
l2 = MOV_get_string(desc2, &p2, &temp2, sizeof(temp2));
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-03-18 14:02:52 +01:00
|
|
|
else {
|
2008-12-20 09:12:19 +01:00
|
|
|
l2 = MOV_make_string2(tdbb, desc2, type1, &p2, match_str);
|
2008-03-18 14:02:52 +01:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-12-20 09:12:19 +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
|
|
|
|
2009-11-22 04:56:20 +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;
|
2008-10-03 07:41:16 +02:00
|
|
|
|
2008-03-18 14:02:52 +01:00
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_like:
|
|
|
|
case nod_similar:
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<TEMP_LENGTH> temp3;
|
2008-03-18 14:02:52 +01:00
|
|
|
const UCHAR* escape_str = NULL;
|
|
|
|
USHORT escape_length = 0;
|
|
|
|
|
2009-11-22 04:56:20 +01:00
|
|
|
// ensure 3rd argument (escape char) is in operation text type
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_count == 3 && !computed_invariant)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// Convert ESCAPE to operation character set
|
2008-03-18 14:02:52 +01:00
|
|
|
DSC* dsc = EVL_expr(tdbb, node->nod_arg[2]);
|
2009-11-23 06:24:29 +01:00
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(node->nod_impure);
|
2008-03-18 14:02:52 +01:00
|
|
|
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-12-20 09:12:19 +01:00
|
|
|
escape_length = MOV_make_string(dsc, type1,
|
|
|
|
reinterpret_cast<const char**>(&escape_str),
|
2009-04-29 16:00:32 +02:00
|
|
|
&temp3, sizeof(temp3));
|
2008-03-18 14:02:52 +01:00
|
|
|
if (!escape_length || charset->length(escape_length, escape_str, true) != 1)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// If characters left, or null byte character, return error
|
2008-03-18 14:02:52 +01:00
|
|
|
BLB_close(tdbb, blob);
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_escape_invalid));
|
2008-03-18 14:02:52 +01:00
|
|
|
}
|
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])
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// If or null byte character, return error
|
2008-03-18 14:02:52 +01:00
|
|
|
BLB_close(tdbb, blob);
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_escape_invalid));
|
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)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(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
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
delete impure->vlu_misc.vlu_invariant;
|
2008-03-18 14:02:52 +01:00
|
|
|
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
|
|
|
{
|
2010-02-21 02:47:54 +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
|
|
|
{
|
2010-02-21 02:47:54 +01:00
|
|
|
impure->vlu_misc.vlu_invariant = evaluator = obj->createSimilarToMatcher(
|
|
|
|
*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length, false);
|
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
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
evaluator = impure->vlu_misc.vlu_invariant;
|
2008-03-18 14:02:52 +01:00
|
|
|
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(),
|
2010-02-21 02:47:54 +01:00
|
|
|
p2, l2, escape_str, escape_length, false);
|
2008-03-18 14:02:52 +01:00
|
|
|
}
|
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
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(node->nod_impure);
|
2008-03-18 14:02:52 +01:00
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
delete 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)
|
|
|
|
{
|
2008-12-20 09:12:19 +01:00
|
|
|
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-12-20 09:12:19 +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
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
evaluator = impure->vlu_misc.vlu_invariant;
|
2008-03-18 14:02:52 +01:00
|
|
|
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
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(node->nod_impure);
|
2008-01-16 09:54:50 +01:00
|
|
|
PatternMatcher* evaluator;
|
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
delete 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)
|
|
|
|
{
|
2008-12-20 09:12:19 +01:00
|
|
|
impure->vlu_misc.vlu_invariant = evaluator =
|
|
|
|
obj->createContainsMatcher(*tdbb->getDefaultPool(), p2, l2);
|
2008-01-16 09:54:50 +01:00
|
|
|
}
|
2009-11-23 06:24:29 +01:00
|
|
|
else
|
2008-01-16 09:54:50 +01:00
|
|
|
{
|
2009-11-23 06:24:29 +01:00
|
|
|
// nod_starts
|
2008-12-20 09:12:19 +01:00
|
|
|
impure->vlu_misc.vlu_invariant = evaluator =
|
|
|
|
obj->createStartsMatcher(*tdbb->getDefaultPool(), p2, l2);
|
2008-01-16 09:54:50 +01:00
|
|
|
}
|
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
|
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
evaluator = 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)
|
|
|
|
{
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<TEMP_LENGTH> temp3;
|
2005-05-28 00:45:31 +02:00
|
|
|
const UCHAR* escape_str = NULL;
|
|
|
|
USHORT escape_length = 0;
|
2009-11-22 04:56:20 +01:00
|
|
|
// ensure 3rd argument (escape char) is in operation text type
|
2009-11-23 06:24:29 +01:00
|
|
|
if (node->nod_count == 3 && !computed_invariant)
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// Convert ESCAPE to operation character set
|
2003-12-27 05:37:23 +01:00
|
|
|
DSC* dsc = EVL_expr(tdbb, node->nod_arg[2]);
|
2009-11-23 06:24:29 +01:00
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
|
|
|
if (node->nod_flags & nod_invariant)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(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
|
|
|
}
|
2008-12-20 09:12:19 +01:00
|
|
|
escape_length = MOV_make_string(dsc, ttype,
|
|
|
|
reinterpret_cast<const char**>(&escape_str),
|
2009-04-29 16:00:32 +02:00
|
|
|
&temp3, 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
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// If characters left, or null byte character, return error
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_escape_invalid));
|
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])
|
|
|
|
{
|
2009-11-22 04:56:20 +01:00
|
|
|
// If or null byte character, return error
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_escape_invalid));
|
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)
|
|
|
|
{
|
2010-04-05 23:20:08 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(node->nod_impure);
|
2008-01-16 09:54:50 +01:00
|
|
|
PatternMatcher* evaluator;
|
|
|
|
|
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
delete 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(
|
2010-02-21 02:47:54 +01:00
|
|
|
*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length, false);
|
2008-01-16 09:54:50 +01:00
|
|
|
}
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
2008-01-16 09:54:50 +01:00
|
|
|
else
|
|
|
|
{
|
2009-11-18 15:31:29 +01:00
|
|
|
evaluator = 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-10-03 07:41:16 +02: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
|
2010-02-21 02:47:54 +01:00
|
|
|
return obj->similarTo(*tdbb->getDefaultPool(), p1, l1, p2, l2, escape_str,
|
|
|
|
escape_length, false);
|
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;
|
2008-12-20 09:12:19 +01:00
|
|
|
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
2005-06-11 02:14:55 +02:00
|
|
|
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.
|
2009-11-23 06:24:29 +01:00
|
|
|
if (!value || (request->req_flags & req_null))
|
|
|
|
{
|
2005-06-11 02:14:55 +02:00
|
|
|
request->req_flags |= req_null;
|
|
|
|
impure->vlu_misc.vlu_long = 0;
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG length;
|
|
|
|
|
2008-12-25 16:05:06 +01:00
|
|
|
if (value->isBlob())
|
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
|
|
|
{
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_numeric_out_of_range));
|
2008-04-04 12:13:44 +02:00
|
|
|
}
|
2008-10-03 07:41:16 +02:00
|
|
|
|
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;
|
|
|
|
|
2008-12-20 09:12:19 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-04-05 23:20:08 +02:00
|
|
|
*(ULONG*) impure->vlu_desc.dsc_address = length;
|
2005-06-11 02:14:55 +02:00
|
|
|
|
|
|
|
BLB_close(tdbb, blob);
|
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
2009-04-29 16:00:32 +02:00
|
|
|
VaryStr<32> temp;
|
2005-06-11 02:14:55 +02:00
|
|
|
USHORT ttype;
|
|
|
|
UCHAR* p;
|
|
|
|
|
2009-04-29 16:00:32 +02:00
|
|
|
length = MOV_get_string_ptr(value, &ttype, &p, &temp, sizeof(temp));
|
2005-06-11 02:14:55 +02:00
|
|
|
|
|
|
|
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
|
|
|
{
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_numeric_out_of_range));
|
2008-04-04 12:13:44 +02:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
2010-04-05 23:20:08 +02:00
|
|
|
*(ULONG*) impure->vlu_desc.dsc_address = length;
|
2005-06-11 02:14:55 +02:00
|
|
|
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;
|
2008-12-20 09:12:19 +01:00
|
|
|
charactersLength = MOV_make_string2(tdbb, characters, ttype, &tempAddress, 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;
|
|
|
|
|
2008-12-25 16:05:06 +01:00
|
|
|
if (value->isBlob())
|
2006-05-13 03:27:35 +02:00
|
|
|
{
|
|
|
|
// 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,
|
2008-12-20 09:12:19 +01:00
|
|
|
reinterpret_cast<bid*>(value->dsc_address));
|
2006-05-13 03:27:35 +02:00
|
|
|
|
|
|
|
// 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
|
|
|
|
2008-12-25 16:05:06 +01:00
|
|
|
if (value->isBlob())
|
2006-05-13 03:27:35 +02:00
|
|
|
{
|
|
|
|
// We have valueCanonical already allocated.
|
|
|
|
// Use it to get the substring that will be written to the new blob.
|
2008-12-20 09:12:19 +01:00
|
|
|
const 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);
|
|
|
|
|
2009-01-20 09:33:59 +01:00
|
|
|
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction, &impure->vlu_misc.vlu_bid);
|
2006-05-13 03:27:35 +02:00
|
|
|
|
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;
|
2009-11-28 20:39:23 +01:00
|
|
|
desc.makeText(valueLength, ttype);
|
2006-05-13 03:27:35 +02:00
|
|
|
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.
|
|
|
|
*
|
|
|
|
**************************************/
|
2010-03-21 08:29:58 +01:00
|
|
|
const jrd_req* const request = tdbb->getRequest();
|
2002-06-14 14:09:37 +02:00
|
|
|
|
2010-03-21 08:29:58 +01:00
|
|
|
fb_assert(value->dsc_dtype == dtype_long);
|
|
|
|
const internal_info_id id = *reinterpret_cast<internal_info_id*>(value->dsc_address);
|
|
|
|
|
|
|
|
if (id == internal_sqlstate)
|
|
|
|
{
|
2010-03-21 18:57:40 +01:00
|
|
|
FB_SQLSTATE_STRING sqlstate;
|
2010-03-21 08:29:58 +01:00
|
|
|
request->req_last_xcp.as_sqlstate(sqlstate);
|
|
|
|
|
|
|
|
dsc desc;
|
|
|
|
desc.makeText(FB_SQLSTATE_LENGTH, ttype_ascii, (UCHAR*) sqlstate);
|
|
|
|
EVL_make_value(tdbb, &desc, impure);
|
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
SLONG result = 0;
|
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:
|
2010-03-21 08:29:58 +01:00
|
|
|
result = 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:
|
2010-03-21 08:29:58 +01:00
|
|
|
result = tdbb->getTransaction()->tra_number;
|
2002-06-14 14:09:37 +02:00
|
|
|
break;
|
2002-09-28 16:04:35 +02:00
|
|
|
case internal_gdscode:
|
2010-03-21 08:29:58 +01:00
|
|
|
result = request->req_last_xcp.as_gdscode();
|
2002-09-28 16:04:35 +02:00
|
|
|
break;
|
|
|
|
case internal_sqlcode:
|
2010-03-21 08:29:58 +01:00
|
|
|
result = request->req_last_xcp.as_sqlcode();
|
2002-09-28 16:04:35 +02:00
|
|
|
break;
|
|
|
|
case internal_rows_affected:
|
2010-03-21 08:29:58 +01:00
|
|
|
result = request->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:
|
2010-03-21 08:29:58 +01:00
|
|
|
result = request->req_trigger_action;
|
2003-01-15 13:08:59 +01:00
|
|
|
break;
|
2002-06-14 14:09:37 +02:00
|
|
|
default:
|
2009-11-22 04:56:20 +01:00
|
|
|
BUGCHECK(232); // msg 232 EVL_expr: invalid operation
|
2002-06-14 14:09:37 +02:00
|
|
|
}
|
2010-03-21 08:29:58 +01:00
|
|
|
|
|
|
|
dsc desc;
|
|
|
|
desc.makeLong(0, &result);
|
|
|
|
EVL_make_value(tdbb, &desc, impure);
|
2002-06-14 14:09:37 +02:00
|
|
|
|
|
|
|
return &impure->vlu_desc;
|
|
|
|
}
|