8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 18:03:02 +01:00
firebird-mirror/src/jrd/par.cpp
2009-12-15 12:25:44 +00:00

3578 lines
95 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: par.cpp
* DESCRIPTION: BLR Parser
*
* 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): ______________________________________.
*
* 27-May-2001 Claudio Valderrama: par_plan() no longer uppercases
* an index's name before doing a lookup of such index.
* 2001.07.28: Added parse code for blr_skip to support LIMIT.
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
* exception handling in SPs/triggers,
* implemented ROWS_AFFECTED system variable
* 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
* 2002.10.29 Mike Nordell - Fixed breakage.
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL
* 2004.01.16 Vlad Horsun: Added support for default parameters
* Adriano dos Santos Fernandes
*/
#include "firebird.h"
#include <stdio.h>
#include <string.h>
#include "../jrd/common.h"
#include "../jrd/jrd.h"
#include "../jrd/ibase.h"
#include "../jrd/ini.h"
#include "../jrd/val.h"
#include "../jrd/align.h"
#include "../jrd/exe.h"
#include "../jrd/extds/ExtDS.h"
#include "../jrd/lls.h"
#include "../jrd/rse.h" // for MAX_STREAMS
#include "../jrd/scl.h"
#include "../jrd/req.h"
#include "../jrd/blb.h"
#include "../jrd/intl.h"
#include "../jrd/met.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/cvt_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/fun_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/par_proto.h"
#include "../common/utils_proto.h"
#include "../jrd/SysFunction.h"
#include "../jrd/BlrReader.h"
using namespace Jrd;
using namespace Firebird;
#include "gen/blrtable.h"
static const TEXT elements[][14] =
{ "", "statement", "boolean", "value", "RecordSelExpr", "TABLE" };
#include "gen/codetext.h"
static NodeParseFunc blr_parsers[256] = {NULL};
static void error(CompilerScratch* csb, const Arg::StatusVector& v, bool isSyntaxError = true);
static SSHORT find_proc_field(const jrd_prc*, const Firebird::MetaName&);
static jrd_nod* par_args(thread_db*, CompilerScratch*, USHORT);
static jrd_nod* par_cast(thread_db*, CompilerScratch*);
static PsqlException* par_condition(thread_db*, CompilerScratch*);
static PsqlException* par_conditions(thread_db*, CompilerScratch*);
static SSHORT par_context(CompilerScratch*, SSHORT *);
static void par_dependency(thread_db*, CompilerScratch*, SSHORT, SSHORT, const Firebird::MetaName&);
static jrd_nod* par_exec_proc(thread_db*, CompilerScratch*, SSHORT);
static jrd_nod* par_fetch(thread_db*, CompilerScratch*, jrd_nod*);
static jrd_nod* par_field(thread_db*, CompilerScratch*, SSHORT);
static jrd_nod* par_function(thread_db*, CompilerScratch*, SSHORT);
static jrd_nod* par_literal(thread_db*, CompilerScratch*);
static jrd_nod* par_map(thread_db*, CompilerScratch*, USHORT);
static jrd_nod* par_message(thread_db*, CompilerScratch*);
static jrd_nod* par_modify(thread_db*, CompilerScratch*, SSHORT);
static jrd_nod* par_partition_by(thread_db*, CompilerScratch*);
static jrd_nod* par_plan(thread_db*, CompilerScratch*);
static jrd_nod* par_procedure(thread_db*, CompilerScratch*, SSHORT);
static void par_procedure_parms(thread_db*, CompilerScratch*, jrd_prc*, jrd_nod**, jrd_nod**, bool);
static jrd_nod* par_relation(thread_db*, CompilerScratch*, SSHORT, bool);
static jrd_nod* par_rse(thread_db*, CompilerScratch*, SSHORT);
static jrd_nod* par_sort(thread_db*, CompilerScratch*, bool);
#ifdef NOT_USED_OR_REPLACED
static jrd_nod* par_stream(thread_db*, CompilerScratch*);
#endif
static jrd_nod* par_sys_function(thread_db*, CompilerScratch*);
static jrd_nod* par_union(thread_db*, CompilerScratch*, bool);
static void warning(const Arg::StatusVector& v);
jrd_nod* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr_length,
CompilerScratch* view_csb, CompilerScratch** csb_ptr, jrd_req** request_ptr,
const bool trigger, USHORT flags)
{
/**************************************
*
* P A R _ b l r
*
**************************************
*
* Functional description
* Parse blr, returning a compiler scratch block with the results.
* Caller must do pool handling.
*
**************************************/
SET_TDBB(tdbb);
#ifdef CMP_DEBUG
cmp_trace("BLR code given for JRD parsing:");
// CVC: Couldn't find isc_trace_printer, so changed it to gds__trace_printer.
fb_print_blr(blr, blr_length, gds__trace_printer, 0, 0);
#endif
CompilerScratch* csb;
if (!(csb_ptr && (csb = *csb_ptr)))
{
size_t count = 5;
if (view_csb)
count += view_csb->csb_rpt.getCapacity();
csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), count);
csb->csb_g_flags |= flags;
}
// If there is a request ptr, this is a trigger. Set up contexts 0 and 1 for
// the target relation
if (trigger)
{
SSHORT stream = csb->nextStream();
CompilerScratch::csb_repeat* t1 = CMP_csb_element(csb, 0);
t1->csb_flags |= csb_used | csb_active | csb_trigger;
t1->csb_relation = relation;
t1->csb_stream = (UCHAR) stream;
stream = csb->nextStream();
t1 = CMP_csb_element(csb, 1);
t1->csb_flags |= csb_used | csb_active | csb_trigger;
t1->csb_relation = relation;
t1->csb_stream = (UCHAR) stream;
}
else if (relation)
{
CompilerScratch::csb_repeat* t1 = CMP_csb_element(csb, 0);
t1->csb_stream = csb->nextStream();
t1->csb_relation = relation;
t1->csb_flags = csb_used | csb_active;
}
csb->csb_blr_reader = BlrReader(blr, blr_length);
if (view_csb)
{
CompilerScratch::rpt_itr ptr = view_csb->csb_rpt.begin();
// AB: csb_n_stream replaced by view_csb->csb_rpt.getCount(), because there could
// be more then just csb_n_stream-numbers that hold data.
// Certainly csb_stream (see par_context where the context is retrieved)
const CompilerScratch::rpt_const_itr end = view_csb->csb_rpt.end();
for (SSHORT stream = 0; ptr != end; ++ptr, ++stream)
{
CompilerScratch::csb_repeat* t2 = CMP_csb_element(csb, stream);
t2->csb_relation = ptr->csb_relation;
t2->csb_procedure = ptr->csb_procedure;
t2->csb_stream = ptr->csb_stream;
t2->csb_flags = ptr->csb_flags & csb_used;
}
csb->csb_n_stream = view_csb->csb_n_stream;
}
const SSHORT version = csb->csb_blr_reader.getByte();
switch (version)
{
case blr_version4:
csb->csb_g_flags |= csb_blr_version4;
break;
case blr_version5:
break; // nothing to do
default:
error(csb, Arg::Gds(isc_metadata_corrupt) <<
Arg::Gds(isc_wroblrver) << Arg::Num(blr_version4) << Arg::Num(version));
}
jrd_nod* node = PAR_parse_node(tdbb, csb, OTHER);
csb->csb_node = node;
if (csb->csb_blr_reader.getByte() != (UCHAR) blr_eoc)
PAR_syntax_error(csb, "end_of_command");
if (request_ptr)
*request_ptr = CMP_make_request(tdbb, csb, true);
if (csb_ptr)
*csb_ptr = csb;
else
delete csb;
return node;
}
USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, DSC* desc, ItemInfo* itemInfo)
{
/**************************************
*
* P A R _ d e s c
*
**************************************
*
* Functional description
* Parse a BLR descriptor. Return the alignment requirements
* of the datatype.
*
**************************************/
if (itemInfo)
{
itemInfo->nullable = true;
itemInfo->explicitCollation = false;
itemInfo->fullDomain = false;
}
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_address = NULL;
desc->dsc_flags = 0;
const USHORT dtype = csb->csb_blr_reader.getByte();
USHORT textType;
switch (dtype)
{
case blr_not_nullable:
PAR_desc(tdbb, csb, desc, itemInfo);
if (itemInfo)
itemInfo->nullable = false;
break;
case blr_text:
desc->makeText(csb->csb_blr_reader.getWord(), ttype_dynamic);
desc->dsc_flags |= DSC_no_subtype;
break;
case blr_cstring:
desc->dsc_dtype = dtype_cstring;
desc->dsc_flags |= DSC_no_subtype;
desc->dsc_length = csb->csb_blr_reader.getWord();
desc->setTextType(ttype_dynamic);
break;
case blr_varying:
desc->makeVarying(csb->csb_blr_reader.getWord(), ttype_dynamic);
desc->dsc_flags |= DSC_no_subtype;
break;
case blr_text2:
textType = csb->csb_blr_reader.getWord();
desc->makeText(csb->csb_blr_reader.getWord(), textType);
break;
case blr_cstring2:
desc->dsc_dtype = dtype_cstring;
desc->setTextType(csb->csb_blr_reader.getWord());
desc->dsc_length = csb->csb_blr_reader.getWord();
break;
case blr_varying2:
textType = csb->csb_blr_reader.getWord();
desc->makeVarying(csb->csb_blr_reader.getWord(), textType);
break;
case blr_short:
desc->dsc_dtype = dtype_short;
desc->dsc_length = sizeof(SSHORT);
desc->dsc_scale = (int) csb->csb_blr_reader.getByte();
break;
case blr_long:
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(SLONG);
desc->dsc_scale = (int) csb->csb_blr_reader.getByte();
break;
case blr_int64:
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
desc->dsc_scale = (int) csb->csb_blr_reader.getByte();
break;
case blr_quad:
desc->dsc_dtype = dtype_quad;
desc->dsc_length = sizeof(ISC_QUAD);
desc->dsc_scale = (int) csb->csb_blr_reader.getByte();
break;
case blr_float:
desc->dsc_dtype = dtype_real;
desc->dsc_length = sizeof(float);
break;
case blr_timestamp:
desc->dsc_dtype = dtype_timestamp;
desc->dsc_length = sizeof(ISC_QUAD);
break;
case blr_sql_date:
desc->dsc_dtype = dtype_sql_date;
desc->dsc_length = type_lengths[dtype_sql_date];
break;
case blr_sql_time:
desc->dsc_dtype = dtype_sql_time;
desc->dsc_length = type_lengths[dtype_sql_time];
break;
case blr_double:
case blr_d_float:
desc->dsc_dtype = dtype_double;
desc->dsc_length = sizeof(double);
break;
case blr_blob2:
{
desc->dsc_dtype = dtype_blob;
desc->dsc_length = sizeof(ISC_QUAD);
desc->dsc_sub_type = csb->csb_blr_reader.getWord();
USHORT ttype = csb->csb_blr_reader.getWord();
desc->dsc_scale = ttype & 0xFF; // BLOB character set
desc->dsc_flags = ttype & 0xFF00; // BLOB collation
break;
}
case blr_domain_name:
case blr_domain_name2:
{
bool fullDomain = (csb->csb_blr_reader.getByte() == blr_domain_full);
Firebird::MetaName* name = FB_NEW(csb->csb_pool) Firebird::MetaName(csb->csb_pool);
PAR_name(csb, *name);
FieldInfo fieldInfo;
bool exist = csb->csb_map_field_info.get(*name, fieldInfo);
MET_get_domain(tdbb, *name, desc, (exist ? NULL : &fieldInfo));
if (!exist)
csb->csb_map_field_info.put(*name, fieldInfo);
if (itemInfo)
{
itemInfo->field = *name;
if (fullDomain)
{
itemInfo->nullable = fieldInfo.nullable;
itemInfo->fullDomain = true;
}
else
itemInfo->nullable = true;
}
if (dtype == blr_domain_name2)
{
const USHORT ttype = csb->csb_blr_reader.getWord();
switch (desc->dsc_dtype)
{
case dtype_cstring:
case dtype_text:
case dtype_varying:
desc->setTextType(ttype);
break;
case dtype_blob:
desc->dsc_scale = ttype & 0xFF; // BLOB character set
desc->dsc_flags = ttype & 0xFF00; // BLOB collation
break;
default:
error(csb, Arg::Gds(isc_collation_requires_text));
break;
}
}
jrd_nod* dep_node = PAR_make_node(tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg[e_dep_object] = (jrd_nod*) name;
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_field;
csb->csb_dependencies.push(dep_node);
break;
}
case blr_column_name:
case blr_column_name2:
{
const bool fullDomain = (csb->csb_blr_reader.getByte() == blr_domain_full);
Firebird::MetaName* relationName = FB_NEW(csb->csb_pool) Firebird::MetaName(csb->csb_pool);
PAR_name(csb, *relationName);
Firebird::MetaName* fieldName = FB_NEW(csb->csb_pool) Firebird::MetaName(csb->csb_pool);
PAR_name(csb, *fieldName);
FieldInfo fieldInfo;
Firebird::MetaName fieldSource = MET_get_relation_field(tdbb, *relationName, *fieldName, desc, &fieldInfo);
bool exist = csb->csb_map_field_info.get(fieldSource, fieldInfo);
if (!exist)
csb->csb_map_field_info.put(fieldSource, fieldInfo);
if (itemInfo)
{
itemInfo->field = fieldSource;
if (fullDomain)
{
itemInfo->nullable = fieldInfo.nullable;
itemInfo->fullDomain = true;
}
else
itemInfo->nullable = true;
}
if (dtype == blr_column_name2)
{
const USHORT ttype = csb->csb_blr_reader.getWord();
switch (desc->dsc_dtype)
{
case dtype_cstring:
case dtype_text:
case dtype_varying:
desc->setTextType(ttype);
break;
case dtype_blob:
desc->dsc_scale = ttype & 0xFF; // BLOB character set
desc->dsc_flags = ttype & 0xFF00; // BLOB collation
break;
default:
error(csb, Arg::Gds(isc_collation_requires_text));
break;
}
}
jrd_nod* dep_node = PAR_make_node(tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg[e_dep_object] = (jrd_nod*) MET_lookup_relation(tdbb, *relationName);
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_relation;
dep_node->nod_arg[e_dep_field] = PAR_make_node(tdbb, 1);
dep_node->nod_arg[e_dep_field]->nod_type = nod_literal;
dep_node->nod_arg[e_dep_field]->nod_arg[0] = (jrd_nod*) fieldName->c_str();
csb->csb_dependencies.push(dep_node);
break;
}
default:
error(csb, Arg::Gds(isc_datnotsup));
}
if (itemInfo)
{
if (dtype == blr_cstring2 || dtype == blr_text2 || dtype == blr_varying2 ||
dtype == blr_blob2 || dtype == blr_domain_name2)
{
itemInfo->explicitCollation = true;
}
}
return type_alignments[desc->dsc_dtype];
}
jrd_nod* PAR_gen_field(thread_db* tdbb, USHORT stream, USHORT id)
{
/**************************************
*
* P A R _ g e n _ f i e l d
*
**************************************
*
* Functional description
* Generate a field block.
*
**************************************/
SET_TDBB(tdbb);
jrd_nod* node = FB_NEW_RPT(*tdbb->getDefaultPool(), e_fld_length) jrd_nod();
node->nod_type = nod_field;
node->nod_arg[e_fld_id] = (jrd_nod*) (IPTR) id;
node->nod_arg[e_fld_stream] = (jrd_nod*) (IPTR) stream;
return node;
}
jrd_nod* PAR_make_field(thread_db* tdbb, CompilerScratch* csb,
USHORT context,
const Firebird::MetaName& base_field)
{
/**************************************
*
* P A R _ m a k e _ f i e l d
*
**************************************
*
* Functional description
* Make up a field node in the permanent pool. This is used
* by MET_scan_relation to handle view fields.
*
**************************************/
SET_TDBB(tdbb);
const USHORT stream = csb->csb_rpt[context].csb_stream;
/* CVC: This is just another case of a custom function that isn't prepared
for quoted identifiers and that causes views with fields names like "z x"
to fail miserably. Since this function was truncating field names like "z x",
MET_lookup_field() call below failed and hence the function returned NULL
so only caller MET_scan_relation() did field->fld_source = 0;
This means a field without entry in rdb$fields. This is the origin of the
mysterious message "cannot access column z x in view VF" when selecting from
such view that has field "z x". This closes Firebird Bug #227758. */
// solved by using MetaName& as parameter - AP
jrd_rel* relation = csb->csb_rpt[stream].csb_relation;
jrd_prc* procedure = csb->csb_rpt[stream].csb_procedure;
const SSHORT id = procedure ? find_proc_field(procedure, base_field) :
MET_lookup_field (tdbb, csb->csb_rpt[stream].csb_relation, base_field, 0);
if (id < 0)
return NULL;
/* If rel_fields is NULL this means that the relation is
* in a temporary state (partially loaded). In this case
* there is nothing we can do but post an error and exit.
* Note: This will most likely happen if we have a large list
* of deferred work which can not complete because of some
* error, and while we are trying to commit, we find
* that we have a dependency on something later in the list.
* IF there were no error, then the dependency woyld have
* been resolved, because we would have fully loaded the
* relation, but if it can not be loaded, then we have this
* problem. The only thing that can be done to remedy this
* problem is to rollback. This will clear the DeferredWork list and
* allow the user to remedy the original error. Note: it would
* be incorrect for us (the server) to perform the rollback
* implicitly, because this is a task for the user to do, and
* should never be decided by the server. This fixes bug 10052 */
// CVC: The code for procedures now compiles correctly, but Vlad has
// pointed out that we don't have default for output fields, therefore
// the code is commented till better times.
jrd_fld* field = NULL;
/*
Parameter* param = NULL;
if (procedure)
{
param = (*procedure->prc_output_fields)[id];
}
else
*/
if (relation)
{
if (!relation->rel_fields) {
ERR_post(Arg::Gds(isc_depend_on_uncommitted_rel));
}
field = (*relation->rel_fields)[id];
}
if (csb->csb_g_flags & csb_get_dependencies) {
par_dependency(tdbb, csb, stream, id, base_field);
}
jrd_nod* temp_node = PAR_gen_field(tdbb, stream, id);
/*
if (param)
{
if (param->prm_default_value) //&& param->prm_not_null)
temp_node->nod_arg[e_fld_default_value] =
param->prm_default_value;
}
else
*/
if (field)
{
if (field->fld_default_value && field->fld_not_null)
temp_node->nod_arg[e_fld_default_value] = field->fld_default_value;
}
return temp_node;
}
jrd_nod* PAR_make_list(thread_db* tdbb, NodeStack& stack)
{
/**************************************
*
* P A R _ m a k e _ l i s t
*
**************************************
*
* Functional description
* Make a list node out of a stack.
*
**************************************/
SET_TDBB(tdbb);
// Count the number of nodes
USHORT count = stack.getCount();
jrd_nod* node = PAR_make_node(tdbb, count);
node->nod_type = nod_list;
jrd_nod** ptr = node->nod_arg + count;
while (stack.hasData())
{
*--ptr = stack.pop();
}
return node;
}
jrd_nod* PAR_make_node(thread_db* tdbb, int size)
{
/**************************************
*
* P A R _ m a k e _ n o d e
*
**************************************
*
* Functional description
* Make a node element and pass it back.
*
**************************************/
SET_TDBB(tdbb);
jrd_nod* node = FB_NEW_RPT(*tdbb->getDefaultPool(), size) jrd_nod();
node->nod_count = size;
return node;
}
CompilerScratch* PAR_parse(thread_db* tdbb, const UCHAR* blr, ULONG blr_length,
bool internal_flag, USHORT dbginfo_length, const UCHAR* dbginfo)
{
/**************************************
*
* P A R _ p a r s e
*
**************************************
*
* Functional description
* Parse blr, returning a compiler scratch block with the results.
*
**************************************/
SET_TDBB(tdbb);
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
csb->csb_blr_reader = BlrReader(blr, blr_length);
if (internal_flag)
csb->csb_g_flags |= csb_internal;
const SSHORT version = csb->csb_blr_reader.getByte();
switch (version)
{
case blr_version4:
csb->csb_g_flags |= csb_blr_version4;
break;
case blr_version5:
break; // nothing to do
default:
error(csb, Arg::Gds(isc_wroblrver) << Arg::Num(blr_version4) << Arg::Num(version));
}
if (dbginfo_length > 0)
DBG_parse_debug_info(dbginfo_length, dbginfo, csb->csb_dbg_info);
jrd_nod* node = PAR_parse_node(tdbb, csb, OTHER);
csb->csb_node = node;
if (csb->csb_blr_reader.getByte() != (UCHAR) blr_eoc)
{
PAR_syntax_error(csb, "end_of_command");
}
return csb;
}
SLONG PAR_symbol_to_gdscode(const Firebird::string& name)
{
/**************************************
*
* P A R _ s y m b o l _ t o _ g d s c o d e
*
**************************************
*
* Functional description
* Symbolic ASCII names are used in blr for posting and handling
* exceptions. They are also used to identify error codes
* within system triggers in a database.
*
* Returns the gds error status code for the given symbolic
* name, or 0 if not found.
*
* Symbolic names may be null or space terminated.
*
**************************************/
for (int i = 0; codes[i].code_number; ++i)
{
if (name == codes[i].code_string) {
return codes[i].code_number;
}
}
return 0;
}
// Registers a parse function (DmlNode creator) for a BLR code.
void PAR_register(UCHAR blr, NodeParseFunc parseFunc)
{
fb_assert(!blr_parsers[blr]);
blr_parsers[blr] = parseFunc;
}
static void error(CompilerScratch* csb, const Arg::StatusVector& v, bool isSyntaxError)
{
/**************************************
*
* e r r o r
*
**************************************
*
* Functional description
* We've got a blr error other than a syntax error. Handle it.
*
**************************************/
fb_assert(v.value()[0] == isc_arg_gds);
// Don't bother to pass tdbb for error handling
thread_db* tdbb = JRD_get_thread_data();
if (isSyntaxError)
{
csb->csb_blr_reader.seekBackward(1);
Arg::Gds p(isc_invalid_blr);
p << Arg::Num(csb->csb_blr_reader.getOffset());
p.append(v);
p.copyTo(tdbb->tdbb_status_vector);
}
else
v.copyTo(tdbb->tdbb_status_vector);
ERR_make_permanent(tdbb->tdbb_status_vector);
// Give up whatever we were doing and return to the user.
ERR_punt();
}
static SSHORT find_proc_field(const jrd_prc* procedure, const Firebird::MetaName& name)
{
/**************************************
*
* f i n d _ p r o c _ f i e l d
*
**************************************
*
* Functional description
* Look for named field in procedure output fields.
*
**************************************/
vec<Parameter*>* list = procedure->prc_output_fields;
if (!list)
return -1;
vec<Parameter*>::const_iterator ptr = list->begin();
for (const vec<Parameter*>::const_iterator end = list->end(); ptr < end; ++ptr)
{
const Parameter* param = *ptr;
if (name == param->prm_name)
return param->prm_number;
}
return -1;
}
static jrd_nod* par_args(thread_db* tdbb, CompilerScratch* csb, USHORT expected)
{
/**************************************
*
* p a r _ a r g s
*
**************************************
*
* Functional description
* Parse a counted argument list.
*
**************************************/
SET_TDBB(tdbb);
USHORT count = csb->csb_blr_reader.getByte();
jrd_nod* node = PAR_make_node(tdbb, count);
node->nod_type = nod_list;
jrd_nod** ptr = node->nod_arg;
if (count)
{
do {
*ptr++ = PAR_parse_node(tdbb, csb, expected);
} while (--count);
}
return node;
}
static jrd_nod* par_cast(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ c a s t
*
**************************************
*
* Functional description
* Parse a datatype cast
*
**************************************/
SET_TDBB(tdbb);
jrd_nod* node = PAR_make_node(tdbb, e_cast_length);
node->nod_count = count_table[blr_cast];
Format* format = Format::newFormat(*tdbb->getDefaultPool(), 1);
node->nod_arg[e_cast_fmt] = (jrd_nod*) format;
dsc* desc = &format->fmt_desc[0];
ItemInfo itemInfo;
PAR_desc(tdbb, csb, desc, &itemInfo);
format->fmt_length = desc->dsc_length;
node->nod_arg[e_cast_source] = PAR_parse_node(tdbb, csb, VALUE);
if (itemInfo.isSpecial())
{
ItemInfo* p = FB_NEW(*tdbb->getDefaultPool()) ItemInfo(*tdbb->getDefaultPool(), itemInfo);
node->nod_arg[e_cast_iteminfo] = (jrd_nod*) p;
}
if (itemInfo.explicitCollation)
{
jrd_nod* dep_node = PAR_make_node (tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg [e_dep_object] = (jrd_nod*)(IPTR) INTL_TEXT_TYPE(*desc);
dep_node->nod_arg [e_dep_object_type] = (jrd_nod*)(IPTR) obj_collation;
csb->csb_dependencies.push(dep_node);
}
return node;
}
static PsqlException* par_condition(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ c o n d i t i o n
*
**************************************
*
* Functional description
* Parse an error conditions list.
*
**************************************/
SET_TDBB(tdbb);
// allocate a node to represent the conditions list
const USHORT code_type = csb->csb_blr_reader.getByte();
// don't create PsqlException if blr_raise is used, just return NULL
if (code_type == blr_raise)
{
return NULL;
}
PsqlException* exception_list = FB_NEW_RPT(*tdbb->getDefaultPool(), 1) PsqlException();
exception_list->xcp_count = 1;
xcp_repeat& item = exception_list->xcp_rpt[0];
switch (code_type)
{
case blr_sql_code:
item.xcp_type = xcp_sql_code;
item.xcp_code = (SSHORT) csb->csb_blr_reader.getWord();
break;
case blr_gds_code:
{
string name;
item.xcp_type = xcp_gds_code;
PAR_name(csb, name);
name.lower();
SLONG code_number = PAR_symbol_to_gdscode(name);
if (code_number)
item.xcp_code = code_number;
else
error(csb, Arg::Gds(isc_codnotdef) << Arg::Str(name));
}
break;
case blr_exception:
case blr_exception_msg:
{
MetaName name;
item.xcp_type = xcp_xcp_code;
PAR_name(csb, name);
if (!(item.xcp_code = MET_lookup_exception_number(tdbb, name)))
error(csb, Arg::Gds(isc_xcpnotdef) << Arg::Str(name));
jrd_nod* dep_node = PAR_make_node(tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg[e_dep_object] = (jrd_nod*)(IPTR) item.xcp_code;
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_exception;
csb->csb_dependencies.push(dep_node);
}
break;
default:
fb_assert(false);
break;
}
return exception_list;
}
static PsqlException* par_conditions(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ c o n d i t i o n s
*
**************************************
*
* Functional description
* Parse an error conditions list.
*
**************************************/
SET_TDBB(tdbb);
// allocate a node to represent the conditions list
const USHORT n = csb->csb_blr_reader.getWord();
PsqlException* exception_list = FB_NEW_RPT(*tdbb->getDefaultPool(), n) PsqlException();
exception_list->xcp_count = n;
for (int i = 0; i < n; i++)
{
const USHORT code_type = csb->csb_blr_reader.getByte();
xcp_repeat& item = exception_list->xcp_rpt[i];
switch (code_type)
{
case blr_sql_code:
item.xcp_type = xcp_sql_code;
item.xcp_code = (SSHORT) csb->csb_blr_reader.getWord();
break;
case blr_gds_code:
{
string name;
item.xcp_type = xcp_gds_code;
PAR_name(csb, name);
name.lower();
SLONG code_number = PAR_symbol_to_gdscode(name);
if (code_number)
item.xcp_code = code_number;
else
error(csb, Arg::Gds(isc_codnotdef) << Arg::Str(name));
}
break;
case blr_exception:
{
MetaName name;
item.xcp_type = xcp_xcp_code;
PAR_name(csb, name);
if (!(item.xcp_code = MET_lookup_exception_number(tdbb, name)))
error(csb, Arg::Gds(isc_xcpnotdef) << Arg::Str(name));
jrd_nod* dep_node = PAR_make_node(tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg[e_dep_object] = (jrd_nod*)(IPTR) item.xcp_code;
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_exception;
csb->csb_dependencies.push(dep_node);
}
break;
case blr_default_code:
item.xcp_type = xcp_default;
item.xcp_code = 0;
break;
default:
fb_assert(FALSE);
break;
}
}
return exception_list;
}
static SSHORT par_context(CompilerScratch* csb, SSHORT* context_ptr)
{
/**************************************
*
* p a r _ c o n t e x t
*
**************************************
*
* Functional description
* Introduce a new context into the system. This involves
* assigning a stream and possibly extending the compile
* scratch block.
*
**************************************/
const SSHORT context = (unsigned int) csb->csb_blr_reader.getByte();
if (context_ptr)
*context_ptr = context;
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, context);
if (tail->csb_flags & csb_used)
{
if (csb->csb_g_flags & csb_reuse_context) {
return tail->csb_stream;
}
error(csb, Arg::Gds(isc_ctxinuse));
}
const SSHORT stream = csb->nextStream(false);
if (stream >= MAX_STREAMS)
{
error(csb, Arg::Gds(isc_too_many_contexts));
}
tail->csb_flags |= csb_used;
tail->csb_stream = (UCHAR) stream;
CMP_csb_element(csb, stream);
return stream;
}
static void par_dependency(thread_db* tdbb,
CompilerScratch* csb,
SSHORT stream,
SSHORT id,
const Firebird::MetaName& field_name)
{
/**************************************
*
* p a r _ d e p e n d e n c y
*
**************************************
*
* Functional description
* Register a field, relation, procedure or exception reference
* as a dependency.
*
**************************************/
SET_TDBB(tdbb);
jrd_nod* node = PAR_make_node(tdbb, e_dep_length);
node->nod_type = nod_dependency;
if (csb->csb_rpt[stream].csb_relation)
{
node->nod_arg[e_dep_object] = (jrd_nod*) csb->csb_rpt[stream].csb_relation;
// How do I determine reliably this is a view?
// At this time, rel_view_rse is still null.
//if (is_view)
// node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_view;
//else
node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_relation;
}
else if (csb->csb_rpt[stream].csb_procedure)
{
node->nod_arg[e_dep_object] = (jrd_nod*) csb->csb_rpt[stream].csb_procedure;
node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_procedure;
}
if (field_name.length() > 0)
{
jrd_nod* field_node = PAR_make_node(tdbb, 1);
node->nod_arg[e_dep_field] = field_node;
field_node->nod_type = nod_literal;
field_node->nod_arg[0] = (jrd_nod*) stringDup(*tdbb->getDefaultPool(), field_name.c_str());
}
else if (id >= 0)
{
jrd_nod* field_node = PAR_make_node(tdbb, 1);
node->nod_arg[e_dep_field] = field_node;
field_node->nod_type = nod_field;
field_node->nod_arg[0] = (jrd_nod*) (IPTR) id;
}
csb->csb_dependencies.push(node);
}
static jrd_nod* par_exec_proc(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_operator)
{
/**************************************
*
* p a r _ e x e c _ p r o c
*
**************************************
*
* Functional description
* Parse an execute procedure reference.
*
**************************************/
SET_TDBB(tdbb);
jrd_prc* procedure = NULL;
QualifiedName name;
if (blr_operator == blr_exec_pid)
{
const USHORT pid = csb->csb_blr_reader.getWord();
if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
name.identifier.printf("id %d", pid);
}
else
{
if (blr_operator == blr_exec_proc2)
PAR_name(csb, name.qualifier);
PAR_name(csb, name.identifier);
procedure = MET_lookup_procedure(tdbb, name, false);
}
if (!procedure)
{
error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()));
}
jrd_nod* node = PAR_make_node(tdbb, e_esp_length);
node->nod_type = nod_exec_proc;
node->nod_count = count_table[blr_exec_proc];
node->nod_arg[e_esp_procedure] = (jrd_nod*) procedure;
par_procedure_parms(tdbb, csb, procedure, &node->nod_arg[e_esp_in_msg],
&node->nod_arg[e_esp_inputs], true);
par_procedure_parms(tdbb, csb, procedure, &node->nod_arg[e_esp_out_msg],
&node->nod_arg[e_esp_outputs], false);
jrd_nod* dep_node = PAR_make_node(tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg[e_dep_object] = (jrd_nod*) procedure;
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_procedure;
csb->csb_dependencies.push(dep_node);
return node;
}
static jrd_nod* par_fetch(thread_db* tdbb, CompilerScratch* csb, jrd_nod* for_node)
{
/**************************************
*
* p a r _ f e t c h
*
**************************************
*
* Functional description
* Parse a FETCH statement, and map it into
*
* FOR x IN relation WITH x.DBKEY EQ value ...
*
**************************************/
SET_TDBB(tdbb);
// Fake RecordSelExpr
for_node->nod_arg[e_for_re] = PAR_make_node(tdbb, 1 + rse_delta + 2);
RecordSelExpr* rse = (RecordSelExpr*) for_node->nod_arg[e_for_re];
rse->nod_type = nod_rse;
rse->nod_count = 0;
rse->rse_count = 1;
jrd_nod* relation = PAR_parse_node(tdbb, csb, RELATION);
rse->rse_relation[0] = relation;
// Fake boolean
jrd_nod* node = rse->rse_boolean = PAR_make_node(tdbb, 2);
node->nod_type = nod_eql;
node->nod_flags = nod_comparison;
node->nod_arg[1] = PAR_parse_node(tdbb, csb, VALUE);
node->nod_arg[0] = PAR_make_node(tdbb, 1);
node = node->nod_arg[0];
node->nod_type = nod_dbkey;
node->nod_count = 0;
node->nod_arg[0] = relation->nod_arg[e_rel_stream];
// Pick up statement
for_node->nod_arg[e_for_statement] = PAR_parse_node(tdbb, csb, STATEMENT);
return for_node;
}
static jrd_nod* par_field(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_operator)
{
/**************************************
*
* p a r _ f i e l d
*
**************************************
*
* Functional description
* Parse a field.
*
**************************************/
SET_TDBB(tdbb);
const USHORT context = (unsigned int) csb->csb_blr_reader.getByte();
// check if this is a VALUE of domain's check constraint
if (!csb->csb_domain_validation.isEmpty() &&
(blr_operator == blr_fid || blr_operator == blr_field) && context == 0)
{
if (blr_operator == blr_fid)
{
#ifdef DEV_BUILD
SSHORT id =
#endif
csb->csb_blr_reader.getWord();
fb_assert(id == 0);
}
else
{
Firebird::MetaName name;
PAR_name(csb, name);
}
jrd_nod* node = PAR_make_node(tdbb, e_domval_length);
node->nod_type = nod_domain_validation;
node->nod_count = 0;
dsc* desc = (dsc*) (node->nod_arg + e_domval_desc);
MET_get_domain(tdbb, csb->csb_domain_validation, desc, NULL);
return node;
}
if (context >= csb->csb_rpt.getCount())/* ||
!(csb->csb_rpt[context].csb_flags & csb_used) )
dimitr: commented out to support system triggers implementing
WITH CHECK OPTION. They reference the relation stream (#2)
directly, without a DSQL context. It breaks the layering,
but we must support legacy BLR.
*/
{
error(csb, Arg::Gds(isc_ctxnotdef));
}
Firebird::MetaName name;
SSHORT id;
const SSHORT stream = csb->csb_rpt[context].csb_stream;
SSHORT flags = 0;
bool is_column = false;
if (blr_operator == blr_fid)
{
id = csb->csb_blr_reader.getWord();
flags = nod_id;
is_column = true;
}
else if (blr_operator == blr_field)
{
CompilerScratch::csb_repeat* tail = &csb->csb_rpt[stream];
const jrd_prc* procedure = tail->csb_procedure;
// make sure procedure has been scanned before using it
if (procedure && (!(procedure->prc_flags & PRC_scanned) ||
(procedure->prc_flags & PRC_being_scanned) ||
(procedure->prc_flags & PRC_being_altered)))
{
const jrd_prc* scan_proc = MET_procedure(tdbb, procedure->prc_id, false, 0);
if (scan_proc != procedure)
procedure = NULL;
}
if (procedure)
{
PAR_name(csb, name);
if ((id = find_proc_field(procedure, name)) == -1)
{
error(csb, Arg::Gds(isc_fldnotdef2) << Arg::Str(name) <<
Arg::Str(procedure->prc_name.toString()));
}
}
else
{
jrd_rel* relation = tail->csb_relation;
if (!relation)
error(csb, Arg::Gds(isc_ctxnotdef));
// make sure relation has been scanned before using it
if (!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned))
{
MET_scan_relation(tdbb, relation);
}
PAR_name(csb, name);
if ((id = MET_lookup_field(tdbb, relation, name.c_str(), 0)) < 0)
{
if (csb->csb_g_flags & csb_validation)
{
id = 0;
flags |= nod_id;
is_column = true;
}
else
{
if (relation->rel_flags & REL_system)
{
jrd_nod* node = PAR_make_node(tdbb, 0);
node->nod_type = nod_null;
return node;
}
if (tdbb->getAttachment()->att_flags & ATT_gbak_attachment)
{
warning(Arg::Warning(isc_fldnotdef) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
}
else if (!(relation->rel_flags & REL_deleted))
{
error(csb, Arg::Gds(isc_fldnotdef) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
}
else
error(csb, Arg::Gds(isc_ctxnotdef));
}
}
}
}
// check for dependencies -- if a field name was given,
// use it because when restoring the database the field
// id's may not be valid yet
if (csb->csb_g_flags & csb_get_dependencies)
{
if (blr_operator == blr_fid)
par_dependency(tdbb, csb, stream, id, "");
else
par_dependency(tdbb, csb, stream, id, name);
}
jrd_nod* node = PAR_gen_field(tdbb, stream, id);
node->nod_flags |= flags;
if (is_column)
{
jrd_rel* temp_rel = csb->csb_rpt[stream].csb_relation;
if (temp_rel)
{
jrd_fld* field;
if (id < (int) temp_rel->rel_fields->count() && (field = (*temp_rel->rel_fields)[id]))
{
if (field->fld_default_value && field->fld_not_null)
node->nod_arg[e_fld_default_value] = field->fld_default_value;
}
else
{
if (temp_rel->rel_flags & REL_system)
{
node = PAR_make_node(tdbb, 0);
node->nod_type = nod_null;
}
}
}
}
return node;
}
static jrd_nod* par_function(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_operator)
{
/**************************************
*
* p a r _ f u n c t i o n
*
**************************************
*
* Functional description
* Parse a function reference.
*
**************************************/
SET_TDBB(tdbb);
const UCHAR* savePos = csb->csb_blr_reader.getPos();
QualifiedName name;
USHORT count = 0;
if (blr_operator == blr_function2)
count = PAR_name(csb, name.qualifier);
count += PAR_name(csb, name.identifier);
if (blr_operator == blr_function &&
(name.identifier == "RDB$GET_CONTEXT" || name.identifier == "RDB$SET_CONTEXT"))
{
csb->csb_blr_reader.setPos(savePos);
jrd_nod* node = par_sys_function(tdbb, csb);
node->nod_type = nod_sys_function;
return node;
}
// Isn't it strange that gbak presence means nothing to this function now?
UserFunction* function =
FUN_lookup_function(tdbb, name); //, !(tdbb->getAttachment()->att_flags & ATT_gbak_attachment));
if (!function)
{
if (tdbb->tdbb_flags & TDBB_prc_being_dropped)
{
jrd_nod* anode = PAR_make_node(tdbb, e_fun_length);
anode->nod_count = 1;
anode->nod_arg[e_fun_function] = NULL;
anode->nod_arg[e_fun_args] = par_args(tdbb, csb, VALUE);
return anode;
}
csb->csb_blr_reader.seekBackward(count);
error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()));
}
if (!function->fun_entrypoint && !function->fun_external)
{
if (tdbb->getAttachment()->att_flags & ATT_gbak_attachment)
{
warning(Arg::Warning(isc_funnotdef) << Arg::Str(name.toString()) <<
Arg::Warning(isc_modnotfound));
}
else
{
csb->csb_blr_reader.seekBackward(count);
error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()) <<
Arg::Gds(isc_modnotfound));
}
}
jrd_nod* node = PAR_make_node(tdbb, e_fun_length);
node->nod_count = 1;
node->nod_type = nod_function;
node->nod_arg[e_fun_function] = (jrd_nod*) function;
node->nod_arg[e_fun_args] = par_args(tdbb, csb, VALUE);
// CVC: I will track ufds only if a proc is not being dropped.
if (csb->csb_g_flags & csb_get_dependencies)
{
jrd_nod* dep_node = PAR_make_node (tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg [e_dep_object] = (jrd_nod*) function;
dep_node->nod_arg [e_dep_object_type] = (jrd_nod*)(IPTR) obj_udf;
csb->csb_dependencies.push(dep_node);
}
return node;
}
static jrd_nod* par_literal(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ l i t e r a l
*
**************************************
*
* Functional description
* Parse a literal value.
*
**************************************/
SSHORT scale;
UCHAR dtype;
SET_TDBB(tdbb);
DSC desc;
PAR_desc(tdbb, csb, &desc);
const int count = lit_delta + (desc.dsc_length + sizeof(jrd_nod*) - 1) / sizeof(jrd_nod*);
jrd_nod* node = PAR_make_node(tdbb, count);
Literal* literal = (Literal*) node;
node->nod_count = 0;
literal->lit_desc = desc;
UCHAR* p = reinterpret_cast<UCHAR*>(literal->lit_data);
literal->lit_desc.dsc_address = p;
literal->lit_desc.dsc_flags = 0;
const UCHAR* q = csb->csb_blr_reader.getPos();
USHORT l = desc.dsc_length;
switch (desc.dsc_dtype)
{
case dtype_short:
l = 2;
*(SSHORT *) p = (SSHORT) gds__vax_integer(q, l);
break;
case dtype_long:
case dtype_sql_date:
case dtype_sql_time:
l = 4;
*(SLONG *) p = gds__vax_integer(q, l);
break;
case dtype_timestamp:
l = 8;
*(SLONG *) p = gds__vax_integer(q, 4);
p += 4;
q += 4;
*(SLONG *) p = gds__vax_integer(q, 4);
break;
case dtype_int64:
l = sizeof(SINT64);
*(SINT64 *) p = isc_portable_integer(q, l);
break;
case dtype_double:
// the double literal could potentially be used for any
// numeric literal - the value is passed as if it were a
// text string. Convert the numeric string to its binary
// value (int64, long or double as appropriate).
l = csb->csb_blr_reader.getWord();
q = csb->csb_blr_reader.getPos();
dtype = CVT_get_numeric(q, l, &scale, (double *) p);
literal->lit_desc.dsc_dtype = dtype;
switch (dtype)
{
case dtype_double:
literal->lit_desc.dsc_length = sizeof(double);
break;
case dtype_long:
literal->lit_desc.dsc_length = sizeof(SLONG);
literal->lit_desc.dsc_scale = (SCHAR) scale;
break;
default:
literal->lit_desc.dsc_length = sizeof(SINT64);
literal->lit_desc.dsc_scale = (SCHAR) scale;
}
break;
case dtype_text:
memcpy(p, q, l);
break;
default:
fb_assert(FALSE);
}
csb->csb_blr_reader.seekForward(l);
return node;
}
static jrd_nod* par_map(thread_db* tdbb, CompilerScratch* csb, USHORT stream)
{
/**************************************
*
* p a r _ m a p
*
**************************************
*
* Functional description
* Parse a MAP clause for a union or global aggregate expression.
*
**************************************/
SET_TDBB(tdbb);
if (csb->csb_blr_reader.getByte() != blr_map)
PAR_syntax_error(csb, "blr_map");
SSHORT count = csb->csb_blr_reader.getWord();
NodeStack map;
while (--count >= 0)
{
jrd_nod* assignment = PAR_make_node(tdbb, e_asgn_length);
assignment->nod_type = nod_assignment;
assignment->nod_count = e_asgn_length;
assignment->nod_arg[e_asgn_to] = PAR_gen_field(tdbb, stream, csb->csb_blr_reader.getWord());
assignment->nod_arg[e_asgn_from] = PAR_parse_node(tdbb, csb, VALUE);
map.push(assignment);
}
jrd_nod* node = PAR_make_list(tdbb, map);
node->nod_type = nod_map;
return node;
}
static jrd_nod* par_message(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ m e s s a g e
*
**************************************
*
* Functional description
* Parse a message declaration, including operator byte.
*
**************************************/
SET_TDBB(tdbb);
// Get message number, register it in the compiler scratch block, and
// allocate a node to represent the message
USHORT n = (unsigned int) csb->csb_blr_reader.getByte();
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, n);
jrd_nod* node = PAR_make_node(tdbb, e_msg_length);
tail->csb_message = node;
node->nod_count = 0;
node->nod_arg[e_msg_number] = (jrd_nod*) (IPTR) n;
if (n > csb->csb_msg_number)
csb->csb_msg_number = n;
// Get the number of parameters in the message and prepare to fill out the format block
n = csb->csb_blr_reader.getWord();
Format* format = Format::newFormat(*tdbb->getDefaultPool(), n);
node->nod_arg[e_msg_format] = (jrd_nod*) format;
ULONG offset = 0;
Format::fmt_desc_iterator desc, end;
USHORT index = 0;
for (desc = format->fmt_desc.begin(), end = desc + n; desc < end; ++desc, ++index)
{
ItemInfo itemInfo;
const USHORT alignment = PAR_desc(tdbb, csb, &*desc, &itemInfo);
if (alignment)
offset = FB_ALIGN(offset, alignment);
desc->dsc_address = (UCHAR *) (IPTR) offset;
offset += desc->dsc_length;
// ASF: Odd indexes are the nullable flag.
// So we only check even indexes, which is the actual parameter.
if (itemInfo.isSpecial() && index % 2 == 0)
{
csb->csb_dbg_info.argInfoToName.get(
Firebird::ArgumentInfo(csb->csb_msg_number, index / 2), itemInfo.name);
csb->csb_map_item_info.put(Item(nod_argument, csb->csb_msg_number, index), itemInfo);
}
}
if (offset > MAX_FORMAT_SIZE)
error(csb, Arg::Gds(isc_imp_exc) << Arg::Gds(isc_blktoobig));
format->fmt_length = (USHORT) offset;
return node;
}
static jrd_nod* par_modify(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_operator)
{
/**************************************
*
* p a r _ m o d i f y
*
**************************************
*
* Functional description
* Parse a modify statement.
*
**************************************/
SET_TDBB(tdbb);
// Parse the original and new contexts
USHORT context = (unsigned int) csb->csb_blr_reader.getByte();
if (context >= csb->csb_rpt.getCount() || !(csb->csb_rpt[context].csb_flags & csb_used))
{
error(csb, Arg::Gds(isc_ctxnotdef));
}
const SSHORT org_stream = csb->csb_rpt[context].csb_stream;
const SSHORT new_stream = csb->nextStream(false);
if (new_stream >= MAX_STREAMS)
{
error(csb, Arg::Gds(isc_too_many_contexts));
}
context = (unsigned int) csb->csb_blr_reader.getByte();
// Make sure the compiler scratch block is big enough to hold everything
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, context);
tail->csb_stream = (UCHAR) new_stream;
tail->csb_flags |= csb_used;
tail = CMP_csb_element(csb, new_stream);
tail->csb_relation = csb->csb_rpt[org_stream].csb_relation;
// Make the node and parse the sub-expression
jrd_nod* node = PAR_make_node(tdbb, e_mod_length);
node->nod_count = 1;
node->nod_arg[e_mod_org_stream] = (jrd_nod*) (IPTR) org_stream;
node->nod_arg[e_mod_new_stream] = (jrd_nod*) (IPTR) new_stream;
node->nod_arg[e_mod_statement] = PAR_parse_node(tdbb, csb, STATEMENT);
if (blr_operator == blr_modify2)
{
node->nod_count = 2;
node->nod_arg[e_mod_statement2] = PAR_parse_node(tdbb, csb, STATEMENT);
}
return node;
}
USHORT PAR_name(CompilerScratch* csb, Firebird::MetaName& name)
{
/**************************************
*
* P A R _ n a m e
*
**************************************
*
* Functional description
* Parse a counted string, returning count.
*
**************************************/
size_t l = csb->csb_blr_reader.getByte();
// Check for overly long identifiers at BLR parse stage to prevent unwanted
// surprises in deeper layers of the engine.
if (l > MAX_SQL_IDENTIFIER_LEN)
{
SqlIdentifier st;
char* s = st;
l = MAX_SQL_IDENTIFIER_LEN;
while (l--)
*s++ = csb->csb_blr_reader.getByte();
*s = 0;
ERR_post(Arg::Gds(isc_identifier_too_long) << Arg::Str(st));
}
char* s = name.getBuffer(l);
while (l--)
*s++ = csb->csb_blr_reader.getByte();
return name.length();
}
size_t PAR_name(CompilerScratch* csb, Firebird::string& name)
{
/**************************************
*
* P A R _ n a m e
*
**************************************
*
* Functional description
* Parse a counted string of virtually unlimited size
* (up to 64K, actually <= 255), returning count.
*
**************************************/
size_t l = csb->csb_blr_reader.getByte();
char* s = name.getBuffer(l);
while (l--)
*s++ = csb->csb_blr_reader.getByte();
return name.length();
}
static jrd_nod* par_partition_by(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ p a r t i t i o n _ b y
*
**************************************
*
* Functional description
* Parse PARTITION BY subclauses of window functions.
*
**************************************/
SET_TDBB(tdbb);
SSHORT count = (unsigned int) csb->csb_blr_reader.getByte();
if (count != 0) // Support for OVER (PARTITION BY <expr>)
status_exception::raise(Arg::Gds(isc_wish_list));
return NULL;
}
static jrd_nod* par_plan(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ p l a n
*
**************************************
*
* Functional description
* Parse an access plan expression.
* At this stage we are just generating the
* parse tree and checking contexts
* and indices.
*
**************************************/
SET_TDBB(tdbb);
USHORT node_type = (USHORT) csb->csb_blr_reader.getByte();
// a join type indicates a cross of two or more streams
if (node_type == blr_join || node_type == blr_merge)
{
USHORT count = (USHORT) csb->csb_blr_reader.getByte();
jrd_nod* plan = PAR_make_node(tdbb, count);
plan->nod_type = (nod_t)(USHORT) blr_table[node_type];
for (jrd_nod** arg = plan->nod_arg; count--;)
*arg++ = par_plan(tdbb, csb);
return plan;
}
// we have hit a stream; parse the context number and access type
if (node_type == blr_retrieve)
{
jrd_nod* plan = PAR_make_node(tdbb, e_retrieve_length);
plan->nod_type = (nod_t)(USHORT) blr_table[node_type];
// parse the relation name and context--the relation
// itself is redundant except in the case of a view,
// in which case the base relation (and alias) must be specified
USHORT n = (unsigned int) csb->csb_blr_reader.getByte();
if (n != blr_relation && n != blr_relation2 && n != blr_rid && n != blr_rid2)
{
PAR_syntax_error(csb, elements[RELATION]);
}
// don't have par_relation() parse the context, because
// this would add a new context; while this is a reference to
// an existing context
jrd_nod* relation_node = par_relation(tdbb, csb, n, false);
plan->nod_arg[e_retrieve_relation] = relation_node;
jrd_rel* relation = (jrd_rel*) relation_node->nod_arg[e_rel_relation];
n = csb->csb_blr_reader.getByte();
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
error(csb, Arg::Gds(isc_ctxnotdef));
const SSHORT stream = csb->csb_rpt[n].csb_stream;
relation_node->nod_arg[e_rel_stream] = (jrd_nod*) (IPTR) stream;
relation_node->nod_arg[e_rel_context] = (jrd_nod*) (IPTR) n;
// Access plan types (sequential is default)
node_type = (USHORT) csb->csb_blr_reader.getByte();
USHORT extra_count = 0;
jrd_nod* access_type = 0;
Firebird::MetaName name;
TEXT* idx_name = 0;
switch (node_type)
{
case blr_navigational:
{
access_type = plan->nod_arg[e_retrieve_access_type] =
PAR_make_node(tdbb, e_access_type_length);
access_type->nod_type = nod_navigational;
// pick up the index name and look up the appropriate ids
PAR_name(csb, name);
/* CVC: We can't do this. Index names are identifiers.
for (p = name; *p; *p++)
*p = UPPER (*p);
*/
SLONG relation_id;
SSHORT idx_status;
const SLONG index_id = MET_lookup_index_name(tdbb, name, &relation_id, &idx_status);
if (idx_status == MET_object_unknown || idx_status == MET_object_inactive)
{
if (tdbb->getAttachment()->att_flags & ATT_gbak_attachment)
{
warning(Arg::Warning(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
}
else
{
error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
}
}
// save both the relation id and the index id, since
// the relation could be a base relation of a view;
// save the index name also, for convenience
access_type->nod_arg[e_access_type_relation] = (jrd_nod*) (IPTR) relation_id;
access_type->nod_arg[e_access_type_index] = (jrd_nod*) (IPTR) index_id;
idx_name = stringDup(*tdbb->getDefaultPool(), name.c_str());
access_type->nod_arg[e_access_type_index_name] = (jrd_nod*) idx_name;
if (csb->csb_g_flags & csb_get_dependencies)
{
jrd_nod* dep_node = PAR_make_node (tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg[e_dep_object] = (jrd_nod*) idx_name;
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_index;
csb->csb_dependencies.push(dep_node);
}
if (csb->csb_blr_reader.peekByte() == blr_indices)
{
// dimitr: FALL INTO, if the plan item is ORDER ... INDEX (...)
extra_count = 3;
}
else
break;
}
case blr_indices:
{
if (extra_count)
csb->csb_blr_reader.getByte(); // skip blr_indices
USHORT count = (USHORT) csb->csb_blr_reader.getByte();
jrd_nod* temp = plan->nod_arg[e_retrieve_access_type] =
PAR_make_node(tdbb, count * e_access_type_length + extra_count);
for (USHORT i = 0; i < extra_count; i++) {
temp->nod_arg[i] = access_type->nod_arg[i];
}
temp->nod_type = extra_count ? nod_navigational : nod_indices;
if (extra_count)
delete access_type;
access_type = temp;
// pick up the index names and look up the appropriate ids
for (jrd_nod** arg = access_type->nod_arg + extra_count; count--;)
{
PAR_name(csb, name);
/* Nickolay Samofatov: We can't do this. Index names are identifiers.
for (p = name; *p; *p++)
*p = UPPER(*p);
*/
SLONG relation_id;
SSHORT idx_status;
const SLONG index_id = MET_lookup_index_name(tdbb, name, &relation_id, &idx_status);
if (idx_status == MET_object_unknown || idx_status == MET_object_inactive)
{
if (tdbb->getAttachment()->att_flags & ATT_gbak_attachment)
{
warning(Arg::Warning(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
}
else
{
error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
}
}
// save both the relation id and the index id, since
// the relation could be a base relation of a view;
// save the index name also, for convenience
*arg++ = (jrd_nod*) (IPTR) relation_id;
*arg++ = (jrd_nod*) (IPTR) index_id;
idx_name = stringDup(*tdbb->getDefaultPool(), name.c_str());
*arg++ = (jrd_nod*) idx_name;
if (csb->csb_g_flags & csb_get_dependencies)
{
jrd_nod* dep_node = PAR_make_node (tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg[e_dep_object] = (jrd_nod*) idx_name;
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*)(IPTR) obj_index;
csb->csb_dependencies.push(dep_node);
}
}
}
break;
case blr_sequential:
break;
default:
PAR_syntax_error(csb, "access type");
}
return plan;
}
PAR_syntax_error(csb, "plan item");
return NULL; // Added to remove compiler warning
}
static jrd_nod* par_procedure(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_operator)
{
/**************************************
*
* p a r _ p r o c e d u r e
*
**************************************
*
* Functional description
* Parse an procedural view reference.
*
**************************************/
SET_TDBB(tdbb);
jrd_prc* procedure = NULL;
Firebird::string* alias_string = NULL;
QualifiedName name;
switch (blr_operator)
{
case blr_pid:
case blr_pid2:
{
const SSHORT pid = csb->csb_blr_reader.getWord();
if (blr_operator == blr_pid2)
{
alias_string = FB_NEW(csb->csb_pool) Firebird::string(csb->csb_pool);
PAR_name(csb, *alias_string);
}
if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
{
name.identifier.printf("id %d", pid);
}
}
break;
case blr_procedure:
case blr_procedure2:
case blr_procedure3:
case blr_procedure4:
{
if (blr_operator == blr_procedure3 || blr_operator == blr_procedure4)
{
PAR_name(csb, name.qualifier);
}
PAR_name(csb, name.identifier);
if (blr_operator == blr_procedure2 || blr_operator == blr_procedure4)
{
alias_string = FB_NEW(csb->csb_pool) Firebird::string(csb->csb_pool);
PAR_name(csb, *alias_string);
}
procedure = MET_lookup_procedure(tdbb, name, false);
}
break;
default:
fb_assert(false);
}
if (!procedure)
{
error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()));
}
if (procedure->prc_type == prc_executable)
{
error(csb, Arg::Gds(isc_illegal_prc_type) << Arg::Str(procedure->prc_name.toString()));
}
jrd_nod* const node = PAR_make_node(tdbb, e_prc_length);
node->nod_type = nod_procedure;
node->nod_count = count_table[blr_procedure];
node->nod_arg[e_prc_procedure] = (jrd_nod*) (IPTR) procedure->prc_id;
SSHORT context;
const SSHORT stream = par_context(csb, &context);
node->nod_arg[e_prc_stream] = (jrd_nod*) (IPTR) stream;
node->nod_arg[e_prc_context] = (jrd_nod*) (IPTR) context;
csb->csb_rpt[stream].csb_procedure = procedure;
csb->csb_rpt[stream].csb_alias = alias_string;
par_procedure_parms(tdbb, csb, procedure, &node->nod_arg[e_prc_in_msg],
&node->nod_arg[e_prc_inputs], true);
if (csb->csb_g_flags & csb_get_dependencies)
{
par_dependency(tdbb, csb, stream, (SSHORT) -1, "");
}
return node;
}
static void par_procedure_parms(thread_db* tdbb,
CompilerScratch* csb,
jrd_prc* procedure,
jrd_nod** message_ptr,
jrd_nod** parameter_ptr, bool input_flag)
{
/**************************************
*
* p a r _ p r o c e d u r e _ p a r m s
*
**************************************
*
* Functional description
* Parse some procedure parameters.
*
**************************************/
SET_TDBB(tdbb);
bool mismatch = false;
SLONG count = csb->csb_blr_reader.getWord();
// Check to see if the parameter count matches
if (input_flag ?
(count < (procedure->prc_inputs - procedure->prc_defaults) ||
(count > procedure->prc_inputs) ) :
(count != procedure->prc_outputs))
{
// They don't match...Hmmm...Its OK if we were dropping the procedure
if (!(tdbb->tdbb_flags & TDBB_prc_being_dropped))
{
error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) <<
Arg::Str(procedure->prc_name.toString()));
}
else
mismatch = true;
}
if (count || input_flag && procedure->prc_defaults)
{
// We have a few parameters. Get on with creating the message block
USHORT n = ++csb->csb_msg_number;
if (n < 2)
csb->csb_msg_number = n = 2;
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, n);
jrd_nod* message = PAR_make_node(tdbb, e_msg_length);
tail->csb_message = message;
message->nod_type = nod_message;
message->nod_count = count_table[blr_message];
*message_ptr = message;
message->nod_count = 0;
message->nod_arg[e_msg_number] = (jrd_nod*)(IPTR) n;
const Format* format = input_flag ? procedure->prc_input_fmt : procedure->prc_output_fmt;
/* dimitr: procedure (with its parameter formats) is allocated out of
its own pool (prc_request->req_pool) and can be freed during
the cache cleanup (MET_clear_cache). Since the current
tdbb default pool is different from the procedure's one,
it's dangerous to copy a pointer from one request to another.
As an experiment, I've decided to copy format by value
instead of copying the reference. Since Format structure
doesn't contain any pointers, it should be safe to use a
default assignment operator which does a simple byte copy.
This change fixes one serious bug in the current codebase.
I think that this situation can (and probably should) be
handled by the metadata cache (via incrementing prc_use_count)
to avoid unexpected cache cleanups, but that area is out of my
knowledge. So this fix should be considered a temporary solution.
message->nod_arg[e_msg_format] = (jrd_nod*) format;
*/
Format* fmt_copy = Format::newFormat(*tdbb->getDefaultPool(), format->fmt_count);
*fmt_copy = *format;
message->nod_arg[e_msg_format] = (jrd_nod*) fmt_copy;
// --- end of fix ---
if (!mismatch)
n = format->fmt_count / 2;
else
{
// There was a parameter mismatch hence can't depend upon the format's
// fmt_count. Use count instead.
n = count;
}
jrd_nod* list = *parameter_ptr = PAR_make_node(tdbb, n);
list->nod_type = nod_list;
list->nod_count = n;
jrd_nod** ptr = list->nod_arg;
USHORT asgn_arg1, asgn_arg2;
if (input_flag)
{
asgn_arg1 = e_asgn_from;
asgn_arg2 = e_asgn_to;
}
else
{
asgn_arg1 = e_asgn_to;
asgn_arg2 = e_asgn_from;
}
for (USHORT i = 0; n; count--, n--)
{
jrd_nod* asgn = PAR_make_node(tdbb, e_asgn_length);
*ptr++ = asgn;
asgn->nod_type = nod_assignment;
asgn->nod_count = count_table[blr_assignment];
// default value for parameter
if ((count <= 0) && input_flag)
{
Parameter* parameter = (*procedure->prc_input_fields)[procedure->prc_inputs - n];
asgn->nod_arg[asgn_arg1] = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
}
else {
asgn->nod_arg[asgn_arg1] = PAR_parse_node(tdbb, csb, VALUE);
}
jrd_nod* prm = asgn->nod_arg[asgn_arg2] = PAR_make_node(tdbb, e_arg_length);
prm->nod_type = nod_argument;
prm->nod_count = 1;
prm->nod_arg[e_arg_message] = message;
prm->nod_arg[e_arg_number] = (jrd_nod*)(IPTR) i++;
jrd_nod* prm_f = prm->nod_arg[e_arg_flag] = PAR_make_node(tdbb, e_arg_length);
prm_f->nod_type = nod_argument;
prm_f->nod_count = 0;
prm_f->nod_arg[e_arg_message] = message;
prm_f->nod_arg[e_arg_number] = (jrd_nod*)(IPTR) i++;
}
}
else if ((input_flag ? procedure->prc_inputs : procedure->prc_outputs) && !mismatch)
{
error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) <<
Arg::Str(procedure->prc_name.toString()));
}
}
static jrd_nod* par_relation(thread_db* tdbb,
CompilerScratch* csb, SSHORT blr_operator, bool parse_context)
{
/**************************************
*
* p a r _ r e l a t i o n
*
**************************************
*
* Functional description
* Parse a relation reference.
*
**************************************/
SET_TDBB(tdbb);
// Find relation either by id or by name
jrd_rel* relation = NULL;
Firebird::string* alias_string = NULL;
Firebird::MetaName name;
switch (blr_operator)
{
case blr_rid:
case blr_rid2:
{
const SSHORT id = csb->csb_blr_reader.getWord();
if (blr_operator == blr_rid2)
{
alias_string = FB_NEW(csb->csb_pool) Firebird::string(csb->csb_pool);
PAR_name(csb, *alias_string);
}
if (!(relation = MET_lookup_relation_id(tdbb, id, false)))
{
name.printf("id %d", id);
}
}
break;
case blr_relation:
case blr_relation2:
{
PAR_name(csb, name);
if (blr_operator == blr_relation2)
{
alias_string = FB_NEW(csb->csb_pool) Firebird::string(csb->csb_pool);
PAR_name(csb, *alias_string);
}
relation = MET_lookup_relation(tdbb, name);
}
break;
default:
fb_assert(false);
}
if (!relation)
{
error(csb, Arg::Gds(isc_relnotdef) << Arg::Str(name), false);
}
// Make a relation reference node
jrd_nod* const node = PAR_make_node(tdbb, e_rel_length);
node->nod_count = 0;
// if an alias was passed, store with the relation
if (alias_string)
{
node->nod_arg[e_rel_alias] =
(jrd_nod*) stringDup(*tdbb->getDefaultPool(), *alias_string);
}
// Scan the relation if it hasn't already been scanned for meta data
if ((!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)) &&
((relation->rel_flags & REL_force_scan) || !(csb->csb_g_flags & csb_internal)))
{
relation->rel_flags &= ~REL_force_scan;
MET_scan_relation(tdbb, relation);
}
else if (relation->rel_flags & REL_sys_triggers)
{
MET_parse_sys_trigger(tdbb, relation);
}
// generate a stream for the relation reference, assuming it is a real reference
if (parse_context)
{
SSHORT context;
const SSHORT stream = par_context(csb, &context);
fb_assert(stream <= MAX_STREAMS);
node->nod_arg[e_rel_stream] = (jrd_nod*) (IPTR) stream;
node->nod_arg[e_rel_context] = (jrd_nod*) (IPTR) context;
csb->csb_rpt[stream].csb_relation = relation;
csb->csb_rpt[stream].csb_alias = alias_string;
if (csb->csb_g_flags & csb_get_dependencies)
{
par_dependency(tdbb, csb, stream, (SSHORT) -1, "");
}
}
else
{
delete alias_string;
}
node->nod_arg[e_rel_relation] = (jrd_nod*) relation;
return node;
}
static jrd_nod* par_rse(thread_db* tdbb, CompilerScratch* csb, SSHORT rse_op)
{
/**************************************
*
* p a r _ r s e
*
**************************************
*
* Functional description
* Parse a record selection expression.
*
**************************************/
SET_TDBB(tdbb);
SSHORT count = (unsigned int) csb->csb_blr_reader.getByte();
RecordSelExpr* rse = (RecordSelExpr*) PAR_make_node(tdbb, count + rse_delta + 2);
rse->nod_count = 0;
rse->rse_count = count;
jrd_nod** ptr = rse->rse_relation;
while (--count >= 0)
{
// AB: Added TYPE_RSE for derived table support
*ptr++ = PAR_parse_node(tdbb, csb, RELATION); // TYPE_RSE);
//*ptr++ = PAR_parse_node(tdbb, csb, RELATION);
}
while (true)
{
const UCHAR op = csb->csb_blr_reader.getByte();
switch (op)
{
case blr_boolean:
rse->rse_boolean = PAR_parse_node(tdbb, csb, TYPE_BOOL);
break;
case blr_first:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
rse->rse_first = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_skip:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
rse->rse_skip = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_sort:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
rse->rse_sorted = par_sort(tdbb, csb, true);
break;
case blr_project:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
rse->rse_projection = par_sort(tdbb, csb, false);
break;
case blr_join_type:
{
const USHORT jointype = (USHORT) csb->csb_blr_reader.getByte();
rse->rse_jointype = jointype;
if (jointype != blr_inner &&
jointype != blr_left &&
jointype != blr_right &&
jointype != blr_full)
{
PAR_syntax_error(csb, "join type clause");
}
break;
}
case blr_plan:
rse->rse_plan = par_plan(tdbb, csb);
break;
case blr_writelock:
rse->nod_flags |= rse_writelock;
break;
default:
if (op == (UCHAR) blr_end)
{
// An outer join is only allowed when the stream count is 2
// and a boolean expression has been supplied
if (!rse->rse_jointype || (rse->rse_count == 2 && rse->rse_boolean))
{
// Convert right outer joins to left joins to avoid
// RIGHT JOIN handling at lower engine levels
if (rse->rse_jointype == blr_right)
{
// Swap sub-streams
jrd_nod* temp = rse->rse_relation[0];
rse->rse_relation[0] = rse->rse_relation[1];
rse->rse_relation[1] = temp;
rse->rse_jointype = blr_left;
}
return (jrd_nod*) rse;
}
}
PAR_syntax_error(csb, (TEXT*)((rse_op == blr_rs_stream) ?
"RecordSelExpr stream clause" :
"record selection expression clause"));
}
}
}
static jrd_nod* par_sort(thread_db* tdbb, CompilerScratch* csb, bool flag)
{
/**************************************
*
* p a r _ s o r t
*
**************************************
*
* Functional description
* Parse a sort clause (sans header byte). This is used for
* BLR_SORT, BLR_PROJECT, and BLR_GROUP.
*
**************************************/
SET_TDBB(tdbb);
SSHORT count = (unsigned int) csb->csb_blr_reader.getByte();
jrd_nod* clause = PAR_make_node(tdbb, count * 3);
if (!flag)
clause->nod_flags = nod_unique_sort;
clause->nod_type = nod_sort;
clause->nod_count = count;
jrd_nod** ptr = clause->nod_arg;
jrd_nod** ptr2 = ptr + count;
jrd_nod** ptr3 = ptr2 + count;
while (--count >= 0)
{
if (flag)
{
UCHAR code = csb->csb_blr_reader.getByte();
switch (code)
{
case blr_nullsfirst:
*ptr3++ = (jrd_nod*) (IPTR) rse_nulls_first;
code = csb->csb_blr_reader.getByte();
break;
case blr_nullslast:
*ptr3++ = (jrd_nod*) (IPTR) rse_nulls_last;
code = csb->csb_blr_reader.getByte();
break;
default:
*ptr3++ = (jrd_nod*) (IPTR) rse_nulls_default;
}
*ptr2++ = (jrd_nod*) (IPTR) ((code == blr_descending) ? TRUE : FALSE);
}
*ptr++ = PAR_parse_node(tdbb, csb, VALUE);
}
return clause;
}
#ifdef NOT_USED_OR_REPLACED
static jrd_nod* par_stream(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ s t r e a m
*
**************************************
*
* Functional description
* Parse a stream expression.
*
**************************************/
SET_TDBB(tdbb);
RecordSelExpr* rse = (RecordSelExpr*) PAR_make_node(tdbb, 1 + rse_delta + 2);
rse->nod_count = 0;
rse->rse_count = 1;
rse->rse_relation[0] = PAR_parse_node(tdbb, csb, RELATION);
while (true)
{
const UCHAR op = csb->csb_blr_reader.getByte();
switch (op)
{
case blr_boolean:
rse->rse_boolean = PAR_parse_node(tdbb, csb, TYPE_BOOL);
break;
default:
if (op == (UCHAR) blr_end)
return (jrd_nod*) rse;
PAR_syntax_error(csb, "stream_clause");
}
}
}
#endif
static jrd_nod* par_sys_function(thread_db* tdbb, CompilerScratch* csb)
{
/**************************************
*
* p a r _ s y s _ f u n c t i o n
*
**************************************
*
* Functional description
* Parse a system function reference.
*
**************************************/
SET_TDBB(tdbb);
Firebird::MetaName name;
const USHORT count = PAR_name(csb, name);
const SysFunction* function = SysFunction::lookup(name);
if (!function)
{
csb->csb_blr_reader.seekBackward(count);
error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name));
}
jrd_nod* node = PAR_make_node(tdbb, e_sysfun_length);
node->nod_count = count_table[blr_sys_function];
node->nod_arg[e_sysfun_args] = par_args(tdbb, csb, VALUE);
node->nod_arg[e_sysfun_function] = (jrd_nod*) function;
return node;
}
static jrd_nod* par_union(thread_db* tdbb, CompilerScratch* csb, bool recursive)
{
/**************************************
*
* p a r _ u n i o n
*
**************************************
*
* Functional description
* Parse a union reference.
*
**************************************/
SET_TDBB(tdbb);
// Make the node, parse the context number, get a stream assigned,
// and get the number of sub-RecordSelExpr's.
jrd_nod* node = PAR_make_node(tdbb, e_uni_length);
node->nod_count = 3;
const USHORT stream = par_context(csb, 0);
node->nod_arg[e_uni_stream] = (jrd_nod*) (IPTR) stream;
// assign separate context for mapped record if union is recursive
USHORT map_stream = stream;
if (recursive)
{
node->nod_flags |= nod_recurse;
map_stream = par_context(csb, 0);
node->nod_arg[e_uni_map_stream] = (jrd_nod*) (IPTR) map_stream;
}
SSHORT count = (unsigned int) csb->csb_blr_reader.getByte();
// Pick up the sub-RecordSelExpr's and maps
NodeStack clauses;
while (--count >= 0)
{
clauses.push(PAR_parse_node(tdbb, csb, TYPE_RSE));
clauses.push(par_map(tdbb, csb, map_stream));
}
node->nod_arg[e_uni_clauses] = PAR_make_list(tdbb, clauses);
return node;
}
jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected)
{
/**************************************
*
* P A R _ p a r s e _ n o d e
*
**************************************
*
* Functional description
* Parse a BLR expression.
*
**************************************/
SET_TDBB(tdbb);
const USHORT blr_offset = csb->csb_blr_reader.getOffset();
const SSHORT blr_operator = csb->csb_blr_reader.getByte();
if (blr_operator < 0 || blr_operator >= FB_NELEM(type_table))
{
// NS: This error string is correct, please do not mangle it again and again.
// The whole error message is "BLR syntax error: expected %s at offset %d, encountered %d"
PAR_syntax_error(csb, "valid BLR code");
}
const SSHORT sub_type = sub_type_table[blr_operator];
// If there is a length given in the length table, pre-allocate
// the node and set its count. This saves an enormous amount of repetitive code.
jrd_nod* node;
jrd_nod** arg;
USHORT n = length_table[blr_operator];
if (n)
{
node = PAR_make_node(tdbb, n);
node->nod_count = count_table[blr_operator];
arg = node->nod_arg;
}
else
{
node = NULL;
arg = NULL;
}
bool set_type = true;
// Dispatch on operator type.
switch (blr_operator)
{
case blr_any:
case blr_unique:
case blr_ansi_any:
case blr_ansi_all:
case blr_exists:
node->nod_arg[e_any_rse] = PAR_parse_node(tdbb, csb, sub_type);
break;
// Boring operators -- no special handling req'd
case blr_value_if:
case blr_substring:
case blr_matching2:
case blr_ansi_like:
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
break;
case blr_trim:
{
node->nod_count = e_trim_count;
node->nod_arg[e_trim_specification] = (jrd_nod*)(U_IPTR) csb->csb_blr_reader.getByte();
BYTE trimWhat = csb->csb_blr_reader.getByte();
if (trimWhat == blr_trim_characters)
node->nod_arg[e_trim_characters] = PAR_parse_node(tdbb, csb, sub_type);
else
{
node->nod_arg[e_trim_characters] = NULL;
--node->nod_count;
}
node->nod_arg[e_trim_value] = PAR_parse_node(tdbb, csb, sub_type);
break;
}
case blr_and:
case blr_or:
case blr_prot_mask:
case blr_containing:
case blr_matching:
case blr_like:
case blr_starting:
case blr_add:
case blr_subtract:
case blr_multiply:
case blr_divide:
case blr_concatenate:
case blr_assignment:
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
// Fall into ...
case blr_handler:
case blr_loop:
case blr_lock_state:
case blr_upcase:
case blr_lowcase:
case blr_negate:
case blr_not:
case blr_missing:
case blr_agg_count2:
case blr_agg_max:
case blr_agg_min:
case blr_agg_total:
case blr_agg_average:
case blr_agg_count_distinct:
case blr_agg_total_distinct:
case blr_agg_average_distinct:
case blr_internal_info:
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
break;
case blr_similar:
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
if (csb->csb_blr_reader.getByte() != 0)
*arg++ = PAR_parse_node(tdbb, csb, sub_type); // escape
else // without escape
{
*arg++ = NULL;
--node->nod_count;
}
break;
case blr_agg_list:
case blr_agg_list_distinct:
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
*arg++ = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_exec_sql:
{
n = e_exec_stmt_fixed_count;
node = PAR_make_node(tdbb, n + e_exec_stmt_extra_count);
node->nod_count = n;
node->nod_type = nod_exec_stmt;
set_type = false;
arg = node->nod_arg;
*arg++ = PAR_parse_node(tdbb, csb, VALUE); // e_exec_stmt_stmt_sql
*arg++ = NULL; // e_exec_stmt_data_src
*arg++ = NULL; // e_exec_stmt_user
*arg++ = NULL; // e_exec_stmt_password
*arg++ = NULL; // e_exec_stmt_role;
*arg++ = NULL; // e_exec_stmt_proc_block;
*arg++ = NULL; // e_exec_stmt_extra_inputs
*arg++ = NULL; // e_exec_stmt_extra_input_names
*arg++ = NULL; // e_exec_stmt_extra_outputs
*arg++ = NULL; // e_exec_stmt_extra_tran
*arg++ = NULL; // e_exec_stmt_extra_privs
}
break;
case blr_exec_into:
{
const USHORT outputs = csb->csb_blr_reader.getWord();
n = outputs + e_exec_stmt_fixed_count;
node = PAR_make_node(tdbb, n + e_exec_stmt_extra_count);
node->nod_count = n;
node->nod_type = nod_exec_stmt;
set_type = false;
arg = node->nod_arg;
*arg++ = PAR_parse_node(tdbb, csb, VALUE); // e_exec_stmt_stmt_sql
*arg++ = NULL; // e_exec_stmt_data_src
*arg++ = NULL; // e_exec_stmt_user
*arg++ = NULL; // e_exec_stmt_password
*arg++ = NULL; // e_exec_stmt_role;
if (csb->csb_blr_reader.getByte()) // singleton flag
*arg++ = NULL; // e_exec_stmt_proc_block
else
*arg++ = PAR_parse_node(tdbb, csb, STATEMENT); // e_exec_stmt_proc_block
// output parameters
for (n = e_exec_stmt_fixed_count; n < node->nod_count; n++) {
*arg++ = PAR_parse_node(tdbb, csb, VALUE);
}
*arg++ = NULL; // e_exec_stmt_extra_inputs
*arg++ = NULL; // e_exec_stmt_extra_input_names
*arg++ = (jrd_nod*)(IPTR) outputs; // e_exec_stmt_extra_outputs
*arg++ = NULL; // e_exec_stmt_extra_tran
*arg++ = NULL; // e_exec_stmt_extra_privs
}
break;
case blr_exec_stmt:
{
USHORT inputs = 0;
USHORT outputs = 0;
UCHAR tra_mode = 0;
int use_caller_privs = 0;
EDS::ParamNames* paramNames = NULL;
while (true)
{
const UCHAR code = csb->csb_blr_reader.getByte();
switch (code)
{
case blr_exec_stmt_inputs:
inputs = csb->csb_blr_reader.getWord();
break;
case blr_exec_stmt_outputs:
outputs = csb->csb_blr_reader.getWord();
break;
case blr_exec_stmt_sql:
n = inputs + outputs + e_exec_stmt_fixed_count;
node = PAR_make_node(tdbb, n + e_exec_stmt_extra_count);
node->nod_count = n;
node->nod_arg[e_exec_stmt_stmt_sql] = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_exec_stmt_proc_block:
node->nod_arg[e_exec_stmt_proc_block] = PAR_parse_node(tdbb, csb, STATEMENT);
break;
case blr_exec_stmt_data_src:
node->nod_arg[e_exec_stmt_data_src] = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_exec_stmt_user:
node->nod_arg[e_exec_stmt_user] = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_exec_stmt_pwd:
node->nod_arg[e_exec_stmt_password] = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_exec_stmt_role:
node->nod_arg[e_exec_stmt_role] = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_exec_stmt_tran:
PAR_syntax_error(csb, "external transaction parameters");
break;
case blr_exec_stmt_tran_clone:
tra_mode = csb->csb_blr_reader.getByte();
break;
case blr_exec_stmt_privs:
use_caller_privs = 1;
break;
case blr_exec_stmt_in_params:
case blr_exec_stmt_in_params2:
// input parameters and their names
n = e_exec_stmt_fixed_count;
arg = node->nod_arg + n;
for (; n < e_exec_stmt_fixed_count + inputs; n++)
{
if (code == blr_exec_stmt_in_params2)
{
Firebird::string name;
if (PAR_name(csb, name))
{
Firebird::MemoryPool& pool = csb->csb_pool;
if (!paramNames) {
paramNames = FB_NEW (pool) EDS::ParamNames(pool);
}
Firebird::string* newName = FB_NEW (pool) Firebird::string(pool, name);
paramNames->add(newName);
}
}
*arg++ = PAR_parse_node(tdbb, csb, VALUE);
}
break;
case blr_exec_stmt_out_params:
// output parameters
n = e_exec_stmt_fixed_count + inputs;
arg = node->nod_arg + n;
for (; n < node->nod_count; n++) {
*arg++ = PAR_parse_node(tdbb, csb, VALUE);
}
break;
case blr_end:
break;
default:
PAR_syntax_error(csb, "unknown EXECUTE STATEMENT option");
}
if (code == blr_end)
break;
}
arg = node->nod_arg + node->nod_count;
*arg++ = (jrd_nod*)(IPTR) inputs; // e_exec_stmt_extra_inputs
*arg++ = (jrd_nod*) paramNames; // e_exec_stmt_extra_input_names
*arg++ = (jrd_nod*)(IPTR) outputs; // e_exec_stmt_extra_outputs
*arg++ = (jrd_nod*)(IPTR) tra_mode; // e_exec_stmt_extra_tran
*arg++ = (jrd_nod*)(IPTR) use_caller_privs; // e_exec_stmt_extra_privs
}
break;
case blr_null:
case blr_agg_count:
case blr_user_name:
case blr_current_role:
case blr_current_date:
case blr_start_savepoint:
case blr_end_savepoint:
break;
case blr_current_time:
node->nod_arg[0] = (jrd_nod*) (IPTR) DEFAULT_TIME_PRECISION;
break;
case blr_current_timestamp:
node->nod_arg[0] = (jrd_nod*) (IPTR) DEFAULT_TIMESTAMP_PRECISION;
break;
case blr_current_time2:
case blr_current_timestamp2:
n = csb->csb_blr_reader.getByte();
if (n > MAX_TIME_PRECISION) {
ERR_post(Arg::Gds(isc_invalid_time_precision) << Arg::Num(MAX_TIME_PRECISION));
}
node->nod_arg[0] = (jrd_nod*) (IPTR) n;
break;
case blr_store:
case blr_store2:
node->nod_arg[e_sto_relation] = PAR_parse_node(tdbb, csb, RELATION);
node->nod_arg[e_sto_statement] = PAR_parse_node(tdbb, csb, sub_type);
if (blr_operator == blr_store2)
node->nod_arg[e_sto_statement2] = PAR_parse_node(tdbb, csb, sub_type);
break;
// Comparison operators
case blr_between:
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
case blr_equiv:
case blr_eql:
case blr_neq:
case blr_geq:
case blr_gtr:
case blr_leq:
case blr_lss:
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
node->nod_flags = nod_comparison;
break;
case blr_erase:
n = csb->csb_blr_reader.getByte();
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
error(csb, Arg::Gds(isc_ctxnotdef));
node->nod_arg[e_erase_stream] = (jrd_nod*) (IPTR) csb->csb_rpt[n].csb_stream;
break;
case blr_modify:
case blr_modify2:
node = par_modify(tdbb, csb, blr_operator);
break;
case blr_exec_proc:
case blr_exec_proc2:
case blr_exec_pid:
node = par_exec_proc(tdbb, csb, blr_operator);
set_type = false;
break;
case blr_pid:
case blr_pid2:
case blr_procedure:
case blr_procedure2:
case blr_procedure3:
case blr_procedure4:
node = par_procedure(tdbb, csb, blr_operator);
set_type = false;
break;
case blr_function:
case blr_function2:
node = par_function(tdbb, csb, blr_operator);
set_type = false;
break;
case blr_index:
node->nod_arg[0] = PAR_parse_node(tdbb, csb, sub_type);
node->nod_arg[1] = par_args(tdbb, csb, sub_type);
break;
case blr_for:
if (csb->csb_blr_reader.peekByte() == (UCHAR) blr_stall)
node->nod_arg[e_for_stall] = PAR_parse_node(tdbb, csb, STATEMENT);
if (csb->csb_blr_reader.peekByte() == (UCHAR) blr_rse ||
csb->csb_blr_reader.peekByte() == (UCHAR) blr_singular ||
csb->csb_blr_reader.peekByte() == (UCHAR) blr_scrollable)
{
node->nod_arg[e_for_re] = PAR_parse_node(tdbb, csb, TYPE_RSE);
}
else
{
node->nod_arg[e_for_re] = par_rse(tdbb, csb, blr_operator);
}
node->nod_arg[e_for_statement] = PAR_parse_node(tdbb, csb, sub_type);
break;
case blr_dcl_cursor:
{
node->nod_arg[e_dcl_cursor_number] = (jrd_nod*) (IPTR) csb->csb_blr_reader.getWord();
node->nod_arg[e_dcl_cursor_rse] = PAR_parse_node(tdbb, csb, TYPE_RSE);
n = csb->csb_blr_reader.getWord();
jrd_nod* temp = PAR_make_node(tdbb, n);
temp->nod_type = nod_list;
for (jrd_nod** ptr = temp->nod_arg; n; n--) {
*ptr++ = PAR_parse_node(tdbb, csb, VALUE);
}
node->nod_arg[e_dcl_cursor_refs] = temp;
}
break;
case blr_cursor_stmt:
n = csb->csb_blr_reader.getByte();
node->nod_arg[e_cursor_stmt_op] = (jrd_nod*) (IPTR) n;
node->nod_arg[e_cursor_stmt_number] = (jrd_nod*) (IPTR) csb->csb_blr_reader.getWord();
switch (n)
{
case blr_cursor_open:
case blr_cursor_close:
break;
case blr_cursor_fetch_scroll:
node->nod_arg[e_cursor_stmt_scroll_op] = (jrd_nod*)(IPTR) csb->csb_blr_reader.getByte();
node->nod_arg[e_cursor_stmt_scroll_val] = PAR_parse_node(tdbb, csb, VALUE);
// FALL INTO
case blr_cursor_fetch:
csb->csb_g_flags |= csb_reuse_context;
node->nod_arg[e_cursor_stmt_into] = PAR_parse_node(tdbb, csb, STATEMENT);
csb->csb_g_flags &= ~csb_reuse_context;
break;
default:
PAR_syntax_error(csb, "cursor operation clause");
}
break;
case blr_rse:
case blr_rs_stream:
node = par_rse(tdbb, csb, blr_operator);
break;
case blr_singular:
node = PAR_parse_node(tdbb, csb, TYPE_RSE);
((RecordSelExpr*) node)->nod_flags |= rse_singular;
break;
case blr_scrollable:
node = PAR_parse_node(tdbb, csb, TYPE_RSE);
((RecordSelExpr*) node)->nod_flags |= rse_scrollable;
break;
case blr_relation:
case blr_rid:
case blr_relation2:
case blr_rid2:
node = par_relation(tdbb, csb, blr_operator, true);
break;
case blr_union:
node = par_union(tdbb, csb, false);
break;
case blr_recurse:
node = par_union(tdbb, csb, true);
break;
case blr_window:
node->nod_flags = nod_window;
// fall into
case blr_aggregate:
node->nod_arg[e_agg_stream] = (jrd_nod*) (IPTR) par_context(csb, 0);
fb_assert((int) (IPTR)node->nod_arg[e_agg_stream] <= MAX_STREAMS);
node->nod_arg[e_agg_rse] = PAR_parse_node(tdbb, csb, TYPE_RSE);
node->nod_arg[e_agg_group] = PAR_parse_node(tdbb, csb, OTHER);
node->nod_arg[e_agg_map] = par_map(tdbb, csb, (USHORT)(IPTR) node->nod_arg[e_agg_stream]);
break;
case blr_group_by:
node = par_sort(tdbb, csb, false);
return node->nod_count ? node : NULL;
case blr_partition_by:
return par_partition_by(tdbb, csb);
case blr_field:
case blr_fid:
node = par_field(tdbb, csb, blr_operator);
if (node->nod_type == nod_domain_validation || node->nod_type == nod_null)
set_type = false; // to not change nod->nod_type to nod_field
break;
case blr_derived_expr:
{
const UCHAR streamCount = csb->csb_blr_reader.getByte();
USHORT* streamList = FB_NEW(*tdbb->getDefaultPool()) USHORT[streamCount];
for (UCHAR i = 0; i < streamCount; ++i)
{
streamList[i] = csb->csb_blr_reader.getByte();
streamList[i] = csb->csb_rpt[streamList[i]].csb_stream;
}
node->nod_arg[e_derived_expr_stream_list] = (jrd_nod*) streamList;
node->nod_arg[e_derived_expr_stream_count] = (jrd_nod*)(IPTR) streamCount;
node->nod_arg[e_derived_expr_expr] = PAR_parse_node(tdbb, csb, sub_type);
node->nod_count = e_derived_expr_count;
break;
}
case blr_gen_id:
case blr_set_generator:
{
Firebird::MetaName name;
PAR_name(csb, name);
const SLONG tmp = MET_lookup_generator(tdbb, name.c_str());
if (tmp < 0) {
error(csb, Arg::Gds(isc_gennotdef) << Arg::Str(name));
}
node->nod_arg[e_gen_relation] = (jrd_nod*) (IPTR) tmp;
node->nod_arg[e_gen_value] = PAR_parse_node(tdbb, csb, VALUE);
// CVC: There're thousand ways to go wrong, but I don't see any value
// in posting dependencies with set generator since it's DDL, so I will
// track only gen_id() in both dialects.
if ((blr_operator == blr_gen_id) && (csb->csb_g_flags & csb_get_dependencies))
{
jrd_nod* dep_node = PAR_make_node (tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg [e_dep_object] = (jrd_nod*) (IPTR) tmp;
dep_node->nod_arg [e_dep_object_type] = (jrd_nod*)(IPTR) obj_generator;
csb->csb_dependencies.push(dep_node);
}
}
break;
case blr_record_version:
case blr_dbkey:
n = csb->csb_blr_reader.getByte();
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
error(csb, Arg::Gds(isc_ctxnotdef));
node->nod_arg[0] = (jrd_nod*) (IPTR) csb->csb_rpt[n].csb_stream;
break;
case blr_fetch:
par_fetch(tdbb, csb, node);
break;
case blr_receive:
n = csb->csb_blr_reader.getByte();
node->nod_arg[e_send_message] = csb->csb_rpt[n].csb_message;
node->nod_arg[e_send_statement] = PAR_parse_node(tdbb, csb, sub_type);
break;
case blr_message:
node = par_message(tdbb, csb);
break;
case blr_literal:
node = par_literal(tdbb, csb);
break;
case blr_cast:
node = par_cast(tdbb, csb);
break;
case blr_extract:
// This forced conversion looks strange, but extract_part fits in a byte
node->nod_arg[e_extract_part] = (jrd_nod*)(U_IPTR) csb->csb_blr_reader.getByte();
node->nod_arg[e_extract_value] = PAR_parse_node(tdbb, csb, sub_type);
node->nod_count = e_extract_count;
break;
case blr_strlen:
// This forced conversion looks strange, but length_type fits in a byte
node->nod_arg[e_strlen_type] = (jrd_nod*)(U_IPTR) csb->csb_blr_reader.getByte();
node->nod_arg[e_strlen_value] = PAR_parse_node(tdbb, csb, sub_type);
node->nod_count = e_strlen_count;
break;
case blr_dcl_variable:
{
dsc* desc = (dsc*) (node->nod_arg + e_dcl_desc);
ItemInfo itemInfo;
n = csb->csb_blr_reader.getWord();
node->nod_arg[e_dcl_id] = (jrd_nod*) (IPTR) n;
PAR_desc(tdbb, csb, desc, &itemInfo);
csb->csb_variables =
vec<jrd_nod*>::newVector(*tdbb->getDefaultPool(), csb->csb_variables, n + 1);
if (itemInfo.isSpecial())
{
csb->csb_dbg_info.varIndexToName.get(n, itemInfo.name);
csb->csb_map_item_info.put(Item(nod_variable, n), itemInfo);
}
if (itemInfo.explicitCollation)
{
jrd_nod* dep_node = PAR_make_node (tdbb, e_dep_length);
dep_node->nod_type = nod_dependency;
dep_node->nod_arg [e_dep_object] = (jrd_nod*)(IPTR) INTL_TEXT_TYPE(*desc);
dep_node->nod_arg [e_dep_object_type] = (jrd_nod*)(IPTR) obj_collation;
csb->csb_dependencies.push(dep_node);
}
}
break;
case blr_variable:
{
n = csb->csb_blr_reader.getWord();
node->nod_arg[e_var_id] = (jrd_nod*)(IPTR) n;
vec<jrd_nod*>* vector = csb->csb_variables;
if (!vector || n >= vector->count())
PAR_syntax_error(csb, "variable identifier");
}
break;
case blr_parameter:
case blr_parameter2:
case blr_parameter3:
{
jrd_nod* message = NULL;
n = (USHORT) csb->csb_blr_reader.getByte();
if (n >= csb->csb_rpt.getCount() || !(message = csb->csb_rpt[n].csb_message))
{
error(csb, Arg::Gds(isc_badmsgnum));
}
node->nod_arg[e_arg_message] = message;
n = csb->csb_blr_reader.getWord();
node->nod_arg[e_arg_number] = (jrd_nod*) (IPTR) n;
const Format* format = (Format*) message->nod_arg[e_msg_format];
if (n >= format->fmt_count)
error(csb, Arg::Gds(isc_badparnum));
if (blr_operator != blr_parameter)
{
jrd_nod* temp = PAR_make_node(tdbb, e_arg_length);
node->nod_arg[e_arg_flag] = temp;
node->nod_count = 1;
temp->nod_count = 0;
temp->nod_type = nod_argument;
temp->nod_arg[e_arg_message] = message;
n = csb->csb_blr_reader.getWord();
temp->nod_arg[e_arg_number] = (jrd_nod*) (IPTR) n;
if (n >= format->fmt_count)
error(csb, Arg::Gds(isc_badparnum));
}
if (blr_operator == blr_parameter3)
{
jrd_nod* temp = PAR_make_node(tdbb, e_arg_length);
node->nod_arg[e_arg_indicator] = temp;
node->nod_count = 2;
temp->nod_count = 0;
temp->nod_type = nod_argument;
temp->nod_arg[e_arg_message] = message;
n = csb->csb_blr_reader.getWord();
temp->nod_arg[e_arg_number] = (jrd_nod*) (IPTR) n;
if (n >= format->fmt_count)
error(csb, Arg::Gds(isc_badparnum));
}
}
break;
case blr_stall:
break;
case blr_select:
case blr_begin:
{
NodeStack stack;
while (csb->csb_blr_reader.peekByte() != (UCHAR) blr_end)
{
if (blr_operator == blr_select && csb->csb_blr_reader.peekByte() != blr_receive)
PAR_syntax_error(csb, "blr_receive");
stack.push(PAR_parse_node(tdbb, csb, sub_type));
}
csb->csb_blr_reader.getByte(); // skip blr_end
node = PAR_make_list(tdbb, stack);
}
break;
case blr_block:
{
NodeStack stack;
node->nod_arg[e_blk_action] = PAR_parse_node(tdbb, csb, sub_type);
while (csb->csb_blr_reader.peekByte() != (UCHAR) blr_end)
{
stack.push(PAR_parse_node(tdbb, csb, sub_type));
}
csb->csb_blr_reader.getByte(); // skip blr_end
node->nod_arg[e_blk_handlers] = PAR_make_list(tdbb, stack);
}
break;
case blr_error_handler:
node->nod_arg[e_err_conditions] = (jrd_nod*) par_conditions(tdbb, csb);
node->nod_arg[e_err_action] = PAR_parse_node(tdbb, csb, sub_type);
break;
case blr_abort:
{
const bool flag = (csb->csb_blr_reader.peekByte() == blr_exception_msg);
node->nod_arg[e_xcp_desc] = (jrd_nod*) par_condition(tdbb, csb);
if (flag)
{
node->nod_arg[e_xcp_msg] = PAR_parse_node(tdbb, csb, sub_type);
}
break;
}
case blr_label:
node->nod_arg[e_lbl_label] = (jrd_nod*) (IPTR) csb->csb_blr_reader.getByte();
node->nod_arg[e_lbl_statement] = PAR_parse_node(tdbb, csb, sub_type);
break;
case blr_leave:
case blr_continue_loop:
node->nod_arg[0] = (jrd_nod*)(IPTR) csb->csb_blr_reader.getByte();
break;
case blr_maximum:
case blr_minimum:
case blr_count:
case blr_average:
case blr_total:
case blr_from:
case blr_via:
node->nod_arg[e_stat_rse] = PAR_parse_node(tdbb, csb, TYPE_RSE);
if (blr_operator != blr_count)
node->nod_arg[e_stat_value] = PAR_parse_node(tdbb, csb, VALUE);
if (blr_operator == blr_via)
node->nod_arg[e_stat_default] = PAR_parse_node(tdbb, csb, VALUE);
break;
case blr_init_variable:
{
n = csb->csb_blr_reader.getWord();
node->nod_arg[e_init_var_id] = (jrd_nod*)(U_IPTR) n;
vec<jrd_nod*>* vector = csb->csb_variables;
if (!vector || n >= vector->count())
PAR_syntax_error(csb, "variable identifier");
}
break;
case blr_sys_function:
node = par_sys_function(tdbb, csb);
break;
case blr_stmt_expr:
node->nod_arg[e_stmt_expr_stmt] = PAR_parse_node(tdbb, csb, STATEMENT);
node->nod_arg[e_stmt_expr_expr] = PAR_parse_node(tdbb, csb, VALUE);
break;
default:
if ((nod_t)(USHORT) blr_table[(int) blr_operator] == nod_class_node_jrd)
{
fb_assert(blr_parsers[blr_operator]);
node->nod_arg[0] = reinterpret_cast<jrd_nod*>(
blr_parsers[blr_operator](tdbb, *tdbb->getDefaultPool(), csb, blr_operator));
}
else
PAR_syntax_error(csb, elements[expected]);
}
if (set_type)
{
if (csb->csb_g_flags & csb_blr_version4)
node->nod_type = (nod_t)(USHORT) blr_table4[(int) blr_operator];
else
node->nod_type = (nod_t)(USHORT) blr_table[(int) blr_operator];
}
size_t pos = 0;
if (csb->csb_dbg_info.blrToSrc.find(blr_offset, pos))
{
Firebird::MapBlrToSrcItem& i = csb->csb_dbg_info.blrToSrc[pos];
jrd_nod* node_src = PAR_make_node(tdbb, e_src_info_length);
node_src->nod_type = nod_src_info;
node_src->nod_arg[e_src_info_line] = (jrd_nod*) (IPTR) i.mbs_src_line;
node_src->nod_arg[e_src_info_col] = (jrd_nod*) (IPTR) i.mbs_src_col;
node_src->nod_arg[e_src_info_node] = node;
return node_src;
}
return node;
}
void PAR_syntax_error(CompilerScratch* csb, const TEXT* string)
{
/**************************************
*
* P A R _ s y n t a x _ e r r o r
*
**************************************
*
* Functional description
* Post a syntax error message.
*
**************************************/
csb->csb_blr_reader.seekBackward(1);
error(csb, Arg::Gds(isc_syntaxerr) << Arg::Str(string) <<
Arg::Num(csb->csb_blr_reader.getOffset()) <<
Arg::Num(csb->csb_blr_reader.peekByte()));
}
static void warning(const Arg::StatusVector& v)
{
/**************************************
*
* w a r n i n g
*
**************************************
*
* Functional description
* This is for GBAK so that we can pass warning messages
* back to the client. DO NOT USE this function until we
* fully implement warning at the engine level.
*
* We will use the status vector like a warning vector. What
* we are going to do is leave the [1] position of the vector
* as 0 so that this will not be treated as an error, and we
* will place our warning message in the consecutive positions.
* It will be up to the caller to check these positions for
* the message.
*
**************************************/
fb_assert(v.value()[0] == isc_arg_warning);
thread_db* tdbb = JRD_get_thread_data();
// Make sure that the [1] position is 0 indicating that no error has occurred
Arg::Gds p(FB_SUCCESS);
// Now place your warning messages
p.append(v);
// Save into tdbb
p.copyTo(tdbb->tdbb_status_vector);
ERR_make_permanent(tdbb->tdbb_status_vector);
}