8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 20:03:03 +01:00
firebird-mirror/src/dsql/make.cpp

1131 lines
28 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: Dynamic SQL runtime support
2003-10-05 08:27:16 +02:00
* MODULE: make.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Routines to make various blocks.
*
* 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): ______________________________________.
2002-06-29 08:56:51 +02:00
* 2001.11.21 Claudio Valderrama: Finally solved the mystery of DSQL
* not recognizing when a UDF returns NULL. This fixes SF bug #484399.
* See case nod_udf in MAKE_desc().
* 2001.02.23 Claudio Valderrama: Fix SF bug #518350 with substring()
* and text blobs containing charsets other than ASCII/NONE/BINARY.
* 2002.07.30 Arno Brinkman:
* COALESCE, CASE support added
* procedure MAKE_desc_from_list added
* 2003.01.25 Dmitry Yemanov: Fixed problem with concatenation which
* trashed RDB$FIELD_LENGTH in the system tables. This change may
* potentially interfere with the one made by Claudio one year ago.
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 <ctype.h>
#include <string.h>
#include "../dsql/dsql.h"
2008-02-28 14:48:16 +01:00
#include "../dsql/node.h"
#include "../dsql/Nodes.h"
#include "../dsql/ExprNodes.h"
2003-11-08 00:27:24 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/intl.h"
#include "../jrd/constants.h"
#include "../jrd/align.h"
#include "../dsql/errd_proto.h"
#include "../dsql/hsh_proto.h"
#include "../dsql/make_proto.h"
2005-05-28 00:45:31 +02:00
#include "../dsql/metd_proto.h"
#include "../dsql/utld_proto.h"
#include "../dsql/DSqlDataTypeUtil.h"
#include "../jrd/DataTypeUtil.h"
2008-02-28 14:48:16 +01:00
#include "../jrd/jrd.h"
#include "../jrd/ods.h"
#include "../jrd/ini.h"
2010-10-12 10:02:57 +02:00
#include "../common/dsc_proto.h"
#include "../common/cvt.h"
2005-05-28 00:45:31 +02:00
#include "../jrd/thread_proto.h"
2010-10-12 10:02:57 +02:00
#include "../yvalve/why_proto.h"
#include "../common/config/config.h"
#include "../common/StatusArg.h"
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
using namespace Jrd;
using namespace Dsql;
using namespace Firebird;
2001-05-23 15:26:42 +02:00
static const char* DB_KEY_NAME = "DB_KEY";
2005-02-14 06:51:25 +01:00
2007-10-19 03:25:11 +02:00
dsql_nod* MAKE_const_slong(SLONG value)
{
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2010-10-24 02:26:00 +02:00
SLONG* valuePtr = FB_NEW(*tdbb->getDefaultPool()) SLONG(value);
LiteralNode* literal = FB_NEW(*tdbb->getDefaultPool()) LiteralNode(*tdbb->getDefaultPool());
literal->litDesc.dsc_dtype = dtype_long;
literal->litDesc.dsc_length = sizeof(SLONG);
literal->litDesc.dsc_scale = 0;
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_address = reinterpret_cast<UCHAR*>(valuePtr);
2010-10-24 02:26:00 +02:00
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
node->nod_flags = 0;
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(literal);
return node;
}
2001-12-24 03:51:06 +01:00
/**
MAKE_constant
@brief Make a constant node.
@param constant
@param numeric_flag
**/
2003-11-10 10:16:38 +01:00
dsql_nod* MAKE_constant(dsql_str* constant, dsql_constant_type numeric_flag)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
2010-10-24 02:26:00 +02:00
LiteralNode* literal = FB_NEW(*tdbb->getDefaultPool()) LiteralNode(*tdbb->getDefaultPool());
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
node->nod_flags = 0;
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(literal);
2001-05-23 15:26:42 +02:00
switch (numeric_flag)
{
case CONSTANT_DOUBLE:
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(constant, dsql_type_str);
2001-05-23 15:26:42 +02:00
2009-04-18 16:13:26 +02:00
// This is a numeric value which is transported to the engine as
// a string. The engine will convert it. Use dtype_double so that
// the engine can distinguish it from an actual string.
// Note: Due to the size of dsc_scale we are limited to numeric
// constants of less than 256 bytes.
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_dtype = dtype_double;
// Scale has no use for double
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_scale = static_cast<signed char>(constant->str_length);
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_length = sizeof(double);
literal->litDesc.dsc_address = (UCHAR*) constant->str_data;
literal->litDesc.dsc_ttype() = ttype_ascii;
literal->dsqlStr = constant;
break;
2001-05-23 15:26:42 +02:00
case CONSTANT_SINT64:
{
2010-10-24 02:26:00 +02:00
// We convert the string to an int64.
literal->litDesc.dsc_dtype = dtype_int64;
literal->litDesc.dsc_length = sizeof(SINT64);
literal->litDesc.dsc_scale = 0;
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_address = (UCHAR*) FB_NEW(*tdbb->getDefaultPool()) SINT64;
// Now convert the string to an int64. We can omit testing for
// overflow, because we would never have gotten here if yylex
// hadn't recognized the string as a valid 64-bit integer value.
// We *might* have "9223372936854775808", which works an an int64
// only if preceded by a '-', but that issue is handled in GEN_expr,
// and need not be addressed here.
// Recent change to support hex numeric constants means the input
// string now can be X8000000000000000, for example.
// Hex constants coming through this code are guaranteed to be
// valid - they start with X and contains only 0-9, A-F.
// And, they will fit in a SINT64 without overflow.
SINT64 value = 0;
const UCHAR* p = reinterpret_cast<const UCHAR*>(constant->str_data);
2003-12-22 11:00:59 +01:00
if (*p == 'X')
{
// oh no, a hex string!
*p++; // skip the 'X' part.
UCHAR byte = 0;
2009-01-03 20:04:32 +01:00
bool nibble = ((strlen(constant->str_data) - 1) & 1);
SSHORT c;
// hex string is already upper-cased
2008-06-05 13:02:42 +02:00
while (isdigit(*p) || ((*p >= 'A') && (*p <= 'F')))
{
// Now convert the character to a nibble
if (*p >= 'A')
c = (*p - 'A') + 10;
else
c = (*p - '0');
if (nibble)
{
byte = (byte << 4) + (UCHAR) c;
2008-06-05 13:02:42 +02:00
nibble = false;
value = (value << 8) + byte;
}
else
{
byte = c;
2008-06-05 13:02:42 +02:00
nibble = true;
}
*p++;
}
// if value is negative, then GEN_constant (from dsql/gen.cpp)
// is going to want 2 nodes: NegateNode (to hold the minus)
2010-10-24 02:26:00 +02:00
// and LiteralNode as a child to hold the value.
if (value < 0)
{
value = -value;
2010-10-24 02:26:00 +02:00
*(SINT64*) literal->litDesc.dsc_address = value;
NegateNode* negateNode = FB_NEW(*tdbb->getDefaultPool()) NegateNode(
*tdbb->getDefaultPool(), node);
node = MAKE_node(nod_class_exprnode, 1);
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(negateNode);
}
else
2010-10-24 02:26:00 +02:00
*(SINT64*) literal->litDesc.dsc_address = value;
} // hex constant
else
{
2010-10-24 02:26:00 +02:00
// Good old-fashioned base-10 number from the lexer.
// We convert the string to an int64.
while (isdigit(*p))
value = 10 * value + (*(p++) - '0');
if (*p++ == '.')
{
while (isdigit(*p))
{
value = 10 * value + (*p++ - '0');
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_scale--;
}
}
2010-10-24 02:26:00 +02:00
*(FB_UINT64*) (literal->litDesc.dsc_address) = value;
}
2001-05-23 15:26:42 +02:00
}
break;
2001-05-23 15:26:42 +02:00
case CONSTANT_DATE:
case CONSTANT_TIME:
case CONSTANT_TIMESTAMP:
{
// Setup the constant's descriptor
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
switch (numeric_flag)
{
case CONSTANT_DATE:
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_dtype = dtype_sql_date;
break;
case CONSTANT_TIME:
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_dtype = dtype_sql_time;
break;
case CONSTANT_TIMESTAMP:
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_dtype = dtype_timestamp;
break;
}
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_scale = 0;
literal->litDesc.dsc_length = type_lengths[literal->litDesc.dsc_dtype];
literal->litDesc.dsc_address =
FB_NEW(*tdbb->getDefaultPool()) UCHAR[literal->litDesc.dsc_length];
2001-05-23 15:26:42 +02:00
// Set up a descriptor to point to the string
2001-05-23 15:26:42 +02:00
dsc tmp;
tmp.dsc_dtype = dtype_text;
tmp.dsc_scale = 0;
tmp.dsc_flags = 0;
tmp.dsc_ttype() = ttype_ascii;
tmp.dsc_length = static_cast<USHORT>(constant->str_length);
tmp.dsc_address = (UCHAR*) constant->str_data;
2001-05-23 15:26:42 +02:00
// Now invoke the string_to_date/time/timestamp routines
2001-05-23 15:26:42 +02:00
2010-10-24 02:26:00 +02:00
CVT_move(&tmp, &literal->litDesc, ERRD_post);
break;
}
2001-05-23 15:26:42 +02:00
default:
2003-11-04 00:59:24 +01:00
fb_assert(numeric_flag == CONSTANT_STRING);
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(constant, dsql_type_str);
2001-05-23 15:26:42 +02:00
2010-10-24 02:26:00 +02:00
literal->litDesc.dsc_dtype = dtype_text;
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_scale = 0;
literal->litDesc.dsc_length = static_cast<USHORT>(constant->str_length);
literal->litDesc.dsc_address = (UCHAR*) constant->str_data;
literal->litDesc.dsc_ttype() = ttype_dynamic;
// carry a pointer to the constant to resolve character set in pass1
2010-10-24 02:26:00 +02:00
literal->dsqlStr = constant;
break;
2001-05-23 15:26:42 +02:00
}
return node;
}
/**
MAKE_str_constant
@brief Make a constant node when the
character set ID is already known.
@param constant
@param character_set
**/
2003-11-10 10:16:38 +01:00
dsql_nod* MAKE_str_constant(dsql_str* constant, SSHORT character_set)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(constant, dsql_type_str);
2001-05-23 15:26:42 +02:00
2010-10-24 02:26:00 +02:00
LiteralNode* literal = FB_NEW(*tdbb->getDefaultPool()) LiteralNode(*tdbb->getDefaultPool());
literal->litDesc.dsc_dtype = dtype_text;
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_scale = 0;
literal->litDesc.dsc_length = static_cast<USHORT>(constant->str_length);
literal->litDesc.dsc_address = (UCHAR*) constant->str_data;
literal->litDesc.dsc_ttype() = character_set;
literal->dsqlStr = constant;
dsql_nod* node = MAKE_node(nod_class_exprnode, 1);
node->nod_flags = 0;
node->nod_arg[0] = reinterpret_cast<dsql_nod*>(literal);
2001-05-23 15:26:42 +02:00
return node;
}
/**
MAKE_cstring
@brief Make a string node for a string whose
length is not known, but is null-terminated.
@param str
**/
2003-11-10 10:16:38 +01:00
dsql_str* MAKE_cstring(const char* str)
2001-05-23 15:26:42 +02:00
{
return MAKE_string(str, strlen(str));
2001-05-23 15:26:42 +02:00
}
/**
MAKE_desc
@brief Make a descriptor from input node.
2003-10-05 08:27:16 +02:00
This function can modify node->nod_flags to add NOD_COMP_DIALECT
@param desc
@param node
@param null_replacement
**/
void MAKE_desc(DsqlCompilerScratch* dsqlScratch, dsc* desc, dsql_nod* node, dsql_nod* null_replacement)
2001-05-23 15:26:42 +02:00
{
dsc desc1, desc2, desc3;
dsql_map* map;
2003-11-10 10:16:38 +01:00
dsql_ctx* context;
dsql_rel* relation;
dsql_fld* field;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(node, dsql_type_nod);
2001-05-23 15:26:42 +02:00
// If we already know the datatype, don't worry about anything.
//
// dimitr: But let's re-evaluate descriptors for expression arguments
// (when a NULL replacement node is provided) to always
// choose the correct resulting datatype. Example:
// NULLIF(NULL, 0) => CHAR(1), but
// 1 + NULLIF(NULL, 0) => INT
// This is required because of MAKE_desc() being called
// from custom pass1 handlers for some node types and thus
// causing an incorrect datatype (determined without
// context) to be cached in nod_desc.
if (node->nod_desc.dsc_dtype && !null_replacement)
{
2001-05-23 15:26:42 +02:00
*desc = node->nod_desc;
return;
}
2009-01-06 06:53:34 +01:00
switch (node->nod_type)
{
case nod_class_exprnode:
{
ValueExprNode* exprNode = reinterpret_cast<ValueExprNode*>(node->nod_arg[0]);
2010-10-09 03:57:37 +02:00
exprNode->make(dsqlScratch, node, desc, null_replacement);
}
return;
2004-09-13 21:36:30 +02:00
case nod_variable:
*desc = node->nod_desc;
return;
2001-05-23 15:26:42 +02:00
case nod_map:
map = (dsql_map*) node->nod_arg[e_map_map];
context = (dsql_ctx*) node->nod_arg[e_map_context];
MAKE_desc(dsqlScratch, desc, map->map_node, null_replacement);
// ASF: We should mark nod_agg_count as nullable when it's in an outer join - CORE-2660.
if (context->ctx_flags & CTX_outer_join)
desc->dsc_flags |= DSC_nullable;
2001-05-23 15:26:42 +02:00
return;
case nod_derived_field:
MAKE_desc(dsqlScratch, desc, node->nod_arg[e_derived_field_value], null_replacement);
return;
2001-05-23 15:26:42 +02:00
case nod_cast:
field = (dsql_fld*) node->nod_arg[e_cast_target];
2001-05-23 15:26:42 +02:00
MAKE_desc_from_field(desc, field);
MAKE_desc(dsqlScratch, &desc1, node->nod_arg[e_cast_source], NULL);
2001-05-23 15:26:42 +02:00
desc->dsc_flags = desc1.dsc_flags & DSC_nullable;
return;
case nod_simple_case:
MAKE_desc_from_list(dsqlScratch, &desc1, node->nod_arg[e_simple_case_results],
null_replacement, "CASE");
*desc = desc1;
return;
case nod_searched_case:
MAKE_desc_from_list(dsqlScratch, &desc1, node->nod_arg[e_searched_case_results],
null_replacement, "CASE");
*desc = desc1;
return;
2003-10-05 08:27:16 +02:00
case nod_coalesce:
MAKE_desc_from_list(dsqlScratch, &desc1, node->nod_arg[0],
null_replacement, "COALESCE");
*desc = desc1;
return;
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
case nod_collate:
ERRD_bugcheck("Not expecting nod_collate in dsql/MAKE_desc");
return;
#endif
/*
2001-05-23 15:26:42 +02:00
case nod_count:
desc->dsc_dtype = dtype_long;
desc->dsc_sub_type = 0;
desc->dsc_scale = 0;
desc->dsc_length = sizeof(SLONG);
desc->dsc_flags = 0;
return;
*/
2001-05-23 15:26:42 +02:00
case nod_alias:
MAKE_desc(dsqlScratch, desc, node->nod_arg[e_alias_value], null_replacement);
2001-05-23 15:26:42 +02:00
return;
case nod_dbkey:
// Fix for bug 10072 check that the target is a relation
2003-11-10 10:16:38 +01:00
context = (dsql_ctx*) node->nod_arg[0]->nod_arg[0];
2003-10-05 08:27:16 +02:00
relation = context->ctx_relation;
2009-01-08 10:26:06 +01:00
if (relation != 0)
{
2001-05-23 15:26:42 +02:00
desc->dsc_dtype = dtype_text;
if (relation->rel_flags & REL_creating)
desc->dsc_length = 8;
else
desc->dsc_length = relation->rel_dbkey_length;
desc->dsc_flags = DSC_nullable;
desc->dsc_ttype() = ttype_binary;
2001-05-23 15:26:42 +02:00
}
2009-11-16 10:18:24 +01:00
else
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
Arg::Gds(isc_dsql_dbkey_from_non_table));
2001-05-23 15:26:42 +02:00
}
return;
2003-10-05 08:27:16 +02:00
case nod_limit:
case nod_rows:
if (dsqlScratch->clientDialect <= SQL_DIALECT_V5)
2009-11-16 10:18:24 +01:00
{
2003-10-05 08:27:16 +02:00
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof (SLONG);
}
2009-11-16 10:18:24 +01:00
else
{
2003-10-05 08:27:16 +02:00
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof (SINT64);
}
desc->dsc_sub_type = 0;
desc->dsc_scale = 0;
2009-04-18 16:13:26 +02:00
desc->dsc_flags = 0; // Can first/skip accept NULL in the future?
2003-10-05 08:27:16 +02:00
return;
2002-06-29 08:56:51 +02:00
2001-05-23 15:26:42 +02:00
case nod_field:
2004-09-05 23:50:54 +02:00
if (node->nod_desc.dsc_dtype)
{
*desc = node->nod_desc;
}
else
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-203) <<
Arg::Gds(isc_dsql_field_ref));
2004-09-05 23:50:54 +02:00
}
2001-05-23 15:26:42 +02:00
return;
case nod_via:
MAKE_desc(dsqlScratch, desc, node->nod_arg[e_via_value_1], null_replacement);
2009-04-18 16:13:26 +02:00
// Set the descriptor flag as nullable. The
// select expression may or may not return
// this row based on the WHERE clause. Setting this
// flag warns the client to expect null values.
// (bug 10379)
2001-05-23 15:26:42 +02:00
desc->dsc_flags |= DSC_nullable;
return;
case nod_hidden_var:
MAKE_desc(dsqlScratch, desc, node->nod_arg[e_hidden_var_expr], null_replacement);
return;
case nod_select_expr: // this should come from pass1_any call to set_parameter_type
node = node->nod_arg[e_sel_query_spec];
fb_assert(node->nod_type == nod_query_spec);
node = node->nod_arg[e_qry_list];
fb_assert(node->nod_type == nod_list && node->nod_count > 0);
MAKE_desc(dsqlScratch, desc, node->nod_arg[0], null_replacement);
return;
2001-05-23 15:26:42 +02:00
default:
fb_assert(false); // unexpected dsql_nod type
2001-05-23 15:26:42 +02:00
case nod_dom_value: // computed value not used
2009-04-18 16:13:26 +02:00
// By the time we get here, any nod_dom_value node should have had
// its descriptor set to the type of the domain being created, or
// to the type of the existing domain to which a CHECK constraint
// is being added.
2003-12-22 11:00:59 +01:00
fb_assert(node->nod_desc.dsc_dtype != dtype_unknown);
2001-05-23 15:26:42 +02:00
if (desc != &node->nod_desc)
*desc = node->nod_desc;
return;
}
}
/**
MAKE_desc_from_field
@brief Compute a DSC from a field's description information.
@param desc
@param field
**/
2003-10-05 08:27:16 +02:00
void MAKE_desc_from_field(dsc* desc, const dsql_fld* field)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field, dsql_type_fld);
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
desc->dsc_dtype = static_cast<UCHAR>(field->fld_dtype);
desc->dsc_scale = static_cast<SCHAR>(field->fld_scale);
2001-05-23 15:26:42 +02:00
desc->dsc_sub_type = field->fld_sub_type;
desc->dsc_length = field->fld_length;
desc->dsc_flags = (field->fld_flags & FLD_nullable) ? DSC_nullable : 0;
2009-11-28 20:39:23 +01:00
if (desc->isText() || desc->isBlob())
2005-05-28 00:45:31 +02:00
{
2009-11-28 20:39:23 +01:00
desc->setTextType(INTL_CS_COLL_TO_TTYPE(
field->fld_character_set_id, field->fld_collation_id));
2005-05-28 00:45:31 +02:00
}
2001-05-23 15:26:42 +02:00
}
/**
MAKE_desc_from_list
@brief Make a descriptor from a list of values
according to the sql-standard.
@param desc
@param node
@param expression_name
**/
void MAKE_desc_from_list(DsqlCompilerScratch* dsqlScratch, dsc* desc, dsql_nod* node,
dsql_nod* null_replacement,
const TEXT* expression_name)
{
Array<const dsc*> args;
2007-04-12 17:56:34 +02:00
fb_assert(node->nod_type == nod_list);
2007-04-12 17:56:34 +02:00
for (dsql_nod** p = node->nod_arg; p < node->nod_arg + node->nod_count; ++p)
{
MAKE_desc(dsqlScratch, &(*p)->nod_desc, *p, NULL);
2007-04-12 17:56:34 +02:00
args.add(&(*p)->nod_desc);
}
DSqlDataTypeUtil(dsqlScratch).makeFromList(desc, expression_name, args.getCount(), args.begin());
// If we have literal NULLs only, let the result be either
// CHAR(1) CHARACTER SET NONE or the context-provided datatype
2007-04-12 17:56:34 +02:00
if (desc->isNull() && null_replacement)
{
MAKE_desc(dsqlScratch, desc, null_replacement, NULL);
desc->dsc_flags |= DSC_null | DSC_nullable;
return;
}
}
/**
MAKE_field
@brief Make up a field node.
@param context
@param field
@param indices
**/
2003-11-10 10:16:38 +01:00
dsql_nod* MAKE_field(dsql_ctx* context, dsql_fld* field, dsql_nod* indices)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(context, dsql_type_ctx);
DEV_BLKCHK(field, dsql_type_fld);
DEV_BLKCHK(indices, dsql_type_nod);
2001-05-23 15:26:42 +02:00
dsql_nod* node = MAKE_node(nod_field, e_fld_count);
node->nod_arg[e_fld_context] = (dsql_nod*) context;
node->nod_arg[e_fld_field] = (dsql_nod*) field;
2009-01-06 06:53:34 +01:00
if (field->fld_dimensions)
{
2009-01-08 10:26:06 +01:00
if (indices)
{
2001-05-23 15:26:42 +02:00
node->nod_arg[e_fld_indices] = indices;
MAKE_desc_from_field(&node->nod_desc, field);
2009-01-06 06:53:34 +01:00
node->nod_desc.dsc_dtype = static_cast<UCHAR>(field->fld_element_dtype);
2001-05-23 15:26:42 +02:00
node->nod_desc.dsc_length = field->fld_element_length;
2009-04-18 16:13:26 +02:00
// node->nod_desc.dsc_scale = field->fld_scale;
// node->nod_desc.dsc_sub_type = field->fld_sub_type;
2001-05-23 15:26:42 +02:00
}
2009-01-08 10:26:06 +01:00
else
{
2001-05-23 15:26:42 +02:00
node->nod_desc.dsc_dtype = dtype_array;
node->nod_desc.dsc_length = sizeof(ISC_QUAD);
2009-01-06 06:53:34 +01:00
node->nod_desc.dsc_scale = static_cast<SCHAR>(field->fld_scale);
2001-05-23 15:26:42 +02:00
node->nod_desc.dsc_sub_type = field->fld_sub_type;
}
}
2009-01-08 10:26:06 +01:00
else
{
if (indices)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
Arg::Gds(isc_dsql_only_can_subscript_array) << Arg::Str(field->fld_name));
}
2001-05-23 15:26:42 +02:00
MAKE_desc_from_field(&node->nod_desc, field);
}
2008-06-05 13:02:42 +02:00
if ((field->fld_flags & FLD_nullable) || (context->ctx_flags & CTX_outer_join))
2003-11-10 10:16:38 +01:00
{
node->nod_desc.dsc_flags |= DSC_nullable;
2003-04-06 12:09:38 +02:00
}
2001-05-23 15:26:42 +02:00
// UNICODE_FSS_HACK
2006-10-07 19:06:59 +02:00
// check if the field is a system domain and the type is CHAR/VARCHAR CHARACTER SET UNICODE_FSS
2009-01-06 06:53:34 +01:00
if ((field->fld_flags & FLD_system) && node->nod_desc.dsc_dtype <= dtype_varying &&
INTL_GET_CHARSET(&node->nod_desc) == CS_METADATA)
2005-05-28 00:45:31 +02:00
{
USHORT adjust = 0;
2005-05-28 00:45:31 +02:00
if (node->nod_desc.dsc_dtype == dtype_varying)
adjust = sizeof(USHORT);
else if (node->nod_desc.dsc_dtype == dtype_cstring)
adjust = 1;
2005-05-28 00:45:31 +02:00
node->nod_desc.dsc_length -= adjust;
node->nod_desc.dsc_length *= 3;
node->nod_desc.dsc_length += adjust;
2005-05-28 00:45:31 +02:00
}
2001-05-23 15:26:42 +02:00
return node;
}
2008-02-28 14:48:16 +01:00
/**
2008-02-28 14:48:16 +01:00
MAKE_field_name
2008-02-28 14:48:16 +01:00
@brief Make up a field name node.
2008-02-28 14:48:16 +01:00
@param field_name
**/
dsql_nod* MAKE_field_name(const char* field_name)
{
dsql_nod* const field_node = MAKE_node(nod_field_name, (int) e_fln_count);
field_node->nod_arg[e_fln_name] = (dsql_nod*) MAKE_cstring(field_name);
return field_node;
}
/**
MAKE_list
@brief Make a list node from a linked list stack of things.
@param stack
**/
dsql_nod* MAKE_list(DsqlNodStack& stack)
2001-05-23 15:26:42 +02:00
{
USHORT count = stack.getCount();
dsql_nod* node = MAKE_node(nod_list, count);
dsql_nod** ptr = node->nod_arg + count;
2001-05-23 15:26:42 +02:00
while (stack.hasData())
{
*--ptr = stack.pop();
}
2001-05-23 15:26:42 +02:00
return node;
}
/**
MAKE_node
@brief Make a node of given type.
@param type
@param count
**/
dsql_nod* MAKE_node(NOD_TYPE type, int count)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_nod* node = FB_NEW_RPT(*tdbb->getDefaultPool(), count) dsql_nod;
2001-05-23 15:26:42 +02:00
node->nod_type = type;
node->nod_count = count;
return node;
}
/**
MAKE_parameter
@brief Generate a parameter block for a message. If requested,
set up for a null flag as well.
@param message
@param sqlda_flag
@param null_flag
@param sqlda_index
@param node
**/
2004-02-02 12:02:12 +01:00
dsql_par* MAKE_parameter(dsql_msg* message, bool sqlda_flag, bool null_flag,
USHORT sqlda_index, const dsql_nod* node)
2001-05-23 15:26:42 +02:00
{
2009-11-16 10:18:24 +01:00
if (!message)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_badmsgnum));
}
if (sqlda_flag && sqlda_index && sqlda_index <= message->msg_index)
{
// This parameter is possibly already here. Look for it.
for (size_t i = 0; i < message->msg_parameters.getCount(); ++i)
2009-11-16 10:18:24 +01:00
{
dsql_par* temp = message->msg_parameters[i];
if (temp->par_index == sqlda_index)
return temp;
}
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
dsql_par* parameter = FB_NEW(*tdbb->getDefaultPool()) dsql_par(*tdbb->getDefaultPool());
2001-05-23 15:26:42 +02:00
parameter->par_message = message;
message->msg_parameters.insert(0, parameter);
2001-05-23 15:26:42 +02:00
parameter->par_parameter = message->msg_parameter++;
2001-05-23 15:26:42 +02:00
parameter->par_rel_name = NULL;
parameter->par_owner_name = NULL;
parameter->par_rel_alias = NULL;
2001-05-23 15:26:42 +02:00
if (node)
MAKE_parameter_names(parameter, node);
2009-04-18 16:13:26 +02:00
// If the parameter is used declared, set SQLDA index
2009-01-08 10:26:06 +01:00
if (sqlda_flag)
{
2009-11-16 10:18:24 +01:00
if (sqlda_index)
{
parameter->par_index = sqlda_index;
2003-12-22 11:00:59 +01:00
if (message->msg_index < sqlda_index)
message->msg_index = sqlda_index;
}
else {
parameter->par_index = ++message->msg_index;
}
}
2009-04-18 16:13:26 +02:00
// If a null handing has been requested, set up a null flag
2001-05-23 15:26:42 +02:00
2009-11-16 10:18:24 +01:00
if (null_flag)
{
dsql_par* null = MAKE_parameter(message, false, false, 0, NULL);
2003-10-05 08:27:16 +02:00
parameter->par_null = null;
2001-05-23 15:26:42 +02:00
null->par_desc.dsc_dtype = dtype_short;
null->par_desc.dsc_scale = 0;
null->par_desc.dsc_length = sizeof(SSHORT);
}
return parameter;
}
/**
MAKE_string
@brief Generalized routine for making a string block.
@param str
@param length
**/
2003-11-10 10:16:38 +01:00
dsql_str* MAKE_string(const char* str, int length)
2001-05-23 15:26:42 +02:00
{
fb_assert(length >= 0);
2001-05-23 15:26:42 +02:00
return MAKE_tagged_string(str, length, NULL);
}
/**
MAKE_symbol
@brief Make a symbol for an object and insert symbol into hash table.
@param database
@param name
@param length
@param type
@param object
**/
2004-02-02 12:02:12 +01:00
dsql_sym* MAKE_symbol(dsql_dbb* database,
const TEXT* name, USHORT length, SYM_TYPE type, dsql_req* object)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(database, dsql_type_dbb);
DEV_BLKCHK(object, dsql_type_req);
2003-11-04 00:59:24 +01:00
fb_assert(name);
fb_assert(length > 0);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_sym* symbol = FB_NEW_RPT(*tdbb->getDefaultPool(), length) dsql_sym;
2001-05-23 15:26:42 +02:00
symbol->sym_type = type;
symbol->sym_object = object;
2001-05-23 15:26:42 +02:00
symbol->sym_dbb = database;
symbol->sym_length = length;
2003-10-05 08:27:16 +02:00
TEXT* p = symbol->sym_name;
symbol->sym_string = p;
2001-05-23 15:26:42 +02:00
if (length)
2008-02-03 12:20:48 +01:00
memcpy(p, name, length);
2001-05-23 15:26:42 +02:00
HSHD_insert(symbol);
return symbol;
}
/**
MAKE_tagged_string
@brief Generalized routine for making a string block.
Which is tagged with a character set descriptor.
@param str_
@param length
@param charset
**/
dsql_str* MAKE_tagged_string(const char* strvar, size_t length, const char* charset)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2008-02-28 14:48:16 +01:00
dsql_str* string = FB_NEW_RPT(*tdbb->getDefaultPool(), length) dsql_str;
string->str_charset = charset;
string->str_length = length;
memcpy(string->str_data, strvar, length);
2001-05-23 15:26:42 +02:00
return string;
}
/**
MAKE_variable
@brief Make up a field node.
@param field
@param name
@param type
@param msg_number
@param item_number
@param local_number
**/
2008-04-18 03:37:44 +02:00
dsql_nod* MAKE_variable(dsql_fld* field, const TEXT* name, const dsql_var_type type,
USHORT msg_number, USHORT item_number, USHORT local_number)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
DEV_BLKCHK(field, dsql_type_fld);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
thread_db* tdbb = JRD_get_thread_data();
2003-10-05 08:27:16 +02:00
2008-02-28 14:48:16 +01:00
dsql_var* variable = FB_NEW_RPT(*tdbb->getDefaultPool(), strlen(name)) dsql_var;
dsql_nod* node = MAKE_node(nod_variable, e_var_count);
node->nod_arg[e_var_variable] = (dsql_nod*) variable;
2003-10-05 08:27:16 +02:00
variable->var_msg_number = msg_number;
variable->var_msg_item = item_number;
variable->var_variable_number = local_number;
variable->var_field = field;
strcpy(variable->var_name, name);
variable->var_type = type;
if (field)
MAKE_desc_from_field(&node->nod_desc, field);
2001-05-23 15:26:42 +02:00
return node;
}
2008-02-28 14:48:16 +01:00
/**
MAKE_parameter_names
@brief Determine relation/column/alias names (if appropriate)
and store them in the given parameter.
@param parameter
@param item
**/
void MAKE_parameter_names(dsql_par* parameter, const dsql_nod* item)
{
const dsql_fld* field;
const dsql_ctx* context = NULL;
const dsql_str* string;
const dsql_nod* alias;
fb_assert(parameter && item);
const char* name_alias = NULL;
2009-01-06 06:53:34 +01:00
switch (item->nod_type)
{
case nod_class_exprnode:
reinterpret_cast<ValueExprNode*>(item->nod_arg[0])->setParameterName(parameter);
break;
case nod_field:
field = (dsql_fld*) item->nod_arg[e_fld_field];
2008-01-16 07:52:43 +01:00
name_alias = field->fld_name.c_str();
context = (dsql_ctx*) item->nod_arg[e_fld_context];
break;
case nod_dbkey:
parameter->par_name = parameter->par_alias = DB_KEY_NAME;
context = (dsql_ctx*) item->nod_arg[0]->nod_arg[0];
break;
case nod_alias:
string = (dsql_str*) item->nod_arg[e_alias_alias];
2009-01-07 10:30:57 +01:00
parameter->par_alias = string->str_data;
alias = item->nod_arg[e_alias_value];
2009-11-16 10:18:24 +01:00
if (alias->nod_type == nod_field)
{
field = (dsql_fld*) alias->nod_arg[e_fld_field];
2008-01-16 07:52:43 +01:00
parameter->par_name = field->fld_name.c_str();
context = (dsql_ctx*) alias->nod_arg[e_fld_context];
}
2009-11-16 10:18:24 +01:00
else if (alias->nod_type == nod_dbkey)
{
parameter->par_name = DB_KEY_NAME;
context = (dsql_ctx*) alias->nod_arg[0]->nod_arg[0];
}
break;
case nod_via:
// subquery, aka sub-select
MAKE_parameter_names(parameter, item->nod_arg[e_via_value_1]);
break;
case nod_derived_field:
string = (dsql_str*) item->nod_arg[e_derived_field_name];
2009-01-07 10:30:57 +01:00
parameter->par_alias = string->str_data;
alias = item->nod_arg[e_derived_field_value];
2009-11-16 10:18:24 +01:00
if (alias->nod_type == nod_field)
{
field = (dsql_fld*) alias->nod_arg[e_fld_field];
2008-01-16 07:52:43 +01:00
parameter->par_name = field->fld_name.c_str();
context = (dsql_ctx*) alias->nod_arg[e_fld_context];
}
2009-11-16 10:18:24 +01:00
else if (alias->nod_type == nod_dbkey)
{
parameter->par_name = DB_KEY_NAME;
context = (dsql_ctx*) alias->nod_arg[0]->nod_arg[0];
}
break;
case nod_map:
{
const dsql_map* map = (dsql_map*) item->nod_arg[e_map_map];
const dsql_nod* map_node = map->map_node;
2009-11-16 10:18:24 +01:00
while (map_node->nod_type == nod_map)
{
// skip all the nod_map nodes
map = (dsql_map*) map_node->nod_arg[e_map_map];
map_node = map->map_node;
}
2009-01-06 06:53:34 +01:00
switch (map_node->nod_type)
{
case nod_field:
field = (dsql_fld*) map_node->nod_arg[e_fld_field];
2008-01-16 07:52:43 +01:00
name_alias = field->fld_name.c_str();
context = (dsql_ctx*) map_node->nod_arg[e_fld_context];
break;
case nod_alias:
string = (dsql_str*) map_node->nod_arg[e_alias_alias];
2009-01-07 10:30:57 +01:00
parameter->par_alias = string->str_data;
alias = map_node->nod_arg[e_alias_value];
2009-11-16 10:18:24 +01:00
if (alias->nod_type == nod_field)
{
field = (dsql_fld*) alias->nod_arg[e_fld_field];
2008-01-16 07:52:43 +01:00
parameter->par_name = field->fld_name.c_str();
context = (dsql_ctx*) alias->nod_arg[e_fld_context];
}
break;
case nod_derived_field:
string = (dsql_str*) map_node->nod_arg[e_derived_field_name];
2009-01-07 10:30:57 +01:00
parameter->par_alias = string->str_data;
alias = map_node->nod_arg[e_derived_field_value];
2009-11-16 10:18:24 +01:00
if (alias->nod_type == nod_field)
{
field = (dsql_fld*) alias->nod_arg[e_fld_field];
2008-01-16 07:52:43 +01:00
parameter->par_name = field->fld_name.c_str();
context = (dsql_ctx*) alias->nod_arg[e_fld_context];
}
break;
case nod_dbkey:
name_alias = DB_KEY_NAME;
break;
case nod_class_exprnode:
{
2010-10-24 02:26:00 +02:00
const AggNode* aggNode;
const LiteralNode* literalNode;
if ((aggNode = ExprNode::as<AggNode>(map_node)))
aggNode->setParameterName(parameter);
2010-10-24 02:26:00 +02:00
else if ((literalNode = ExprNode::as<LiteralNode>(map_node)))
literalNode->setParameterName(parameter);
}
break;
} // switch(map_node->nod_type)
break;
} // case nod_map
case nod_variable:
{
dsql_var* variable = (dsql_var*) item->nod_arg[e_var_variable];
if (variable->var_field)
name_alias = variable->var_field->fld_name.c_str();
break;
}
case nod_cast:
name_alias = "CAST";
break;
case nod_searched_case:
case nod_simple_case:
name_alias = "CASE";
break;
case nod_coalesce:
name_alias = "COALESCE";
break;
}
if (name_alias)
parameter->par_name = parameter->par_alias = name_alias;
if (context)
{
if (context->ctx_relation)
{
2008-01-16 07:52:43 +01:00
parameter->par_rel_name = context->ctx_relation->rel_name.c_str();
parameter->par_owner_name = context->ctx_relation->rel_owner.c_str();
}
else if (context->ctx_procedure)
{
parameter->par_rel_name = context->ctx_procedure->prc_name.identifier.c_str();
2008-01-16 07:52:43 +01:00
parameter->par_owner_name = context->ctx_procedure->prc_owner.c_str();
}
parameter->par_rel_alias = context->ctx_alias;
}
}