8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 23:23:04 +01:00
firebird-mirror/src/jrd/evl.cpp

4377 lines
121 KiB
C++
Raw Normal View History

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 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
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
#include <math.h>
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
#include "../jrd/ibase.h"
#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"
#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"
#include "../common/classes/timestamp.h"
#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"
#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/sort_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/align.h"
2002-06-30 11:58:20 +02:00
#include "../jrd/met_proto.h"
#include "../jrd/misc_func_ids.h"
#include "../common/config/config.h"
2007-04-12 17:56:34 +02:00
#include "../jrd/SysFunction.h"
#include "../common/classes/FpeControl.h"
#include "../jrd/recsrc/RecordSource.h"
#include "../jrd/recsrc/Cursor.h"
#include "../common/classes/Aligner.h"
#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 ***
* 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.
*/
using namespace Jrd;
using namespace Firebird;
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*);
static dsc* dbkey(thread_db*, const jrd_nod*, impure_value*);
static dsc* eval_statistical(thread_db*, jrd_nod*, impure_value*);
static dsc* extract(thread_db*, jrd_nod*, impure_value*);
static SINT64 get_day_fraction(const dsc* d);
static dsc* get_mask(thread_db*, jrd_nod*, impure_value*);
static SINT64 get_timestamp_to_isc_ticks(const dsc* d);
static dsc* lock_state(thread_db*, jrd_nod*, impure_value*);
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*));
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*);
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);
static dsc* string_length(thread_db*, jrd_nod*, impure_value*);
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*);
static dsc* internal_info(thread_db*, const dsc*, impure_value*);
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
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;
Record* record;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
DEV_BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest();
impure_value* impure = (impure_value*) ((SCHAR *) request + 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
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];
desc = &format->fmt_desc[arg_number];
2001-05-23 15:26:42 +02:00
impure->vlu_desc.dsc_address =
(UCHAR *) request + message->nod_impure + (IPTR) desc->dsc_address;
2001-05-23 15:26:42 +02:00
impure->vlu_desc.dsc_dtype = desc->dsc_dtype;
impure->vlu_desc.dsc_length = desc->dsc_length;
impure->vlu_desc.dsc_scale = desc->dsc_scale;
impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type;
2003-10-21 00:41:11 +02:00
if (DTYPE_IS_TEXT(desc->dsc_dtype) &&
2008-12-20 09:12:19 +01:00
((INTL_TTYPE(desc) == ttype_dynamic) || (INTL_GET_CHARSET(desc) == CS_dynamic)))
{
2001-05-23 15:26:42 +02:00
/* Value is a text value, we're assigning it back to the user
process, user process has not specified a subtype, user
2003-12-22 11:00:59 +01:00
process specified dynamic translation and the dsc isn't from
2001-05-23 15:26:42 +02:00
a 3.3 type request (blr_cstring2 instead of blr_cstring) so
convert the charset to the declared charset of the process. */
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))
{
// The below condition means that EVL_field() returned
// a read-only dummy value which cannot be assigned to.
// The usual reason is a field being unexpectedly dropped.
if (impure->vlu_desc.dsc_address &&
!(impure->vlu_desc.dsc_flags & DSC_null))
{
ERR_post(Arg::Gds(isc_field_disappeared));
}
}
2001-05-23 15:26:42 +02:00
if (!impure->vlu_desc.dsc_address)
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:
// Calculate descriptor
2001-05-23 15:26:42 +02:00
node = node->nod_arg[e_var_variable];
impure = (impure_value*) ((SCHAR *) request + 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;
}
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:
{
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);
}
2001-05-23 15:26:42 +02:00
case nod_bit_or:
return RecordBitmap::bit_or(
EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and),
EVL_bitmap(tdbb, node->nod_arg[1], bitmap_and));
2001-05-23 15:26:42 +02:00
case nod_bit_in:
{
RecordBitmap** inv_bitmap = EVL_bitmap(tdbb, node->nod_arg[0], bitmap_and);
BTR_evaluate(tdbb,
reinterpret_cast<IndexRetrieval*>(node->nod_arg[1]->nod_arg[e_idx_retrieval]),
inv_bitmap, bitmap_and);
return inv_bitmap;
}
2001-05-23 15:26:42 +02:00
case nod_bit_dbkey:
{
2007-12-05 01:03:15 +01:00
impure_inversion* impure = (impure_inversion*) ((SCHAR*) tdbb->getRequest() + node->nod_impure);
RecordBitmap::reset(impure->inv_bitmap);
const dsc* desc = EVL_expr(tdbb, node->nod_arg[0]);
if (!(tdbb->getRequest()->req_flags & req_null) &&
desc->dsc_length == sizeof(RecordNumber::Packed))
{
const USHORT id = (USHORT)(IPTR) node->nod_arg[1];
Firebird::Aligner<RecordNumber::Packed> alignedNumbers(desc->dsc_address, desc->dsc_length);
const RecordNumber::Packed* numbers = alignedNumbers;
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)
rel_dbkey.decrement();
if (!bitmap_and || bitmap_and->test(rel_dbkey.getValue()))
RBM_SET(tdbb->getDefaultPool(), &impure->inv_bitmap, rel_dbkey.getValue());
}
return &impure->inv_bitmap;
2001-05-23 15:26:42 +02:00
}
case nod_index:
{
2007-12-05 01:03:15 +01:00
impure_inversion* impure = (impure_inversion*) ((SCHAR*) tdbb->getRequest() + node->nod_impure);
RecordBitmap::reset(impure->inv_bitmap);
2001-05-23 15:26:42 +02:00
BTR_evaluate(tdbb,
reinterpret_cast<IndexRetrieval*>(node->nod_arg[e_idx_retrieval]),
&impure->inv_bitmap, bitmap_and);
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;
}
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];
bool value;
2001-05-23 15:26:42 +02:00
SSHORT comparison;
impure_value* impure;
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
jrd_req* request = tdbb->getRequest();
jrd_nod** ptr = node->nod_arg;
2001-05-23 15:26:42 +02: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:
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;
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
const jrd_nod* rec_version = *ptr;
2001-05-23 15:26:42 +02:00
desc[0] = EVL_expr(tdbb, *ptr++);
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)
{
impure = reinterpret_cast<impure_value*>((SCHAR *)request + node->nod_impure);
// Check that data type of operand is still the same.
// It may change due to multiple formats present in stream
2004-05-09 07:48:33 +02:00
// System tables are the good example of such streams -
// data coming from ini.epp has ASCII ttype, user data is UNICODE_FSS
//
// Note that value descriptor may be NULL pointer if value is SQL NULL
if ((impure->vlu_flags & VLU_computed) && desc[0] &&
(impure->vlu_desc.dsc_dtype != desc[0]->dsc_dtype ||
impure->vlu_desc.dsc_sub_type != desc[0]->dsc_sub_type ||
impure->vlu_desc.dsc_scale != desc[0]->dsc_scale)
)
{
impure->vlu_flags &= ~VLU_computed;
}
2008-11-30 03:23:01 +01:00
if (impure->vlu_flags & VLU_computed)
{
if (impure->vlu_flags & VLU_null)
request->req_flags |= req_null;
else
computed_invariant = true;
2005-05-28 00:45:31 +02:00
}
2008-11-30 03:23:01 +01:00
else
{
desc[1] = EVL_expr(tdbb, *ptr++);
2008-11-30 03:23:01 +01:00
if (request->req_flags & req_null)
{
impure->vlu_flags |= VLU_computed;
impure->vlu_flags |= VLU_null;
}
2008-11-30 03:23:01 +01:00
else
{
impure->vlu_flags &= ~VLU_null;
// 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-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;
}
}
}
}
else
desc[1] = EVL_expr(tdbb, *ptr++);
// An equivalence operator evaluates to true when both operands
// are NULL and behaves like an equality operator otherwise.
// Note that this operator never sets req_null flag
if (node->nod_type == nod_equiv)
{
if ((flags & req_null) && (request->req_flags & req_null))
{
request->req_flags &= ~req_null;
return true;
}
2006-09-09 10:32:32 +02:00
if ((flags & req_null) || (request->req_flags & req_null))
{
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
// 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)
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++);
break;
2009-11-22 04:56:20 +01:00
default: // Shut up some compiler warnings
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
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
const USHORT firstnull = request->req_flags & req_null;
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_null;
if (!value && !firstnull)
{
// First term is FALSE, why the whole expression is false.
// NULL flag is already turned off a few lines above.
return false;
}
const bool value2 = EVL_boolean(tdbb, *ptr);
const USHORT secondnull = request->req_flags & req_null;
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_null;
if ((!value && !firstnull) || (!value2 && !secondnull)) {
2009-11-22 04:56:20 +01:00
return false; // at least one operand was false
}
2006-09-09 10:32:32 +02:00
if (value && value2) {
2009-11-22 04:56:20 +01:00
return true; // both true
}
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:
{
USHORT* invariant_flags;
2001-05-23 15:26:42 +02:00
if (node->nod_flags & nod_invariant)
{
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
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))
{
2001-05-23 15:26:42 +02:00
request->req_flags |= req_null;
}
2001-05-23 15:26:42 +02:00
else
{
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_null;
}
return impure->vlu_misc.vlu_short != 0;
2001-05-23 15:26:42 +02:00
}
}
Cursor* const select = (Cursor*) node->nod_arg[e_any_rsb];
select->open(tdbb);
value = select->fetchNext(tdbb);
select->close(tdbb);
2001-05-23 15:26:42 +02:00
if (node->nod_type == nod_any)
{
2001-05-23 15:26:42 +02: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
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))
{
*invariant_flags |= VLU_null;
}
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:
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)
{
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)
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 */
const ULONG flags = request->req_flags;
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_null;
if (value)
{
// First term is TRUE, why the whole expression is true.
// NULL flag is already turned off a few lines above.
return true;
}
2005-05-28 00:45:31 +02: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;
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
if (flags & req_null) {
2001-05-23 15:26:42 +02:00
request->req_flags |= req_null;
}
return false;
2001-05-23 15:26:42 +02:00
}
case nod_unique:
{
USHORT* invariant_flags;
2001-05-23 15:26:42 +02:00
if (node->nod_flags & nod_invariant)
{
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
invariant_flags = & impure->vlu_flags;
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;
return impure->vlu_misc.vlu_short != 0;
2001-05-23 15:26:42 +02:00
}
}
Cursor* const urs = reinterpret_cast<Cursor*>(node->nod_arg[e_any_rsb]);
urs->open(tdbb);
value = urs->fetchNext(tdbb);
if (value)
{
value = !urs->fetchNext(tdbb);
}
urs->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
if (node->nod_flags & nod_invariant)
{
2001-05-23 15:26:42 +02:00
*invariant_flags |= VLU_computed;
impure->vlu_misc.vlu_short = value ? TRUE : FALSE;
2001-05-23 15:26:42 +02:00
}
return value;
}
case nod_equiv:
2001-05-23 15:26:42 +02:00
case nod_eql:
return (comparison == 0);
2001-05-23 15:26:42 +02:00
case nod_neq:
return (comparison != 0);
2001-05-23 15:26:42 +02:00
case nod_gtr:
return (comparison > 0);
2001-05-23 15:26:42 +02:00
case nod_geq:
return (comparison >= 0);
2001-05-23 15:26:42 +02:00
case nod_lss:
return (comparison < 0);
2001-05-23 15:26:42 +02:00
case nod_leq:
return (comparison <= 0);
2001-05-23 15:26:42 +02:00
case nod_between:
desc[1] = EVL_expr(tdbb, node->nod_arg[2]);
if (request->req_flags & req_null)
return false;
return (comparison >= 0 && MOV_compare(desc[0], desc[1]) <= 0);
2001-05-23 15:26:42 +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
}
return false;
2001-05-23 15:26:42 +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);
jrd_req* const request = tdbb->getRequest();
impure_value* const impure = (impure_value*) ((SCHAR *) request + 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)
{
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:
{
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;
}
}
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];
desc = &format->fmt_desc[(int)(IPTR) node->nod_arg[e_arg_number]];
2001-05-23 15:26:42 +02:00
impure->vlu_desc.dsc_address = (UCHAR *) request +
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)
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
2005-05-28 00:45:31 +02:00
USHORT* impure_flags = (USHORT*) ((UCHAR *) request +
(IPTR) message->nod_arg[e_msg_impure_flags] +
(sizeof(USHORT) * (IPTR) node->nod_arg[e_arg_number]));
if (!(*impure_flags & VLU_checked))
{
if (node->nod_arg[e_arg_info])
{
EVL_validate(tdbb,
Item(nod_argument, (IPTR) node->nod_arg[e_arg_message]->nod_arg[e_msg_number],
(IPTR) node->nod_arg[e_arg_number]),
reinterpret_cast<const ItemInfo*>(node->nod_arg[e_arg_info]),
&impure->vlu_desc, request->req_flags & req_null);
}
*impure_flags |= VLU_checked;
}
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];
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;
else
{
2008-12-01 10:21:31 +01:00
const Format* compileFormat = (Format*) node->nod_arg[e_fld_format];
// ASF: CORE-1432 - If the the record is not on the latest format, upgrade it.
if (compileFormat &&
record->rec_format->fmt_version != compileFormat->fmt_version &&
!DSC_EQUIV(&impure->vlu_desc, &compileFormat->fmt_desc[id], true))
{
dsc desc = impure->vlu_desc;
impure->vlu_desc = compileFormat->fmt_desc[id];
if (impure->vlu_desc.isText())
{
// Allocate a string block of sufficient size.
VaryingString* string = impure->vlu_string;
if (string && string->str_length < impure->vlu_desc.dsc_length)
{
delete string;
string = NULL;
}
if (!string)
{
string = impure->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(),
impure->vlu_desc.dsc_length) VaryingString();
string->str_length = impure->vlu_desc.dsc_length;
}
impure->vlu_desc.dsc_address = string->str_data;
}
else
impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc;
MOV_move(tdbb, &desc, &impure->vlu_desc);
}
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)
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;
}
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];
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;
}
2001-05-23 15:26:42 +02:00
case nod_function:
{
2009-12-27 20:05:38 +01:00
const Function* function = reinterpret_cast<Function*>(node->nod_arg[e_fun_function]);
return function->execute(tdbb, node->nod_arg[e_fun_args], impure);
}
2001-05-23 15:26:42 +02:00
2007-04-12 17:56:34 +02:00
case nod_sys_function:
{
const SysFunction* sysFunction = reinterpret_cast<SysFunction*>(node->nod_arg[e_sysfun_function]);
2007-04-12 17:56:34 +02:00
return sysFunction->evlFunc(tdbb, sysFunction, node->nod_arg[e_sysfun_args], impure);
}
2001-05-23 15:26:42 +02:00
case nod_literal:
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:
{
// 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;
2008-12-20 09:12:19 +01:00
if (node->nod_type == nod_current_time || node->nod_type == nod_current_timestamp)
{
const int precision = (int)(IPTR) node->nod_arg[0];
fb_assert(precision >= 0);
Firebird::TimeStamp::round_time(enc_times.timestamp_time, precision);
}
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];
*((ISC_TIMESTAMP *) impure->vlu_desc.dsc_address) = enc_times;
2001-05-23 15:26:42 +02:00
break;
default:
fb_assert(false);
2001-05-23 15:26:42 +02:00
break;
}
}
return &impure->vlu_desc;
case nod_user_name:
{
impure->vlu_desc.dsc_dtype = dtype_text;
impure->vlu_desc.dsc_sub_type = 0;
impure->vlu_desc.dsc_scale = 0;
2009-11-28 20:39:23 +01:00
impure->vlu_desc.setTextType(ttype_metadata);
const char* cur_user = 0;
if (tdbb->getAttachment()->att_user)
{
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));
}
if (cur_user)
impure->vlu_desc.dsc_length = strlen(cur_user);
else
impure->vlu_desc.dsc_length = 0;
}
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:
{
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);
const char* cur_role = 0;
if (tdbb->getAttachment()->att_user)
{
cur_role = tdbb->getAttachment()->att_user->usr_sql_role_name.c_str();
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(const_cast<char*>(cur_role));
}
if (cur_role)
impure->vlu_desc.dsc_length = strlen(cur_role);
else
impure->vlu_desc.dsc_length = 0;
}
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:
return extract(tdbb, node, impure);
2001-05-23 15:26:42 +02:00
case nod_strlen:
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:
{
const jrd_nod* node2 = node->nod_arg[e_var_variable];
2005-05-28 00:45:31 +02:00
impure_value* impure2 = (impure_value*) ((SCHAR *) request + node2->nod_impure);
if (impure2->vlu_desc.dsc_flags & DSC_null)
request->req_flags |= req_null;
2005-05-28 00:45:31 +02:00
impure->vlu_desc = impure2->vlu_desc;
2005-05-28 00:45:31 +02:00
if (impure->vlu_desc.dsc_dtype == dtype_text)
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
2005-05-28 00:45:31 +02:00
if (!(impure2->vlu_flags & VLU_checked))
{
if (node->nod_arg[e_var_info])
{
EVL_validate(tdbb, Item(nod_variable, (IPTR) node->nod_arg[e_var_id]),
reinterpret_cast<const ItemInfo*>(node->nod_arg[e_var_info]),
&impure->vlu_desc, impure->vlu_desc.dsc_flags & DSC_null);
}
impure2->vlu_flags |= VLU_checked;
}
return &impure->vlu_desc;
}
2001-05-23 15:26:42 +02:00
case nod_domain_validation:
if (request->req_domain_validation == NULL ||
(request->req_domain_validation->dsc_flags & DSC_null))
{
request->req_flags |= req_null;
}
return request->req_domain_validation;
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;
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++);
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
{
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));
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:
{
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));
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);
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;
}
bool EVL_field(jrd_rel* relation, Record* record, USHORT id, dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E V L _ f i e l d
*
**************************************
*
* Functional description
* Evaluate a field by filling out a descriptor.
*
**************************************/
DEV_BLKCHK(record, type_rec);
if (!record)
{
// 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;
if (format && id < format->fmt_count) {
2001-05-23 15:26:42 +02:00
*desc = format->fmt_desc[id];
}
2001-05-23 15:26:42 +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.
if (!format || id >= format->fmt_count || !desc->dsc_length)
*/
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
if (record && record->rec_format && relation)
{
thread_db* tdbb = JRD_get_thread_data();
while (format &&
(id >= format->fmt_defaults.getCount() ||
format->fmt_defaults[id].vlu_desc.isUnknown()))
{
if (format->fmt_version >= relation->rel_current_format->fmt_version)
{
format = NULL;
break;
}
format = MET_format(tdbb, relation, format->fmt_version + 1);
2001-05-23 15:26:42 +02: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;
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
if (!desc->dsc_address) {
2003-12-22 11:00:59 +01:00
return false;
}
2001-05-23 15:26:42 +02: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
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:
case dtype_dbkey:
2001-05-23 15:26:42 +02:00
break;
case dtype_blob:
value->vlu_misc.vlu_bid = *(bid*)from.dsc_address;
return;
2001-05-23 15:26:42 +02:00
default:
fb_assert(false);
2001-05-23 15:26:42 +02:00
break;
}
VaryStr<128> temp;
UCHAR* address;
USHORT ttype;
// Get string. If necessary, get_string will convert the string into a
// temporary buffer. Since this will always be the result of a conversion,
// this isn't a serious problem.
const USHORT length = MOV_get_string_ptr(&from, &ttype, &address, &temp, sizeof(temp));
// 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)
{
delete string;
string = NULL;
}
2009-07-14 03:18:17 +02:00
if (!string)
{
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;
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);
}
2001-05-23 15:26:42 +02:00
if (address && length && target != address)
memcpy(target, address, length);
2001-05-23 15:26:42 +02: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;
jrd_req* request = tdbb->getRequest();
bool err = false;
if (null && !itemInfo->nullable)
err = true;
const char* value = NULL_STRING_MARK;
VaryStr<128> temp;
MapFieldInfo::ValueType fieldInfo;
if (!err && itemInfo->fullDomain &&
2008-12-20 09:12:19 +01:00
request->req_map_field_info.get(itemInfo->field, fieldInfo) && fieldInfo.validation)
{
if (desc && null)
{
desc->dsc_flags |= DSC_null;
}
const bool desc_is_null = !desc || (desc->dsc_flags & DSC_null);
request->req_domain_validation = desc;
2007-05-18 08:15:26 +02:00
const USHORT flags = request->req_flags;
2008-12-20 09:12:19 +01:00
if (!EVL_boolean(tdbb, fieldInfo.validation) && !(request->req_flags & req_null))
{
const USHORT length = desc_is_null ? 0 :
MOV_make_string(desc, ttype_dynamic, &value, &temp, sizeof(temp) - 1);
if (desc_is_null)
value = NULL_STRING_MARK;
else if (!length)
value = "";
else
const_cast<char*>(value)[length] = 0; // safe cast - data is on our local stack
err = true;
}
request->req_flags = flags;
}
Firebird::string s;
if (err)
{
ISC_STATUS status = isc_not_valid_for_var;
const char* arg;
if (item.type == nod_cast)
{
status = isc_not_valid_for;
arg = "CAST";
}
else
{
if (itemInfo->name.isEmpty())
{
int index = item.index + 1;
status = isc_not_valid_for;
if (item.type == nod_variable)
{
if (request->req_procedure)
{
if (index <= int(request->req_procedure->prc_output_fields.getCount()))
s.printf("output parameter number %d", index);
else
{
s.printf("variable number %d",
index - int(request->req_procedure->prc_output_fields.getCount()));
}
}
else
s.printf("variable number %d", index);
}
else if (item.type == nod_argument && item.subType == 0)
s.printf("input parameter number %d", (index - 1) / 2 + 1);
else if (item.type == nod_argument && item.subType == 1)
s.printf("output parameter number %d", index);
if (s.isEmpty())
arg = UNKNOWN_STRING_MARK;
else
arg = s.c_str();
}
else
arg = itemInfo->name.c_str();
}
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(status) << Arg::Str(arg) << Arg::Str(value));
}
}
dsc* EVL_add(const dsc* desc, const jrd_nod* node, impure_value* value)
2001-05-23 15:26:42 +02: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);
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
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)
{
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;
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)
{
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) ?
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
// CVC: Maybe we should upgrade the sum to double if it doesn't fit?
// This is what was done for multiplicaton in dialect 1.
const SLONG l1 = MOV_get_long(desc, node->nod_scale);
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;
}
dsc* EVL_add2(const dsc* desc, const jrd_nod* node, impure_value* value)
2001-05-23 15:26:42 +02: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);
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
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)
{
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;
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)
{
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) ?
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
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))
ERR_post(Arg::Gds(isc_exception_integer_overflow));
2001-05-23 15:26:42 +02:00
return result;
}
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)))
{
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:
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;
}
2001-05-23 15:26:42 +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
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
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);
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
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);
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)
{
2001-05-23 15:26:42 +02:00
d2 = d1 - d2;
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;
if (!Firebird::TimeStamp::isValidDate(value->vlu_misc.vlu_sql_date)) {
ERR_post(Arg::Gds(isc_date_range_exceeded));
}
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;
}
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
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
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;
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
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;
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)
{
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;
}
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
dsc* result = &value->vlu_desc;
2001-05-23 15:26:42 +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)
{
// DATE + TIME
2001-05-23 15:26:42 +02:00
if ((desc->dsc_dtype == dtype_sql_time) &&
((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;
}
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)
{
// 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)))
{
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
{
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
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
{ // 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
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
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
{
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
if (!Firebird::TimeStamp::isValidTimeStamp(value->vlu_misc.vlu_timestamp)) {
ERR_post(Arg::Gds(isc_datetime_range_exceeded));
2006-09-09 10:32:32 +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
} // 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;
}
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);
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
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;
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:
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
{
const double divisor = MOV_get_double(desc2);
if (divisor == 0)
{
ERR_post(Arg::Gds(isc_arith_except) <<
Arg::Gds(isc_exception_float_divide_by_zero));
}
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));
}
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:
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))
{
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
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)
{
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;
}
EVL_validate(tdbb, Item(nod_cast), (ItemInfo*) node->nod_arg[e_cast_iteminfo],
value, value == NULL || (value->dsc_flags & DSC_null));
if (value == NULL)
{
tdbb->getRequest()->req_flags |= req_null;
return NULL;
}
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);
else
MOV_move(tdbb, value, &impure->vlu_desc);
2001-05-23 15:26:42 +02:00
if (impure->vlu_desc.dsc_dtype == dtype_text)
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
2001-05-23 15:26:42 +02:00
return &impure->vlu_desc;
}
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);
// Get request, record parameter block, and relation for stream
2001-05-23 15:26:42 +02:00
jrd_req* request = tdbb->getRequest();
impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
const record_param* rpb = &request->req_rpb[(int) (IPTR) node->nod_arg[0]];
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
if (!rpb->rpb_number.isValid() || !relation)
{
request->req_flags |= req_null;
return NULL;
}
// Format dbkey as vector of relation id, record number
2001-05-23 15:26:42 +02:00
// Initialize first 32 bits of DB_KEY
impure->vlu_misc.vlu_dbkey[0] = 0;
// Now, put relation ID into first 16 bits of DB_KEY
// We do not assign it as SLONG because of big-endian machines.
2009-12-10 01:02:01 +01:00
*(USHORT*) impure->vlu_misc.vlu_dbkey = relation->rel_id;
// 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
// Initialize descriptor
2001-05-23 15:26:42 +02:00
impure->vlu_desc.dsc_address = (UCHAR *) impure->vlu_misc.vlu_dbkey;
impure->vlu_desc.dsc_dtype = dtype_dbkey;
impure->vlu_desc.dsc_length = type_lengths[dtype_dbkey];
impure->vlu_desc.dsc_ttype() = ttype_binary;
2001-05-23 15:26:42 +02:00
return &impure->vlu_desc;
}
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.
*
**************************************/
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
jrd_req* request = tdbb->getRequest();
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)
{
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);
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_int64;
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);
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long;
}
Cursor* const rsb = (Cursor*) node->nod_arg[e_stat_rsb];
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;
2009-05-10 17:23:31 +02:00
try
{
// Handle each variety separately
2001-05-23 15:26:42 +02:00
switch (node->nod_type)
2001-05-23 15:26:42 +02:00
{
case nod_count:
flag = 0;
while (rsb->fetchNext(tdbb))
{
2001-05-23 15:26:42 +02:00
++impure->vlu_misc.vlu_long;
}
break;
2001-05-23 15:26:42 +02:00
case nod_min:
case nod_max:
while (rsb->fetchNext(tdbb))
{
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
}
break;
2001-05-23 15:26:42 +02:00
case nod_from:
if (rsb->fetchNext(tdbb))
{
desc = EVL_expr(tdbb, node->nod_arg[e_stat_value]);
}
2001-05-23 15:26:42 +02:00
else
{
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
case nod_total:
while (rsb->fetchNext(tdbb))
{
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.
EVL_add(desc, node, impure);
count++;
}
desc = &impure->vlu_desc;
2009-11-23 06:24:29 +01:00
if (node->nod_type == nod_total)
{
flag = 0;
break;
}
if (!count)
break;
d = MOV_get_double(&impure->vlu_desc);
impure->vlu_misc.vlu_double = d / count;
impure->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
impure->vlu_desc.dsc_length = sizeof(double);
impure->vlu_desc.dsc_scale = 0;
2001-05-23 15:26:42 +02:00
flag = 0;
break;
2009-11-22 04:56:20 +01:00
case nod_average2: // average with dialect-3 semantics
while (rsb->fetchNext(tdbb))
{
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.
EVL_add(desc, node, impure);
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.
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;
default:
2009-11-22 04:56:20 +01:00
BUGCHECK(233); // msg 233 eval_statistical: invalid operation
}
}
catch (const Firebird::Exception&)
{
2009-05-10 17:23:31 +02:00
// close stream
// ignore any error during it to keep original
try
2001-05-23 15:26:42 +02:00
{
rsb->close(tdbb);
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&)
{
}
2001-05-23 15:26:42 +02:00
throw;
}
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
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
// *************
// 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);
impure->vlu_desc.dsc_length = sizeof(SSHORT);
jrd_req* request = tdbb->getRequest();
// 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))
{
request->req_flags |= req_null;
impure->vlu_misc.vlu_short = 0;
return &impure->vlu_desc;
}
tm times = {0};
int fractions;
switch (value->dsc_dtype)
{
case dtype_sql_time:
2008-12-20 09:12:19 +01:00
switch (extract_part)
{
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
&times.tm_hour, &times.tm_min, &times.tm_sec, &fractions);
break;
default:
ERR_post(Arg::Gds(isc_expression_eval_err) <<
Arg::Gds(isc_invalid_extractpart_time));
}
break;
case dtype_sql_date:
2008-12-20 09:12:19 +01:00
switch (extract_part)
{
2008-12-20 09:12:19 +01:00
case blr_extract_hour:
case blr_extract_minute:
case blr_extract_second:
case blr_extract_millisecond:
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, &times);
}
break;
case dtype_timestamp:
2008-12-20 20:57:43 +01:00
Firebird::TimeStamp::decode_timestamp(*(GDS_TIMESTAMP*) value->dsc_address,
&times, &fractions);
break;
default:
ERR_post(Arg::Gds(isc_expression_eval_err) <<
Arg::Gds(isc_invalidarg_extract));
break;
}
USHORT part;
2009-01-20 09:33:59 +01:00
switch (extract_part)
{
case blr_extract_year:
part = times.tm_year + 1900;
break;
case blr_extract_month:
part = times.tm_mon + 1;
break;
case blr_extract_day:
part = times.tm_mday;
break;
case blr_extract_hour:
part = times.tm_hour;
break;
case blr_extract_minute:
part = times.tm_min;
break;
case blr_extract_second:
impure->vlu_desc.dsc_dtype = dtype_long;
impure->vlu_desc.dsc_length = sizeof(ULONG);
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
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;
return &impure->vlu_desc;
case blr_extract_millisecond:
impure->vlu_desc.dsc_dtype = dtype_long;
impure->vlu_desc.dsc_length = sizeof(ULONG);
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;
return &impure->vlu_desc;
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-08-01 11:00:20 +02:00
const int y = times.tm_year + 1900;
const int dayOfYearNumber = times.tm_yday + 1;
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-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-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-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-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-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-08-01 11:00:20 +02:00
int j = dayOfYearNumber + (7 - weekday) + (jan1Weekday - 1);
weekNumber = j / 7;
if (jan1Weekday > 4)
weekNumber--;
}
2007-08-01 11:00:20 +02:00
part = weekNumber;
}
break;
case blr_extract_yearday:
part = times.tm_yday;
break;
case blr_extract_weekday:
part = times.tm_wday;
break;
default:
fb_assert(false);
part = 0;
}
*(USHORT *) impure->vlu_desc.dsc_address = part;
return &impure->vlu_desc;
}
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);
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
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.
#ifdef HAVE_LLRINT
return llrint(result_days * ISC_TICKS_PER_DAY);
#else
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);
#endif
2001-05-23 15:26:42 +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);
jrd_req* request = tdbb->getRequest();
TEXT* p1 = NULL;
TEXT* p2 = NULL;
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;
// SecurityClass::flags_t is USHORT for now, so it fits in vlu_long.
impure->make_long(SCL_get_mask(tdbb, p1, p2));
2001-05-23 15:26:42 +02:00
return &impure->vlu_desc;
}
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);
result.dsc_address = reinterpret_cast<UCHAR*>(&result_timestamp);
2001-05-23 15:26:42 +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
}
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);
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
jrd_req* request = tdbb->getRequest();
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
{
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
{
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;
}
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*))
{
/**************************************
*
* 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());
if (value->isBlob())
{
EVL_make_value(tdbb, value, impure);
if (value->dsc_sub_type != isc_blob_text)
return &impure->vlu_desc;
CharSet* charSet = textType->getCharSet();
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
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
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
2006-04-18 14:40:52 +02:00
&impure->vlu_misc.vlu_bid);
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());
BLB_put_data(tdbb, newBlob, buffer.begin(), len);
}
}
BLB_close(tdbb, newBlob);
BLB_close(tdbb, blob);
}
else
{
UCHAR* ptr;
VaryStr<32> temp;
USHORT ttype;
dsc desc;
desc.dsc_length = MOV_get_string_ptr(value, &ttype, &ptr, &temp, sizeof(temp));
desc.dsc_dtype = dtype_text;
desc.dsc_address = NULL;
desc.setTextType(ttype);
EVL_make_value(tdbb, &desc, impure);
impure->vlu_desc.dsc_length = (textType->*tt_str_to_case)(desc.dsc_length,
ptr, desc.dsc_length, impure->vlu_desc.dsc_address);
}
return &impure->vlu_desc;
}
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)
{
const double d1 = MOV_get_double(desc);
const double d2 = MOV_get_double(&value->vlu_desc);
value->vlu_misc.vlu_double = d1 * d2;
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)
{
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;
//{
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;
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);
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
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;
}
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
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);
value->vlu_misc.vlu_double = d1 * d2;
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
if (node->nod_flags & nod_quad)
{
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
const SSHORT scale = NUMERIC_SCALE(value->vlu_desc);
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
if ((u1 != 0) && ((u_limit / u1) < u2)) {
ERR_post(Arg::Gds(isc_exception_integer_overflow));
}
2001-05-23 15:26:42 +02: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;
}
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)
{
const double d2 = MOV_get_double(desc);
2001-05-23 15:26:42 +02:00
if (d2 == 0.0)
{
ERR_post(Arg::Gds(isc_arith_except) <<
Arg::Gds(isc_exception_float_divide_by_zero));
}
const double d1 = MOV_get_double(&value->vlu_desc);
value->vlu_misc.vlu_double = d1 / d2;
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
*/
SINT64 i2 = MOV_get_int64(desc, desc->dsc_scale);
2001-05-23 15:26:42 +02:00
if (i2 == 0)
{
ERR_post(Arg::Gds(isc_arith_except) <<
Arg::Gds(isc_exception_integer_divide_by_zero));
}
2001-05-23 15:26:42 +02: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))
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.
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
}
2001-05-23 15:26:42 +02:00
if (addl_scale < 0)
{
ERR_post(Arg::Gds(isc_arith_except) <<
Arg::Gds(isc_numeric_out_of_range));
}
2001-05-23 15:26:42 +02:00
return &value->vlu_desc;
}
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)
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)
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 =
QUAD_NEGATE(value->vlu_misc.vlu_quad, ERR_post);
2001-05-23 15:26:42 +02:00
break;
case dtype_int64:
if (value->vlu_misc.vlu_int64 == MIN_SINT64)
ERR_post(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;
}
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
jrd_req* request = tdbb->getRequest();
2006-09-09 10:32:32 +02:00
// Already set by the caller
//impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
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)
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)
{
if (request->req_transaction->tra_commit_sub_trans->test(rpb->rpb_transaction_nr))
{
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
impure->vlu_misc.vlu_long = rpb->rpb_transaction_nr;
2001-05-23 15:26:42 +02:00
impure->vlu_desc.dsc_address = (UCHAR *) & impure->vlu_misc.vlu_long;
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;
}
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);
const dsc* desc = EVL_expr(tdbb, node->nod_arg[e_scl_field]);
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
jrd_nod* list = node->nod_arg[e_scl_subscripts];
if (list->nod_count > MAX_ARRAY_DIMENSIONS)
ERR_post(Arg::Gds(isc_array_max_dimensions) << Arg::Num(MAX_ARRAY_DIMENSIONS));
2001-05-23 15:26:42 +02:00
SLONG subscripts[MAX_ARRAY_DIMENSIONS];
int iter = 0;
jrd_nod** ptr = list->nod_arg;
for (const jrd_nod* const* const end = ptr + list->nod_count; ptr < end;)
{
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);
else
2001-05-23 15:26:42 +02:00
return NULL;
}
BLB_scalar(tdbb,
request->req_transaction,
reinterpret_cast<bid*>(desc->dsc_address),
2001-05-23 15:26:42 +02:00
list->nod_count,
subscripts, impure);
// 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;
}
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
USHORT ttype;
if (desc1->isBlob())
{
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);
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
dsc* desc3 = EVL_expr(tdbb, node->nod_arg[2]);
UCHAR* p1;
MoveBuffer sleuth_str;
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
UCHAR* p2;
MoveBuffer match_str;
USHORT l2 = MOV_make_string2(tdbb, desc2, ttype, &p2, match_str);
2009-11-22 04:56:20 +01:00
// Merge search and control strings
UCHAR control[BUFFER_SMALL];
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)
bool ret_val;
MoveBuffer data_str;
if (!desc1->isBlob())
{
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
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
UCHAR buffer[BUFFER_LARGE];
ret_val = false;
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
{
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,
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;
USHORT type1;
MoveBuffer match_str;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
DEV_BLKCHK(node, type_nod);
if (!desc1->isBlob())
{
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
if (!computed_invariant) {
l2 = MOV_make_string2(tdbb, desc2, type1, &p2, match_str);
}
VaryStr<256> temp1;
USHORT xtype1;
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);
}
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;
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
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) {
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-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
{
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)
{
2008-03-18 14:02:52 +01:00
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
impure->vlu_flags |= VLU_computed;
impure->vlu_flags |= VLU_null;
}
2008-03-18 14:02:52 +01:00
ret_val = false;
break;
}
2008-12-20 09:12:19 +01:00
escape_length = MOV_make_string(dsc, type1,
reinterpret_cast<const char**>(&escape_str),
&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);
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};
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);
ERR_post(Arg::Gds(isc_escape_invalid));
}
2008-03-18 14:02:52 +01:00
}
PatternMatcher* evaluator;
2008-03-18 14:02:52 +01:00
if (node->nod_flags & nod_invariant)
{
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
2008-01-16 09:54:50 +01:00
2008-03-18 14:02:52 +01:00
if (!(impure->vlu_flags & VLU_computed))
2008-01-16 09:54:50 +01:00
{
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
{
2008-03-18 14:02:52 +01:00
impure->vlu_misc.vlu_invariant = evaluator =
obj->createLikeMatcher(*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length);
2003-12-31 17:09:07 +01:00
}
2008-03-18 14:02:52 +01:00
else // nod_similar
2008-01-16 09:54:50 +01:00
{
2008-03-18 14:02:52 +01:00
impure->vlu_misc.vlu_invariant = evaluator =
obj->createSimilarToMatcher(*tdbb->getDefaultPool(),
p2, l2, escape_str, escape_length);
}
}
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(),
p2, l2, escape_str, escape_length);
}
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;
}
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)
{
2008-03-18 14:02:52 +01:00
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
if (!(impure->vlu_flags & VLU_computed))
2008-01-16 09:54:50 +01:00
{
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);
}
2008-03-18 14:02:52 +01:00
impure->vlu_flags |= VLU_computed;
}
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-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);
}
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;
}
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);
jrd_req* request = tdbb->getRequest();
Collation* obj = INTL_texttype_lookup(tdbb, ttype);
2005-05-28 00:45:31 +02:00
CharSet* charset = obj->getCharSet();
2008-01-16 09:54:50 +01:00
// Handle contains and starts
if (node->nod_type == nod_contains || node->nod_type == nod_starts)
{
if (node->nod_flags & nod_invariant)
2005-05-28 00:45:31 +02:00
{
2008-01-16 09:54:50 +01:00
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
PatternMatcher* evaluator;
if (!(impure->vlu_flags & VLU_computed))
{
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
impure->vlu_flags |= VLU_computed;
}
2008-01-16 09:54:50 +01:00
else
{
2009-11-18 15:31:29 +01:00
evaluator = impure->vlu_misc.vlu_invariant;
evaluator->reset();
}
2008-01-16 09:54:50 +01:00
evaluator->process(p1, l1);
return evaluator->result();
}
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)
{
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
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)
{
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
impure->vlu_flags |= VLU_computed;
impure->vlu_flags |= VLU_null;
}
return false;
}
2008-12-20 09:12:19 +01:00
escape_length = MOV_make_string(dsc, ttype,
reinterpret_cast<const char**>(&escape_str),
&temp3, sizeof(temp3));
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
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
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
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)
{
impure_value* impure = (impure_value*) ((SCHAR *) request + node->nod_impure);
2008-01-16 09:54:50 +01:00
PatternMatcher* evaluator;
if (!(impure->vlu_flags & VLU_computed))
{
2009-11-18 15:31:29 +01:00
delete impure->vlu_misc.vlu_invariant;
impure->vlu_flags |= VLU_computed;
2008-01-16 09:54:50 +01:00
if (node->nod_type == nod_like)
{
impure->vlu_misc.vlu_invariant = evaluator = obj->createLikeMatcher(
*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length);
}
else // nod_similar
{
impure->vlu_misc.vlu_invariant = evaluator = obj->createSimilarToMatcher(
*tdbb->getDefaultPool(), p2, l2, escape_str, escape_length);
}
}
2008-01-16 09:54:50 +01:00
else
{
2009-11-18 15:31:29 +01:00
evaluator = impure->vlu_misc.vlu_invariant;
evaluator->reset();
}
2008-01-16 09:54:50 +01:00
evaluator->process(p1, l1);
return evaluator->result();
}
2008-01-16 09:54:50 +01:00
if (node->nod_type == nod_like)
return obj->like(*tdbb->getDefaultPool(), p1, l1, p2, l2, escape_str, escape_length);
// nod_similar
return obj->similarTo(*tdbb->getDefaultPool(), p1, l1, p2, l2, escape_str, escape_length);
2001-05-23 15:26:42 +02:00
}
2008-01-16 09:54:50 +01:00
// Handle MATCHES
return obj->matches(*tdbb->getDefaultPool(), p1, l1, p2, l2);
2001-05-23 15:26:42 +02:00
}
// *************************
// 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);
impure->vlu_desc.dsc_length = sizeof(ULONG);
jrd_req* request = tdbb->getRequest();
// 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))
{
request->req_flags |= req_null;
impure->vlu_misc.vlu_long = 0;
return &impure->vlu_desc;
}
ULONG length;
if (value->isBlob())
{
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
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;
if (l > MAX_SINT64)
{
ERR_post(Arg::Gds(isc_arith_except) <<
Arg::Gds(isc_numeric_out_of_range));
}
length = l;
}
break;
case blr_strlen_octet:
length = blob->blb_length;
break;
case blr_strlen_char:
{
CharSet* charSet = INTL_charset_lookup(tdbb, value->dsc_blob_ttype());
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);
length = charSet->length(length, buffer.begin(), true);
}
else
length = blob->blb_length / charSet->maxBytesPerChar();
break;
}
default:
fb_assert(false);
length = 0;
}
*(ULONG*)impure->vlu_desc.dsc_address = length;
BLB_close(tdbb, blob);
return &impure->vlu_desc;
}
VaryStr<32> temp;
USHORT ttype;
UCHAR* p;
length = MOV_get_string_ptr(value, &ttype, &p, &temp, sizeof(temp));
switch (length_type)
{
case blr_strlen_bit:
{
2007-11-12 15:26:44 +01:00
FB_UINT64 l = (FB_UINT64) length * 8;
if (l > MAX_SINT64)
{
ERR_post(Arg::Gds(isc_arith_except) <<
Arg::Gds(isc_numeric_out_of_range));
}
length = l;
}
break;
case blr_strlen_octet:
break;
case blr_strlen_char:
{
CharSet* charSet = INTL_charset_lookup(tdbb, ttype);
length = charSet->length(length, p, true);
break;
}
default:
fb_assert(false);
length = 0;
}
*(ULONG *) impure->vlu_desc.dsc_address = length;
return &impure->vlu_desc;
}
static dsc* substring(thread_db* tdbb, impure_value* impure,
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
* Perform trim function = TRIM([where what FROM] string)
2005-05-28 00:45:31 +02:00
*
**************************************/
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
2005-05-28 00:45:31 +02:00
const ULONG specification = (IPTR) node->nod_arg[e_trim_specification];
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;
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;
USHORT ttype = INTL_TEXT_TYPE(*value);
TextType* tt = INTL_texttype_lookup(tdbb, ttype);
CharSet* cs = tt->getCharSet();
2005-05-28 00:45:31 +02:00
const UCHAR* charactersAddress;
2005-05-28 00:45:31 +02:00
MoveBuffer charactersBuffer;
USHORT charactersLength;
if (characters)
{
UCHAR* tempAddress = 0;
2008-12-20 09:12:19 +01:00
charactersLength = MOV_make_string2(tdbb, characters, ttype, &tempAddress, charactersBuffer);
charactersAddress = tempAddress;
2005-05-28 00:45:31 +02:00
}
else
{
charactersLength = tt->getCharSet()->getSpaceLength();
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());
const SLONG charactersCanonicalLen = tt->canonical(charactersLength, charactersAddress,
2005-05-28 00:45:31 +02:00
charactersCanonical.getCount(), charactersCanonical.begin()) * tt->getCanonicalWidth();
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> blobBuffer;
MoveBuffer valueBuffer;
UCHAR* valueAddress;
ULONG valueLength;
if (value->isBlob())
{
// Source string is a blob, things get interesting.
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
2008-12-20 09:12:19 +01:00
reinterpret_cast<bid*>(value->dsc_address));
// It's very difficult (and probably not very efficient) to trim a blob in chunks.
// So go simple way and always read entire blob in memory.
valueAddress = blobBuffer.getBuffer(blob->blb_length);
valueLength = BLB_get_data(tdbb, blob, valueAddress, blob->blb_length, true);
}
else
valueLength = MOV_make_string2(tdbb, value, ttype, &valueAddress, valueBuffer);
2005-05-28 00:45:31 +02:00
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> valueCanonical;
valueCanonical.getBuffer(valueLength / cs->minBytesPerChar() * tt->getCanonicalWidth());
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;
// CVC: Avoid endless loop with zero length trim chars.
if (charactersCanonicalLen)
2005-05-28 00:45:31 +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.
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
if (specification == blr_trim_both || specification == blr_trim_trailing)
2005-05-28 00:45:31 +02:00
{
for (; offsetTrail - charactersCanonicalLen >= offsetLead; offsetTrail -= charactersCanonicalLen)
2002-06-30 11:58:20 +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
if (value->isBlob())
{
// 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,
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);
BLB_put_data(tdbb, newBlob, valueCanonical.begin(), len);
BLB_close(tdbb, newBlob);
}
else
{
dsc desc;
2009-11-28 20:39:23 +01:00
desc.makeText(valueLength, ttype);
EVL_make_value(tdbb, &desc, impure);
impure->vlu_desc.dsc_length = cs->substring(valueLength, valueAddress,
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;
}
static dsc* internal_info(thread_db* tdbb, const dsc* value, impure_value* impure)
{
/**************************************
*
* i n t e r n a l _ i n f o
*
**************************************
*
* Functional description
* Return a given element
* of the internal engine data.
*
**************************************/
EVL_make_value(tdbb, value, impure);
2009-01-20 09:33:59 +01:00
const internal_info_id id = *reinterpret_cast<internal_info_id*>(impure->vlu_desc.dsc_address);
switch (id)
{
case internal_connection_id:
2006-05-22 00:07:35 +02:00
impure->vlu_misc.vlu_long = PAG_attachment_id(tdbb);
break;
case internal_transaction_id:
impure->vlu_misc.vlu_long = tdbb->getTransaction()->tra_number;
break;
case internal_gdscode:
2008-12-20 09:12:19 +01:00
impure->vlu_misc.vlu_long = tdbb->getRequest()->req_last_xcp.as_gdscode();
break;
case internal_sqlcode:
2008-12-20 09:12:19 +01:00
impure->vlu_misc.vlu_long = tdbb->getRequest()->req_last_xcp.as_sqlcode();
break;
case internal_rows_affected:
2008-12-20 09:12:19 +01:00
impure->vlu_misc.vlu_long = tdbb->getRequest()->req_records_affected.getCount();
break;
case internal_trigger_action:
2008-12-20 09:12:19 +01:00
impure->vlu_misc.vlu_long = tdbb->getRequest()->req_trigger_action;
break;
default:
2009-11-22 04:56:20 +01:00
BUGCHECK(232); // msg 232 EVL_expr: invalid operation
}
impure->vlu_desc.dsc_address = (UCHAR *) &impure->vlu_misc.vlu_long;
return &impure->vlu_desc;
}