mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 23:23:04 +01:00
3052 lines
78 KiB
C++
3052 lines
78 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
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include <string.h>
|
|
#include "../jrd/common.h"
|
|
#include <stdarg.h>
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/y_ref.h"
|
|
#include "../jrd/ibase.h"
|
|
#include "../jrd/val.h"
|
|
#include "../jrd/align.h"
|
|
#include "../jrd/exe.h"
|
|
#include "../jrd/lls.h"
|
|
#include "../jrd/rse.h" // for MAX_STREAMS
|
|
|
|
#include "../jrd/scl.h"
|
|
#include "../jrd/all.h"
|
|
#include "../jrd/req.h"
|
|
#include "../jrd/blb.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../jrd/met.h"
|
|
#include "../jrd/all_proto.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 "../jrd/thd_proto.h"
|
|
#include "../common/utils_proto.h"
|
|
|
|
|
|
/* blr type classes */
|
|
|
|
#define OTHER 0
|
|
#define STATEMENT 1
|
|
#define BOOL 2
|
|
#define VALUE 3
|
|
#define TYPE_RSE 4
|
|
#define RELATION 5
|
|
#define ACCESS_TYPE 6
|
|
|
|
#include "gen/blrtable.h"
|
|
|
|
|
|
|
|
static const TEXT elements[][10] =
|
|
{ "", "statement", "boolean", "value", "RSE", "TABLE" };
|
|
|
|
#include "gen/codetext.h"
|
|
|
|
static void error(Csb*, ...);
|
|
static SSHORT find_proc_field(const jrd_prc*, const TEXT*);
|
|
static jrd_nod* par_args(thread_db*, Csb*, USHORT);
|
|
static jrd_nod* par_cast(thread_db*, Csb*);
|
|
static XCP par_condition(thread_db*, Csb*);
|
|
static XCP par_conditions(thread_db*, Csb*);
|
|
static SSHORT par_context(Csb*, SSHORT *);
|
|
static void par_dependency(thread_db*, Csb*, SSHORT, SSHORT, TEXT *);
|
|
static jrd_nod* par_exec_proc(thread_db*, Csb*, SSHORT);
|
|
static jrd_nod* par_fetch(thread_db*, Csb*, jrd_nod*);
|
|
static jrd_nod* par_field(thread_db*, Csb*, SSHORT);
|
|
static jrd_nod* par_function(thread_db*, Csb*);
|
|
static jrd_nod* par_literal(thread_db*, Csb*);
|
|
static jrd_nod* par_map(thread_db*, Csb*, USHORT);
|
|
static jrd_nod* par_message(thread_db*, Csb*);
|
|
static jrd_nod* par_modify(thread_db*, Csb*);
|
|
static USHORT par_name(Csb*, TEXT*);
|
|
static jrd_nod* par_plan(thread_db*, Csb*);
|
|
static jrd_nod* par_procedure(thread_db*, Csb*, SSHORT);
|
|
static void par_procedure_parms(thread_db*, Csb*, jrd_prc*, jrd_nod**, jrd_nod**, USHORT);
|
|
static jrd_nod* par_relation(thread_db*, Csb*, SSHORT, BOOLEAN);
|
|
static jrd_nod* par_rse(thread_db*, Csb*, SSHORT);
|
|
static jrd_nod* par_sort(thread_db*, Csb*, BOOLEAN);
|
|
static jrd_nod* par_stream(thread_db*, Csb*);
|
|
static jrd_nod* par_union(thread_db*, Csb*);
|
|
static USHORT par_word(Csb*);
|
|
static jrd_nod* parse(thread_db*, Csb*, USHORT, USHORT expected_optional = 0);
|
|
static void syntax_error(Csb*, const TEXT *);
|
|
static void warning(Csb*, ...);
|
|
|
|
#define BLR_PEEK *(csb->csb_running)
|
|
#define BLR_BYTE *(csb->csb_running)++
|
|
#define BLR_PUSH (csb->csb_running)--
|
|
#define BLR_WORD par_word (csb)
|
|
|
|
|
|
jrd_nod* PAR_blr(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
const UCHAR* blr,
|
|
Csb* view_csb,
|
|
Csb** 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.
|
|
gds__print_blr(blr, gds__trace_printer, 0, 0);
|
|
#endif
|
|
|
|
Csb* csb;
|
|
if (!(csb_ptr && (csb = *csb_ptr))) {
|
|
size_t count = 5;
|
|
if (view_csb)
|
|
count += view_csb->csb_rpt.getCapacity();
|
|
csb = Csb::newCsb(*tdbb->tdbb_default, 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();
|
|
Csb::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) {
|
|
Csb::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_running = csb->csb_blr = blr;
|
|
|
|
if (view_csb) {
|
|
Csb::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 Csb::rpt_const_itr end = view_csb->csb_rpt.end();
|
|
for (SSHORT stream = 0; ptr != end; ++ptr, ++stream) {
|
|
Csb::csb_repeat* t2 = CMP_csb_element(csb, stream);
|
|
t2->csb_relation = ptr->csb_relation;
|
|
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_running++;
|
|
|
|
if (version != blr_version4 && version != blr_version5) {
|
|
error(csb, isc_metadata_corrupt,
|
|
isc_arg_gds, isc_wroblrver,
|
|
isc_arg_number, (SLONG) blr_version4,
|
|
isc_arg_number, (SLONG) version, 0);
|
|
}
|
|
|
|
if (version == blr_version4)
|
|
csb->csb_g_flags |= csb_blr_version4;
|
|
|
|
jrd_nod* node = parse(tdbb, csb, OTHER);
|
|
csb->csb_node = node;
|
|
|
|
if (*csb->csb_running++ != (UCHAR) blr_eoc)
|
|
syntax_error(csb, "end_of_command");
|
|
|
|
if (request_ptr)
|
|
*request_ptr = CMP_make_request(tdbb, csb);
|
|
|
|
if (csb_ptr)
|
|
*csb_ptr = csb;
|
|
else
|
|
delete csb;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
USHORT PAR_desc(Csb* csb, DSC* desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P A R _ d e s c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a BLR descriptor. Return the alignment requirements
|
|
* of the datatype.
|
|
*
|
|
**************************************/
|
|
desc->dsc_scale = 0;
|
|
desc->dsc_sub_type = 0;
|
|
desc->dsc_address = NULL;
|
|
desc->dsc_flags = 0;
|
|
|
|
const USHORT dtype = BLR_BYTE;
|
|
switch (dtype) {
|
|
case blr_text:
|
|
desc->dsc_dtype = dtype_text;
|
|
desc->dsc_flags |= DSC_no_subtype;
|
|
desc->dsc_length = BLR_WORD;
|
|
INTL_ASSIGN_TTYPE(desc, ttype_dynamic);
|
|
break;
|
|
|
|
case blr_cstring:
|
|
desc->dsc_dtype = dtype_cstring;
|
|
desc->dsc_flags |= DSC_no_subtype;
|
|
desc->dsc_length = BLR_WORD;
|
|
INTL_ASSIGN_TTYPE(desc, ttype_dynamic);
|
|
break;
|
|
|
|
case blr_varying:
|
|
desc->dsc_dtype = dtype_varying;
|
|
desc->dsc_flags |= DSC_no_subtype;
|
|
desc->dsc_length = BLR_WORD + sizeof(USHORT);
|
|
INTL_ASSIGN_TTYPE(desc, ttype_dynamic);
|
|
break;
|
|
|
|
case blr_text2:
|
|
desc->dsc_dtype = dtype_text;
|
|
INTL_ASSIGN_TTYPE(desc, BLR_WORD);
|
|
desc->dsc_length = BLR_WORD;
|
|
break;
|
|
|
|
case blr_cstring2:
|
|
desc->dsc_dtype = dtype_cstring;
|
|
INTL_ASSIGN_TTYPE(desc, BLR_WORD);
|
|
desc->dsc_length = BLR_WORD;
|
|
break;
|
|
|
|
case blr_varying2:
|
|
desc->dsc_dtype = dtype_varying;
|
|
INTL_ASSIGN_TTYPE(desc, BLR_WORD);
|
|
desc->dsc_length = BLR_WORD + sizeof(USHORT);
|
|
break;
|
|
|
|
case blr_short:
|
|
desc->dsc_dtype = dtype_short;
|
|
desc->dsc_length = sizeof(SSHORT);
|
|
desc->dsc_scale = (int) BLR_BYTE;
|
|
break;
|
|
|
|
case blr_long:
|
|
desc->dsc_dtype = dtype_long;
|
|
desc->dsc_length = sizeof(SLONG);
|
|
desc->dsc_scale = (int) BLR_BYTE;
|
|
break;
|
|
|
|
case blr_int64:
|
|
desc->dsc_dtype = dtype_int64;
|
|
desc->dsc_length = sizeof(SINT64);
|
|
desc->dsc_scale = (int) BLR_BYTE;
|
|
break;
|
|
|
|
case blr_quad:
|
|
desc->dsc_dtype = dtype_quad;
|
|
desc->dsc_length = sizeof(ISC_QUAD);
|
|
desc->dsc_scale = (int) BLR_BYTE;
|
|
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:
|
|
#ifndef VMS
|
|
case blr_d_float:
|
|
#endif
|
|
desc->dsc_dtype = dtype_double;
|
|
desc->dsc_length = sizeof(double);
|
|
break;
|
|
|
|
#ifdef VMS
|
|
case blr_d_float:
|
|
desc->dsc_dtype = dtype_d_float;
|
|
desc->dsc_length = sizeof(double);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
if (dtype == blr_blob) {
|
|
desc->dsc_dtype = dtype_blob;
|
|
desc->dsc_length = sizeof(ISC_QUAD);
|
|
break;
|
|
}
|
|
error(csb, isc_datnotsup, 0);
|
|
}
|
|
|
|
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->tdbb_default, 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, Csb* csb, USHORT context,
|
|
const TEXT* 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. */
|
|
TEXT name[32];
|
|
strcpy (name, base_field);
|
|
fb_utils::fb_exact_name(name);
|
|
|
|
const SSHORT id =
|
|
MET_lookup_field (tdbb, csb->csb_rpt [stream].csb_relation, name, 0);
|
|
|
|
if (id < 0)
|
|
return NULL;
|
|
|
|
jrd_rel* temp_rel = csb->csb_rpt[stream].csb_relation;
|
|
|
|
/* 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 deffered 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 */
|
|
if (!temp_rel->rel_fields) {
|
|
ERR_post(isc_depend_on_uncommitted_rel, 0);
|
|
}
|
|
|
|
jrd_nod* temp_node = PAR_gen_field(tdbb, stream, id);
|
|
jrd_fld* field = (jrd_fld*) (*temp_rel->rel_fields)[id];
|
|
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, LLS 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 = 0;
|
|
for (const lls* temp = stack; temp; count++) {
|
|
temp = temp->lls_next;
|
|
}
|
|
|
|
jrd_nod* node = PAR_make_node(tdbb, count);
|
|
node->nod_type = nod_list;
|
|
jrd_nod** ptr = node->nod_arg + count;
|
|
|
|
while (stack)
|
|
*--ptr = (jrd_nod*) LLS_POP(&stack);
|
|
|
|
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->tdbb_default, size) jrd_nod();
|
|
node->nod_count = size;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
Csb* PAR_parse(thread_db* tdbb, const UCHAR* blr, USHORT internal_flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P A R _ p a r s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse blr, returning a compiler scratch block with the results.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
Csb* csb = Csb::newCsb(*tdbb->tdbb_default, 5);
|
|
csb->csb_running = csb->csb_blr = blr;
|
|
const SSHORT version = *csb->csb_running++;
|
|
if (internal_flag)
|
|
csb->csb_g_flags |= csb_internal;
|
|
|
|
if (version != blr_version4 && version != blr_version5)
|
|
{
|
|
error(csb, isc_wroblrver,
|
|
isc_arg_number, (SLONG) blr_version4,
|
|
isc_arg_number, (SLONG) version, 0);
|
|
}
|
|
|
|
if (version == blr_version4)
|
|
{
|
|
csb->csb_g_flags |= csb_blr_version4;
|
|
}
|
|
|
|
jrd_nod* node = parse(tdbb, csb, OTHER);
|
|
csb->csb_node = node;
|
|
|
|
if (*csb->csb_running++ != (UCHAR) blr_eoc)
|
|
{
|
|
syntax_error(csb, "end_of_command");
|
|
}
|
|
|
|
return csb;
|
|
}
|
|
|
|
|
|
SLONG PAR_symbol_to_gdscode(const char* 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.
|
|
*
|
|
**************************************/
|
|
|
|
const char* p = name;
|
|
|
|
while (*p && *p != ' ') {
|
|
p++;
|
|
}
|
|
const size_t length = p - name;
|
|
for (int i = 0; codes[i].code_number; ++i) {
|
|
if (!strncmp(name, codes[i].code_string, length)) {
|
|
return codes[i].code_number;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void error(Csb* csb, ...)
|
|
{
|
|
/**************************************
|
|
*
|
|
* e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* We've got a blr error other than a syntax error. Handle it.
|
|
*
|
|
**************************************/
|
|
ISC_STATUS *p;
|
|
USHORT offset;
|
|
int type;
|
|
va_list args;
|
|
|
|
/* Don't bother to pass tdbb for error handling */
|
|
thread_db* tdbb = GET_THREAD_DATA;
|
|
|
|
VA_START(args, csb);
|
|
|
|
csb->csb_running--;
|
|
offset = csb->csb_running - csb->csb_blr;
|
|
p = tdbb->tdbb_status_vector;
|
|
*p++ = isc_arg_gds;
|
|
*p++ = isc_invalid_blr;
|
|
*p++ = isc_arg_number;
|
|
*p++ = offset;
|
|
|
|
*p++ = isc_arg_gds;
|
|
*p++ = va_arg(args, ISC_STATUS);
|
|
|
|
/* Pick up remaining args */
|
|
|
|
while ( (*p++ = type = va_arg(args, int)) )
|
|
{
|
|
switch (type) {
|
|
case isc_arg_gds:
|
|
*p++ = (ISC_STATUS) va_arg(args, ISC_STATUS);
|
|
break;
|
|
|
|
case isc_arg_string:
|
|
case isc_arg_interpreted:
|
|
*p++ = (ISC_STATUS) va_arg(args, TEXT *);
|
|
break;
|
|
|
|
case isc_arg_cstring:
|
|
*p++ = (ISC_STATUS) va_arg(args, int);
|
|
*p++ = (ISC_STATUS) va_arg(args, TEXT *);
|
|
break;
|
|
|
|
case isc_arg_number:
|
|
*p++ = (ISC_STATUS) va_arg(args, SLONG);
|
|
break;
|
|
|
|
default:
|
|
fb_assert(FALSE);
|
|
case isc_arg_vms:
|
|
case isc_arg_unix:
|
|
case isc_arg_win32:
|
|
*p++ = va_arg(args, int);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Give up whatever we were doing and return to the user. */
|
|
|
|
ERR_punt();
|
|
}
|
|
|
|
|
|
static SSHORT find_proc_field(const jrd_prc* procedure, const TEXT* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* f i n d _ p r o c _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look for named field in procedure output fields.
|
|
*
|
|
**************************************/
|
|
#pragma FB_COMPILER_MESSAGE("Vector's last element still unresolved")
|
|
// JMB: Is there a reason we are skipping the last element in the array?
|
|
// CVC: This should be const vec*, but then the compiler rejects the iterator!
|
|
vec* list = procedure->prc_output_fields;
|
|
vec::const_iterator ptr = list->begin();
|
|
for (const vec::const_iterator end = list->end() - 1; ptr < end;
|
|
ptr++)
|
|
{
|
|
const prm* param = (PRM) * ptr;
|
|
if (!strcmp(name, param->prm_name))
|
|
return param->prm_number;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_args(thread_db* tdbb, Csb* csb, USHORT expected)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ a r g s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a counted argument list.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
USHORT count = BLR_BYTE;
|
|
jrd_nod* node = PAR_make_node(tdbb, count);
|
|
node->nod_type = nod_list;
|
|
jrd_nod** ptr = node->nod_arg;
|
|
|
|
if (count) {
|
|
do {
|
|
*ptr++ = parse(tdbb, csb, expected);
|
|
} while (--count);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_cast(thread_db* tdbb, Csb* 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];
|
|
|
|
fmt* format = fmt::newFmt(*tdbb->tdbb_default, 1);
|
|
format->fmt_count = 1;
|
|
node->nod_arg[e_cast_fmt] = (jrd_nod*) format;
|
|
|
|
dsc* desc = &format->fmt_desc[0];
|
|
PAR_desc(csb, desc);
|
|
format->fmt_length = desc->dsc_length;
|
|
|
|
node->nod_arg[e_cast_source] = parse(tdbb, csb, VALUE);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static XCP par_condition(thread_db* tdbb, Csb* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ c o n d i t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse an error conditions list.
|
|
*
|
|
**************************************/
|
|
jrd_nod* dep_node;
|
|
TEXT name[32], *p;
|
|
SLONG code_number;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
/* allocate a node to represent the conditions list */
|
|
|
|
const USHORT code_type = BLR_BYTE;
|
|
|
|
/* don't create XCP if blr_raise is used,
|
|
just return NULL */
|
|
if (code_type == blr_raise)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
XCP exception_list = FB_NEW_RPT(*tdbb->tdbb_default, 1) xcp();
|
|
exception_list->xcp_count = 1;
|
|
|
|
switch (code_type) {
|
|
case blr_sql_code:
|
|
exception_list->xcp_rpt[0].xcp_type = xcp_sql_code;
|
|
exception_list->xcp_rpt[0].xcp_code = (SSHORT) BLR_WORD;
|
|
break;
|
|
|
|
case blr_gds_code:
|
|
exception_list->xcp_rpt[0].xcp_type = xcp_gds_code;
|
|
par_name(csb, name);
|
|
for (p = name; *p; *p++)
|
|
*p = LOWWER(*p);
|
|
code_number = PAR_symbol_to_gdscode(name);
|
|
if (code_number)
|
|
exception_list->xcp_rpt[0].xcp_code = code_number;
|
|
else
|
|
error(csb, isc_codnotdef, isc_arg_string, ERR_cstring(name), 0);
|
|
break;
|
|
|
|
case blr_exception:
|
|
case blr_exception_msg:
|
|
exception_list->xcp_rpt[0].xcp_type = xcp_xcp_code;
|
|
par_name(csb, name);
|
|
if (!(exception_list->xcp_rpt[0].xcp_code =
|
|
MET_lookup_exception_number(tdbb, name)))
|
|
error(csb, isc_xcpnotdef, isc_arg_string, ERR_cstring(name), 0);
|
|
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) exception_list->xcp_rpt[0].xcp_code;
|
|
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*) obj_exception;
|
|
LLS_PUSH(dep_node, &csb->csb_dependencies);
|
|
break;
|
|
|
|
default:
|
|
fb_assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
return exception_list;
|
|
}
|
|
|
|
|
|
static XCP par_conditions(thread_db* tdbb, Csb* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ c o n d i t i o n s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse an error conditions list.
|
|
*
|
|
**************************************/
|
|
jrd_nod* dep_node;
|
|
TEXT name[32], *p;
|
|
SLONG code_number;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
/* allocate a node to represent the conditions list */
|
|
|
|
const USHORT n = BLR_WORD;
|
|
XCP exception_list = FB_NEW_RPT(*tdbb->tdbb_default, n) xcp();
|
|
exception_list->xcp_count = n;
|
|
for (int i = 0; i < n; i++) {
|
|
const USHORT code_type = BLR_BYTE;
|
|
switch (code_type) {
|
|
case blr_sql_code:
|
|
exception_list->xcp_rpt[i].xcp_type = xcp_sql_code;
|
|
exception_list->xcp_rpt[i].xcp_code = (SSHORT) BLR_WORD;
|
|
break;
|
|
|
|
case blr_gds_code:
|
|
exception_list->xcp_rpt[i].xcp_type = xcp_gds_code;
|
|
par_name(csb, name);
|
|
for (p = name; *p; *p++)
|
|
*p = LOWWER(*p);
|
|
code_number = PAR_symbol_to_gdscode(name);
|
|
if (code_number)
|
|
exception_list->xcp_rpt[i].xcp_code = code_number;
|
|
else
|
|
error(csb, isc_codnotdef,
|
|
isc_arg_string, ERR_cstring(name), 0);
|
|
break;
|
|
|
|
case blr_exception:
|
|
exception_list->xcp_rpt[i].xcp_type = xcp_xcp_code;
|
|
par_name(csb, name);
|
|
if (!(exception_list->xcp_rpt[i].xcp_code =
|
|
MET_lookup_exception_number(tdbb, name)))
|
|
error(csb, isc_xcpnotdef,
|
|
isc_arg_string, ERR_cstring(name), 0);
|
|
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)exception_list->xcp_rpt[0].xcp_code;
|
|
dep_node->nod_arg[e_dep_object_type] = (jrd_nod*) obj_exception;
|
|
LLS_PUSH(dep_node, &csb->csb_dependencies);
|
|
break;
|
|
|
|
case blr_default_code:
|
|
exception_list->xcp_rpt[i].xcp_type = xcp_default;
|
|
exception_list->xcp_rpt[i].xcp_code = 0;
|
|
break;
|
|
|
|
default:
|
|
fb_assert(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return exception_list;
|
|
}
|
|
|
|
|
|
static SSHORT par_context(Csb* 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 stream = csb->nextStream(false);
|
|
if (stream > MAX_STREAMS)
|
|
{
|
|
error(csb, isc_too_many_contexts, 0);
|
|
}
|
|
const SSHORT context = (unsigned int) BLR_BYTE;
|
|
CMP_csb_element(csb, stream);
|
|
Csb::csb_repeat* tail = CMP_csb_element(csb, context);
|
|
|
|
if (tail->csb_flags & csb_used)
|
|
error(csb, isc_ctxinuse, 0);
|
|
|
|
tail->csb_flags |= csb_used;
|
|
tail->csb_stream = (UCHAR) stream;
|
|
|
|
if (context_ptr)
|
|
*context_ptr = context;
|
|
|
|
return stream;
|
|
}
|
|
|
|
|
|
static void par_dependency(thread_db* tdbb,
|
|
Csb* csb,
|
|
SSHORT stream,
|
|
SSHORT id,
|
|
TEXT* 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;
|
|
node->nod_arg[e_dep_object_type] = (jrd_nod*) 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*) obj_procedure;
|
|
}
|
|
|
|
if (field_name) {
|
|
jrd_nod* field_node = PAR_make_node(tdbb, 1);
|
|
node->nod_arg[e_dep_field] = field_node;
|
|
field_node->nod_type = nod_literal;
|
|
const int length = strlen(field_name);
|
|
str* string = FB_NEW_RPT(*tdbb->tdbb_default, length) str();
|
|
string->str_length = length;
|
|
strcpy(reinterpret_cast<char*>(string->str_data), field_name);
|
|
field_node->nod_arg[0] = (jrd_nod*) string->str_data;
|
|
}
|
|
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;
|
|
}
|
|
|
|
LLS_PUSH(node, &csb->csb_dependencies);
|
|
}
|
|
|
|
|
|
static jrd_nod* par_exec_proc(thread_db* tdbb, Csb* 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;
|
|
{
|
|
TEXT name[32];
|
|
|
|
if (blr_operator == blr_exec_pid) {
|
|
const USHORT pid = BLR_WORD;
|
|
if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
|
|
sprintf(name, "id %d", pid);
|
|
}
|
|
else {
|
|
par_name(csb, name);
|
|
procedure = MET_lookup_procedure(tdbb, name, false);
|
|
}
|
|
if (!procedure)
|
|
error(csb, isc_prcnotdef, isc_arg_string, ERR_cstring(name), 0);
|
|
}
|
|
|
|
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*) obj_procedure;
|
|
|
|
LLS_PUSH(dep_node, &csb->csb_dependencies);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_fetch(thread_db* tdbb, Csb* 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 RSE */
|
|
|
|
for_node->nod_arg[e_for_re] = PAR_make_node(tdbb, 1 + rse_delta + 2);
|
|
RSE rse = (RSE) for_node->nod_arg[e_for_re];
|
|
rse->nod_type = nod_rse;
|
|
rse->nod_count = 0;
|
|
rse->rse_count = 1;
|
|
jrd_nod* relation = parse(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] = parse(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] = parse(tdbb, csb, STATEMENT);
|
|
|
|
return for_node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_field(thread_db* tdbb, Csb* csb, SSHORT blr_operator)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a field.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
const SSHORT context = (unsigned int) BLR_BYTE;
|
|
|
|
if (context >= csb->csb_rpt.getCount() ||
|
|
!(csb->csb_rpt[context].csb_flags & csb_used) )
|
|
{
|
|
error(csb, isc_ctxnotdef, 0);
|
|
}
|
|
|
|
SqlIdentifier name;
|
|
name[0] = 0;
|
|
SSHORT id;
|
|
const SSHORT stream = csb->csb_rpt[context].csb_stream;
|
|
SSHORT flags = 0;
|
|
bool is_column = false;
|
|
if (blr_operator == blr_fid) {
|
|
id = BLR_WORD;
|
|
flags = nod_id;
|
|
is_column = true;
|
|
}
|
|
else if (blr_operator == blr_field) {
|
|
Csb::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,
|
|
isc_fldnotdef,
|
|
isc_arg_string, ERR_cstring(name),
|
|
isc_arg_string, procedure->prc_name->str_data, 0);
|
|
}
|
|
else {
|
|
jrd_rel* relation = tail->csb_relation;
|
|
if (!relation)
|
|
error(csb, isc_ctxnotdef, 0);
|
|
|
|
/* 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, 0)) < 0) {
|
|
if (csb->csb_g_flags & csb_validation) {
|
|
id = 0;
|
|
flags |= nod_id;
|
|
is_column = true;
|
|
}
|
|
else {
|
|
if (tdbb->
|
|
tdbb_attachment->att_flags & ATT_gbak_attachment)
|
|
warning(csb, isc_fldnotdef, isc_arg_string,
|
|
ERR_cstring(name), isc_arg_string,
|
|
relation->rel_name, 0);
|
|
else if (relation->rel_name)
|
|
error(csb, isc_fldnotdef, isc_arg_string,
|
|
ERR_cstring(name), isc_arg_string,
|
|
relation->rel_name, 0);
|
|
else
|
|
error(csb, isc_ctxnotdef, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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, 0);
|
|
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 = (jrd_fld*) (*temp_rel->rel_fields)[id];
|
|
if (field) {
|
|
if (field->fld_default_value && field->fld_not_null)
|
|
node->nod_arg[e_fld_default_value] =
|
|
field->fld_default_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_function(thread_db* tdbb, Csb* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ f u n c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a function reference.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
SqlIdentifier name;
|
|
const USHORT count = par_name(csb, name);
|
|
|
|
fun* function = FUN_lookup_function(name,
|
|
!(tdbb->tdbb_attachment->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;
|
|
}
|
|
else {
|
|
csb->csb_running -= count;
|
|
error(csb, isc_funnotdef, isc_arg_string, ERR_cstring(name), 0);
|
|
}
|
|
}
|
|
|
|
fun* homonyms;
|
|
for (homonyms = function; homonyms; homonyms = homonyms->fun_homonym) {
|
|
if (homonyms->fun_entrypoint)
|
|
break;
|
|
}
|
|
|
|
if (!homonyms)
|
|
if (tdbb->tdbb_attachment->att_flags & ATT_gbak_attachment)
|
|
warning(csb, isc_funnotdef,
|
|
isc_arg_string, ERR_cstring(name),
|
|
isc_arg_interpreted,
|
|
"module name or entrypoint could not be found", 0);
|
|
else {
|
|
csb->csb_running -= count;
|
|
error(csb, isc_funnotdef,
|
|
isc_arg_string, ERR_cstring(name),
|
|
isc_arg_interpreted,
|
|
"module name or entrypoint could not be found", 0);
|
|
}
|
|
|
|
jrd_nod* node = PAR_make_node(tdbb, e_fun_length);
|
|
node->nod_count = 1;
|
|
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*) obj_udf;
|
|
LLS_PUSH (dep_node, &csb->csb_dependencies);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_literal(thread_db* tdbb, Csb* 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(csb, &desc);
|
|
const SSHORT 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_running;
|
|
SSHORT 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 = (SLONG) gds__vax_integer(q, l);
|
|
break;
|
|
|
|
case dtype_timestamp:
|
|
l = 8;
|
|
*(SLONG *) p = (SLONG) gds__vax_integer(q, 4);
|
|
p += 4;
|
|
q += 4;
|
|
*(SLONG *) p = (SLONG) gds__vax_integer(q, 4);
|
|
break;
|
|
|
|
case dtype_int64:
|
|
l = sizeof(SINT64);
|
|
*(SINT64 *) p = (SINT64) 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 = BLR_WORD;
|
|
q = csb->csb_running;
|
|
dtype =
|
|
CVT_get_numeric(q, l, &scale, (double *) p, ERR_post);
|
|
literal->lit_desc.dsc_dtype = dtype;
|
|
if (dtype == dtype_double)
|
|
literal->lit_desc.dsc_length = sizeof(double);
|
|
else if (dtype == dtype_long) {
|
|
literal->lit_desc.dsc_length = sizeof(SLONG);
|
|
literal->lit_desc.dsc_scale = (SCHAR) scale;
|
|
}
|
|
else {
|
|
literal->lit_desc.dsc_length = sizeof(SINT64);
|
|
literal->lit_desc.dsc_scale = (SCHAR) scale;
|
|
}
|
|
break;
|
|
|
|
case dtype_text:
|
|
{
|
|
SSHORT ct = l;
|
|
if (ct) {
|
|
do {
|
|
*p++ = *q++;
|
|
} while (--ct);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fb_assert(FALSE);
|
|
}
|
|
|
|
csb->csb_running += l;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_map(thread_db* tdbb, Csb* 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 (BLR_BYTE != blr_map)
|
|
syntax_error(csb, "blr_map");
|
|
|
|
SSHORT count = BLR_WORD;
|
|
lls* map = NULL;
|
|
|
|
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, BLR_WORD);
|
|
assignment->nod_arg[e_asgn_from] = parse(tdbb, csb, VALUE);
|
|
LLS_PUSH(assignment, &map);
|
|
}
|
|
|
|
jrd_nod* node = PAR_make_list(tdbb, map);
|
|
node->nod_type = nod_map;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_message(thread_db* tdbb, Csb* 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 compile scratch block, and
|
|
allocate a node to represent the message */
|
|
|
|
USHORT n = (unsigned int) BLR_BYTE;
|
|
Csb::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 = BLR_WORD;
|
|
fmt* format = fmt::newFmt(*tdbb->tdbb_default, n);
|
|
node->nod_arg[e_msg_format] = (jrd_nod*) format;
|
|
format->fmt_count = n;
|
|
ULONG offset = 0;
|
|
|
|
fmt::fmt_desc_iterator desc, end;
|
|
for (desc = format->fmt_desc.begin(), end = desc + n; desc < end; desc++) {
|
|
const USHORT alignment = PAR_desc(csb, &*desc);
|
|
if (alignment)
|
|
offset = FB_ALIGN(offset, alignment);
|
|
desc->dsc_address = (UCHAR *) (IPTR) offset;
|
|
offset += desc->dsc_length;
|
|
}
|
|
|
|
if (offset > MAX_FORMAT_SIZE)
|
|
error(csb, isc_imp_exc, isc_arg_gds, isc_blktoobig, 0);
|
|
|
|
format->fmt_length = (USHORT) offset;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_modify(thread_db* tdbb, Csb* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ m o d i f y
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a modify statement.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
/* Parse the original and new contexts */
|
|
|
|
SSHORT context = (unsigned int) BLR_BYTE;
|
|
if (context >= csb->csb_rpt.getCount() ||
|
|
!(csb->csb_rpt[context].csb_flags & csb_used))
|
|
{
|
|
error(csb, isc_ctxnotdef, 0);
|
|
}
|
|
const SSHORT org_stream = csb->csb_rpt[context].csb_stream;
|
|
const SSHORT new_stream = csb->nextStream(false);
|
|
if (new_stream > MAX_STREAMS)
|
|
{
|
|
error(csb, isc_too_many_contexts, 0);
|
|
}
|
|
context = (unsigned int) BLR_BYTE;
|
|
|
|
/* Make sure the compiler scratch block is big enough to hold
|
|
everything */
|
|
|
|
Csb::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] = parse(tdbb, csb, STATEMENT);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static USHORT par_name(Csb* csb, TEXT* string)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a counted string, returning count.
|
|
*
|
|
**************************************/
|
|
USHORT l = BLR_BYTE;
|
|
const USHORT count = l;
|
|
|
|
if (count) {
|
|
do {
|
|
*string++ = BLR_BYTE;
|
|
} while (--l);
|
|
}
|
|
|
|
*string = 0;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_plan(thread_db* tdbb, Csb* 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) BLR_BYTE;
|
|
|
|
/* a join type indicates a cross of two or more streams */
|
|
|
|
if (node_type == blr_join || node_type == blr_merge) {
|
|
USHORT count = (USHORT) BLR_BYTE;
|
|
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) {
|
|
TEXT name[32];
|
|
|
|
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 */
|
|
|
|
SSHORT n = BLR_BYTE;
|
|
if (n != blr_relation && n != blr_relation2 &&
|
|
n != blr_rid && n != blr_rid2)
|
|
{
|
|
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 = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
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) BLR_BYTE;
|
|
USHORT extra_count = 0;
|
|
jrd_nod* access_type = 0;
|
|
|
|
switch (node_type) {
|
|
case blr_navigational:
|
|
{
|
|
access_type = plan->nod_arg[e_retrieve_access_type] =
|
|
PAR_make_node(tdbb, 3);
|
|
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->tdbb_attachment->att_flags & ATT_gbak_attachment)
|
|
warning(csb, isc_indexname, isc_arg_string,
|
|
ERR_cstring(name), isc_arg_string,
|
|
relation->rel_name, 0);
|
|
else
|
|
error(csb, isc_indexname, isc_arg_string,
|
|
ERR_cstring(name), isc_arg_string,
|
|
relation->rel_name, 0);
|
|
}
|
|
|
|
/* 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[0] = (jrd_nod*) (IPTR) relation_id;
|
|
access_type->nod_arg[1] = (jrd_nod*) (IPTR) index_id;
|
|
access_type->nod_arg[2] = (jrd_nod*) ALL_cstring(name);
|
|
|
|
if (BLR_PEEK == blr_indices)
|
|
// dimitr: FALL INTO, if the plan item is ORDER ... INDEX (...)
|
|
extra_count = 3;
|
|
else
|
|
break;
|
|
}
|
|
case blr_indices:
|
|
{
|
|
if (extra_count)
|
|
BLR_BYTE; // skip blr_indices
|
|
USHORT count = (USHORT) BLR_BYTE;
|
|
jrd_nod* temp = plan->nod_arg[e_retrieve_access_type] =
|
|
PAR_make_node(tdbb, count * 3 + 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->tdbb_attachment->att_flags & ATT_gbak_attachment)
|
|
warning(csb, isc_indexname, isc_arg_string,
|
|
ERR_cstring(name), isc_arg_string,
|
|
relation->rel_name, 0);
|
|
else
|
|
error(csb, isc_indexname, isc_arg_string,
|
|
ERR_cstring(name), isc_arg_string,
|
|
relation->rel_name, 0);
|
|
}
|
|
|
|
/* 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;
|
|
*arg++ = (jrd_nod*) ALL_cstring(name);
|
|
}
|
|
break;
|
|
}
|
|
case blr_sequential:
|
|
break;
|
|
default:
|
|
syntax_error(csb, "access type");
|
|
}
|
|
|
|
return plan;
|
|
}
|
|
|
|
syntax_error(csb, "plan item");
|
|
return NULL; /* Added to remove compiler warning */
|
|
}
|
|
|
|
|
|
static jrd_nod* par_procedure(thread_db* tdbb, Csb* csb, SSHORT blr_operator)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse an procedural view reference.
|
|
*
|
|
**************************************/
|
|
jrd_prc* procedure;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
{
|
|
TEXT name[32];
|
|
|
|
if (blr_operator == blr_procedure) {
|
|
par_name(csb, name);
|
|
procedure = MET_lookup_procedure(tdbb, name, false);
|
|
}
|
|
else {
|
|
const SSHORT pid = BLR_WORD;
|
|
if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
|
|
sprintf(name, "id %d", pid);
|
|
}
|
|
if (!procedure)
|
|
error(csb, isc_prcnotdef, isc_arg_string, ERR_cstring(name), 0);
|
|
}
|
|
|
|
jrd_nod* 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;
|
|
|
|
const USHORT stream = par_context(csb, 0);
|
|
node->nod_arg[e_prc_stream] = (jrd_nod*) (IPTR) stream;
|
|
csb->csb_rpt[stream].csb_procedure = procedure;
|
|
|
|
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, 0);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static void par_procedure_parms(
|
|
thread_db* tdbb,
|
|
Csb* csb,
|
|
jrd_prc* procedure,
|
|
jrd_nod** message_ptr,
|
|
jrd_nod** parameter_ptr, USHORT 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;
|
|
USHORT count = BLR_WORD;
|
|
|
|
/** 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,
|
|
isc_prcmismat,
|
|
isc_arg_string,
|
|
ERR_cstring(reinterpret_cast<const char*>
|
|
(procedure->prc_name->str_data)),
|
|
0);
|
|
}
|
|
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;
|
|
Csb::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 fmt* 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 fmt 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;
|
|
*/
|
|
fmt* fmt_copy = fmt::newFmt(*tdbb->tdbb_default, 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) {
|
|
prm* parameter = (prm*)(*procedure->prc_input_fields)
|
|
[procedure->prc_inputs - n];
|
|
|
|
asgn->nod_arg[asgn_arg1] = parameter->prm_default_val;
|
|
}
|
|
else {
|
|
asgn->nod_arg[asgn_arg1] = parse(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,
|
|
isc_prcmismat,
|
|
isc_arg_string,
|
|
ERR_cstring(reinterpret_cast<const char*>(procedure->prc_name->str_data)),
|
|
0);
|
|
}
|
|
}
|
|
|
|
|
|
static jrd_nod* par_relation(
|
|
thread_db* tdbb,
|
|
Csb* csb, SSHORT blr_operator, BOOLEAN parse_context)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a relation reference.
|
|
*
|
|
**************************************/
|
|
TEXT name[32];
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
/* Make a relation reference node */
|
|
|
|
jrd_nod* node = PAR_make_node(tdbb, e_rel_length);
|
|
node->nod_count = 0;
|
|
|
|
/* Find relation either by id or by name */
|
|
str* alias_string = NULL;
|
|
jrd_rel* relation = 0;
|
|
if (blr_operator == blr_rid || blr_operator == blr_rid2) {
|
|
const SSHORT id = BLR_WORD;
|
|
if (blr_operator == blr_rid2) {
|
|
const SSHORT length = BLR_PEEK;
|
|
alias_string = FB_NEW_RPT(*tdbb->tdbb_default, length + 1) str();
|
|
alias_string->str_length = length;
|
|
par_name(csb, reinterpret_cast<char*>(alias_string->str_data));
|
|
}
|
|
if (!(relation = MET_lookup_relation_id(tdbb, id, false))) {
|
|
sprintf(name, "id %d", id);
|
|
error(csb, isc_relnotdef, isc_arg_string, ERR_cstring(name), 0);
|
|
}
|
|
}
|
|
else if (blr_operator == blr_relation || blr_operator == blr_relation2) {
|
|
par_name(csb, name);
|
|
if (blr_operator == blr_relation2) {
|
|
const SSHORT length = BLR_PEEK;
|
|
alias_string = FB_NEW_RPT(*tdbb->tdbb_default, length + 1) str();
|
|
alias_string->str_length = length;
|
|
par_name(csb, reinterpret_cast<char*>(alias_string->str_data));
|
|
}
|
|
if (!(relation = MET_lookup_relation(tdbb, name)))
|
|
error(csb, isc_relnotdef, isc_arg_string, ERR_cstring(name), 0);
|
|
}
|
|
|
|
/* if an alias was passed, store with the relation */
|
|
|
|
if (alias_string)
|
|
node->nod_arg[e_rel_alias] = (jrd_nod*) 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, 0);
|
|
}
|
|
|
|
node->nod_arg[e_rel_relation] = (jrd_nod*) relation;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_rse(thread_db* tdbb, Csb* csb, SSHORT rse_op)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ r s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a record selection expression.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
SSHORT count = (unsigned int) BLR_BYTE;
|
|
RSE rse = (RSE) 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++ = parse(tdbb, csb, RELATION, TYPE_RSE);
|
|
//*ptr++ = parse(tdbb, csb, RELATION);
|
|
}
|
|
|
|
while (true) {
|
|
const UCHAR op = BLR_BYTE;
|
|
switch (op) {
|
|
case blr_boolean:
|
|
rse->rse_boolean = parse(tdbb, csb, BOOL);
|
|
break;
|
|
|
|
case blr_first:
|
|
if (rse_op == blr_rs_stream)
|
|
syntax_error(csb, "rse stream clause");
|
|
rse->rse_first = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_skip:
|
|
if (rse_op == blr_rs_stream)
|
|
syntax_error (csb, "rse stream clause");
|
|
rse->rse_skip = parse (tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_sort:
|
|
if (rse_op == blr_rs_stream)
|
|
syntax_error(csb, "rse stream clause");
|
|
rse->rse_sorted = par_sort(tdbb, csb, TRUE);
|
|
break;
|
|
|
|
case blr_project:
|
|
if (rse_op == blr_rs_stream)
|
|
syntax_error(csb, "rse stream clause");
|
|
rse->rse_projection = par_sort(tdbb, csb, FALSE);
|
|
break;
|
|
|
|
case blr_join_type:
|
|
{
|
|
const USHORT jointype = (USHORT) BLR_BYTE;
|
|
rse->rse_jointype = jointype;
|
|
if (jointype != blr_inner
|
|
&& jointype != blr_left && jointype != blr_right
|
|
&& jointype != blr_full)
|
|
{
|
|
syntax_error(csb, "join type clause");
|
|
}
|
|
break;
|
|
}
|
|
|
|
case blr_plan:
|
|
rse->rse_plan = par_plan(tdbb, csb);
|
|
break;
|
|
|
|
case blr_writelock:
|
|
rse->rse_writelock = true;
|
|
break;
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
/* if a receive is seen here, then it is intended to be an asynchronous
|
|
receive which can happen at any time during the scope of the rse--
|
|
this is intended to be a more efficient mechanism for scrolling through
|
|
a record stream, to prevent having to send a message to the engine
|
|
for each record */
|
|
|
|
case blr_receive:
|
|
BLR_PUSH;
|
|
rse->rse_async_message = parse(tdbb, csb, STATEMENT);
|
|
break;
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
syntax_error(csb, (TEXT*)((rse_op == blr_rs_stream) ?
|
|
"rse stream clause" :
|
|
"record selection expression clause"));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static jrd_nod* par_sort(thread_db* tdbb, Csb* csb, BOOLEAN 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) BLR_BYTE;
|
|
jrd_nod* clause = PAR_make_node(tdbb, count * 3);
|
|
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 = BLR_BYTE;
|
|
switch (code) {
|
|
case blr_nullsfirst:
|
|
*ptr3++ = (jrd_nod*) (IPTR) rse_nulls_first;
|
|
code = BLR_BYTE;
|
|
break;
|
|
case blr_nullslast:
|
|
*ptr3++ = (jrd_nod*) (IPTR) rse_nulls_last;
|
|
code = BLR_BYTE;
|
|
break;
|
|
default:
|
|
*ptr3++ = (jrd_nod*) (IPTR) rse_nulls_default;
|
|
}
|
|
|
|
*ptr2++ =
|
|
(jrd_nod*) (IPTR) ((code == blr_descending) ? TRUE : FALSE);
|
|
}
|
|
*ptr++ = parse(tdbb, csb, VALUE);
|
|
}
|
|
|
|
return clause;
|
|
}
|
|
|
|
|
|
static jrd_nod* par_stream(thread_db* tdbb, Csb* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ s t r e a m
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a stream expression.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
RSE rse = (RSE) PAR_make_node(tdbb, 1 + rse_delta + 2);
|
|
rse->nod_count = 0;
|
|
rse->rse_count = 1;
|
|
rse->rse_relation[0] = parse(tdbb, csb, RELATION);
|
|
|
|
while (true) {
|
|
const UCHAR op = BLR_BYTE;
|
|
switch (op) {
|
|
case blr_boolean:
|
|
rse->rse_boolean = parse(tdbb, csb, BOOL);
|
|
break;
|
|
|
|
default:
|
|
if (op == (UCHAR) blr_end)
|
|
return (jrd_nod*) rse;
|
|
syntax_error(csb, "stream_clause");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static jrd_nod* par_union(thread_db* tdbb, Csb* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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-rse's. */
|
|
|
|
jrd_nod* node = PAR_make_node(tdbb, e_uni_length);
|
|
node->nod_count = 2;
|
|
const USHORT stream = par_context(csb, 0);
|
|
node->nod_arg[e_uni_stream] = (jrd_nod*) (IPTR) stream;
|
|
SSHORT count = (unsigned int) BLR_BYTE;
|
|
|
|
/* Pick up the sub-rse's and maps */
|
|
|
|
lls* clauses = NULL;
|
|
|
|
while (--count >= 0) {
|
|
LLS_PUSH(parse(tdbb, csb, TYPE_RSE), &clauses);
|
|
LLS_PUSH(par_map(tdbb, csb, stream), &clauses);
|
|
}
|
|
|
|
node->nod_arg[e_uni_clauses] = PAR_make_list(tdbb, clauses);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static USHORT par_word(Csb* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ w o r d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up a BLR word.
|
|
*
|
|
**************************************/
|
|
const UCHAR low = BLR_BYTE;
|
|
const UCHAR high = BLR_BYTE;
|
|
|
|
return high * 256 + low;
|
|
}
|
|
|
|
|
|
static jrd_nod* parse(thread_db* tdbb, Csb* csb, USHORT expected, USHORT expected_optional)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a BLR expression.
|
|
*
|
|
**************************************/
|
|
TEXT name[32];
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
const SSHORT blr_operator = BLR_BYTE;
|
|
|
|
if (blr_operator < 0 || blr_operator >= FB_NELEM(type_table)) {
|
|
syntax_error(csb, "invalid BLR code");
|
|
}
|
|
|
|
const SSHORT sub_type = sub_type_table[blr_operator];
|
|
|
|
if (expected && (expected != type_table[blr_operator])) {
|
|
if (expected_optional) {
|
|
if (expected_optional != type_table[blr_operator]) {
|
|
syntax_error(csb, elements[expected]);
|
|
}
|
|
}
|
|
else {
|
|
syntax_error(csb, elements[expected]);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
};
|
|
|
|
/* 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] = parse(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++ = parse(tdbb, csb, sub_type);
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
*arg++ = parse(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++ = parse(tdbb, csb, sub_type);
|
|
/* Fall into ... */
|
|
|
|
case blr_handler:
|
|
case blr_loop:
|
|
|
|
case blr_lock_state:
|
|
case blr_upcase:
|
|
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_post:
|
|
case blr_internal_info:
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_exec_sql:
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_exec_into:
|
|
n = BLR_WORD + 2 /*e_exec_into_count - 1*/ ;
|
|
node = PAR_make_node(tdbb, n);
|
|
arg = node->nod_arg;
|
|
*arg++ = parse(tdbb, csb, VALUE);
|
|
if (BLR_BYTE) // singleton
|
|
*arg++ = 0;
|
|
else
|
|
*arg++ = parse(tdbb, csb, STATEMENT);
|
|
for (n = 2/*e_exec_into_list*/; n < node->nod_count; n++)
|
|
*arg++ = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_post_arg:
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_null:
|
|
case blr_agg_count:
|
|
case blr_user_name:
|
|
case blr_current_role:
|
|
case blr_current_date:
|
|
case blr_current_time:
|
|
case blr_current_timestamp:
|
|
case blr_start_savepoint:
|
|
case blr_end_savepoint:
|
|
break;
|
|
|
|
case blr_user_savepoint:
|
|
*arg++ = (jrd_nod*) (IPTR) BLR_BYTE;
|
|
par_name(csb, name);
|
|
*arg++ = (jrd_nod*) ALL_cstring(name);
|
|
break;
|
|
|
|
case blr_store:
|
|
case blr_store2:
|
|
node->nod_arg[e_sto_relation] = parse(tdbb, csb, RELATION);
|
|
node->nod_arg[e_sto_statement] = parse(tdbb, csb, sub_type);
|
|
if (blr_operator == blr_store2)
|
|
node->nod_arg[e_sto_statement2] = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
/* Comparison operators */
|
|
|
|
case blr_between:
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
|
|
case blr_eql:
|
|
case blr_neq:
|
|
case blr_geq:
|
|
case blr_gtr:
|
|
case blr_leq:
|
|
case blr_lss:
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
*arg++ = parse(tdbb, csb, sub_type);
|
|
node->nod_flags = nod_comparison;
|
|
break;
|
|
|
|
case blr_erase:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_erase_stream] =
|
|
(jrd_nod*) (IPTR) csb->csb_rpt[n].csb_stream;
|
|
break;
|
|
|
|
case blr_modify:
|
|
node = par_modify(tdbb, csb);
|
|
break;
|
|
|
|
case blr_exec_proc:
|
|
case blr_exec_pid:
|
|
node = par_exec_proc(tdbb, csb, blr_operator);
|
|
break;
|
|
|
|
case blr_pid:
|
|
case blr_procedure:
|
|
node = par_procedure(tdbb, csb, blr_operator);
|
|
break;
|
|
|
|
case blr_function:
|
|
node = par_function(tdbb, csb);
|
|
break;
|
|
|
|
case blr_index:
|
|
node->nod_arg[0] = parse(tdbb, csb, sub_type);
|
|
node->nod_arg[1] = par_args(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_for:
|
|
if (BLR_PEEK == (UCHAR) blr_stall)
|
|
node->nod_arg[e_for_stall] = parse(tdbb, csb, STATEMENT);
|
|
|
|
if (BLR_PEEK == (UCHAR) blr_rse ||
|
|
BLR_PEEK == (UCHAR) blr_singular ||
|
|
BLR_PEEK == (UCHAR) blr_stream)
|
|
node->nod_arg[e_for_re] = parse(tdbb, csb, TYPE_RSE);
|
|
else
|
|
node->nod_arg[e_for_re] = par_rse(tdbb, csb, blr_operator);
|
|
node->nod_arg[e_for_statement] = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_dcl_cursor:
|
|
node->nod_arg[e_dcl_cursor_number] = (jrd_nod*) (IPTR) BLR_WORD;
|
|
node->nod_arg[e_dcl_cursor_rse] = parse(tdbb, csb, TYPE_RSE);
|
|
break;
|
|
|
|
case blr_cursor_stmt:
|
|
n = BLR_BYTE;
|
|
node->nod_arg[e_cursor_stmt_op] = (jrd_nod*) (IPTR) n;
|
|
node->nod_arg[e_cursor_stmt_number] = (jrd_nod*) (IPTR) BLR_WORD;
|
|
switch (n) {
|
|
case blr_cursor_open:
|
|
case blr_cursor_close:
|
|
break;
|
|
case blr_cursor_fetch:
|
|
#ifdef SCROLLABLE_CURSORS
|
|
if (BLR_PEEK == blr_seek)
|
|
node->nod_arg[e_cursor_stmt_seek] = parse(tdbb, csb, STATEMENT);
|
|
#endif
|
|
node->nod_arg[e_cursor_stmt_into] = parse(tdbb, csb, STATEMENT);
|
|
break;
|
|
default:
|
|
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 = parse(tdbb, csb, TYPE_RSE);
|
|
((RSE) node)->nod_flags |= rse_singular;
|
|
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);
|
|
break;
|
|
|
|
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] = parse(tdbb, csb, TYPE_RSE);
|
|
node->nod_arg[e_agg_group] = parse(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_field:
|
|
case blr_fid:
|
|
node = par_field(tdbb, csb, blr_operator);
|
|
break;
|
|
|
|
case blr_gen_id:
|
|
case blr_set_generator:
|
|
{
|
|
TEXT name[32];
|
|
|
|
par_name(csb, name);
|
|
const SLONG tmp = MET_lookup_generator(tdbb, name);
|
|
if (tmp < 0) {
|
|
error(csb, isc_gennotdef,
|
|
isc_arg_string, ERR_cstring(name), 0);
|
|
}
|
|
node->nod_arg[e_gen_relation] = (jrd_nod*) (IPTR) tmp;
|
|
node->nod_arg[e_gen_value] = parse(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*) obj_generator;
|
|
LLS_PUSH (dep_node, &csb->csb_dependencies);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case blr_record_version:
|
|
case blr_dbkey:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
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_send:
|
|
case blr_receive:
|
|
n = BLR_BYTE;
|
|
node->nod_arg[e_send_message] = csb->csb_rpt[n].csb_message;
|
|
node->nod_arg[e_send_statement] = parse(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) BLR_BYTE;
|
|
node->nod_arg[e_extract_value] = parse(tdbb, csb, sub_type);
|
|
node->nod_count = e_extract_count;
|
|
break;
|
|
|
|
case blr_dcl_variable:
|
|
{
|
|
n = BLR_WORD;
|
|
node->nod_arg[e_dcl_id] = (jrd_nod*) (IPTR) n;
|
|
PAR_desc(csb, (DSC *) (node->nod_arg + e_dcl_desc));
|
|
vec* vector = csb->csb_variables =
|
|
vec::newVector(*tdbb->tdbb_default, csb->csb_variables, n + 1);
|
|
(*vector)[n] = (BLK) node;
|
|
}
|
|
break;
|
|
|
|
case blr_variable:
|
|
{
|
|
n = BLR_WORD;
|
|
node->nod_arg[e_var_id] = (jrd_nod*) (IPTR) n;
|
|
vec* vector = csb->csb_variables;
|
|
if (!vector || n >= vector->count() ||
|
|
!(node->nod_arg[e_var_variable] = (jrd_nod*) (*vector)[n]))
|
|
{
|
|
syntax_error(csb, "variable identifier");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case blr_parameter:
|
|
case blr_parameter2:
|
|
case blr_parameter3:
|
|
{
|
|
jrd_nod* message;
|
|
n = (USHORT) BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() ||
|
|
!(message = csb->csb_rpt[n].csb_message))
|
|
{
|
|
error(csb, isc_badmsgnum, 0);
|
|
}
|
|
node->nod_arg[e_arg_message] = message;
|
|
n = BLR_WORD;
|
|
node->nod_arg[e_arg_number] = (jrd_nod*) (IPTR) n;
|
|
const fmt* format = (fmt*) message->nod_arg[e_msg_format];
|
|
if (n >= format->fmt_count)
|
|
error(csb, isc_badparnum, 0);
|
|
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 = BLR_WORD;
|
|
temp->nod_arg[e_arg_number] = (jrd_nod*) (IPTR) n;
|
|
if (n >= format->fmt_count)
|
|
error(csb, isc_badparnum, 0);
|
|
}
|
|
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 = BLR_WORD;
|
|
temp->nod_arg[e_arg_number] = (jrd_nod*) (IPTR) n;
|
|
if (n >= format->fmt_count)
|
|
error(csb, isc_badparnum, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case blr_stall:
|
|
break;
|
|
|
|
case blr_select:
|
|
case blr_begin:
|
|
{
|
|
lls* stack = NULL;
|
|
|
|
while (BLR_PEEK != (UCHAR) blr_end) {
|
|
if (blr_operator == blr_select && BLR_PEEK != blr_receive)
|
|
syntax_error(csb, "blr_receive");
|
|
LLS_PUSH(parse(tdbb, csb, sub_type), &stack);
|
|
}
|
|
BLR_BYTE; // skip blr_end
|
|
node = PAR_make_list(tdbb, stack);
|
|
}
|
|
break;
|
|
|
|
case blr_block:
|
|
{
|
|
lls* stack = NULL;
|
|
|
|
node->nod_arg[e_blk_action] = parse(tdbb, csb, sub_type);
|
|
while (BLR_PEEK != (UCHAR) blr_end)
|
|
LLS_PUSH(parse(tdbb, csb, sub_type), &stack);
|
|
BLR_BYTE; // 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] = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_abort:
|
|
{
|
|
const bool flag = (BLR_PEEK == blr_exception_msg);
|
|
node->nod_arg[e_xcp_desc] = (jrd_nod*) par_condition(tdbb, csb);
|
|
if (flag)
|
|
{
|
|
node->nod_arg[e_xcp_msg] = parse(tdbb, csb, sub_type);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case blr_if:
|
|
node->nod_arg[e_if_boolean] = parse(tdbb, csb, BOOL);
|
|
node->nod_arg[e_if_true] = parse(tdbb, csb, sub_type);
|
|
if (BLR_PEEK == (UCHAR) blr_end) {
|
|
node->nod_count = 2;
|
|
BLR_BYTE; // skip blr_end
|
|
break;
|
|
}
|
|
node->nod_arg[e_if_false] = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_label:
|
|
node->nod_arg[e_lbl_label] = (jrd_nod*) (IPTR) BLR_BYTE;
|
|
node->nod_arg[e_lbl_statement] = parse(tdbb, csb, sub_type);
|
|
break;
|
|
|
|
case blr_leave:
|
|
node->nod_arg[0] = (jrd_nod*) (IPTR) BLR_BYTE;
|
|
break;
|
|
|
|
|
|
case blr_maximum:
|
|
case blr_minimum:
|
|
case blr_count:
|
|
/* count2
|
|
case blr_count2:
|
|
*/
|
|
case blr_average:
|
|
case blr_total:
|
|
case blr_from:
|
|
case blr_via:
|
|
if (BLR_PEEK == (UCHAR) blr_stream)
|
|
node->nod_arg[e_stat_rse] = parse(tdbb, csb, OTHER);
|
|
else
|
|
node->nod_arg[e_stat_rse] = parse(tdbb, csb, TYPE_RSE);
|
|
if (blr_operator != blr_count)
|
|
node->nod_arg[e_stat_value] = parse(tdbb, csb, VALUE);
|
|
if (blr_operator == blr_via)
|
|
node->nod_arg[e_stat_default] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
/* Client/Server Express Features */
|
|
|
|
case blr_stream:
|
|
#ifdef PROD_BUILD
|
|
error(csb, isc_cse_not_supported, 0);
|
|
#endif
|
|
node = par_stream(tdbb, csb);
|
|
break;
|
|
|
|
#ifdef PC_ENGINE
|
|
case blr_set_index:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_index_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
node->nod_arg[e_index_index] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_find:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_find_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
node->nod_arg[e_find_operator] = parse(tdbb, csb, VALUE);
|
|
node->nod_arg[e_find_direction] = parse(tdbb, csb, VALUE);
|
|
node->nod_arg[e_find_args] = par_args(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_find_dbkey:
|
|
case blr_find_dbkey_version:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_find_dbkey_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
node->nod_arg[e_find_dbkey_dbkey] = parse(tdbb, csb, VALUE);
|
|
|
|
if (blr_operator == blr_find_dbkey_version)
|
|
node->nod_arg[e_find_dbkey_version] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_get_bookmark:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_getmark_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
break;
|
|
|
|
case blr_set_bookmark:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_setmark_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
node->nod_arg[e_setmark_id] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_release_bookmark:
|
|
node->nod_arg[e_relmark_id] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_bookmark:
|
|
node->nod_arg[e_bookmark_id] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_force_crack:
|
|
case blr_crack:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[0] = (jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
break;
|
|
|
|
case blr_reset_stream:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_reset_from_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_reset_to_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
break;
|
|
|
|
case blr_release_lock:
|
|
node->nod_arg[e_rellock_lock] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_lock_relation:
|
|
n = BLR_BYTE;
|
|
if (n != blr_relation && n != blr_relation2 &&
|
|
n != blr_rid && n != blr_rid2)
|
|
syntax_error(csb, elements[RELATION]);
|
|
node->nod_arg[e_lockrel_relation] = par_relation(tdbb, csb, n, FALSE);
|
|
node->nod_arg[e_lockrel_level] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_lock_record:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_lockrec_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
node->nod_arg[e_lockrec_level] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_begin_range:
|
|
node->nod_arg[e_brange_number] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_end_range:
|
|
node->nod_arg[e_erange_number] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_delete_range:
|
|
node->nod_arg[e_drange_number] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
|
|
case blr_range_relation:
|
|
node->nod_arg[e_range_relation_number] = parse(tdbb, csb, VALUE);
|
|
n = BLR_BYTE;
|
|
if (n != blr_relation && n != blr_relation2 &&
|
|
n != blr_rid && n != blr_rid2)
|
|
{
|
|
syntax_error(csb, elements[RELATION]);
|
|
}
|
|
node->nod_arg[e_range_relation_relation] =
|
|
par_relation(tdbb, csb, n, FALSE);
|
|
break;
|
|
|
|
case blr_release_locks:
|
|
case blr_delete_ranges:
|
|
node = PAR_make_node(tdbb, 0);
|
|
node->nod_count = 0;
|
|
break;
|
|
|
|
case blr_cardinality:
|
|
n = BLR_BYTE;
|
|
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
|
|
error(csb, isc_ctxnotdef, 0);
|
|
node->nod_arg[e_card_stream] =
|
|
(jrd_nod*) (SLONG) csb->csb_rpt[n].csb_stream;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
case blr_seek:
|
|
case blr_seek_no_warn:
|
|
node->nod_arg[e_seek_direction] = parse(tdbb, csb, VALUE);
|
|
node->nod_arg[e_seek_offset] = parse(tdbb, csb, VALUE);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
syntax_error(csb, elements[expected]);
|
|
}
|
|
|
|
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];
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static void syntax_error(Csb* csb, const TEXT* string)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s y n t a x _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Post a syntax error message.
|
|
*
|
|
**************************************/
|
|
|
|
error(csb, isc_syntaxerr,
|
|
isc_arg_string, string,
|
|
isc_arg_number, (SLONG) (csb->csb_running - csb->csb_blr - 1),
|
|
isc_arg_number, (SLONG) csb->csb_running[-1], 0);
|
|
}
|
|
|
|
|
|
static void warning(Csb* csb, ...)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
ISC_STATUS *p;
|
|
int type;
|
|
va_list args;
|
|
|
|
thread_db* tdbb = GET_THREAD_DATA;
|
|
|
|
VA_START(args, csb);
|
|
|
|
p = tdbb->tdbb_status_vector;
|
|
|
|
/* Make sure that the [1] position is 0
|
|
indicating that no error has occured */
|
|
|
|
*p++ = isc_arg_gds;
|
|
*p++ = 0;
|
|
|
|
/* Now place your warning messages starting
|
|
with position [2] */
|
|
|
|
*p++ = isc_arg_gds;
|
|
*p++ = va_arg(args, ISC_STATUS);
|
|
|
|
/* Pick up remaining args */
|
|
|
|
while ( (*p++ = type = va_arg(args, int)) )
|
|
{
|
|
switch (type) {
|
|
case isc_arg_gds:
|
|
*p++ = (ISC_STATUS) va_arg(args, ISC_STATUS);
|
|
break;
|
|
|
|
case isc_arg_string:
|
|
case isc_arg_interpreted:
|
|
*p++ = (ISC_STATUS) va_arg(args, TEXT *);
|
|
break;
|
|
|
|
case isc_arg_cstring:
|
|
*p++ = (ISC_STATUS) va_arg(args, int);
|
|
*p++ = (ISC_STATUS) va_arg(args, TEXT *);
|
|
break;
|
|
|
|
case isc_arg_number:
|
|
*p++ = (ISC_STATUS) va_arg(args, SLONG);
|
|
break;
|
|
|
|
default:
|
|
fb_assert(FALSE);
|
|
case isc_arg_vms:
|
|
case isc_arg_unix:
|
|
case isc_arg_win32:
|
|
*p++ = va_arg(args, int);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|