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

1783 lines
46 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"
#include "../dsql/ExprNodes.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/gds_proto.h"
#include "../jrd/align.h"
2002-06-30 11:58:20 +02:00
#include "../jrd/met_proto.h"
#include "../common/config/config.h"
2007-04-12 17:56:34 +02:00
#include "../jrd/SysFunction.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
using namespace Jrd;
using namespace Firebird;
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*, const jrd_nod*, impure_value*);
static dsc* extract(thread_db*, const jrd_nod*, impure_value*);
static dsc* get_mask(thread_db*, const jrd_nod*, impure_value*);
static dsc* lock_state(thread_db*, const jrd_nod*, impure_value*);
static dsc* record_version(thread_db*, const jrd_nod*, impure_value*);
static dsc* scalar(thread_db*, const jrd_nod*, impure_value*);
static dsc* string_length(thread_db*, const jrd_nod*, impure_value*);
static dsc* substring(thread_db*, impure_value*, dsc*, const dsc*, const dsc*);
2001-05-23 15:26:42 +02:00
dsc* EVL_assign_to(thread_db* tdbb, const 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.
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest();
impure_value* impure = request->getImpure<impure_value>(node->nod_impure);
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// The only nodes that can be assigned to are: argument, field and variable.
2001-05-23 15:26:42 +02:00
int arg_number;
2010-10-09 03:57:37 +02:00
const dsc* desc;
const Format* format;
const jrd_nod* message;
Record* record;
const ParameterNode* paramNode;
2010-10-09 03:57:37 +02:00
if ((paramNode = ExprNode::as<ParameterNode>(node)))
2009-01-20 09:33:59 +01:00
{
2010-10-09 03:57:37 +02:00
message = paramNode->message;
2004-03-30 06:10:52 +02:00
format = (Format*) message->nod_arg[e_msg_format];
2010-10-09 03:57:37 +02:00
arg_number = paramNode->argNumber;
desc = &format->fmt_desc[arg_number];
2010-10-09 03:57:37 +02:00
impure->vlu_desc.dsc_address = request->getImpure<UCHAR>(
message->nod_impure + (IPTR) desc->dsc_address);
2001-05-23 15:26:42 +02:00
impure->vlu_desc.dsc_dtype = desc->dsc_dtype;
impure->vlu_desc.dsc_length = desc->dsc_length;
impure->vlu_desc.dsc_scale = desc->dsc_scale;
impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type;
2010-10-09 03:57:37 +02:00
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)))
{
2010-10-09 03:57:37 +02:00
// Value is a text value, we're assigning it back to the user
// process, user process has not specified a subtype, user
// process specified dynamic translation and the dsc isn't from
// a 3.3 type request (blr_cstring2 instead of blr_cstring) so
// convert the charset to the declared charset of the process.
2001-05-23 15:26:42 +02:00
2009-11-28 20:39:23 +01:00
impure->vlu_desc.setTextType(tdbb->getCharSet());
2001-05-23 15:26:42 +02:00
}
2010-10-09 03:57:37 +02:00
2001-05-23 15:26:42 +02:00
return &impure->vlu_desc;
2010-10-09 03:57:37 +02:00
}
2001-05-23 15:26:42 +02:00
2010-10-09 03:57:37 +02:00
switch (node->nod_type)
{
2001-05-23 15:26:42 +02:00
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 = request->getImpure<impure_value>(node->nod_impure);
2001-05-23 15:26:42 +02:00
return &impure->vlu_desc;
default:
2009-11-22 04:56:20 +01:00
BUGCHECK(229); // msg 229 EVL_assign_to: invalid operation
2001-05-23 15:26:42 +02:00
}
return NULL;
}
RecordBitmap** EVL_bitmap(thread_db* tdbb, const InversionNode* 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);
switch (node->type)
2009-01-20 09:33:59 +01:00
{
case InversionNode::TYPE_AND:
{
RecordBitmap** bitmap = EVL_bitmap(tdbb, node->node1, bitmap_and);
if (!(*bitmap) || !(*bitmap)->getFirst())
return bitmap;
2008-01-16 09:54:50 +01:00
return EVL_bitmap(tdbb, node->node2, *bitmap);
}
2001-05-23 15:26:42 +02:00
case InversionNode::TYPE_OR:
return RecordBitmap::bit_or(
EVL_bitmap(tdbb, node->node1, bitmap_and),
EVL_bitmap(tdbb, node->node2, bitmap_and));
2001-05-23 15:26:42 +02:00
case InversionNode::TYPE_IN:
{
RecordBitmap** inv_bitmap = EVL_bitmap(tdbb, node->node1, bitmap_and);
BTR_evaluate(tdbb, node->node2->retrieval, inv_bitmap, bitmap_and);
return inv_bitmap;
}
case InversionNode::TYPE_DBKEY:
2001-05-23 15:26:42 +02:00
{
impure_inversion* impure = tdbb->getRequest()->getImpure<impure_inversion>(node->impure);
RecordBitmap::reset(impure->inv_bitmap);
const dsc* desc = EVL_expr(tdbb, node->value);
if (!(tdbb->getRequest()->req_flags & req_null) &&
desc->dsc_length == sizeof(RecordNumber::Packed))
{
const USHORT id = node->id;
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 InversionNode::TYPE_INDEX:
2001-05-23 15:26:42 +02:00
{
impure_inversion* impure = tdbb->getRequest()->getImpure<impure_inversion>(node->impure);
RecordBitmap::reset(impure->inv_bitmap);
BTR_evaluate(tdbb, node->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
}
2001-05-23 15:26:42 +02:00
return NULL;
}
bool EVL_boolean(thread_db* tdbb, const 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.
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
switch (node->nod_type)
{
case nod_class_exprnode_jrd:
return reinterpret_cast<BoolExprNode*>(node->nod_arg[0])->execute(tdbb, request);
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]);
default:
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, const jrd_nod* 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 = request->getImpure<impure_value>(node->nod_impure);
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_null;
2009-11-22 04:56:20 +01:00
// Do a preliminary screen for either simple nodes or nodes that are special cased elsewhere
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
switch (node->nod_type)
{
case nod_class_exprnode_jrd:
{
const ValueExprNode* exprNode = reinterpret_cast<const ValueExprNode*>(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_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.
// AP: for fields that are missing in original format use record's one.
if (compileFormat &&
record->rec_format->fmt_version != compileFormat->fmt_version &&
id < compileFormat->fmt_desc.getCount() &&
!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_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_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 = request->getImpure<impure_value>(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])
{
2010-10-09 03:57:37 +02:00
EVL_validate(tdbb, Item(Item::TYPE_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;
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;
const jrd_nod* const* 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_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_cast:
return cast(tdbb, values[0], node, impure);
2001-05-23 15:26:42 +02:00
2006-09-09 10:32:32 +02:00
default:
2009-11-22 04:56:20 +01:00
BUGCHECK(232); // msg 232 EVL_expr: invalid operation
2001-05-23 15:26:42 +02:00
}
2006-09-09 10:32:32 +02:00
2001-05-23 15:26:42 +02:00
return NULL;
}
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 &&
request->getStatement()->mapFieldInfo.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;
2010-10-09 03:57:37 +02:00
if (item.type == Item::TYPE_CAST)
{
status = isc_not_valid_for;
arg = "CAST";
}
else
{
if (itemInfo->name.isEmpty())
{
int index = item.index + 1;
2001-05-23 15:26:42 +02:00
status = isc_not_valid_for;
2001-05-23 15:26:42 +02:00
2010-10-09 03:57:37 +02:00
if (item.type == Item::TYPE_VARIABLE)
{
const jrd_prc* procedure = request->getStatement()->procedure;
2001-05-23 15:26:42 +02:00
if (procedure)
{
if (index <= int(procedure->prc_output_fields.getCount()))
s.printf("output parameter number %d", index);
else
{
s.printf("variable number %d",
index - int(procedure->prc_output_fields.getCount()));
}
}
else
s.printf("variable number %d", index);
}
2010-10-09 03:57:37 +02:00
else if (item.type == Item::TYPE_PARAMETER && item.subType == 0)
s.printf("input parameter number %d", (index - 1) / 2 + 1);
2010-10-09 03:57:37 +02:00
else if (item.type == Item::TYPE_PARAMETER && item.subType == 1)
s.printf("output parameter number %d", index);
2001-05-23 15:26:42 +02:00
if (s.isEmpty())
arg = UNKNOWN_STRING_MARK;
else
arg = s.c_str();
}
else
arg = itemInfo->name.c_str();
}
2001-05-23 15:26:42 +02:00
ERR_post(Arg::Gds(status) << Arg::Str(arg) << Arg::Str(value));
2001-05-23 15:26:42 +02:00
}
}
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;
}
2010-10-09 03:57:37 +02:00
EVL_validate(tdbb, Item(Item::TYPE_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();
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, const 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;
2001-05-23 15:26:42 +02:00
impure->vlu_desc.dsc_scale = 0;
}
2009-11-23 06:24:29 +01:00
else
{
2001-05-23 15:26:42 +02:00
impure->vlu_misc.vlu_long = 0;
impure->vlu_desc.dsc_dtype = dtype_long;
impure->vlu_desc.dsc_length = sizeof(SLONG);
impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_long;
2001-05-23 15:26:42 +02:00
}
const RecordSource* const rsb = reinterpret_cast<const RecordSource*>(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->getRecord(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->getRecord(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->getRecord(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->getRecord(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,
2010-09-23 11:53:50 +02:00
// impure will stay long, and the first add() will
2009-11-22 04:56:20 +01:00
// set the correct scale; if it is approximate numeric,
2010-09-23 11:53:50 +02:00
// the first add() will convert impure to double.
ArithmeticNode::add(desc, impure, node, blr_add);
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->getRecord(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
2010-09-23 11:53:50 +02:00
// numeric, impure will stay int64, and the first add() will
2009-11-22 04:56:20 +01:00
// set the correct scale; if it is approximate numeric,
2010-09-23 11:53:50 +02:00
// the first add() will convert impure to double.
ArithmeticNode::add(desc, impure, node, blr_add);
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&)
{} // no-op
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, const 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 dsc* get_mask(thread_db* tdbb, const 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 dsc* lock_state(thread_db* tdbb, const 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* 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();
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;
2010-04-07 04:04:22 +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, const 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
const 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;
const jrd_nod* const* 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;
}
// *************************
// 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, const 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
}