8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 08:43:03 +01:00
firebird-mirror/src/jrd/cmp.cpp

5440 lines
142 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: cmp.c
* DESCRIPTION: Request compiler
*
* 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): ______________________________________.
*
* 2001.07.28: John Bellardo: Added code to handle rse_skip.
* 2001.07.17 Claudio Valderrama: Stop crash when parsing user-supplied SQL plan.
* 2001.10.04 Claudio Valderrama: Fix annoying & invalid server complaint about
* triggers not having REFERENCES privilege over their owner table.
* 2002.02.24 Claudio Valderrama: substring() should signal output as string even
* if source is blob and should check implementation limits on field lengths.
* 2002.02.25 Claudio Valderrama: concatenate() should be a civilized function.
* This closes the heart of SF Bug #518282.
*/
/*
$Id: cmp.cpp,v 1.10 2002-09-19 16:02:56 skidder Exp $
*/
#include "firebird.h"
#include "../jrd/ibsetjmp.h"
#include <string.h>
#include <stdlib.h> /* abort */
#include "../jrd/common.h"
#include "../jrd/gds.h"
#include "../jrd/jrd.h"
#include "../jrd/req.h"
#include "../jrd/val.h"
#include "../jrd/align.h"
#include "../jrd/lls.h"
#include "../jrd/exe.h"
#include "../jrd/rse.h"
#include "../jrd/scl.h"
#include "../jrd/tra.h"
#include "../jrd/all.h"
#include "../jrd/lck.h"
#include "../jrd/irq.h"
#include "../jrd/drq.h"
#include "../jrd/license.h"
#include "../jrd/intl.h"
#include "../jrd/rng.h"
#include "../jrd/btr.h"
#include "../jrd/constants.h"
#include "../jrd/gdsassert.h"
#include "../jrd/all_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dsc_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/fun_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/idx_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/opt_proto.h"
#include "../jrd/par_proto.h"
#include "../jrd/rng_proto.h"
#include "../jrd/sbm_proto.h"
#include "../jrd/scl_proto.h"
#include "../jrd/thd_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/dsc_proto.h"
#include "../jrd/dbg_proto.h" /* DBG_supervisor */
/* Pick up relation ids */
#define RELATION(name,id,ods) id,
#define FIELD(symbol,name,id,update,ods,new_id,new_ods)
#define END_RELATION
typedef ENUM rids {
#include "../jrd/relations.h"
rel_MAX} RIDS;
#undef RELATION
#undef FIELD
#undef END_RELATION
/* InterBase provides transparent conversion from string to date in
* contexts where it makes sense. This macro checks a descriptor to
* see if it is something that *could* represent a date value
*/
#define COULD_BE_DATE(d) ((DTYPE_IS_DATE((d).dsc_dtype)) || ((d).dsc_dtype <= dtype_any_text))
/* One of d1,d2 is time, the other is date */
#define IS_DATE_AND_TIME(d1,d2) \
((((d1).dsc_dtype==dtype_sql_time)&&((d2).dsc_dtype==dtype_sql_date)) || \
(((d2).dsc_dtype==dtype_sql_time)&&((d1).dsc_dtype==dtype_sql_date)))
#define REQ_TAIL sizeof (((REQ) 0)->req_rpb[0])
#define MAP_LENGTH 256
/* RITTER - changed HP10 to HPUX */
#if defined (HPUX) && defined (SUPERSERVER)
#define MAX_RECURSION 96
#endif
#ifndef MAX_RECURSION
#define MAX_RECURSION 128
#endif
#if (defined PC_PLATFORM && !defined NETWARE_386)
#define MAX_REQUEST_SIZE 65534
#else
#define MAX_REQUEST_SIZE 262144
#endif
#ifdef SHLIB_DEFS
#undef access
#endif
static UCHAR *alloc_map(TDBB, CSB *, USHORT);
static NOD catenate_nodes(TDBB, LLS);
static NOD copy(TDBB, CSB *, NOD, UCHAR *, USHORT, USHORT);
static void expand_view_nodes(TDBB, CSB, USHORT, LLS *, NOD_T);
static void ignore_dbkey(TDBB, CSB, RSE, REL);
static NOD make_defaults(TDBB, CSB *, USHORT, NOD);
static NOD make_validation(TDBB, CSB *, USHORT);
static NOD pass1(TDBB, CSB *, NOD, REL, USHORT, BOOLEAN);
static void pass1_erase(TDBB, CSB *, NOD);
static NOD pass1_expand_view(TDBB, CSB, USHORT, USHORT, USHORT);
static void pass1_modify(TDBB, CSB *, NOD);
static RSE pass1_rse(TDBB, CSB *, RSE, REL, USHORT);
static void pass1_source(TDBB, CSB *, RSE, NOD, NOD *, LLS *, REL, USHORT);
static NOD pass1_store(TDBB, CSB *, NOD);
static NOD pass1_update(TDBB, CSB *, REL, TRIG_VEC, USHORT, USHORT, USHORT, REL,
USHORT);
static NOD pass2(TDBB, register CSB, register NOD, NOD);
static void pass2_rse(TDBB, CSB, RSE);
static NOD pass2_union(TDBB, CSB, NOD);
static void plan_check(CSB, RSE);
static void plan_set(CSB, RSE, NOD);
static void post_procedure_access(TDBB, CSB, PRC);
static RSB post_rse(TDBB, CSB, RSE);
static void post_trigger_access(TDBB, CSB, REL, TRIG_VEC, REL);
static void process_map(TDBB, CSB, NOD, FMT *);
static BOOLEAN stream_in_rse(USHORT, RSE);
static SSHORT strcmp_space(TEXT *, TEXT *);
#ifdef PC_ENGINE
static USHORT base_stream(CSB, NOD *, BOOLEAN);
#endif
int DLL_EXPORT CMP_clone_active(REQ request)
{
/**************************************
*
* C M P _ c l o n e _ a c t i v e
*
**************************************
*
* Functional description
* Determine if a request or any of its clones are active.
*
**************************************/
VEC vector;
vec::iterator sub_req, end;
DEV_BLKCHK(request, type_req);
if (request->req_flags & req_in_use)
return TRUE;
if ( (vector = request->req_sub_requests) )
for (sub_req = vector->begin(), end = vector->end();
sub_req < end; sub_req++)
if (*sub_req && ((REQ)(*sub_req))->req_flags & req_in_use)
return TRUE;
return FALSE;
}
NOD DLL_EXPORT CMP_clone_node(TDBB tdbb, CSB csb, NOD node)
{
/**************************************
*
* C M P _ c l o n e _ n o d e
*
**************************************
*
* Functional description
* Clone a value node for the optimizer. Make a copy of the node
* (if necessary) and assign impure space.
*
**************************************/
NOD clone;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(node, type_nod);
if (node->nod_type == nod_argument)
return node;
clone = copy(tdbb, &csb, node, NULL, 0, FALSE);
pass2(tdbb, csb, clone, 0);
return clone;
}
REQ DLL_EXPORT CMP_clone_request(TDBB tdbb,
REQ request, USHORT level, BOOLEAN validate)
{
/**************************************
*
* C M P _ c l o n e _ r e q u e s t
*
**************************************
*
* Functional description
* Get the incarnation of the request appropriate for a given level.
* If the incarnation doesn't exist, clone the request.
*
**************************************/
REQ clone;
VEC vector;
RPB *rpb1, *rpb2, *end;
USHORT n;
ACC access;
SCL class_;
PRC procedure;
TEXT *prc_sec_name;
DEV_BLKCHK(request, type_req);
SET_TDBB(tdbb);
/* Find the request if we've got it. */
if (!level)
return request;
if ((vector = request->req_sub_requests) &&
level < vector->count() &&
(clone = (REQ) (*vector)[level])) return clone;
/* We need to clone the request -- find someplace to put it */
if (validate) {
if ( (procedure = request->req_procedure) ) {
prc_sec_name = (procedure->prc_security_name ?
(TEXT *) procedure->
prc_security_name->str_data : (TEXT *) 0);
class_ = SCL_get_class(prc_sec_name);
SCL_check_access(class_, 0, 0,
0, SCL_execute, object_procedure,
reinterpret_cast <
char *>(procedure->prc_name->str_data));
}
for (access = request->req_access; access; access = access->acc_next) {
class_ = SCL_get_class(access->acc_security_name);
SCL_check_access(class_, access->acc_view, access->acc_trg_name,
access->acc_prc_name, access->acc_mask,
access->acc_type, access->acc_name);
}
}
if (!vector)
{
vector = request->req_sub_requests =
vec::newVector(*request->req_pool, level+1);
}
if (level >= vector->count())
vector->resize(level + 1);
/* Clone the request */
n =
(USHORT) ((request->req_impure_size - REQ_SIZE + REQ_TAIL - 1) /
REQ_TAIL);
clone = new(*request->req_pool, n) req;
(*vector)[level] = (BLK) clone;
clone->req_attachment = tdbb->tdbb_attachment;
clone->req_count = request->req_count;
clone->req_pool = request->req_pool;
clone->req_impure_size = request->req_impure_size;
clone->req_top_node = request->req_top_node;
clone->req_trg_name = request->req_trg_name;
clone->req_flags = request->req_flags & REQ_FLAGS_CLONE_MASK;
rpb1 = clone->req_rpb;
end = rpb1 + clone->req_count;
for (rpb2 = request->req_rpb; rpb1 < end; rpb1++, rpb2++) {
if (rpb2->rpb_stream_flags & RPB_s_update)
rpb1->rpb_stream_flags |= RPB_s_update;
rpb1->rpb_relation = rpb2->rpb_relation;
}
return clone;
}
REQ DLL_EXPORT CMP_compile(USHORT blr_length,
UCHAR * blr, USHORT internal_flag)
{
/**************************************
*
* C M P _ c o m p i l e
*
**************************************
*
* Functional description
* Compile a BLR request.
* Wrapper for CMP_compile2 - an API change
* was made for CMP_compile, but as calls to this
* are generated by gpre it's necessary to have a
* wrapper function to keep the build from breaking.
* This function can be removed after the next full
* product build is completed.
* 1997-Jan-20 David Schnepper
*
**************************************/
return CMP_compile2(GET_THREAD_DATA, blr, internal_flag);
}
REQ DLL_EXPORT CMP_compile2(TDBB tdbb, UCHAR* blr, USHORT internal_flag)
{
/**************************************
*
* C M P _ c o m p i l e 2
*
**************************************
*
* Functional description
* Compile a BLR request.
*
**************************************/
REQ request = 0;
ACC access;
SET_TDBB(tdbb);
JrdMemoryPool* old_pool = tdbb->tdbb_default;
JrdMemoryPool* new_pool = new(*tdbb->tdbb_database->dbb_permanent)
JrdMemoryPool;
tdbb->tdbb_default = new_pool;
try {
CSB csb = PAR_parse(tdbb, blr, internal_flag);
request = CMP_make_request(tdbb, &csb);
if (internal_flag) {
request->req_flags |= req_internal;
}
for (access = request->req_access; access; access = access->acc_next)
{
SCL class_ = SCL_get_class(access->acc_security_name);
SCL_check_access(class_, access->acc_view, access->acc_trg_name,
access->acc_prc_name, access->acc_mask,
access->acc_type, access->acc_name);
}
delete csb;
tdbb->tdbb_default = old_pool;
}
catch (...) {
tdbb->tdbb_default = old_pool;
if (request) {
CMP_release(tdbb, request);
} else if (new_pool) {
// TMN: Are we not to release the pool, just beqause
// we have a request?!
delete new_pool;
}
ERR_punt();
}
return request;
}
csb_repeat* DLL_EXPORT CMP_csb_element(CSB* csb, USHORT element)
{
/**************************************
*
* C M P _ c s b _ e l e m e n t
*
**************************************
*
* Functional description
* Find tail element of compile scratch block. If the csb isn't big
* enough, extend it.
*
**************************************/
DEV_BLKCHK(*csb, type_csb);
if (element >= (*csb)->csb_rpt.size()) {
(*csb)->csb_rpt.resize(element + 5);
(*csb)->csb_count = element + 5;
}
return &(*csb)->csb_rpt[element];
}
void DLL_EXPORT CMP_expunge_transaction(TRA transaction)
{
/**************************************
*
* C M P _ e x p u n g e _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Get rid of all references to a given transaction in existing
* requests.
*
**************************************/
VEC vector;
REQ request;
vec::iterator sub, end;
DEV_BLKCHK(transaction, type_tra);
for (request = transaction->tra_attachment->att_requests;
request; request = request->req_request) {
if (request->req_transaction == transaction)
request->req_transaction = NULL;
if ( (vector = request->req_sub_requests) )
for (sub = vector->begin(), end = vector->end();
sub < end; sub++)
if (*sub && ((REQ)(*sub))->req_transaction == transaction)
((REQ)(*sub))->req_transaction = NULL;
}
}
REQ DLL_EXPORT CMP_find_request(TDBB tdbb, USHORT id, USHORT which)
{
/**************************************
*
* C M P _ f i n d _ r e q u e s t
*
**************************************
*
* Functional description
* Find an inactive incarnation of a system request. If necessary,
* clone it.
*
**************************************/
DBB dbb;
REQ request, clone;
USHORT n;
SET_TDBB(tdbb);
dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
/* If the request hasn't been compiled or isn't active,
there're nothing to do */
THD_MUTEX_LOCK(dbb->dbb_mutexes + DBB_MUTX_cmp_clone);
if ((which == IRQ_REQUESTS && !(request = (REQ) REQUEST(id))) ||
(which == DYN_REQUESTS && !(request = (REQ) DYN_REQUEST(id))) ||
!(request->req_flags & (req_active | req_reserved))) {
if (request)
request->req_flags |= req_reserved;
THD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_cmp_clone);
return request;
}
/* Request exists and is in use. Look for clones until we find
one that is available */
for (n = 1;; n++) {
if (n > MAX_RECURSION) {
THD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_cmp_clone);
ERR_post(gds_no_meta_update,
gds_arg_gds, gds_req_depth_exceeded,
gds_arg_number, (SLONG) MAX_RECURSION, 0);
/* Msg363 "request depth exceeded. (Recursive definition?)" */
}
clone = CMP_clone_request(tdbb, request, n, FALSE);
if (!(clone->req_flags & (req_active | req_reserved))) {
clone->req_flags |= req_reserved;
THD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_cmp_clone);
return clone;
}
}
}
void DLL_EXPORT CMP_fini(TDBB tdbb)
{
/**************************************
*
* C M P _ f i n i
*
**************************************
*
* Functional description
* Get rid of resource locks during shutdown.
*
**************************************/
SET_TDBB(tdbb);
CMP_shutdown_database(tdbb);
}
FMT DLL_EXPORT CMP_format(TDBB tdbb, CSB csb, USHORT stream)
{
/**************************************
*
* C M P _ f o r m a t
*
**************************************
*
* Functional description
* Pick up a format for a stream.
*
**************************************/
csb_repeat *tail;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
tail = &csb->csb_rpt[stream];
if (tail->csb_format)
return tail->csb_format;
if (tail->csb_relation)
return tail->csb_format = MET_current(tdbb, tail->csb_relation);
else if (tail->csb_procedure)
return tail->csb_format = tail->csb_procedure->prc_format;
IBERROR(222); /* msg 222 bad blr -- invalid stream */
return ((FMT) NULL);
}
void DLL_EXPORT CMP_get_desc(
TDBB tdbb,
register CSB csb, register NOD node, DSC * desc)
{
/**************************************
*
* C M P _ g e t _ d e s c
*
**************************************
*
* Functional description
* Compute descriptor for value expression.
*
**************************************/
USHORT dtype, dtype1, dtype2;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(node, type_nod);
switch (node->nod_type) {
case nod_max:
case nod_min:
case nod_from:
CMP_get_desc(tdbb, csb, node->nod_arg[e_stat_value], desc);
return;
case nod_agg_total:
case nod_agg_total_distinct:
case nod_total:
if (node->nod_type == nod_total)
CMP_get_desc(tdbb, csb, node->nod_arg[e_stat_value], desc);
else
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
switch (dtype = desc->dsc_dtype) {
case dtype_short:
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(SLONG);
node->nod_scale = desc->dsc_scale;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_null:
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
node->nod_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_long:
case dtype_int64:
case dtype_real:
case dtype_double:
#ifdef VMS
case dtype_d_float:
#endif
case dtype_text:
case dtype_cstring:
case dtype_varying:
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
node->nod_flags |= nod_double;
return;
case dtype_quad:
desc->dsc_dtype = dtype_quad;
desc->dsc_length = sizeof(SQUAD);
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
node->nod_scale = desc->dsc_scale;
node->nod_flags |= nod_quad;
#ifdef NATIVE_QUAD
return;
#endif
default:
assert(FALSE);
/* FALLINTO */
case dtype_sql_time:
case dtype_sql_date:
case dtype_timestamp:
case dtype_blob:
case dtype_array:
/* break to error reporting code */
break;
}
break;
case nod_agg_total2:
case nod_agg_total_distinct2:
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
switch (dtype = desc->dsc_dtype) {
case dtype_short:
case dtype_long:
case dtype_int64:
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
node->nod_scale = desc->dsc_scale;
desc->dsc_flags = 0;
return;
case dtype_null:
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
node->nod_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_real:
case dtype_double:
#ifdef VMS
case dtype_d_float:
#endif
case dtype_text:
case dtype_cstring:
case dtype_varying:
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
node->nod_flags |= nod_double;
return;
case dtype_quad:
desc->dsc_dtype = dtype_quad;
desc->dsc_length = sizeof(SQUAD);
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
node->nod_scale = desc->dsc_scale;
node->nod_flags |= nod_quad;
#ifdef NATIVE_QUAD
return;
#endif
default:
assert(FALSE);
/* FALLINTO */
case dtype_sql_time:
case dtype_sql_date:
case dtype_timestamp:
case dtype_blob:
case dtype_array:
/* break to error reporting code */
break;
}
break;
case nod_prot_mask:
case nod_null:
case nod_agg_count:
case nod_agg_count2:
case nod_agg_count_distinct:
case nod_count2:
case nod_count:
case nod_gen_id:
case nod_lock_state:
#ifdef PC_ENGINE
case nod_lock_record:
case nod_lock_relation:
case nod_seek:
case nod_seek_no_warn:
case nod_crack:
#endif
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(SLONG);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
#ifdef PC_ENGINE
case nod_begin_range:
desc->dsc_dtype = dtype_text;
desc->dsc_ttype = ttype_ascii;
desc->dsc_scale = 0;
desc->dsc_length = RANGE_NAME_LENGTH;
desc->dsc_flags = 0;
return;
#endif
case nod_field:
{
FMT format;
USHORT id;
id = (USHORT) node->nod_arg[e_fld_id];
format =
CMP_format(tdbb, csb, (USHORT) node->nod_arg[e_fld_stream]);
if (id >= format->fmt_count) {
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else
*desc = format->fmt_desc[id];
return;
}
case nod_scalar:
{
NOD sub;
REL relation;
USHORT id;
FLD field;
ARR array;
sub = node->nod_arg[e_scl_field];
relation =
csb->csb_rpt[(USHORT) sub->
nod_arg[e_fld_stream]].csb_relation;
id = (USHORT) sub->nod_arg[e_fld_id];
field = MET_get_field(relation, id);
if (!field || !(array = field->fld_array))
IBERROR(223); /* msg 223 argument of scalar operation must be an array */
*desc = array->arr_desc.ads_rpt[0].ads_desc;
return;
}
case nod_divide:
{
DSC desc1, desc2;
CMP_get_desc(tdbb, csb, node->nod_arg[0], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &desc2);
/* For compatibility with older versions of the product, we accept
text types for division in blr_version4 (dialect <= 1) only. */
if (!(DTYPE_CAN_DIVIDE(desc1.dsc_dtype) ||
DTYPE_IS_TEXT(desc1.dsc_dtype))) {
if (desc1.dsc_dtype != dtype_null)
break; /* error, dtype not supported by arithmetic */
}
if (!(DTYPE_CAN_DIVIDE(desc2.dsc_dtype) ||
DTYPE_IS_TEXT(desc2.dsc_dtype))) {
if (desc2.dsc_dtype != dtype_null)
break; /* error, dtype not supported by arithmetic */
}
}
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case nod_agg_average:
case nod_agg_average_distinct:
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
/* FALL INTO */
case nod_average:
if (node->nod_type == nod_average)
CMP_get_desc(tdbb, csb, node->nod_arg[e_stat_value], desc);
if (!DTYPE_CAN_AVERAGE(desc->dsc_dtype)) {
if (desc->dsc_dtype != dtype_null)
break;
}
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
/* In 6.0, the AVERAGE of an exact numeric type is int64 with the
same scale. Only AVERAGE on an approximate numeric type can
return a double. */
case nod_agg_average2:
case nod_agg_average_distinct2:
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
/* In V6, the average of an exact type is computed in SINT64,
rather than double as in prior releases. */
switch (dtype = desc->dsc_dtype) {
case dtype_short:
case dtype_long:
case dtype_int64:
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
node->nod_scale = desc->dsc_scale;
return;
case dtype_null:
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
default:
if (!DTYPE_CAN_AVERAGE(desc->dsc_dtype))
break;
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
node->nod_flags |= nod_double;
return;
}
break;
case nod_add:
case nod_subtract:
{
DSC desc1, desc2;
CMP_get_desc(tdbb, csb, node->nod_arg[0], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &desc2);
/* 92/05/29 DAVES - don't understand why this is done for ONLY
dtype_text (eg: not dtype_cstring or dtype_varying) Doesn't
appear to hurt.
94/04/04 DAVES - NOW I understand it! QLI will pass floating
point values to the engine as text. All other numeric constants
it turns into either integers or longs (with scale).
*/
dtype1 = desc1.dsc_dtype;
if (dtype_int64 == dtype1)
dtype1 = dtype_double;
dtype2 = desc2.dsc_dtype;
if (dtype_int64 == dtype2)
dtype2 = dtype_double;
if ((dtype1 == dtype_text) || (dtype2 == dtype_text))
dtype = MAX(MAX(dtype1, dtype2), (UCHAR) DEFAULT_DOUBLE);
else
dtype = MAX(dtype1, dtype2);
switch (dtype) {
case dtype_short:
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(SLONG);
if (IS_DTYPE_ANY_TEXT(desc1.dsc_dtype) ||
IS_DTYPE_ANY_TEXT(desc2.dsc_dtype)) desc->dsc_scale = 0;
else
desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale);
node->nod_scale = desc->dsc_scale;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_sql_date:
case dtype_sql_time:
if (IS_DTYPE_ANY_TEXT(desc1.dsc_dtype) ||
IS_DTYPE_ANY_TEXT(desc2.dsc_dtype))
ERR_post(gds_expression_eval_err, 0);
/* FALL INTO */
case dtype_timestamp:
node->nod_flags |= nod_date;
assert(DTYPE_IS_DATE(desc1.dsc_dtype) ||
DTYPE_IS_DATE(desc2.dsc_dtype));
if (COULD_BE_DATE(desc1) && COULD_BE_DATE(desc2)) {
if (node->nod_type == nod_subtract) {
/* <any date> - <any date> */
/* Legal permutations are:
<timestamp> - <timestamp>
<timestamp> - <date>
<date> - <date>
<date> - <timestamp>
<time> - <time>
<timestamp> - <string>
<string> - <timestamp>
<string> - <string> */
if (IS_DTYPE_ANY_TEXT(dtype1))
dtype = dtype_timestamp;
else if (IS_DTYPE_ANY_TEXT(dtype2))
dtype = dtype_timestamp;
else if (dtype1 == dtype2)
dtype = dtype1;
else if ((dtype1 == dtype_timestamp) &&
(dtype2 == dtype_sql_date))
dtype = dtype_timestamp;
else if ((dtype2 == dtype_timestamp) &&
(dtype1 == dtype_sql_date))
dtype = dtype_timestamp;
else
ERR_post(gds_expression_eval_err, 0);
if (dtype == dtype_sql_date) {
desc->dsc_dtype = dtype_long;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else if (dtype == dtype_sql_time) {
desc->dsc_dtype = dtype_long;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale =
ISC_TIME_SECONDS_PRECISION_SCALE;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else {
assert(dtype == dtype_timestamp);
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
}
else if (IS_DATE_AND_TIME(desc1, desc2)) {
/* <date> + <time> */
/* <time> + <date> */
desc->dsc_dtype = dtype_timestamp;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else
/* <date> + <date> */
ERR_post(gds_expression_eval_err, 0);
}
else if (DTYPE_IS_DATE(desc1.dsc_dtype) ||
/* <date> +/- <non-date> */
(node->nod_type == nod_add))
/* <non-date> + <date> */
{
desc->dsc_dtype = desc1.dsc_dtype;
if (!DTYPE_IS_DATE(desc->dsc_dtype))
desc->dsc_dtype = desc2.dsc_dtype;
assert(DTYPE_IS_DATE(desc->dsc_dtype));
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else
/* <non-date> - <date> */
ERR_post(gds_expression_eval_err, 0);
return;
case dtype_text:
case dtype_cstring:
case dtype_varying:
case dtype_long:
case dtype_real:
case dtype_double:
#ifdef VMS
case dtype_d_float:
#endif
node->nod_flags |= nod_double;
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_null:
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_quad:
node->nod_flags |= nod_quad;
desc->dsc_dtype = dtype_quad;
desc->dsc_length = sizeof(SQUAD);
if (IS_DTYPE_ANY_TEXT(desc1.dsc_dtype) ||
IS_DTYPE_ANY_TEXT(desc2.dsc_dtype)) desc->dsc_scale = 0;
else
desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale);
node->nod_scale = desc->dsc_scale;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
#ifdef NATIVE_QUAD
return;
#endif
default:
assert(FALSE);
/* FALLINTO */
case dtype_blob:
case dtype_array:
break;
}
}
break;
case nod_gen_id2:
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case nod_add2:
case nod_subtract2:
{
DSC desc1, desc2;
CMP_get_desc(tdbb, csb, node->nod_arg[0], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &desc2);
dtype1 = desc1.dsc_dtype;
dtype2 = desc2.dsc_dtype;
/* Because dtype_int64 > dtype_double, we cannot just use the MAX macro to set
the result dtype. The rule is that two exact numeric operands yield an int64
result, while an approximate numeric and anything yield a double result. */
if (DTYPE_IS_EXACT(desc1.dsc_dtype)
&& DTYPE_IS_EXACT(desc2.dsc_dtype))
dtype = dtype_int64;
else if (DTYPE_IS_NUMERIC(desc1.dsc_dtype) &&
DTYPE_IS_NUMERIC(desc2.dsc_dtype)) dtype = dtype_double;
else {
/* mixed numeric and non-numeric: */
assert(COULD_BE_DATE(desc1) || COULD_BE_DATE(desc2));
/* The MAX(dtype) rule doesn't apply with dtype_int64 */
if (dtype_int64 == dtype1)
dtype1 = dtype_double;
if (dtype_int64 == dtype2)
dtype2 = dtype_double;
dtype = MAX(dtype1, dtype2);
}
switch (dtype) {
case dtype_timestamp:
case dtype_sql_date:
case dtype_sql_time:
node->nod_flags |= nod_date;
assert(DTYPE_IS_DATE(desc1.dsc_dtype) ||
DTYPE_IS_DATE(desc2.dsc_dtype));
if ((DTYPE_IS_DATE(dtype1) || (dtype1 == dtype_null)) &&
(DTYPE_IS_DATE(dtype2) || (dtype2 == dtype_null))) {
if (node->nod_type == nod_subtract2) {
/* <any date> - <any date> */
/* Legal permutations are:
<timestamp> - <timestamp>
<timestamp> - <date>
<date> - <date>
<date> - <timestamp>
<time> - <time> */
if (dtype1 == dtype_null)
dtype1 = dtype2;
else if (dtype2 == dtype_null)
dtype2 = dtype1;
if (dtype1 == dtype2)
dtype = dtype1;
else if ((dtype1 == dtype_timestamp) &&
(dtype2 == dtype_sql_date))
dtype = dtype_timestamp;
else if ((dtype2 == dtype_timestamp) &&
(dtype1 == dtype_sql_date))
dtype = dtype_timestamp;
else
ERR_post(gds_expression_eval_err, 0);
if (dtype == dtype_sql_date) {
desc->dsc_dtype = dtype_long;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else if (dtype == dtype_sql_time) {
desc->dsc_dtype = dtype_long;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale =
ISC_TIME_SECONDS_PRECISION_SCALE;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else {
assert(dtype == dtype_timestamp
|| dtype == dtype_null);
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
}
else if (IS_DATE_AND_TIME(desc1, desc2)) {
/* <date> + <time> */
/* <time> + <date> */
desc->dsc_dtype = dtype_timestamp;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else
/* <date> + <date> */
ERR_post(gds_expression_eval_err, 0);
}
else if (DTYPE_IS_DATE(desc1.dsc_dtype) ||
/* <date> +/- <non-date> */
(node->nod_type == nod_add2))
/* <non-date> + <date> */
{
desc->dsc_dtype = desc1.dsc_dtype;
if (!DTYPE_IS_DATE(desc->dsc_dtype))
desc->dsc_dtype = desc2.dsc_dtype;
assert(DTYPE_IS_DATE(desc->dsc_dtype));
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else
/* <non-date> - <date> */
ERR_post(gds_expression_eval_err, 0);
return;
case dtype_text:
case dtype_cstring:
case dtype_varying:
case dtype_real:
case dtype_double:
node->nod_flags |= nod_double;
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_short:
case dtype_long:
case dtype_int64:
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
if (IS_DTYPE_ANY_TEXT(desc1.dsc_dtype) ||
IS_DTYPE_ANY_TEXT(desc2.dsc_dtype)) desc->dsc_scale = 0;
else
desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale);
node->nod_scale = desc->dsc_scale;
desc->dsc_sub_type =
MAX(desc1.dsc_sub_type, desc2.dsc_sub_type);
desc->dsc_flags = 0;
return;
case dtype_null:
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_quad:
node->nod_flags |= nod_quad;
desc->dsc_dtype = dtype_quad;
desc->dsc_length = sizeof(SQUAD);
if (IS_DTYPE_ANY_TEXT(desc1.dsc_dtype) ||
IS_DTYPE_ANY_TEXT(desc2.dsc_dtype)) desc->dsc_scale = 0;
else
desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale);
node->nod_scale = desc->dsc_scale;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
#ifdef NATIVE_QUAD
return;
#endif
default:
assert(FALSE);
/* FALLINTO */
case dtype_blob:
case dtype_array:
break;
}
}
break;
case nod_multiply:
{
DSC desc1, desc2;
CMP_get_desc(tdbb, csb, node->nod_arg[0], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &desc2);
dtype =
DSC_multiply_blr4_result[desc1.dsc_dtype][desc2.dsc_dtype];
switch (dtype) {
case dtype_long:
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(SLONG);
desc->dsc_scale = node->nod_scale =
NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2);
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_double:
#ifdef VMS
case dtype_d_float:
#endif
node->nod_flags |= nod_double;
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_null:
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
default:
assert(FALSE);
/* FALLINTO */
case DTYPE_CANNOT:
/* break to error reporting code */
break;
}
}
break;
case nod_multiply2:
case nod_divide2:
{
DSC desc1, desc2;
CMP_get_desc(tdbb, csb, node->nod_arg[0], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &desc2);
dtype = DSC_multiply_result[desc1.dsc_dtype][desc2.dsc_dtype];
switch (dtype) {
case dtype_double:
node->nod_flags |= nod_double;
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case dtype_int64:
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
desc->dsc_scale = node->nod_scale =
NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2);
desc->dsc_sub_type =
MAX(desc1.dsc_sub_type, desc2.dsc_sub_type);
desc->dsc_flags = 0;
return;
case dtype_null:
desc->dsc_dtype = dtype_null;
desc->dsc_length = 0;
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
default:
assert(FALSE);
/* FALLINTO */
case DTYPE_CANNOT:
/* break to error reporting code */
break;
}
}
break;
case nod_concatenate:
{
DSC desc1, desc2;
ULONG rc_len;
CMP_get_desc(tdbb, csb, node->nod_arg[0], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &desc2);
desc->dsc_dtype = dtype_text;
if (desc1.dsc_dtype <= dtype_varying) {
rc_len = DSC_string_length(&desc1);
desc->dsc_ttype = desc1.dsc_ttype;
}
else {
rc_len = DSC_convert_to_text_length(desc1.dsc_dtype);
desc->dsc_ttype = ttype_ascii;
}
if (desc2.dsc_dtype <= dtype_varying)
rc_len += DSC_string_length (&desc2);
else
rc_len += DSC_convert_to_text_length(desc2.dsc_dtype);
/* error() is a local routine in par.c, so we use plain ERR_post. */
if (rc_len > MAX_FORMAT_SIZE)
ERR_post(gds_imp_exc, gds_arg_gds, gds_blktoobig, 0);
desc->dsc_length = static_cast<USHORT>(rc_len);
desc->dsc_scale = 0;
desc->dsc_flags = 0;
return;
}
case nod_upcase:
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
if (desc->dsc_dtype > dtype_varying) {
desc->dsc_length = DSC_convert_to_text_length(desc->dsc_dtype);
desc->dsc_dtype = dtype_text;
desc->dsc_ttype = ttype_ascii;
desc->dsc_scale = 0;
desc->dsc_flags = 0;
}
return;
case nod_dbkey:
desc->dsc_dtype = dtype_text;
desc->dsc_ttype = ttype_binary;
desc->dsc_length = 8;
desc->dsc_scale = 0;
desc->dsc_flags = 0;
return;
case nod_rec_version:
desc->dsc_dtype = dtype_text;
desc->dsc_ttype = ttype_binary;
desc->dsc_length = 4;
desc->dsc_scale = 0;
desc->dsc_flags = 0;
return;
case nod_current_time:
desc->dsc_dtype = dtype_sql_time;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case nod_current_timestamp:
desc->dsc_dtype = dtype_timestamp;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case nod_current_date:
desc->dsc_dtype = dtype_sql_date;
desc->dsc_length = type_lengths[desc->dsc_dtype];
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
return;
case nod_user_name:
case nod_current_role:
desc->dsc_dtype = dtype_text;
desc->dsc_ttype = ttype_metadata;
desc->dsc_length = USERNAME_LENGTH;
desc->dsc_scale = 0;
desc->dsc_flags = 0;
return;
case nod_internal_info:
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(SLONG);
desc->dsc_scale = 0;
desc->dsc_flags = 0;
return;
case nod_extract:
if ((ULONG) node->nod_arg[e_extract_part] == blr_extract_second) {
/* QUADDATE - SECOND returns a float, or scaled! */
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(ULONG);
desc->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else {
desc->dsc_dtype = dtype_short;
desc->dsc_length = sizeof(SSHORT);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
return;
case nod_agg_min:
case nod_agg_max:
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
return;
case nod_negate:
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
node->nod_flags =
node->nod_arg[0]->nod_flags & (nod_double | nod_quad);
return;
case nod_literal:
*desc = ((LIT) node)->lit_desc;
return;
case nod_cast:
{
FMT format;
DSC desc1;
format = (FMT) node->nod_arg[e_cast_fmt];
*desc = format->fmt_desc[0];
if ((desc->dsc_dtype <= dtype_any_text && !desc->dsc_length) ||
(desc->dsc_dtype == dtype_varying
&& desc->dsc_length <= sizeof(USHORT))) {
CMP_get_desc(tdbb, csb, node->nod_arg[e_cast_source], &desc1);
desc->dsc_length = DSC_string_length(&desc1);
if (desc->dsc_dtype == dtype_cstring)
desc->dsc_length++;
else if (desc->dsc_dtype == dtype_varying)
desc->dsc_length += sizeof(USHORT);
}
return;
}
case nod_argument:
{
FMT format;
NOD message;
message = node->nod_arg[e_arg_message];
format = (FMT) message->nod_arg[e_msg_format];
*desc = format->fmt_desc[(int) node->nod_arg[e_arg_number]];
return;
}
case nod_substr:
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
if (desc->dsc_dtype == dtype_blob)
{
DSC desc1, desc2;
ULONG rc_len;
CMP_get_desc(tdbb, csb, node->nod_arg [1], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg [2], &desc2);
if (desc1.dsc_flags & DSC_null || desc2.dsc_flags & DSC_null)
{
rc_len = 0;
desc->dsc_flags |= DSC_null;
}
else
{
SLONG sl1 = MOV_get_long(&desc1, 0);
SLONG sl2 = MOV_get_long(&desc2, 0);
/* error() is a local routine in par.c, so we use plain ERR_post. */
if (sl1 < 0 || sl2 < 0 || sl2 > MAX_COLUMN_SIZE - sizeof(USHORT))
ERR_post(gds_imp_exc, gds_arg_gds, gds_blktoobig, 0);
rc_len = sl2;
}
desc->dsc_dtype = dtype_varying;
desc->dsc_ttype = desc->dsc_scale;
desc->dsc_scale = 0;
desc->dsc_length = static_cast<USHORT>(rc_len) + sizeof(USHORT);
}
return;
case nod_function:
{
FUN function;
function = (FUN) node->nod_arg[e_fun_function];
/** Null value for the function indicates that the function was not
looked up during parsing the blr. This is true if the function
referenced in the procedure blr was dropped before dropping the
procedure itself. Ignore the case because we are currently trying
to drop the procedure.
For normal requests, function would never be null. We would have
created a valid block while parsing in par_function/par.c.
**/
if (function)
*desc = function->fun_rpt[function->fun_return_arg].fun_desc;
else
/* Note that CMP_get_desc is always called with a pre-allocated
DSC i.e
DSC desc;
CMP_get_desc (.... &desc); Hence the code
*desc = NULL; will not work. What I've done is memset the
structure to zero.
*/
MOVE_CLEAR(desc, sizeof(DSC));
return;
}
case nod_variable:
{
NOD value;
value = node->nod_arg[e_var_variable];
*desc = *(DSC *) (value->nod_arg + e_dcl_desc);
return;
}
case nod_value_if:
CMP_get_desc(tdbb, csb, node->nod_arg[1], desc);
return;
case nod_bookmark:
desc->dsc_dtype = dtype_text;
desc->dsc_ttype = ttype_binary;
desc->dsc_length = 0;
desc->dsc_scale = 0;
desc->dsc_flags = 0;
return;
default:
assert(FALSE);
break;
}
if (dtype == dtype_quad)
IBERROR(224); /* msg 224 quad word arithmetic not supported */
ERR_post(isc_datype_notsup, 0); /* data type not supported for arithmetic */
}
IDL DLL_EXPORT CMP_get_index_lock(TDBB tdbb, REL relation, USHORT id)
{
/**************************************
*
* C M P _ g e t _ i n d e x _ l o c k
*
**************************************
*
* Functional description
* Get index lock block for index. If one doesn't exist,
* make one.
*
**************************************/
DBB dbb;
IDL index;
LCK lock;
SET_TDBB(tdbb);
dbb = tdbb->tdbb_database;
DEV_BLKCHK(relation, type_rel);
if (relation->rel_id < (USHORT) rel_MAX)
return NULL;
/* For for an existing block */
for (index = relation->rel_index_locks; index; index = index->idl_next)
if (index->idl_id == id)
return index;
index = new(*dbb->dbb_permanent) idl();
index->idl_next = relation->rel_index_locks;
relation->rel_index_locks = index;
index->idl_relation = relation;
index->idl_id = id;
index->idl_lock = lock = new(*dbb->dbb_permanent, 0) lck;
lock->lck_parent = dbb->dbb_lock;
lock->lck_dbb = dbb;
lock->lck_key.lck_long = relation->rel_id * 1000 + id;
lock->lck_length = sizeof(lock->lck_key.lck_long);
lock->lck_type = LCK_idx_exist;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
return index;
}
SLONG DLL_EXPORT CMP_impure(CSB csb, USHORT size)
{
/**************************************
*
* C M P _ i m p u r e
*
**************************************
*
* Functional description
* Allocate space (offset) in request.
*
**************************************/
SLONG offset;
DEV_BLKCHK(csb, type_csb);
if (!csb)
return 0;
offset = FB_ALIGN(csb->csb_impure, ALIGNMENT);
csb->csb_impure = offset + size;
return offset;
}
REQ DLL_EXPORT CMP_make_request(TDBB tdbb, CSB * csb_ptr)
{
/**************************************
*
* C M P _ m a k e _ r e q u e s t
*
**************************************
*
* Functional description
* Turn a parsed request into an executable request.
*
**************************************/
REQ request = 0;
LLS temp;
vec::iterator ptr;
DEV_BLKCHK(*csb_ptr, type_csb);
SET_TDBB(tdbb);
REQ old_request = tdbb->tdbb_request;
tdbb->tdbb_request = NULL;
try {
/* Once any expansion required has been done, make a pass to assign offsets
into the impure area and throw away any unnecessary crude. Execution
optimizations can be performed here */
DEBUG;
NOD node = pass1(tdbb, csb_ptr, (*csb_ptr)->csb_node, 0, 0, FALSE);
CSB csb = *csb_ptr;
csb->csb_node = node;
csb->csb_impure = REQ_SIZE + REQ_TAIL * csb->csb_n_stream;
csb->csb_node = pass2(tdbb, csb, csb->csb_node, 0);
if (csb->csb_impure > MAX_REQUEST_SIZE)
IBERROR(226); /* msg 226 request size limit exceeded */
/* Build the final request block. First, compute the "effective" repeat
count of hold the impure areas. */
int n = (csb->csb_impure - REQ_SIZE + REQ_TAIL - 1) / REQ_TAIL;
request = new(*tdbb->tdbb_default, n) req;
request->req_count = csb->csb_n_stream;
request->req_pool = tdbb->tdbb_default;
request->req_impure_size = csb->csb_impure;
request->req_top_node = csb->csb_node;
request->req_access = csb->csb_access;
request->req_variables = csb->csb_variables;
request->req_resources = csb->csb_resources;
if (csb->csb_g_flags & csb_blr_version4) {
request->req_flags |= req_blr_version4;
}
#ifdef SCROLLABLE_CURSORS
request->req_async_message = csb->csb_async_message;
#endif
/* Take out existence locks on resources used in request. This is
a little complicated since relation locks MUST be taken before
index locks */
for (RSC resource = request->req_resources; resource;
resource = resource->rsc_next)
{
switch (resource->rsc_type)
{
case rsc_relation:
{
REL relation = resource->rsc_rel;
MET_post_existence(tdbb, relation);
break;
}
case rsc_index:
{
REL relation = resource->rsc_rel;
IDL index =
CMP_get_index_lock(tdbb, relation, resource->rsc_id);
if (index)
{
if (!index->idl_count)
{
LCK_lock_non_blocking( tdbb,
index->idl_lock,
LCK_SR,
TRUE);
}
++index->idl_count;
}
break;
}
case rsc_procedure:
{
PRC procedure = resource->rsc_prc;
procedure->prc_use_count++;
#ifdef DEBUG_PROCS
{
char buffer[256];
sprintf(buffer,
"Called from CMP_make_request():\n\t Incrementing use count of %s\n",
procedure->prc_name->str_data);
JRD_print_procedure_info(tdbb, buffer);
}
#endif
break;
}
default:
BUGCHECK(219); /* msg 219 request of unknown resource */
}
}
//csb_repeat* tail = &*(csb->csb_rpt.begin());
csb_repeat* tail = &(csb->csb_rpt[0]);
csb_repeat* end = tail + csb->csb_n_stream;
DEBUG;
for (RPB* rpb = request->req_rpb; tail < end; rpb++, tail++)
{
/* Fetch input stream for update if all booleans matched against indices. */
if (tail->csb_flags & csb_update
&& !(tail->csb_flags & csb_unmatched)) rpb->rpb_stream_flags |=
RPB_s_update;
rpb->rpb_relation = tail->csb_relation;
SBM_release(tail->csb_fields);
}
USHORT count;
for (temp = csb->csb_fors, count = 0; temp; count++) {
temp = temp->lls_next;
}
if (count) {
if (!request->req_fors)
{
request->req_fors =
vec::newVector(*request->req_pool, count+1);
}
if (count >= request->req_fors->count())
{
request->req_fors->resize(count+1);
}
ptr = request->req_fors->begin();
while (csb->csb_fors)
*ptr++ = (BLK) LLS_POP(&csb->csb_fors);
}
/* make a vector of all invariant-type nodes, so that we will
be able to easily reinitialize them when we restart the request */
for (temp = csb->csb_invariants, count = 0; temp; count++) {
temp = temp->lls_next;
}
if (count) {
if (!request->req_invariants)
{
request->req_invariants =
vec::newVector(*request->req_pool , count+1);
}
if (count >= request->req_invariants->count())
{
request->req_invariants->resize(count+1);
}
ptr = request->req_invariants->begin();
while (csb->csb_invariants)
*ptr++ = (BLK) LLS_POP(&csb->csb_invariants);
}
DEBUG;
tdbb->tdbb_request = old_request;
} // try
catch (...) {
tdbb->tdbb_request = old_request;
ERR_punt();
}
return request;
}
int DLL_EXPORT CMP_post_access(TDBB tdbb,
CSB csb,
TEXT* security_name,
REL view,
CONST TEXT* trig,
CONST TEXT* proc,
USHORT mask,
CONST TEXT* type_name,
CONST TEXT* name)
{
/**************************************
*
* C M P _ p o s t _ a c c e s s
*
**************************************
*
* Functional description
* Post access to security class to request.
* We append the new security class to the existing list of
* security classes for that request.
*
**************************************/
ACC access, last_entry;
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(view, type_rel);
SET_TDBB(tdbb);
/* allow all access to internal requests */
if (csb->csb_g_flags & (csb_internal | csb_ignore_perm))
return TRUE;
last_entry = NULL;
for (access = csb->csb_access; access; access = access->acc_next)
{
if (access->acc_security_name == security_name &&
access->acc_view == view &&
access->acc_trg_name == trig &&
access->acc_prc_name == proc &&
access->acc_mask == mask &&
!strcmp(access->acc_type, type_name) &&
!strcmp(access->acc_name, name))
{
return FALSE;
}
if (!access->acc_next)
{
last_entry = access;
}
}
access = new(*tdbb->tdbb_default) acc;
/* append the security class to the existing list */
if (last_entry)
{
access->acc_next = NULL;
last_entry->acc_next = access;
}
else
{
access->acc_next = csb->csb_access;
csb->csb_access = access;
}
access->acc_security_name = security_name;
access->acc_view = view;
access->acc_trg_name = trig;
access->acc_prc_name = proc;
access->acc_mask = mask;
access->acc_type = type_name;
access->acc_name = name;
#ifdef DEBUG_TRACE
ib_printf("%x: require %05X access to %s %s (sec %s view %s trg %s prc %s)\n",
csb, access->acc_mask, access->acc_type, access->acc_name,
access->acc_security_name ? access->acc_security_name : "NULL",
access->acc_view ? access->acc_view->rel_name : "NULL",
access->acc_trg_name ? access->acc_trg_name : "NULL",
access->acc_prc_name ? access->acc_prc_name : "NULL");
#endif
return TRUE;
}
void DLL_EXPORT CMP_post_resource(
TDBB tdbb,
RSC * rsc_ptr,
BLK rel_or_prc, ENUM rsc_s type, USHORT id)
{
/**************************************
*
* C M P _ p o s t _ r e s o u r c e
*
**************************************
*
* Functional description
* Post a resource usage to the compiler scratch block.
*
**************************************/
RSC resource;
DEV_BLKCHK(*rsc_ptr, type_rsc);
SET_TDBB(tdbb);
for (resource = *rsc_ptr; resource; resource = resource->rsc_next)
if (resource->rsc_type == type && resource->rsc_id == id)
return;
resource = new(*tdbb->tdbb_default) Rsc;
resource->rsc_next = *rsc_ptr;
*rsc_ptr = resource;
resource->rsc_type = type;
resource->rsc_id = id;
switch (type) {
case rsc_relation:
case rsc_index:
resource->rsc_rel = (REL) rel_or_prc;
break;
case rsc_procedure:
resource->rsc_prc = (PRC) rel_or_prc;
break;
default:
BUGCHECK(220); /* msg 220 unknown resource */
break;
}
}
void DLL_EXPORT CMP_release_resource(
RSC * rsc_ptr,
ENUM rsc_s type, USHORT id)
{
/**************************************
*
* C M P _ r e l e a s e _ r e s o u r c e
*
**************************************
*
* Functional description
* Release resource from request.
*
**************************************/
RSC resource;
DEV_BLKCHK(*rsc_ptr, type_rsc);
for (; (resource = *rsc_ptr); rsc_ptr = &resource->rsc_next)
if (resource->rsc_type == type && resource->rsc_id == id)
break;
if (!resource)
return;
/* take out of the linked list and release */
*rsc_ptr = resource->rsc_next;
delete resource;
}
void DLL_EXPORT CMP_decrement_prc_use_count(TDBB tdbb, PRC procedure)
{
/*********************************************
*
* C M P _ d e c r e m e n t _ p r c _ u s e _ c o u n t
*
*********************************************
*
* Functional description
* decrement the procedure's use count
*
*********************************************/
DEV_BLKCHK(procedure, type_prc);
assert(procedure->prc_use_count > 0);
--procedure->prc_use_count;
#ifdef DEBUG_PROCS
{
char buffer[256];
sprintf(buffer,
"Called from CMP_decrement():\n\t Decrementing use count of %s\n",
procedure->prc_name->str_data);
JRD_print_procedure_info(tdbb, buffer);
}
#endif
/* Call recursively if and only if the use count is zero AND the procedure
in dbb_procedures is different than this procedure.
The procedure will be different than in dbb_procedures only if it is a
floating copy .i.e. an old copy or a deleted procedure.
*/
if ((procedure->prc_use_count == 0) &&
( (*tdbb->tdbb_database->dbb_procedures)[procedure->prc_id]
//!= &procedure->prc_header)) {
!= (BLK) procedure)) {
CMP_release(tdbb, procedure->prc_request);
procedure->prc_flags &= ~PRC_being_altered;
MET_remove_procedure(tdbb, procedure->prc_id, procedure);
}
}
void DLL_EXPORT CMP_release(TDBB tdbb, REQ request)
{
/**************************************
*
* C M P _ r e l e a s e
*
**************************************
*
* Functional description
* Release an unneeded and unloved request.
*
**************************************/
REQ *next;
IDL index;
REL relation;
RSC resource;
ATT attachment;
SET_TDBB(tdbb);
DEV_BLKCHK(request, type_req);
/* Release existence locks on references */
if (!(attachment = request->req_attachment)
|| !(attachment->att_flags & ATT_shutdown))
for (resource =
request->req_resources;
resource; resource = resource->rsc_next) {
switch (resource->rsc_type) {
case rsc_relation:
{
relation = resource->rsc_rel;
MET_release_existence(relation);
break;
}
case rsc_index:
{
relation = resource->rsc_rel;
if ( (index =
CMP_get_index_lock(tdbb, relation,
resource->
rsc_id)) ) if (!--index->idl_count)
LCK_release(tdbb, index->idl_lock);
break;
}
case rsc_procedure:
{
CMP_decrement_prc_use_count(tdbb, resource->rsc_prc);
break;
}
default:
BUGCHECK(220); /* msg 220 release of unknown resource */
break;
}
}
EXE_unwind(tdbb, request);
#ifdef PC_ENGINE
RNG_release_ranges(request);
#endif
if (request->req_attachment)
for (next = &request->req_attachment->att_requests; *next;
next = &(*next)->req_request)
if (*next == request) {
*next = request->req_request;
break;
}
delete request->req_pool;
}
void DLL_EXPORT CMP_shutdown_database(TDBB tdbb)
{
/**************************************
*
* C M P _ s h u t d o w n _ d a t a b a s e
*
**************************************
*
* Functional description
* Release compile-time locks for database.
* Since this can be called at AST level, don't
* release any data structures.
*
**************************************/
REL relation;
vec::iterator ptr, end;
PRC procedure;
vec::iterator pptr, pend;
IDL index;
VEC vector;
DBB dbb;
SET_TDBB(tdbb);
dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
DEV_BLKCHK(dbb, type_dbb);
if (!(vector = dbb->dbb_relations))
return;
/* Go through relations and indeces and release
all existence locks that might have been taken
*/
for (ptr = vector->begin(), end = vector->end(); ptr < end; ptr++)
if ( (relation = (REL)*ptr) ) {
if (relation->rel_existence_lock) {
LCK_release(tdbb, relation->rel_existence_lock);
relation->rel_use_count = 0;
}
for (index = relation->rel_index_locks; index;
index = index->idl_next) if (index->idl_lock) {
LCK_release(tdbb, index->idl_lock);
index->idl_count = 0;
}
}
if (!(vector = dbb->dbb_procedures))
return;
/* Release all procedure existence locks that
might have been taken
*/
for (pptr = vector->begin(), pend = vector->end(); pptr < pend; pptr++)
if ( (procedure = (PRC)*pptr) ) {
if (procedure->prc_existence_lock) {
LCK_release(tdbb, procedure->prc_existence_lock);
procedure->prc_use_count = 0;
}
}
}
static UCHAR *alloc_map(TDBB tdbb, CSB * csb, USHORT stream)
{
/**************************************
*
* a l l o c _ m a p
*
**************************************
*
* Functional description
* Allocate and initialize stream map for view processing.
*
**************************************/
STR string;
DEV_BLKCHK(*csb, type_csb);
SET_TDBB(tdbb);
string = new(*tdbb->tdbb_default, MAP_LENGTH) str;
string->str_length = MAP_LENGTH;
(*csb)->csb_rpt[stream].csb_map = (UCHAR *) string->str_data;
/* TMN: Here we should really have the following assert */
/* assert(stream <= MAX_UCHAR); */
string->str_data[0] = (UCHAR) stream;
return (UCHAR *) string->str_data;
}
#ifdef PC_ENGINE
static USHORT base_stream(CSB csb, NOD * stream_number, BOOLEAN nav_stream)
{
/**************************************
*
* b a s e _ s t r e a m
*
**************************************
*
* Functional description
* Find the base stream of a view for navigational
* access. If there is more than one base table,
* give an error.
*
**************************************/
UCHAR *map;
USHORT stream;
DEV_BLKCHK(csb, type_csb);
/* Note: *stream_number is NOT a NOD */
stream = (USHORT) * stream_number;
/* If the stream references a view, follow map */
if (map = csb->csb_rpt[stream].csb_map)
if (map[2]) {
if (nav_stream)
/* navigational stream %ld references a view with more than one base table */
ERR_post(isc_complex_view, gds_arg_number, (SLONG) stream, 0);
}
else {
map++;
stream = *map;
}
/* if this is a navigational stream, fix up the stream number
in the node tree to point to the base table from now on */
if (nav_stream)
*stream_number = (NOD) stream;
return stream;
}
#endif
static NOD catenate_nodes(TDBB tdbb, LLS stack)
{
/**************************************
*
* c a t e n a t e _ n o d e s
*
**************************************
*
* Functional description
* Take a stack of nodes
* and turn them into a tree of concatenations.
*
**************************************/
NOD cat_node, node1;
SET_TDBB(tdbb);
DEV_BLKCHK(stack, type_lls);
node1 = (NOD) LLS_POP(&stack);
if (!stack)
return node1;
cat_node = PAR_make_node(tdbb, 2);
cat_node->nod_type = nod_concatenate;
cat_node->nod_arg[0] = node1;
cat_node->nod_arg[1] = catenate_nodes(tdbb, stack);
return cat_node;
}
static NOD copy(
TDBB tdbb,
CSB * csb,
NOD input, UCHAR * remap, USHORT field_id, USHORT remap_fld)
{
/**************************************
*
* c o p y
*
**************************************
*
* Functional description
* Copy an expression tree remapping field streams. If the
* map isn't present, don't remap.
*
**************************************/
NOD node, *arg1, *arg2, *end;
USHORT stream, new_stream, args;
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(input, type_nod);
if (!input)
return NULL;
/* Special case interesting nodes */
args = input->nod_count;
switch (input->nod_type) {
case nod_ansi_all:
case nod_ansi_any:
case nod_any:
case nod_exists:
case nod_unique:
args = e_any_length;
break;
case nod_for:
args = e_for_length;
break;
case nod_argument:
if (remap_fld)
return input;
node = PAR_make_node(tdbb, e_arg_length);
node->nod_count = input->nod_count;
node->nod_flags = input->nod_flags;
node->nod_type = input->nod_type;
node->nod_arg[e_arg_number] = input->nod_arg[e_arg_number];
node->nod_arg[e_arg_message] = input->nod_arg[e_arg_message];
node->nod_arg[e_arg_flag] =
copy(tdbb, csb, input->nod_arg[e_arg_flag], remap, field_id,
remap_fld);
node->nod_arg[e_arg_indicator] =
copy(tdbb, csb, input->nod_arg[e_arg_indicator], remap, field_id,
remap_fld);
return (node);
break;
case nod_assignment:
args = e_asgn_length;
break;
case nod_erase:
args = e_erase_length;
break;
case nod_modify:
args = e_mod_length;
break;
case nod_variable:
case nod_literal:
return input;
case nod_field:
{
NOD temp_node;
if (field_id &&
(input->nod_flags & nod_id) &&
!input->nod_arg[e_fld_id] && !input->nod_arg[e_fld_stream])
--field_id;
else
field_id = (USHORT) input->nod_arg[e_fld_id];
stream = (USHORT) input->nod_arg[e_fld_stream];
if (remap_fld) {
REL relation;
FLD field;
relation = (*csb)->csb_rpt[stream].csb_relation;
field = MET_get_field(relation, field_id);
if (field->fld_source)
field_id = (USHORT) field->fld_source->nod_arg[e_fld_id];
}
if (remap)
stream = remap[stream];
temp_node = PAR_gen_field(tdbb, stream, field_id);
if (input->nod_type == nod_field &&
input->nod_arg[e_fld_default_value])
temp_node->nod_arg[e_fld_default_value] =
input->nod_arg[e_fld_default_value];
return temp_node;
}
case nod_function:
node = PAR_make_node(tdbb, e_fun_length);
node->nod_count = input->nod_count;
node->nod_type = input->nod_type;
node->nod_arg[e_fun_args] =
copy(tdbb, csb, input->nod_arg[e_fun_args], remap, field_id,
remap_fld);
node->nod_arg[e_fun_function] = input->nod_arg[e_fun_function];
return (node);
case nod_gen_id:
case nod_gen_id2: /* 20001013 PJPG */
node = PAR_make_node(tdbb, e_gen_length);
node->nod_count = input->nod_count;
node->nod_type = input->nod_type;
node->nod_arg[e_gen_value] =
copy(tdbb, csb, input->nod_arg[e_gen_value], remap, field_id,
remap_fld);
node->nod_arg[e_gen_relation] = input->nod_arg[e_gen_relation];
return (node);
case nod_cast:
node = PAR_make_node(tdbb, e_cast_length);
node->nod_count = input->nod_count;
node->nod_type = input->nod_type;
node->nod_arg[e_cast_source] =
copy(tdbb, csb, input->nod_arg[e_cast_source], remap, field_id,
remap_fld);
node->nod_arg[e_cast_fmt] = input->nod_arg[e_cast_fmt];
return (node);
case nod_extract:
node = PAR_make_node(tdbb, e_extract_length);
node->nod_count = input->nod_count;
node->nod_type = input->nod_type;
node->nod_arg[e_extract_value] =
copy(tdbb, csb, input->nod_arg[e_extract_value], remap, field_id,
remap_fld);
node->nod_arg[e_extract_part] = input->nod_arg[e_extract_part];
return (node);
case nod_count:
case nod_count2:
case nod_max:
case nod_min:
case nod_total:
case nod_average:
case nod_from:
args = e_stat_length;
break;
case nod_rse:
case nod_stream:
{
RSE new_rse, old_rse;
old_rse = (RSE) input;
new_rse =
(RSE) PAR_make_node(tdbb, old_rse->rse_count + rse_delta + 2);
new_rse->nod_type = input->nod_type;
new_rse->nod_count = 0;
new_rse->rse_count = old_rse->rse_count;
arg1 = old_rse->rse_relation;
arg2 = new_rse->rse_relation;
for (end = arg1 + old_rse->rse_count; arg1 < end; arg1++, arg2++)
*arg2 = copy(tdbb, csb, *arg1, remap, field_id, remap_fld);
new_rse->rse_jointype = old_rse->rse_jointype;
new_rse->rse_first =
copy(tdbb, csb, old_rse->rse_first, remap, field_id,
remap_fld);
new_rse->rse_skip =
copy (tdbb, csb, old_rse->rse_skip, remap, field_id,
remap_fld);
new_rse->rse_boolean =
copy(tdbb, csb, old_rse->rse_boolean, remap, field_id,
remap_fld);
new_rse->rse_sorted =
copy(tdbb, csb, old_rse->rse_sorted, remap, field_id,
remap_fld);
new_rse->rse_projection =
copy(tdbb, csb, old_rse->rse_projection, remap, field_id,
remap_fld);
return (NOD) new_rse;
}
case nod_relation:
{
csb_repeat *element;
int relative_stream;
if (!remap)
BUGCHECK(221); /* msg 221 (CMP) copy: cannot remap */
node = PAR_make_node(tdbb, e_rel_length);
node->nod_type = input->nod_type;
node->nod_count = 0;
stream = (USHORT) input->nod_arg[e_rel_stream];
/**
Last entry in the remap contains the the original stream number.
Get that stream number so that the flags can be copied
into the newly created child stream.
**/
relative_stream = (stream) ? remap[stream - 1] : stream;
new_stream = (*csb)->csb_n_stream++;
node->nod_arg[e_rel_stream] = (NOD) (SLONG) new_stream;
/* TMN: Here we should really have the following assert */
/* assert(new_stream <= MAX_UCHAR); */
remap[stream] = (UCHAR) new_stream;
node->nod_arg[e_rel_context] = input->nod_arg[e_rel_context];
node->nod_arg[e_rel_relation] = input->nod_arg[e_rel_relation];
node->nod_arg[e_rel_view] = input->nod_arg[e_rel_view];
element = CMP_csb_element(csb, new_stream);
element->csb_relation = (REL) node->nod_arg[e_rel_relation];
element->csb_view = (REL) node->nod_arg[e_rel_view];
element->csb_view_stream = remap[0];
/** If there was a parent stream no., then copy the flags
from that stream to its children streams. (Bug 10164/10166)
For e.g.
consider a view V1 with 2 streams
stream #1 from table T1
stream #2 from table T2
consider a procedure P1 with 2 streams
stream #1 from table X
stream #2 from view V1
During pass1 of procedure request, the engine tries to expand
all the views into their base tables. It creates a compilier
scratch block which initially looks like this
stream 1 -------- X
stream 2 -------- V1
while expanding V1 the engine calls copy() with nod_relation.
A new stream 3 is created. Now the CSB looks like
stream 1 -------- X
stream 2 -------- V1 map [2,3]
stream 3 -------- T1
After T1 stream has been created the flags are copied from
stream #1 because V1's definition said the original stream
number for T1 was 1. However since its being merged with
the procedure request, stream #1 belongs to a different table.
The flags should be copied from stream 2 i.e. V1. We can get
this info from variable remap.
Since we didn't do this properly before, V1's children got
tagged with whatever flags X possesed leading to various
errors.
We now store the proper stream no in relative_stream and
later use it to copy the flags. -Sudesh (03/05/99)
**/
(*csb)->csb_rpt[new_stream].csb_flags |=
(*csb)->csb_rpt[relative_stream].csb_flags & csb_no_dbkey;
return node;
}
case nod_procedure:
{
csb_repeat *element;
if (!remap)
BUGCHECK(221); /* msg 221 (CMP) copy: cannot remap */
node = PAR_make_node(tdbb, e_prc_length);
node->nod_type = input->nod_type;
node->nod_count = input->nod_count;
node->nod_arg[e_prc_inputs] =
copy(tdbb, csb, input->nod_arg[e_prc_inputs], remap, field_id,
remap_fld);
node->nod_arg[e_prc_in_msg] = input->nod_arg[e_prc_in_msg];
stream = (USHORT) input->nod_arg[e_prc_stream];
new_stream = (*csb)->csb_n_stream++;
node->nod_arg[e_prc_stream] = (NOD) (SLONG) new_stream;
/* TMN: Here we should really have the following assert */
/* assert(new_stream <= MAX_UCHAR); */
remap[stream] = (UCHAR) new_stream;
node->nod_arg[e_prc_procedure] = input->nod_arg[e_prc_procedure];
element = CMP_csb_element(csb, new_stream);
element->csb_procedure = (PRC) node->nod_arg[e_prc_procedure];
(*csb)->csb_rpt[new_stream].csb_flags |=
(*csb)->csb_rpt[stream].csb_flags & csb_no_dbkey;
return node;
}
case nod_aggregate:
if (!remap)
BUGCHECK(221); /* msg 221 (CMP) copy: cannot remap */
node = PAR_make_node(tdbb, e_agg_length);
node->nod_type = input->nod_type;
node->nod_count = 0;
stream = (USHORT) input->nod_arg[e_agg_stream];
new_stream = (*csb)->csb_n_stream++;
node->nod_arg[e_agg_stream] = (NOD) (SLONG) new_stream;
/* TMN: Here we should really have the following assert */
/* assert(new_stream <= MAX_UCHAR); */
remap[stream] = (UCHAR) new_stream;
CMP_csb_element(csb, new_stream);
(*csb)->csb_rpt[new_stream].csb_flags |=
(*csb)->csb_rpt[stream].csb_flags & csb_no_dbkey;
node->nod_arg[e_agg_rse] =
copy(tdbb, csb, input->nod_arg[e_agg_rse], remap, field_id,
remap_fld);
node->nod_arg[e_agg_group] =
copy(tdbb, csb, input->nod_arg[e_agg_group], remap, field_id,
remap_fld);
node->nod_arg[e_agg_map] =
copy(tdbb, csb, input->nod_arg[e_agg_map], remap, field_id,
remap_fld);
return node;
case nod_union:
if (!remap)
BUGCHECK(221); /* msg 221 (CMP) copy: cannot remap */
node = PAR_make_node(tdbb, e_uni_length);
node->nod_type = input->nod_type;
node->nod_count = 2;
stream = (USHORT) input->nod_arg[e_uni_stream];
new_stream = (*csb)->csb_n_stream++;
node->nod_arg[e_uni_stream] = (NOD) (SLONG) new_stream;
/* TMN: Here we should really have the following assert */
/* assert(new_stream <= MAX_UCHAR); */
remap[stream] = (UCHAR) new_stream;
CMP_csb_element(csb, new_stream);
(*csb)->csb_rpt[new_stream].csb_flags |=
(*csb)->csb_rpt[stream].csb_flags & csb_no_dbkey;
node->nod_arg[e_uni_clauses] =
copy(tdbb, csb, input->nod_arg[e_uni_clauses], remap, field_id,
remap_fld);
return node;
case nod_sort:
args += args;
break;
default:
break;
}
/* Fall thru on generic nodes */
node = PAR_make_node(tdbb, args);
node->nod_count = input->nod_count;
node->nod_type = input->nod_type;
node->nod_flags = input->nod_flags;
arg1 = input->nod_arg;
arg2 = node->nod_arg;
for (end = arg1 + input->nod_count; arg1 < end; arg1++, arg2++)
if (*arg1)
*arg2 = copy(tdbb, csb, *arg1, remap, field_id, remap_fld);
/* Finish off sort */
if (input->nod_type == nod_sort)
for (end = arg1 + input->nod_count; arg1 < end; arg1++, arg2++)
*arg2 = *arg1;
return node;
}
static void expand_view_nodes(
TDBB tdbb,
CSB csb, USHORT stream, LLS * stack, NOD_T type)
{
/**************************************
*
* e x p a n d _ v i e w _ n o d e s
*
**************************************
*
* Functional description
* Expand dbkey for view.
*
**************************************/
UCHAR *map;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(*stack, type_lls);
/* If the stream's dbkey should be ignored, do so. */
if (csb->csb_rpt[stream].csb_flags & csb_no_dbkey)
return;
/* If the stream references a view, follow map */
if ( (map = csb->csb_rpt[stream].csb_map) ) {
++map;
while (*map)
expand_view_nodes(tdbb, csb, *map++, stack, type);
return;
}
/* Relation is primitive -- make dbkey node */
if (csb->csb_rpt[stream].csb_relation) {
NOD node;
node = PAR_make_node(tdbb, 1);
node->nod_count = 0;
node->nod_type = type;
node->nod_arg[0] = (NOD) (SLONG) stream;
LLS_PUSH(node, stack);
}
}
static void ignore_dbkey(TDBB tdbb, CSB csb, RSE rse, REL view)
{
/**************************************
*
* i g n o r e _ d b k e y
*
**************************************
*
* Functional description
* For each relation or aggregate in the
* rse, mark it as not having a dbkey.
*
**************************************/
NOD *ptr, *end, node;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(rse, type_nod);
DEV_BLKCHK(view, type_rel);
for (ptr = rse->rse_relation, end = ptr + rse->rse_count; ptr < end;) {
node = *ptr++;
if (node->nod_type == nod_relation) {
USHORT stream;
csb_repeat *tail;
REL relation;
stream = (USHORT) node->nod_arg[e_rel_stream];
csb->csb_rpt[stream].csb_flags |= csb_no_dbkey;
tail = &csb->csb_rpt[stream];
if ( (relation = tail->csb_relation) )
CMP_post_access(tdbb, csb, relation->rel_security_name,
(tail->csb_view) ? tail->csb_view : view,
0, 0, SCL_read, object_table,
relation->rel_name);
}
else if (node->nod_type == nod_rse)
ignore_dbkey(tdbb, csb, (RSE) node, view);
else if (node->nod_type == nod_aggregate)
ignore_dbkey(tdbb, csb, (RSE) node->nod_arg[e_agg_rse], view);
else if (node->nod_type == nod_union) {
NOD clauses, *ptr_uni, *end_uni;
clauses = node->nod_arg[e_uni_clauses];
for (ptr_uni = clauses->nod_arg, end_uni =
ptr_uni + clauses->nod_count; ptr_uni < end_uni; ptr_uni++)
ignore_dbkey(tdbb, csb, (RSE) * ptr_uni++, view);
}
}
}
static NOD make_defaults(TDBB tdbb, CSB * csb, USHORT stream, NOD statement)
{
/**************************************
*
* m a k e _ d e f a u l t s
*
**************************************
*
* Functional description
* Build an default value assignments.
*
**************************************/
NOD node, value;
LLS stack;
VEC vector;
vec::iterator ptr1, end;
REL relation;
USHORT field_id;
UCHAR *map, local_map[MAP_LENGTH];
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(statement, type_nod);
relation = (*csb)->csb_rpt[stream].csb_relation;
if (!(vector = relation->rel_fields))
return statement;
if (!(map = (*csb)->csb_rpt[stream].csb_map)) {
map = local_map;
/* TMN: Here we should really have the following assert */
/* assert(stream <= MAX_UCHAR); */
map[0] = (UCHAR) stream;
map[1] = 1;
map[2] = 2;
}
stack = NULL;
for (ptr1 = vector->begin(), end = vector->end(), field_id = 0;
ptr1 < end; ptr1++, field_id++)
if (*ptr1 && (value = ((FLD)(*ptr1))->fld_default_value)) {
node = PAR_make_node(tdbb, e_asgn_length);
node->nod_type = nod_assignment;
node->nod_arg[e_asgn_from] =
copy(tdbb, csb, value, map, (USHORT) (field_id + 1), FALSE);
node->nod_arg[e_asgn_to] = PAR_gen_field(tdbb, stream, field_id);
LLS_PUSH(node, &stack);
}
if (!stack)
return statement;
/* We have some default -- add the original statement and make a list out of
the whole mess */
LLS_PUSH(statement, &stack);
return PAR_make_list(tdbb, stack);
}
static NOD make_validation(TDBB tdbb, CSB * csb, USHORT stream)
{
/**************************************
*
* m a k e _ v a l i d a t i o n
*
**************************************
*
* Functional description
* Build a validation list for a relation, if appropriate.
*
**************************************/
NOD node, validation;
LLS stack;
VEC vector;
vec::iterator ptr1, end;
REL relation;
USHORT field_id;
UCHAR *map, local_map[MAP_LENGTH];
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
relation = (*csb)->csb_rpt[stream].csb_relation;
if (!(vector = relation->rel_fields))
return NULL;
if (!(map = (*csb)->csb_rpt[stream].csb_map)) {
map = local_map;
/* TMN: Here we should really have the following assert */
/* assert(stream <= MAX_UCHAR); */
map[0] = (UCHAR) stream;
}
stack = NULL;
for (ptr1 = vector->begin(), end = vector->end(), field_id = 0;
ptr1 < end; ptr1++, field_id++) {
if (*ptr1 && (validation = ((FLD)(*ptr1))->fld_validation)) {
node = PAR_make_node(tdbb, e_val_length);
node->nod_type = nod_validate;
node->nod_arg[e_val_boolean] =
copy(tdbb, csb, validation, map, (USHORT) (field_id + 1),
FALSE);
node->nod_arg[e_val_value] =
PAR_gen_field(tdbb, stream, field_id);
LLS_PUSH(node, &stack);
}
if (*ptr1 && (validation = ((FLD)(*ptr1))->fld_not_null)) {
node = PAR_make_node(tdbb, e_val_length);
node->nod_type = nod_validate;
node->nod_arg[e_val_boolean] =
copy(tdbb, csb, validation, map, (USHORT) (field_id + 1),
FALSE);
node->nod_arg[e_val_value] =
PAR_gen_field(tdbb, stream, field_id);
LLS_PUSH(node, &stack);
}
}
if (!stack)
return NULL;
return PAR_make_list(tdbb, stack);
}
static NOD pass1(
TDBB tdbb,
CSB * csb,
NOD node,
REL view, USHORT view_stream, BOOLEAN validate_expr)
{
/**************************************
*
* p a s s 1
*
**************************************
*
* Functional description
* Merge missing values, computed values, validation expressions,
* and views into a parsed request.
*
* The argument validate_expr is TRUE if an ancestor of the
* current node (the one being passed in) in the parse tree has nod_type
* == nod_validate. "ancestor" does not include the current node
* being passed in as an argument.
* If we are in a "validate subtree" (as determined by the
* validate_expr), we must not post update access to the fields involved
* in the validation clause. (see the call for CMP_post_access in this
* function.)
*
**************************************/
NOD sub, *ptr, *end;
USHORT stream;
csb_repeat *tail;
PRC procedure;
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(node, type_nod);
DEV_BLKCHK(view, type_rel);
if (!node)
return node;
validate_expr = validate_expr || (node->nod_type == nod_validate);
/* If there is processing to be done before sub expressions, do it here */
switch (node->nod_type) {
case nod_field:
{
LLS stack;
REL relation;
FLD field;
UCHAR *map, local_map[MAP_LENGTH];
stream = (USHORT) node->nod_arg[e_fld_stream];
/* Look at all rse's which are lower in scope than the rse which this field
is referencing, and mark them as varying -- the rule is that if a field
from one rse is referenced within the scope of another rse, the first rse
can't be invariant. This won't optimize all cases, but it is the simplest
operating assumption for now. */
for (stack = (*csb)->csb_current_rses; stack;
stack = stack->lls_next) {
RSE rse;
rse = (RSE) stack->lls_object;
if (stream_in_rse(stream, rse))
break;
rse->nod_flags |= rse_variant;
}
tail = &(*csb)->csb_rpt[stream];
if (!(relation = tail->csb_relation) ||
!(field =
MET_get_field(relation,
(USHORT) node->nod_arg[e_fld_id]))) break;
/* if this is a modify or store, check REFERENCES access to any foreign keys. */
/* CVC: This is against the SQL standard. REFERENCES should be enforced only at the
time the FK is defined in DDL, not when a DML is going to be executed.
if (((tail->csb_flags & csb_modify)
|| (tail->csb_flags & csb_store)) && !(relation->rel_view_rse
||
relation->rel_file))
IDX_check_access(tdbb, *csb, tail->csb_view, relation,
field);
*/
/* Posting the required privilege access to the current relation and field. */
/* if this is in a "validate_subtree" then we must not
post access checks to the table and the fields in the table.
If any node of the parse tree is a nod_validate type node,
the nodes in the subtree are involved in a validation
clause only, the subtree is a validate_subtree in our
notation. */
if (tail->csb_flags & csb_modify) {
if (!validate_expr) {
CMP_post_access(tdbb, *csb, relation->rel_security_name,
(tail->csb_view) ? tail->csb_view : view,
0, 0, SCL_sql_update, object_table,
relation->rel_name);
CMP_post_access(tdbb, *csb, field->fld_security_name,
(tail->csb_view) ? tail->csb_view : view,
0, 0, SCL_sql_update, object_column,
field->fld_name);
}
}
else if (tail->csb_flags & csb_erase) {
CMP_post_access(tdbb, *csb, relation->rel_security_name,
(tail->csb_view) ? tail->csb_view : view,
0, 0, SCL_sql_delete, object_table,
relation->rel_name);
}
else if (tail->csb_flags & csb_store) {
CMP_post_access(tdbb, *csb, relation->rel_security_name,
(tail->csb_view) ? tail->csb_view : view,
0, 0, SCL_sql_insert, object_table,
relation->rel_name);
CMP_post_access(tdbb, *csb, field->fld_security_name,
(tail->csb_view) ? tail->csb_view : view, 0,
0, SCL_sql_insert, object_column, field->fld_name);
}
else {
CMP_post_access(tdbb, *csb, relation->rel_security_name,
(tail->csb_view) ? tail->csb_view : view,
0, 0, SCL_read, object_table, relation->rel_name);
CMP_post_access(tdbb, *csb, field->fld_security_name,
(tail->csb_view) ? tail->csb_view : view,
0, 0, SCL_read, object_column, field->fld_name);
}
if (!(sub = field->fld_computation) && !(sub = field->fld_source)) {
if (!relation->rel_view_rse)
break;
ERR_post(gds_no_field_access,
gds_arg_string, ERR_cstring(field->fld_name),
gds_arg_string, ERR_cstring(relation->rel_name), 0);
/* Msg 364 "cannot access column %s in view %s" */
}
/* The previous test below is an apparent temporary fix
* put in by Root & Harrison in Summer/Fall 1991.
* Old Code:
* if (tail->csb_flags & (csb_view_update | csb_trigger))
* break;
* If the field is a computed field - we'll go on and make
* the substitution.
* Comment 1994-August-08 David Schnepper
*/
if (tail->csb_flags & (csb_view_update | csb_trigger)) {
if (!(field->fld_computation))
break;
}
if (!(map = tail->csb_map)) {
map = local_map;
/* TMN: Here we should really have the following assert */
/* assert(stream + 2 <= MAX_UCHAR); */
local_map[0] = (UCHAR) stream;
map[1] = stream + 1;
map[2] = stream + 2;
}
sub = copy(tdbb, csb, sub, map, 0, FALSE);
return pass1(tdbb, csb, sub, view, view_stream, validate_expr);
}
case nod_assignment:
{
FLD field;
sub = node->nod_arg[e_asgn_from];
if (sub->nod_type == nod_field) {
stream = (USHORT) sub->nod_arg[e_fld_stream];
field = MET_get_field((*csb)->csb_rpt[stream].csb_relation,
(USHORT) sub->nod_arg[e_fld_id]);
if (field)
node->nod_arg[e_asgn_missing2] = field->fld_missing_value;
}
sub = node->nod_arg[e_asgn_to];
if (sub->nod_type != nod_field)
break;
stream = (USHORT) sub->nod_arg[e_fld_stream];
tail = &(*csb)->csb_rpt[stream];
if (!
(field =
MET_get_field(tail->csb_relation,
(USHORT) sub->nod_arg[e_fld_id]))) break;
if (field->fld_missing_value) {
node->nod_arg[e_asgn_missing] = field->fld_missing_value;
node->nod_count = 3;
}
}
break;
case nod_modify:
stream = (USHORT) node->nod_arg[e_mod_new_stream];
tail = &(*csb)->csb_rpt[stream];
tail->csb_flags |= csb_modify;
pass1_modify(tdbb, csb, node);
/* TMN: Here we should really have the following assert */
/* assert(node->nod_arg [e_mod_new_stream] <= MAX_USHORT); */
if ( (node->nod_arg[e_mod_validate] =
make_validation(tdbb, csb,
(USHORT) node->
nod_arg[e_mod_new_stream])) ) node->nod_count =
MAX(node->nod_count, (USHORT) e_mod_validate + 1);
break;
case nod_erase:
stream = (USHORT) node->nod_arg[e_erase_stream];
tail = &(*csb)->csb_rpt[stream];
tail->csb_flags |= csb_erase;
pass1_erase(tdbb, csb, node);
break;
case nod_exec_proc:
procedure = (PRC) node->nod_arg[e_esp_procedure];
post_procedure_access(tdbb, *csb, procedure);
CMP_post_resource(tdbb, &(*csb)->csb_resources, (BLK) procedure,
rsc_procedure, procedure->prc_id);
break;
case nod_store:
sub = node->nod_arg[e_sto_relation];
stream = (USHORT) sub->nod_arg[e_rel_stream];
tail = &(*csb)->csb_rpt[stream];
tail->csb_flags |= csb_store;
sub = pass1_store(tdbb, csb, node);
if (sub) {
stream = (USHORT) sub->nod_arg[e_rel_stream];
if ((!node->nod_arg[e_sto_sub_store]) &&
(node->nod_arg[e_sto_validate] =
make_validation(tdbb, csb, stream))) node->nod_count =
MAX(node->nod_count, (USHORT) e_sto_validate + 1);
node->nod_arg[e_sto_statement] =
make_defaults(tdbb, csb, stream,
node->nod_arg[e_sto_statement]);
}
break;
case nod_rse:
case nod_stream:
return (NOD) pass1_rse(tdbb, csb, (RSE) node, view, view_stream);
case nod_max:
case nod_min:
case nod_average:
case nod_from:
case nod_count:
case nod_count2:
case nod_total:
ignore_dbkey(tdbb, *csb, (RSE) node->nod_arg[e_stat_rse], view);
break;
case nod_aggregate:
(*csb)->csb_rpt[(USHORT) node->nod_arg[e_agg_stream]].csb_flags |=
csb_no_dbkey;
ignore_dbkey(tdbb, *csb, (RSE) node->nod_arg[e_agg_rse], view);
node->nod_arg[e_agg_rse] =
pass1(tdbb, csb, node->nod_arg[e_agg_rse], view, view_stream,
validate_expr);
node->nod_arg[e_agg_map] =
pass1(tdbb, csb, node->nod_arg[e_agg_map], view, view_stream,
validate_expr);
node->nod_arg[e_agg_group] =
pass1(tdbb, csb, node->nod_arg[e_agg_group], view, view_stream,
validate_expr);
break;
case nod_gen_id:
case nod_gen_id2:
node->nod_arg[e_gen_value] =
pass1(tdbb, csb, node->nod_arg[e_gen_value], view, view_stream,
validate_expr);
return node;
case nod_rec_version:
case nod_dbkey:
{
LLS stack;
NOD_T type;
type = node->nod_type;
stream = (USHORT) node->nod_arg[0];
if (!(*csb)->csb_rpt[stream].csb_map)
return node;
stack = NULL;
expand_view_nodes(tdbb, *csb, stream, &stack, type);
if (stack)
return catenate_nodes(tdbb, stack);
/* The user is asking for the dbkey/record version of an aggregate.
Humor him with a key filled with zeros.
*/
node = PAR_make_node(tdbb, 1);
node->nod_count = 0;
node->nod_type = type;
node->nod_flags |= nod_agg_dbkey;
node->nod_arg[0] = (NOD) (SLONG) stream;
return node;
}
case nod_function:
pass1(tdbb, csb, node->nod_arg[e_fun_args], view, view_stream,
validate_expr);
break;
case nod_ansi_all:
case nod_ansi_any:
case nod_any:
case nod_exists:
case nod_unique:
ignore_dbkey(tdbb, *csb, (RSE) node->nod_arg[e_any_rse], view);
break;
case nod_cardinality:
stream = (USHORT) node->nod_arg[e_card_stream];
(*csb)->csb_rpt[stream].csb_flags |= csb_compute;
break;
default:
break;
}
/* Handle sub-expressions here */
ptr = node->nod_arg;
for (end = ptr + node->nod_count; ptr < end; ptr++)
*ptr = pass1(tdbb, csb, *ptr, view, view_stream, validate_expr);
/* perform any post-processing here */
if (node->nod_type == nod_assignment) {
sub = node->nod_arg[e_asgn_to];
if (sub->nod_type != nod_field &&
sub->nod_type != nod_argument && sub->nod_type != nod_variable)
ERR_post(gds_read_only_field, 0);
}
return node;
}
static void pass1_erase(TDBB tdbb, CSB * csb, NOD node)
{
/**************************************
*
* p a s s 1 _ e r a s e
*
**************************************
*
* Functional description
* Checkout an erase statement. If it references a view, and
* is kosher, fix it up.
*
**************************************/
REL relation, parent, view;
NOD source, view_node;
UCHAR *map;
USHORT stream, new_stream, parent_stream = 0;
TRIG_VEC trigger;
csb_repeat *tail;
USHORT priv;
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(node, type_nod);
/* If updateable views with triggers are involved, there
maybe a recursive call to be ignored */
if (node->nod_arg[e_erase_sub_erase])
return;
parent = view = NULL;
/* To support views of views, loop until we hit a real relation */
for (;;) {
stream = new_stream = (USHORT) node->nod_arg[e_erase_stream];
tail = &(*csb)->csb_rpt[stream];
tail->csb_flags |= csb_erase;
relation = (*csb)->csb_rpt[stream].csb_relation;
view = (relation->rel_view_rse) ? relation : view;
if (!parent)
parent = tail->csb_view;
post_trigger_access(tdbb, *csb, relation, relation->rel_pre_erase, view);
post_trigger_access(tdbb, *csb, relation, relation->rel_post_erase, view);
/* If this is a view trigger operation, get an extra stream to play with */
trigger =
(relation->rel_pre_erase) ? relation->
rel_pre_erase : relation->rel_post_erase;
if (relation->rel_view_rse && trigger) {
new_stream = (*csb)->csb_n_stream++;
node->nod_arg[e_erase_stream] = (NOD) (SLONG) new_stream;
CMP_csb_element(csb, new_stream)->csb_relation = relation;
}
/* Check out delete. If this is a delete thru a view, verify the
view by checking for read access on the base table. If field-level select
privileges are implemented, this needs to be enhanced. */
priv = SCL_sql_delete;
if (parent)
priv |= SCL_read;
source =
pass1_update(tdbb, csb, relation, trigger, stream, new_stream,
priv, parent, parent_stream);
if ((*csb)->csb_rpt[new_stream].csb_flags & csb_view_update) {
/* We have a view either updateable or non-updateable */
node->nod_arg[e_erase_statement] =
pass1_expand_view(tdbb, *csb, stream, new_stream, FALSE);
node->nod_count =
MAX(node->nod_count, (USHORT) e_erase_statement + 1);
}
if (!source)
return;
/* We have a updateable view. If there is a trigger on it, create a
dummy erase record. */
map = (*csb)->csb_rpt[stream].csb_map;
if (trigger) {
view_node = copy(tdbb, csb, node, map, 0, FALSE);
node->nod_arg[e_erase_sub_erase] = view_node;
node->nod_count =
MAX(node->nod_count, (USHORT) e_erase_sub_erase + 1);
node = view_node;
node->nod_arg[e_erase_statement] = 0;
node->nod_arg[e_erase_sub_erase] = 0;
}
else
(*csb)->csb_rpt[new_stream].csb_flags &= ~csb_view_update;
/* So far, so good. Lookup view context in instance map to get target
stream */
parent = relation;
parent_stream = stream;
new_stream = (USHORT) source->nod_arg[e_rel_stream];
node->nod_arg[e_erase_stream] = (NOD) (SLONG) map[new_stream];
}
}
static NOD pass1_expand_view(
TDBB tdbb,
CSB csb,
USHORT org_stream,
USHORT new_stream, USHORT remap)
{
/**************************************
*
* p a s s 1 _ e x p a n d _ v i e w
*
**************************************
*
* Functional description
* Process a view update performed by a trigger.
*
**************************************/
NOD assign, node;
REL relation;
VEC fields;
vec::iterator ptr, end;
FLD field;
LLS stack;
USHORT id = 0, new_id = 0;
DSC desc;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
stack = NULL;
relation = csb->csb_rpt[org_stream].csb_relation;
fields = relation->rel_fields;
for (ptr = fields->begin(), end = fields->end(), id = 0;
ptr < end; ptr++, id++)
if (*ptr && !((FLD)(*ptr))->fld_computation) {
if (remap) {
field = MET_get_field(relation, id);
if (field->fld_source)
new_id =
(USHORT) (NOD) (field->fld_source)->nod_arg[e_fld_id];
else
new_id = id;
}
else
new_id = id;
node = PAR_gen_field(tdbb, new_stream, new_id);
CMP_get_desc(tdbb, csb, node, &desc);
if (!desc.dsc_address) {
delete node;
continue;
}
assign = PAR_make_node(tdbb, e_asgn_length);
assign->nod_type = nod_assignment;
assign->nod_arg[e_asgn_to] = node;
assign->nod_arg[e_asgn_from] =
PAR_gen_field(tdbb, org_stream, id);
LLS_PUSH(assign, &stack);
}
return PAR_make_list(tdbb, stack);
}
static void pass1_modify(TDBB tdbb, CSB * csb, NOD node)
{
/**************************************
*
* p a s s 1 _ m o d i f y
*
**************************************
*
* Functional description
* Process a source for a modify statement. This can
* get a little tricky if the relation is a view.
*
**************************************/
NOD source, view_node;
REL relation, parent, view;
UCHAR *map;
USHORT view_stream, stream, new_stream, parent_stream = 0;
TRIG_VEC trigger;
csb_repeat *tail;
USHORT priv;
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(node, type_nod);
/* If updateable views with triggers are involved, there
maybe a recursive call to be ignored */
if (node->nod_arg[e_mod_sub_mod])
return;
parent = view = NULL;
/* To support views of views, loop until we hit a real relation */
for (;;) {
stream = (USHORT) node->nod_arg[e_mod_org_stream];
new_stream = (USHORT) node->nod_arg[e_mod_new_stream];
tail = &(*csb)->csb_rpt[new_stream];
tail->csb_flags |= csb_modify;
relation = (*csb)->csb_rpt[stream].csb_relation;
view = (relation->rel_view_rse) ? relation : view;
if (!parent)
parent = tail->csb_view;
post_trigger_access(tdbb, *csb, relation, relation->rel_pre_modify, view);
post_trigger_access(tdbb, *csb, relation, relation->rel_post_modify, view);
trigger =
(relation->rel_pre_modify) ? relation->
rel_pre_modify : relation->rel_post_modify;
/* Check out update. If this is an update thru a view, verify the
view by checking for read access on the base table. If field-level select
privileges are implemented, this needs to be enhanced. */
priv = SCL_sql_update;
if (parent)
priv |= SCL_read;
if (!(source = pass1_update(tdbb, csb, relation, trigger, stream,
new_stream, priv, parent, parent_stream))) {
if ((*csb)->csb_rpt[new_stream].csb_flags & csb_view_update) {
node->nod_arg[e_mod_map_view] =
pass1_expand_view(tdbb, *csb, stream, new_stream, FALSE);
node->nod_count =
MAX(node->nod_count, (USHORT) e_mod_map_view + 1);
}
return;
}
parent = relation;
parent_stream = stream;
if (trigger) {
node->nod_arg[e_mod_map_view] =
pass1_expand_view(tdbb, *csb, stream, new_stream, FALSE);
node->nod_count =
MAX(node->nod_count, (USHORT) e_mod_map_view + 1);
map = (*csb)->csb_rpt[stream].csb_map;
stream = (USHORT) source->nod_arg[e_rel_stream];
stream = map[stream];
view_stream = new_stream;
/* Next, do update stream */
map =
alloc_map(tdbb, csb,
(SSHORT) node->nod_arg[e_mod_new_stream]);
source = copy(tdbb, csb, source, map, 0, FALSE);
/* TMN: Here we should really have the following assert */
/* assert(source->nod_arg [e_rel_stream] <= MAX_UCHAR); */
map[new_stream] = (UCHAR) source->nod_arg[e_rel_stream];
view_node = copy(tdbb, csb, node, map, 0, TRUE);
view_node->nod_arg[e_mod_org_stream] = (NOD) (SLONG) stream;
view_node->nod_arg[e_mod_new_stream] =
source->nod_arg[e_rel_stream];
view_node->nod_arg[e_mod_map_view] = NULL;
node->nod_arg[e_mod_sub_mod] = view_node;
new_stream = (USHORT) source->nod_arg[e_rel_stream];
view_node->nod_arg[e_mod_statement] =
pass1_expand_view(tdbb, *csb, view_stream, new_stream, TRUE);
node->nod_count =
MAX(node->nod_count, (USHORT) e_mod_sub_mod + 1);
node = view_node;
}
else {
(*csb)->csb_rpt[new_stream].csb_flags &= ~csb_view_update;
/* View passes muster -- do some translation. Start with source stream */
map = (*csb)->csb_rpt[stream].csb_map;
stream = (USHORT) source->nod_arg[e_rel_stream];
node->nod_arg[e_mod_org_stream] = (NOD) (SLONG) map[stream];
/* Next, do update stream */
map =
alloc_map(tdbb, csb,
(SSHORT) node->nod_arg[e_mod_new_stream]);
source = copy(tdbb, csb, source, map, 0, FALSE);
node->nod_arg[e_mod_new_stream] = source->nod_arg[e_rel_stream];
}
}
}
static RSE pass1_rse(
TDBB tdbb,
CSB * csb, RSE rse, REL view, USHORT view_stream)
{
/**************************************
*
* p a s s 1 _ r s e
*
**************************************
*
* Functional description
* Process a record select expression during pass 1 of compilation.
* Mostly this involves expanding views.
*
**************************************/
USHORT count;
LLS stack, temp;
NOD *arg, *end, boolean, sort, project, first, skip, plan;
#ifdef SCROLLABLE_CURSORS
NOD async_message;
#endif
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(rse, type_nod);
DEV_BLKCHK(view, type_rel);
/* for scoping purposes, maintain a stack of rse's which are
currently being parsed; if there are none on the stack as
yet, mark the rse as variant to make sure that statement-
level aggregates are not treated as invariants--bug #6535 */
if (!(*csb)->csb_current_rses)
rse->nod_flags |= rse_variant;
LLS_PUSH(rse, &(*csb)->csb_current_rses);
stack = NULL;
boolean = NULL;
sort = rse->rse_sorted;
project = rse->rse_projection;
first = rse->rse_first;
skip = rse->rse_skip;
plan = rse->rse_plan;
#ifdef SCROLLABLE_CURSORS
async_message = rse->rse_async_message;
#endif
/* Zip thru rse expanding views and inner joins */
for (arg = rse->rse_relation, end = arg + rse->rse_count; arg < end;
arg++) pass1_source(tdbb, csb, rse, *arg, &boolean, &stack, view,
view_stream);
/* Now, rebuild the rse block. If possible, re-use the old block,
otherwise allocate a new one. */
for (count = 0, temp = stack; temp; temp = temp->lls_next)
++count;
if (count != rse->rse_count) {
RSE new_rse;
new_rse = (RSE) PAR_make_node(tdbb, count + rse_delta + 2);
*new_rse = *rse;
new_rse->rse_count = count;
rse = new_rse;
}
arg = rse->rse_relation + count;
while (stack)
*--arg = (NOD) LLS_POP(&stack);
/* Finish of by processing other clauses */
if (first)
rse->rse_first = pass1(tdbb, csb, first, view, view_stream, FALSE);
if (skip)
rse->rse_skip = pass1(tdbb, csb, skip, view, view_stream, FALSE);
if (boolean) {
if (rse->rse_boolean) {
NOD additional;
additional = PAR_make_node(tdbb, 2);
additional->nod_type = nod_and;
additional->nod_arg[0] = boolean;
additional->nod_arg[1] =
pass1(tdbb, csb, rse->rse_boolean, view, view_stream, FALSE);
rse->rse_boolean = additional;
}
else
rse->rse_boolean = boolean;
}
else
rse->rse_boolean =
pass1(tdbb, csb, rse->rse_boolean, view, view_stream, FALSE);
if (sort)
rse->rse_sorted = pass1(tdbb, csb, sort, view, view_stream, FALSE);
if (project)
rse->rse_projection =
pass1(tdbb, csb, project, view, view_stream, FALSE);
if (plan)
rse->rse_plan = plan;
#ifdef SCROLLABLE_CURSORS
if (async_message) {
rse->rse_async_message =
pass1(tdbb, csb, async_message, view, view_stream, FALSE);
(*csb)->csb_async_message = rse->rse_async_message;
}
#endif
/* we are no longer in the scope of this rse */
LLS_POP(&(*csb)->csb_current_rses);
return rse;
}
static void pass1_source(
TDBB tdbb,
CSB * csb,
RSE rse,
NOD source,
NOD * boolean,
LLS * stack, REL parent_view, USHORT view_stream)
{
/**************************************
*
* p a s s 1 _ s o u r c e
*
**************************************
*
* Functional description
* Process a single record source stream from an rse. Obviously,
* if the source is a view, there is more work to do.
*
**************************************/
DBB dbb;
RSE view_rse;
NOD *arg, *end, node;
REL view;
UCHAR *map;
USHORT stream;
csb_repeat *element;
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(rse, type_nod);
DEV_BLKCHK(source, type_nod);
DEV_BLKCHK(*boolean, type_nod);
DEV_BLKCHK(*stack, type_lls);
DEV_BLKCHK(parent_view, type_rel);
dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
/* in the case of an rse, it is possible that a new rse will be generated,
so wait to process the source before we push it on the stack (bug 8039) */
if (source->nod_type == nod_rse) {
RSE sub_rse;
/* The addition of the JOIN syntax for specifying inner joins causes an
rse tree to be generated, which is undesirable in the simplest case
where we are just trying to inner join more than 2 streams. If possible,
try to flatten the tree out before we go any further. */
sub_rse = (RSE) source;
if (!rse->rse_jointype && !sub_rse->rse_jointype
&& !sub_rse->rse_sorted && !sub_rse->rse_projection
&& !sub_rse->rse_first && !sub_rse->rse_plan) {
for (arg = sub_rse->rse_relation, end = arg + sub_rse->rse_count;
arg < end; arg++)
pass1_source(tdbb, csb, rse, *arg, boolean, stack,
parent_view, view_stream);
/* fold in the boolean for this inner join with the one for the parent */
if (sub_rse->rse_boolean) {
node =
pass1(tdbb, csb, sub_rse->rse_boolean, parent_view,
view_stream, FALSE);
if (*boolean) {
NOD additional;
additional = PAR_make_node(tdbb, 2);
additional->nod_type = nod_and;
additional->nod_arg[0] = node;
additional->nod_arg[1] = *boolean;
*boolean = additional;
}
else
*boolean = node;
}
return;
}
source = pass1(tdbb, csb, source, parent_view, view_stream, FALSE);
LLS_PUSH(source, stack);
return;
}
/* Assume that the source will be used. Push it on the final
stream stack */
LLS_PUSH(source, stack);
/* Special case procedure */
if (source->nod_type == nod_procedure) {
PRC procedure;
pass1(tdbb, csb, source, parent_view, view_stream, FALSE);
procedure = (PRC) source->nod_arg[e_prc_procedure];
post_procedure_access(tdbb, *csb, procedure);
CMP_post_resource(tdbb, &(*csb)->csb_resources, (BLK) procedure,
rsc_procedure, procedure->prc_id);
return;
}
/* Special case union */
if (source->nod_type == nod_union) {
pass1(tdbb, csb, source->nod_arg[e_uni_clauses], parent_view,
view_stream, FALSE);
return;
}
/* Special case group-by/global aggregates */
if (source->nod_type == nod_aggregate) {
pass1(tdbb, csb, source, parent_view, view_stream, FALSE);
return;
}
/* All the special cases are exhausted, so we must have a view or a base table;
prepare to check protection of relation when a field in the stream of the
relation is accessed */
view = (REL) source->nod_arg[e_rel_relation];
CMP_post_resource(tdbb, &(*csb)->csb_resources, (BLK) view, rsc_relation,
view->rel_id);
source->nod_arg[e_rel_view] = (NOD) parent_view;
stream = (USHORT) source->nod_arg[e_rel_stream];
element = CMP_csb_element(csb, stream);
element->csb_view = parent_view;
/* TMN: Here we should really have the following assert */
/* assert(view_stream <= MAX_UCHAR); */
element->csb_view_stream = (UCHAR) view_stream;
/* in the case where there is a parent view, find the context name */
if (parent_view) {
VCX *vcx_ptr;
for (vcx_ptr = &parent_view->rel_view_contexts; *vcx_ptr;
vcx_ptr = &(*vcx_ptr)->vcx_next)
if ((*vcx_ptr)->vcx_context ==
(USHORT) source->nod_arg[e_rel_context]) {
element->csb_alias = (*vcx_ptr)->vcx_context_name;
break;
}
}
/* Check for a view -- if not, nothing more to do */
if (!(view_rse = view->rel_view_rse)) {
return;
}
/* We've got a view, expand it */
DEBUG;
LLS_POP(stack);
map = alloc_map(tdbb, csb, stream);
/* We don't expand the view in two cases:
1) If the view has a projection, and the query rse already has a projection
defined; there is probably some way to merge these projections and do them
both at once, but for now we'll punt on that.
2) If it's part of an outer join. */
if ((view_rse->rse_projection && rse->rse_projection)
|| rse->rse_jointype) {
node = copy(tdbb, csb, (NOD) view_rse, map, 0, FALSE);
DEBUG;
LLS_PUSH(pass1(tdbb, csb, node, view, stream, FALSE), stack);
DEBUG;
return;
}
/* if we have a projection which we can bubble up to the parent rse, set the
parent rse to our projection temporarily to flag the fact that we have already
seen one so that lower-level views will not try to map their projection; the
projection will be copied and correctly mapped later, but we don't have all
the base streams yet */
if (view_rse->rse_projection)
rse->rse_projection = view_rse->rse_projection;
/* Disect view into component relations */
for (arg = view_rse->rse_relation, end = arg + view_rse->rse_count;
arg < end; arg++) {
/* this call not only copies the node, it adds any streams it finds to the map */
node = copy(tdbb, csb, *arg, map, 0, FALSE);
/* Now go out and process the base table itself. This table might also be a view,
in which case we will continue the process by recursion. */
pass1_source(tdbb, csb, rse, node, boolean, stack, view, stream);
}
/* When there is a projection in the view, copy the projection up to the query rse.
In order to make this work properly, we must remap the stream numbers of the fields
in the view to the stream number of the base table. Note that the map at this point
contains the stream numbers of the referenced relations, since it was added during the call
to copy() above. After the copy() below, the fields in the projection will reference the
base table(s) instead of the view's context (see bug #8822), so we are ready to context-
recognize them in pass1()--that is, replace the field nodes with actual field blocks. */
if (view_rse->rse_projection)
rse->rse_projection =
pass1(tdbb, csb,
copy(tdbb, csb, view_rse->rse_projection, map, 0, FALSE),
view, stream, FALSE);
/* If we encounter a boolean, copy it and retain it by ANDing it in with the
boolean on the parent view, if any. */
if (view_rse->rse_boolean) {
node =
pass1(tdbb, csb,
copy(tdbb, csb, view_rse->rse_boolean, map, 0, FALSE), view,
stream, FALSE);
if (*boolean) {
NOD additional;
/* The order of the nodes here is important! The
boolean from the view must appear first so that
it gets expanded first in pass1. */
additional = PAR_make_node(tdbb, 2);
additional->nod_type = nod_and;
additional->nod_arg[0] = node;
additional->nod_arg[1] = *boolean;
*boolean = additional;
}
else
*boolean = node;
}
return;
}
static NOD pass1_store(TDBB tdbb, CSB * csb, NOD node)
{
/**************************************
*
* p a s s 1 _ s t o r e
*
**************************************
*
* Functional description
* Process a source for a store statement. This can get a little tricky if
* the relation is a view.
*
**************************************/
NOD source, original, view_node, very_orig;
REL relation, parent, view;
UCHAR *map;
USHORT stream, new_stream, trigger_seen, parent_stream = 0;
TRIG_VEC trigger;
csb_repeat *tail;
USHORT priv;
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(node, type_nod);
/* If updateable views with triggers are involved, there
maybe a recursive call to be ignored */
if (node->nod_arg[e_sto_sub_store])
return NULL;
parent = view = NULL;
trigger_seen = FALSE;
very_orig = node->nod_arg[e_sto_relation];
/* To support views of views, loop until we hit a real relation */
for (;;) {
original = node->nod_arg[e_sto_relation];
stream = (USHORT) original->nod_arg[e_rel_stream];
tail = &(*csb)->csb_rpt[stream];
tail->csb_flags |= csb_store;
relation = (*csb)->csb_rpt[stream].csb_relation;
view = (relation->rel_view_rse) ? relation : view;
if (!parent)
parent = tail->csb_view;
post_trigger_access(tdbb, *csb, relation, relation->rel_pre_store, view);
post_trigger_access(tdbb, *csb, relation, relation->rel_post_store, view);
trigger =
(relation->rel_pre_store) ? relation->
rel_pre_store : relation->rel_post_store;
/* Check out insert. If this is an insert thru a view, verify the
view by checking for read access on the base table. If field-level select
privileges are implemented, this needs to be enhanced. */
priv = SCL_sql_insert;
if (parent)
priv |= SCL_read;
if (!
(source =
pass1_update(tdbb, csb, relation, trigger, stream, stream, priv,
parent, parent_stream))) {
CMP_post_resource(tdbb, &(*csb)->csb_resources, (BLK) relation,
rsc_relation, relation->rel_id);
return very_orig;
}
/* View passes muster -- do some translation */
parent = relation;
parent_stream = stream;
map = alloc_map(tdbb, csb, stream);
if (!trigger) {
(*csb)->csb_rpt[stream].csb_flags &= ~csb_view_update;
node->nod_arg[e_sto_relation] =
copy(tdbb, csb, source, map, 0, FALSE);
if (!trigger_seen)
very_orig = node->nod_arg[e_sto_relation];
}
else {
CMP_post_resource(tdbb, &(*csb)->csb_resources, (BLK) relation,
rsc_relation, relation->rel_id);
trigger_seen = TRUE;
view_node = copy(tdbb, csb, node, map, 0, FALSE);
node->nod_arg[e_sto_sub_store] = view_node;
node->nod_count =
MAX(node->nod_count, (USHORT) e_sto_sub_store + 1);
view_node->nod_arg[e_sto_sub_store] = 0;
node = view_node;
node->nod_arg[e_sto_relation] =
copy(tdbb, csb, source, map, 0, FALSE);
new_stream =
(USHORT) node->nod_arg[e_sto_relation]->nod_arg[e_rel_stream];
node->nod_arg[e_sto_statement] =
pass1_expand_view(tdbb, *csb, stream, new_stream, TRUE);
node->nod_arg[e_sto_statement] =
copy(tdbb, csb, node->nod_arg[e_sto_statement],
(UCHAR *) NULL, 0, FALSE);
/* bug 8150: use of blr_store2 against a view with a trigger was causing
the second statement to be executed, which is not desirable */
node->nod_arg[e_sto_statement2] = NULL;
}
}
}
static NOD pass1_update(
TDBB tdbb,
CSB * csb,
REL relation,
TRIG_VEC trigger,
USHORT stream,
USHORT update_stream, USHORT priv, REL view, USHORT view_stream)
{
/**************************************
*
* p a s s 1 _ u p d a t e
*
**************************************
*
* Functional description
* Check out a prospective update to a relation. If it fails
* security check, bounce it. If it's a view update, make sure
* the view is updatable, and return the view source for redirection.
* If it's a simple relation, return NULL.
*
**************************************/
RSE rse;
NOD node;
SET_TDBB(tdbb);
DEV_BLKCHK(*csb, type_csb);
DEV_BLKCHK(relation, type_rel);
//DEV_BLKCHK(trigger, type_vec);
DEV_BLKCHK(view, type_rel);
/* Unless this is an internal request, check access permission */
CMP_post_access(tdbb, *csb, relation->rel_security_name, view,
0, 0, priv, object_table, relation->rel_name);
/* ensure that the view is set for the input streams,
so that access to views can be checked at the field level */
CMP_csb_element(csb, stream)->csb_view = view;
/* TMN: Here we should really have the following assert */
/* assert(view_stream <= MAX_UCHAR); */
CMP_csb_element(csb, stream)->csb_view_stream = (UCHAR) view_stream;
CMP_csb_element(csb, update_stream)->csb_view = view;
/* TMN: Here we should really have the following assert */
/* assert(view_stream <= MAX_UCHAR); */
CMP_csb_element(csb, update_stream)->csb_view_stream =
(UCHAR) view_stream;
/* If we're not a view, everything's cool */
if (!(rse = relation->rel_view_rse))
return NULL;
/* We've got a view, is it updateable? */
if (rse->rse_count != 1 ||
rse->rse_projection ||
rse->rse_sorted ||
!(node = rse->rse_relation[0]) || node->nod_type != nod_relation) {
/* We've got a non-updateable view. If there's a trigger,
don't expand it */
if (trigger) {
(*csb)->csb_rpt[update_stream].csb_flags |= csb_view_update;
return NULL;
}
else {
ERR_post(gds_read_only_view,
gds_arg_string, relation->rel_name, 0);
return ((NOD) NULL); /* Added to remove compiler warnings */
}
}
else {
/* It's an updateable view, return the view source */
(*csb)->csb_rpt[update_stream].csb_flags |= csb_view_update;
return rse->rse_relation[0];
}
}
static NOD pass2(TDBB tdbb, register CSB csb, register NOD node, NOD parent)
{
/**************************************
*
* p a s s 2
*
**************************************
*
* Functional description
* Allocate and assign impure space for various nodes.
*
**************************************/
NOD rse_node, *ptr, *end;
ULONG id;
USHORT stream;
RSB *rsb_ptr;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(node, type_nod);
DEV_BLKCHK(parent, type_nod);
if (!node)
return node;
if (parent)
node->nod_parent = parent;
/* If there is processing to be done before sub expressions, do it here */
DEBUG;
rse_node = NULL;
switch (node->nod_type) {
case nod_rse:
return NULL;
case nod_union:
return pass2_union(tdbb, csb, node);
case nod_for:
rse_node = node->nod_arg[e_for_re];
rsb_ptr = (RSB *) & node->nod_arg[e_for_rsb];
#ifdef SCROLLABLE_CURSORS
csb->csb_current_rse = rse_node;
#endif
break;
#ifdef SCROLLABLE_CURSORS
case nod_seek:
case nod_seek_no_warn:
/* store the rse in whose scope we are defined */
node->nod_arg[e_seek_rse] = (NOD) csb->csb_current_rse;
break;
#endif
case nod_max:
case nod_min:
case nod_count:
case nod_count2:
case nod_average:
case nod_total:
case nod_from:
rse_node = node->nod_arg[e_stat_rse];
if (!(rse_node->nod_flags & rse_variant)) {
node->nod_flags |= nod_invariant;
LLS_PUSH(node, &csb->csb_invariants);
}
rsb_ptr = (RSB *) & node->nod_arg[e_stat_rsb];
break;
case nod_ansi_all:
case nod_ansi_any:
case nod_any:
case nod_exists:
case nod_unique:
rse_node = node->nod_arg[e_any_rse];
if (!(rse_node->nod_flags & rse_variant)) {
node->nod_flags |= nod_invariant;
LLS_PUSH(node, &csb->csb_invariants);
}
rsb_ptr = (RSB *) & node->nod_arg[e_any_rsb];
break;
case nod_sort:
ptr = node->nod_arg;
for (end = ptr + node->nod_count; ptr < end; ptr++)
(*ptr)->nod_flags |= nod_value;
break;
case nod_function:
{
NOD value;
FUN function;
value = node->nod_arg[e_fun_args];
function = (FUN) node->nod_arg[e_fun_function];
pass2(tdbb, csb, value, node);
/* For gbak attachments, there is no need to resolve the UDF function */
/* Also if we are dropping a procedure don't bother resolving the
UDF that the procedure invokes.
*/
if (!(tdbb->tdbb_attachment->att_flags & ATT_gbak_attachment) &&
!(tdbb->tdbb_flags & TDBB_prc_being_dropped)) {
node->nod_arg[e_fun_function] =
(NOD) FUN_resolve(csb, function, value);
if (!node->nod_arg[e_fun_function])
ERR_post(gds_funmismat, gds_arg_string,
function->fun_symbol->sym_string, 0);
}
}
break;
#ifdef PC_ENGINE
/* the remainder of the node types are for IDAPI support:
fix up the stream to point to the base table, and preserve
the pointers to the navigational rsb for easy reference
later during execution */
case nod_stream:
{
NOD relation;
RSE rse;
rse = (RSE) node;
rse_node = node;
/* setting the stream flag will allow the optimizer to
detect that a SET INDEX may be done on this stream */
rse_node->nod_flags |= rse_stream;
rsb_ptr = &rse->rse_rsb;
relation = rse->rse_relation[0];
stream = base_stream(csb, &relation->nod_arg[e_rel_stream], TRUE);
csb->csb_rpt[stream].csb_rsb_ptr = &rse->rse_rsb;
}
break;
case nod_find:
stream = base_stream(csb, &node->nod_arg[e_find_stream], TRUE);
if (!
(node->nod_arg[e_find_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
case nod_find_dbkey:
case nod_find_dbkey_version:
stream = base_stream(csb, &node->nod_arg[e_find_dbkey_stream], TRUE);
if (!
(node->nod_arg[e_find_dbkey_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
case nod_set_index:
stream = base_stream(csb, &node->nod_arg[e_index_stream], TRUE);
if (!
(node->nod_arg[e_index_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
case nod_get_bookmark:
stream = base_stream(csb, &node->nod_arg[e_getmark_stream], TRUE);
if (!
(node->nod_arg[e_getmark_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
case nod_set_bookmark:
stream = base_stream(csb, &node->nod_arg[e_setmark_stream], TRUE);
if (!
(node->nod_arg[e_setmark_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
case nod_lock_record:
stream = base_stream(csb, &node->nod_arg[e_lockrec_stream], TRUE);
if (!
(node->nod_arg[e_lockrec_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
case nod_crack:
case nod_force_crack:
stream = base_stream(csb, &node->nod_arg[0], TRUE);
if (!(node->nod_arg[1] = (NOD) csb->csb_rpt[stream].csb_rsb_ptr))
ERR_post(gds__stream_not_defined, 0);
break;
case nod_reset_stream:
stream = base_stream(csb, &node->nod_arg[e_reset_from_stream], TRUE);
if (!
(node->nod_arg[e_reset_from_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
case nod_cardinality:
stream = base_stream(csb, &node->nod_arg[e_card_stream], TRUE);
if (!
(node->nod_arg[e_card_rsb] =
(NOD) csb->
csb_rpt[stream].csb_rsb_ptr)) ERR_post(gds__stream_not_defined,
0);
break;
/* the following DML nodes need to have their rsb's stored when
they are referencing a navigational stream, so that we can
follow proper IDAPI semantics in manipulating a stream */
case nod_erase:
stream = base_stream(csb, &node->nod_arg[e_erase_stream], FALSE);
node->nod_arg[e_erase_rsb] = (NOD) csb->csb_rpt[stream].csb_rsb_ptr;
break;
case nod_modify:
stream = base_stream(csb, &node->nod_arg[e_mod_org_stream], FALSE);
node->nod_arg[e_mod_rsb] = (NOD) csb->csb_rpt[stream].csb_rsb_ptr;
break;
#endif
default:
break;
}
if (rse_node)
pass2_rse(tdbb, csb, (RSE) rse_node);
/* Handle sub-expressions here */
ptr = node->nod_arg;
for (end = ptr + node->nod_count; ptr < end; ptr++)
pass2(tdbb, csb, *ptr, node);
/* Handle any residual work */
node->nod_impure = CMP_impure(csb, 0);
switch (node->nod_type) {
case nod_assignment:
{
NOD value;
if ( (value = node->nod_arg[e_asgn_missing2]) )
pass2(tdbb, csb, value, node);
}
break;
case nod_average:
case nod_agg_average:
case nod_agg_average_distinct:
node->nod_flags |= nod_double;
/* FALL INTO */
case nod_max:
case nod_min:
case nod_from:
case nod_count:
case nod_agg_count2:
case nod_agg_count_distinct:
case nod_count2:
case nod_agg_min:
case nod_agg_max:
case nod_agg_count:
node->nod_count = 0;
csb->csb_impure += sizeof(struct vlux);
break;
case nod_ansi_all:
case nod_ansi_any:
case nod_any:
case nod_exists:
case nod_unique:
if (node->nod_flags & nod_invariant)
csb->csb_impure += sizeof(struct vlu);
break;
case nod_block:
csb->csb_impure += sizeof(SLONG);
break;
case nod_dcl_variable:
{
DSC *desc;
desc = (DSC *) (node->nod_arg + e_dcl_desc);
csb->csb_impure += sizeof(struct vlu) + desc->dsc_length;
}
break;
case nod_agg_total:
case nod_agg_total_distinct:
case nod_total:
case nod_agg_total2:
case nod_agg_total_distinct2:
{
DSC descriptor_a;
node->nod_count = 0;
csb->csb_impure += sizeof(struct vlu);
CMP_get_desc(tdbb, csb, node, &descriptor_a);
}
break;
case nod_agg_average2:
case nod_agg_average_distinct2:
{
DSC descriptor_a;
node->nod_count = 0;
csb->csb_impure += sizeof(struct vlux);
CMP_get_desc(tdbb, csb, node, &descriptor_a);
}
break;
case nod_message:
{
FMT format;
format = (FMT) node->nod_arg[e_msg_format];
if (!((tdbb->tdbb_flags & TDBB_prc_being_dropped) && !format))
csb->csb_impure += FB_ALIGN(format->fmt_length, 2);
}
break;
case nod_modify:
{
FMT format;
fmt::fmt_desc_iterator desc;
stream = (USHORT) node->nod_arg[e_mod_org_stream];
csb->csb_rpt[stream].csb_flags |= csb_update;
format = CMP_format(tdbb, csb, stream);
desc = format->fmt_desc.begin();
for (id = 0; id < format->fmt_count; id++, desc++)
if (desc->dsc_dtype)
SBM_set(tdbb, &csb->csb_rpt[stream].csb_fields, id);
csb->csb_impure += sizeof(struct sta);
}
break;
case nod_list:
node->nod_type = nod_asn_list;
for (ptr = node->nod_arg; ptr < end; ptr++)
if ((*ptr)->nod_type != nod_assignment) {
node->nod_type = nod_list;
break;
}
/* FALL INTO */
case nod_store:
csb->csb_impure += sizeof(struct sta);
break;
case nod_erase:
stream = (USHORT) node->nod_arg[e_erase_stream];
csb->csb_rpt[stream].csb_flags |= csb_update;
break;
case nod_field:
stream = (USHORT) node->nod_arg[e_fld_stream];
id = (USHORT) node->nod_arg[e_fld_id];
SBM_set(tdbb, &csb->csb_rpt[stream].csb_fields, id);
if (node->nod_flags & nod_value) {
csb->csb_impure += sizeof(struct vlux);
break;
}
/* FALL INTO */
case nod_argument:
csb->csb_impure += sizeof(struct dsc);
break;
case nod_concatenate:
case nod_dbkey:
case nod_rec_version:
case nod_negate:
case nod_substr:
case nod_divide:
case nod_null:
case nod_user_name:
case nod_current_role:
case nod_internal_info:
case nod_gen_id:
case nod_gen_id2:
case nod_upcase:
case nod_prot_mask:
case nod_lock_state:
#ifdef PC_ENGINE
case nod_lock_record:
case nod_lock_relation:
#endif
case nod_scalar:
case nod_cast:
case nod_extract:
case nod_current_time:
case nod_current_timestamp:
case nod_current_date:
#ifdef PC_ENGINE
case nod_cardinality:
case nod_seek:
case nod_seek_no_warn:
case nod_crack:
case nod_begin_range:
#endif
{
DSC descriptor_a;
CMP_get_desc(tdbb, csb, node, &descriptor_a);
csb->csb_impure += sizeof(struct vlu);
}
break;
/* Compute the target descriptor to compute computational class */
case nod_multiply:
case nod_add:
case nod_subtract:
case nod_function:
case nod_add2:
case nod_subtract2:
case nod_multiply2:
case nod_divide2:
{
DSC descriptor_a;
CMP_get_desc(tdbb, csb, node, &descriptor_a);
csb->csb_impure += sizeof(struct vlu);
}
break;
case nod_aggregate:
pass2_rse(tdbb, csb, (RSE) node->nod_arg[e_agg_rse]);
pass2(tdbb, csb, node->nod_arg[e_agg_map], node);
pass2(tdbb, csb, node->nod_arg[e_agg_group], node);
stream = (USHORT) node->nod_arg[e_agg_stream];
process_map(tdbb, csb, node->nod_arg[e_agg_map],
&csb->csb_rpt[stream].csb_format);
break;
/* Boolean Nodes taking three values as inputs */
case nod_like:
case nod_between:
case nod_sleuth:
if (node->nod_count > 2) {
DSC descriptor_c;
if (node->nod_arg[2]->nod_flags & nod_agg_dbkey)
ERR_post(gds_bad_dbkey, 0);
CMP_get_desc(tdbb, csb, node->nod_arg[0], &descriptor_c);
if (DTYPE_IS_DATE(descriptor_c.dsc_dtype)) {
node->nod_arg[0]->nod_flags |= nod_date;
node->nod_arg[1]->nod_flags |= nod_date;
}
};
/* FALLINTO */
/* Boolean Nodes taking two values as inputs */
case nod_matches:
case nod_contains:
case nod_starts:
case nod_eql:
case nod_neq:
case nod_geq:
case nod_gtr:
case nod_lss:
case nod_leq:
{
DSC descriptor_a, descriptor_b;
if ((node->nod_arg[0]->nod_flags & nod_agg_dbkey) ||
(node->nod_arg[1]->nod_flags & nod_agg_dbkey))
ERR_post(gds_bad_dbkey, 0);
CMP_get_desc(tdbb, csb, node->nod_arg[0], &descriptor_a);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &descriptor_b);
if (DTYPE_IS_DATE(descriptor_a.dsc_dtype))
node->nod_arg[1]->nod_flags |= nod_date;
else if (DTYPE_IS_DATE(descriptor_b.dsc_dtype))
node->nod_arg[0]->nod_flags |= nod_date;
}
break;
/* Boolean nodes taking 1 Value as input */
case nod_missing:
{
DSC descriptor_a;
if (node->nod_arg[0]->nod_flags & nod_agg_dbkey)
ERR_post(gds_bad_dbkey, 0);
/* Check for syntax errors in the calculation */
CMP_get_desc(tdbb, csb, node->nod_arg[0], &descriptor_a);
}
break;
default:
/* Note: no assert (FALSE); here as too many nodes are missing */
break;
}
/* Finish up processing of record selection expressions */
if (rse_node)
*rsb_ptr = post_rse(tdbb, csb, (RSE) rse_node);
return node;
}
static void pass2_rse(TDBB tdbb, CSB csb, RSE rse)
{
/**************************************
*
* p a s s 2 _ r s e
*
**************************************
*
* Functional description
* Perform the first half of record selection expression compilation.
* The actual optimization is done in "post_rse".
*
**************************************/
NOD *ptr, *end;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(rse, type_nod);
if (rse->rse_first)
pass2(tdbb, csb, rse->rse_first, 0);
if (rse->rse_skip)
pass2(tdbb, csb, rse->rse_skip, 0);
for (ptr = rse->rse_relation, end = ptr + rse->rse_count;
ptr < end; ptr++) {
NOD node;
node = *ptr;
if (node->nod_type == nod_relation) {
USHORT stream;
stream = (USHORT) node->nod_arg[e_rel_stream];
csb->csb_rpt[stream].csb_flags |= csb_active;
pass2(tdbb, csb, node, (NOD) rse);
}
else if (node->nod_type == nod_rse)
pass2_rse(tdbb, csb, (RSE) node);
else
pass2(tdbb, csb, node, (NOD) rse);
}
if (rse->rse_boolean)
pass2(tdbb, csb, rse->rse_boolean, 0);
if (rse->rse_sorted)
pass2(tdbb, csb, rse->rse_sorted, 0);
if (rse->rse_projection)
pass2(tdbb, csb, rse->rse_projection, 0);
/* if the user has submitted a plan for this rse, check it for correctness */
if (rse->rse_plan) {
plan_set(csb, rse, rse->rse_plan);
plan_check(csb, rse);
}
#ifdef SCROLLABLE_CURSORS
if (rse->rse_async_message)
pass2(tdbb, csb, rse->rse_async_message, 0);
#endif
}
static NOD pass2_union(TDBB tdbb, CSB csb, NOD node)
{
/**************************************
*
* p a s s 2 _ u n i o n
*
**************************************
*
* Functional description
* Process a union clause of an rse.
*
**************************************/
NOD clauses, *ptr, *end, map;
FMT *format;
USHORT id;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(node, type_nod);
/* Make up a format block sufficiently large to hold instantiated record */
clauses = node->nod_arg[e_uni_clauses];
id = (USHORT) node->nod_arg[e_uni_stream];
format = &csb->csb_rpt[id].csb_format;
/* Process alternating rse and map blocks */
for (ptr = clauses->nod_arg, end = ptr + clauses->nod_count; ptr < end;) {
pass2_rse(tdbb, csb, (RSE) * ptr++);
map = *ptr++;
pass2(tdbb, csb, map, node);
process_map(tdbb, csb, map, format);
}
return node;
}
static void plan_check(CSB csb, RSE rse)
{
/**************************************
*
* p l a n _ c h e c k
*
**************************************
*
* Functional description
* Check that all streams in the rse have
* a plan specified for them.
* If they are not, there are streams
* in the rse which were not mentioned
* in the plan.
*
**************************************/
NOD *ptr, *end;
USHORT stream;
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(rse, type_nod);
/* if any streams are not marked with a plan, give an error */
for (ptr = rse->rse_relation, end = ptr + rse->rse_count; ptr < end;
ptr++) if ((*ptr)->nod_type == nod_relation) {
stream = (USHORT) (*ptr)->nod_arg[e_rel_stream];
if (!(csb->csb_rpt[stream].csb_plan))
ERR_post(gds_no_stream_plan, gds_arg_string,
csb->csb_rpt[stream].csb_relation->rel_name, 0);
}
}
static void plan_set(CSB csb, RSE rse, NOD plan)
{
/**************************************
*
* p l a n _ s e t
*
**************************************
*
* Functional description
* Go through the streams in the plan, find the
* corresponding streams in the rse and store the
* plan for that stream. Do it once and only once
* to make sure there is a one-to-one correspondence
* between streams in the query and streams in
* the plan.
*
**************************************/
NOD plan_relation_node, *ptr, *end;
USHORT stream;
UCHAR *map, *map_base, *duplicate_map;
REL relation, plan_relation, view_relation, duplicate_relation;
STR alias, plan_alias;
TEXT *p;
csb_repeat *tail, *duplicate_tail;
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(rse, type_nod);
DEV_BLKCHK(plan, type_nod);
if (plan->nod_type == nod_join || plan->nod_type == nod_merge) {
for (ptr = plan->nod_arg, end = ptr + plan->nod_count; ptr < end;
ptr++)
plan_set(csb, rse, *ptr);
return;
}
if (plan->nod_type != nod_retrieve)
return;
plan_relation_node = plan->nod_arg[e_retrieve_relation];
plan_relation = (REL) plan_relation_node->nod_arg[e_rel_relation];
plan_alias = (STR) plan_relation_node->nod_arg[e_rel_alias];
/* find the tail for the relation specified in the rse */
stream = (USHORT) plan_relation_node->nod_arg[e_rel_stream];
tail = &csb->csb_rpt[stream];
/* if the plan references a view, find the real base relation
we are interested in by searching the view map */
if (tail->csb_map) {
if (plan_alias)
p = (TEXT *) plan_alias->str_data;
else
p = "\0";
/* if the user has specified an alias, skip past it to find the alias
for the base table (if multiple aliases are specified) */
if (*p &&
(tail->csb_relation
&& !strcmp_space(tail->csb_relation->rel_name, p))
|| (tail->csb_alias
&& !strcmp_space(reinterpret_cast <
char *>(tail->csb_alias->str_data), p))) {
while (*p && *p != ' ')
p++;
if (*p == ' ')
p++;
}
/* loop through potentially a stack of views to find the appropriate base table */
while ( (map_base = tail->csb_map) ) {
map = map_base;
tail = &csb->csb_rpt[*map];
view_relation = tail->csb_relation;
/* if the plan references the view itself, make sure that
the view is on a single table; if it is, fix up the plan
to point to the base relation */
if (view_relation->rel_id == plan_relation->rel_id) {
if (!map_base[2]) {
map++;
tail = &csb->csb_rpt[*map];
}
else
/* view %s has more than one base relation; use aliases to distinguish */
ERR_post(gds_view_alias, gds_arg_string,
plan_relation->rel_name, 0);
break;
}
else
view_relation = NULL;
/* if the user didn't specify an alias (or didn't specify one
for this level), check to make sure there is one and only one
base relation in the table which matches the plan relation */
if (!*p) {
duplicate_relation = NULL;
duplicate_map = map_base;
map = NULL;
for (duplicate_map++; *duplicate_map; duplicate_map++) {
duplicate_tail = &csb->csb_rpt[*duplicate_map];
relation = duplicate_tail->csb_relation;
if (relation && relation->rel_id == plan_relation->rel_id) {
if (duplicate_relation)
/* table %s is referenced twice in view; use an alias to distinguish */
ERR_post(gds_duplicate_base_table,
gds_arg_string,
duplicate_relation->rel_name, 0);
else {
duplicate_relation = relation;
map = duplicate_map;
tail = duplicate_tail;
}
}
}
break;
}
/* look through all the base relations for a match */
map = map_base;
for (map++; *map; map++) {
tail = &csb->csb_rpt[*map];
relation = tail->csb_relation;
alias = tail->csb_alias;
/* match the user-supplied alias with the alias supplied
with the view definition; failing that, try the base
table name itself */
/* CVC: I found that "relation" can be NULL, too. This may be an
indication of a logic flaw while parsing the user supplied SQL plan
and not an oversight here. It's hard to imagine a csb->csb_rpt with
a NULL relation. See exe.h for csb struct and its inner csb_repeat struct. */
if (
(alias
&& !strcmp_space(reinterpret_cast <
char *>(alias->str_data), p))
|| (relation && !strcmp_space(relation->rel_name, p)))
break;
}
/* skip past the alias */
while (*p && *p != ' ')
p++;
if (*p == ' ')
p++;
if (!*map)
/* table %s is referenced in the plan but not the from list */
ERR_post(gds_stream_not_found, gds_arg_string,
plan_relation->rel_name, 0);
}
/* fix up the relation node to point to the base relation's stream */
if (!map || !*map)
/* table %s is referenced in the plan but not the from list */
ERR_post(gds_stream_not_found, gds_arg_string,
plan_relation->rel_name, 0);
plan_relation_node->nod_arg[e_rel_stream] = (NOD) (SLONG) * map;
}
/* make some validity checks */
if (!tail->csb_relation)
/* table %s is referenced in the plan but not the from list */
ERR_post(gds_stream_not_found, gds_arg_string,
plan_relation->rel_name, 0);
if ((tail->csb_relation->rel_id != plan_relation->rel_id)
&& !view_relation)
/* table %s is referenced in the plan but not the from list */
ERR_post(gds_stream_not_found, gds_arg_string,
plan_relation->rel_name, 0);
/* check if we already have a plan for this stream */
if (tail->csb_plan)
/* table %s is referenced more than once in plan; use aliases to distinguish */
ERR_post(gds_stream_twice, gds_arg_string,
tail->csb_relation->rel_name, 0);
tail->csb_plan = plan;
}
static void post_procedure_access(TDBB tdbb, CSB csb, PRC procedure)
{
/**************************************
*
* p o s t _ p r o c e d u r e _ a c c e s s
*
**************************************
*
* Functional description
*
* The request will inherit access requirements to all the objects
* the called stored procedure has access requirements for.
*
**************************************/
ACC access;
TEXT *prc_sec_name;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(procedure, type_prc);
prc_sec_name = (procedure->prc_security_name ?
(TEXT *) procedure->
prc_security_name->str_data : (TEXT *) 0);
/* This request must have EXECUTE permission on the stored procedure */
CMP_post_access(tdbb, csb, prc_sec_name, 0,
0, 0, SCL_execute,
object_procedure,
reinterpret_cast <
char *>(procedure->prc_name->str_data));
/* This request also inherits all the access requirements that
the procedure has */
if (procedure->prc_request)
for (access = procedure->prc_request->req_access; access;
access = access->acc_next) {
if (access->acc_trg_name ||
access->acc_prc_name || access->acc_view)
/* Inherited access needs from the trigger, view or SP
that this SP fires off */
CMP_post_access(tdbb, csb, access->acc_security_name,
access->acc_view, access->acc_trg_name,
access->acc_prc_name, access->acc_mask,
access->acc_type, access->acc_name);
else
/* Direct access from this SP to a resource */
CMP_post_access(tdbb, csb, access->acc_security_name,
0, 0,
reinterpret_cast <
char *>(procedure->prc_name->str_data),
access->acc_mask, access->acc_type,
access->acc_name);
}
}
static RSB post_rse(TDBB tdbb, CSB csb, RSE rse)
{
/**************************************
*
* p o s t _ r s e
*
**************************************
*
* Functional description
* Perform actual optimization of an rse and clear activity.
*
**************************************/
RSB rsb;
NOD node, *ptr, *end;
USHORT stream;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(rse, type_nod);
rsb = OPT_compile(tdbb, csb, rse, NULL);
if (rse->nod_flags & rse_singular)
rsb->rsb_flags |= rsb_singular;
#ifdef PC_ENGINE
/* this flag lets the VIO layer know to add a page to the
cache range */
if (rse->nod_flags & rse_stream)
rsb->rsb_flags |= rsb_stream_type;
#endif
/* mark all the substreams as inactive */
for (ptr = rse->rse_relation, end = ptr + rse->rse_count;
ptr < end; ptr++) {
node = *ptr;
if (node->nod_type == nod_relation) {
stream = (USHORT) node->nod_arg[e_rel_stream];
csb->csb_rpt[stream].csb_flags &= ~csb_active;
}
}
LLS_PUSH(rsb, &csb->csb_fors);
#ifdef SCROLLABLE_CURSORS
rse->rse_rsb = rsb;
#endif
return rsb;
}
static void post_trigger_access(TDBB tdbb, CSB csb, REL owner_relation, TRIG_VEC triggers, REL view)
{
/**************************************
*
* p o s t _ t r i g g e r _ a c c e s s
*
**************************************
*
* Functional description
* Inherit access to triggers to be fired.
*
* When we detect that a trigger could be fired by a request,
* then we add the access list for that trigger to the access
* list for this request. That way, when we check access for
* the request we also check access for any other objects that
* could be fired off by the request.
*
* Note that when we add the access item, we specify that
* Trigger X needs access to resource Y.
* In the access list we parse here, if there is no "accessor"
* name then the trigger must access it directly. If there is
* an "accessor" name, then something accessed by this trigger
* must require the access.
*
* CVC: The third parameter is the owner of the triggers vector
* and was added to avoid triggers posting access checks to
* their base tables, since it's nonsense and causes weird
* messages about false REFERENCES right failures.
*
**************************************/
ACC access;
trig_vec::iterator ptr, end;
USHORT read_only;
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
//DEV_BLKCHK(triggers, type_vec);
DEV_BLKCHK(view, type_rel);
if (!triggers)
return;
for (ptr = triggers->begin(), end = triggers->end(); ptr < end; ptr++) {
ptr->compile(tdbb);
if (ptr->request) {
/* CVC: Definitely, I'm going to disable this check because REFERENCES should
be checked only at DDL time. If we discover another thing in the fluffy SQL
standard, we can revisit those lines.
read_only = TRUE;
for (access = ((REQ)(*ptr))->req_access; access;
access = access->acc_next) if (access->acc_mask & ~SCL_read) {
read_only = FALSE;
break;
}
*/
/* for read-only triggers, translate a READ access into a REFERENCES;
we must check for read-only to make sure people don't abuse the
REFERENCES privilege */
for (access = ptr->request->req_access; access;
access = access->acc_next) {
/* CVC: Can't make any sense of this code, hence I disabled it.
if (read_only && (access->acc_mask & SCL_read)) {
access->acc_mask &= ~SCL_read;
access->acc_mask |= SCL_sql_references;
}
*/
if (access->acc_trg_name ||
access->acc_prc_name || access->acc_view)
{
/* If this is not a system relation, we don't post access check if:
- The table being checked is the owner of the trigger that's accessing it.
- The field being checked is owned by the same table than the trigger
that's accessing the field.
- Since the trigger name comes in the access list, we need to validate that
it's a trigger defined on our target table.
- Incidentally, access requests made through objects accessed by this trigger
are granted automatically. We should achieve the same propagation in
post_procedure_access() in the future, so the called proc/trg can use the
rights of the caller even if the latter is a procedure or a trigger, with
the difference that proc aren't bound to tables, so we need another place
instead of post_procedure_access() to achieve such propagation.
*/
if (access->acc_trg_name && !(owner_relation->rel_flags & REL_system))
{
if (!strcmp(access->acc_type, object_table)
&& !strcmp(access->acc_name, owner_relation->rel_name)
&& MET_relation_owns_trigger(tdbb, access->acc_name, access->acc_trg_name))
continue;
if (!strcmp(access->acc_type, object_column)
&& MET_relation_owns_trigger(tdbb, access->acc_name, access->acc_trg_name)
&& (MET_lookup_field(tdbb, owner_relation, access->acc_name, access->acc_security_name) >= 0
|| MET_relation_default_class(tdbb, owner_relation->rel_name, access->acc_security_name)))
continue;
}
/* Inherited access needs from "object" to acc_security_name */
CMP_post_access(tdbb, csb, access->acc_security_name,
access->acc_view,
access->acc_trg_name,
access->acc_prc_name, access->acc_mask,
access->acc_type, access->acc_name);
}
else
{
/* If this is not a system relation, we don't post access check if:
- The table being checked is the owner of the trigger that's accessing it.
- The field being checked is owned by the same table than the trigger
that's accessing the field.
- Since the trigger name comes in the triggers vector of the table and each
trigger can be owned by only one table for now, we know for sure that
it's a trigger defined on our target table.
*/
if (!(owner_relation->rel_flags & REL_system))
{
if (!strcmp(access->acc_type, object_table)
&& !strcmp(access->acc_name, owner_relation->rel_name))
continue;
if (!strcmp(access->acc_type, object_column)
&& (MET_lookup_field(tdbb, owner_relation, access->acc_name, access->acc_security_name) >= 0
|| MET_relation_default_class(tdbb, owner_relation->rel_name, access->acc_security_name)))
continue;
}
/* A direct access to an object from this trigger */
CMP_post_access(tdbb, csb, access->acc_security_name,
(access->
acc_view) ? access->acc_view : view,
ptr->request->req_trg_name, 0, access->acc_mask,
access->acc_type, access->acc_name);
}
}
}
}
}
static void process_map(TDBB tdbb, CSB csb, NOD map, FMT * input_format)
{
/**************************************
*
* p r o c e s s _ m a p
*
**************************************
*
* Functional description
* Translate a map block into a format. If the format is
* is missing or incomplete, extend it.
*
**************************************/
NOD *ptr, *end, assignment, field;
FMT format;
DSC *desc, desc2;
USHORT id, min, max, align;
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(map, type_nod);
DEV_BLKCHK(*input_format, type_fmt);
SET_TDBB(tdbb);
if (!(format = *input_format)) {
format = *input_format = fmt::newFmt(*tdbb->tdbb_default, map->nod_count);
format->fmt_count = map->nod_count;
}
/* Process alternating rse and map blocks */
ptr = map->nod_arg;
for (end = ptr + map->nod_count; ptr < end; ptr++) {
assignment = *ptr;
field = assignment->nod_arg[e_asgn_to];
id = (USHORT) field->nod_arg[e_fld_id];
if (id >= format->fmt_count) {
format->fmt_desc.resize(id + 1);
}
desc = &format->fmt_desc[id];
CMP_get_desc(tdbb, csb, assignment->nod_arg[e_asgn_from], &desc2);
min = MIN(desc->dsc_dtype, desc2.dsc_dtype);
max = MAX(desc->dsc_dtype, desc2.dsc_dtype);
if (max == dtype_blob) {
desc->dsc_dtype = dtype_quad;
desc->dsc_length = 8;
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else if (!min) /* eg: dtype_null */
*desc = desc2;
else if (min <= dtype_any_text) { /* either field a text field? */
USHORT len1, len2;
len1 = DSC_string_length(desc);
len2 = DSC_string_length(&desc2);
desc->dsc_dtype = dtype_varying;
desc->dsc_length = MAX(len1, len2) + sizeof(USHORT);
/* pick the Max text type, so any transparent casts from ints are
not left in ASCII format, but converted to the richer text format. */
INTL_ASSIGN_TTYPE(desc,
MAX(INTL_TEXT_TYPE(*desc),
INTL_TEXT_TYPE(desc2)));
desc->dsc_scale = 0;
desc->dsc_flags = 0;
}
else if (DTYPE_IS_DATE(max) && !DTYPE_IS_DATE(min)) {
desc->dsc_dtype = dtype_varying;
desc->dsc_length =
DSC_convert_to_text_length(max) + sizeof(USHORT);
desc->dsc_ttype = ttype_ascii;
desc->dsc_scale = 0;
desc->dsc_flags = 0;
}
else if (max != min) {
/* different numeric types: if one is inexact use double,
if both are exact use int64. */
if ((!DTYPE_IS_EXACT(max)) || (!DTYPE_IS_EXACT(min))) {
desc->dsc_dtype = DEFAULT_DOUBLE;
desc->dsc_length = sizeof(double);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
}
else {
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
desc->dsc_scale = MIN(desc->dsc_scale, desc2.dsc_scale);
desc->dsc_sub_type =
MAX(desc->dsc_sub_type, desc2.dsc_sub_type);
desc->dsc_flags = 0;
}
}
}
/* Flesh out the format of the record */
/* TMN: Here we should really have the following assert */
/* assert(FLAG_BYTES (format->fmt_count) <= MAX_USHORT); */
format->fmt_length = (USHORT) FLAG_BYTES(format->fmt_count);
fmt::fmt_desc_iterator desc3, end_desc;
for (desc3 = format->fmt_desc.begin(), end_desc= format->fmt_desc.end();
desc3 < end_desc; desc3++) {
align = type_alignments[desc3->dsc_dtype];
if (align)
format->fmt_length = FB_ALIGN(format->fmt_length, align);
desc3->dsc_address = (UCHAR *) (SLONG) format->fmt_length;
format->fmt_length += desc3->dsc_length;
}
}
static SSHORT strcmp_space(TEXT * p, TEXT * q)
{
/**************************************
*
* s t r c m p _ s p a c e
*
**************************************
*
* Functional description
* Compare two strings, which could be either
* space-terminated or null-terminated.
*
**************************************/
for (; *p && *p != ' ' && *q && *q != ' '; p++, q++)
if (*p != *q)
break;
if ((!*p || *p == ' ') && (!*q || *q == ' '))
return 0;
if (*p > *q)
return 1;
else
return -1;
}
static BOOLEAN stream_in_rse(USHORT stream, RSE rse)
{
/**************************************
*
* s t r e a m _ i n _ r s e
*
**************************************
*
* Functional description
* Return TRUE if stream is contained in
* the specified RSE.
*
**************************************/
NOD sub, *ptr, *end;
DEV_BLKCHK(rse, type_nod);
/* look through all relation nodes in this rse to see
if the field references this instance of the relation */
for (ptr = rse->rse_relation, end = ptr + rse->rse_count; ptr < end;
ptr++) {
sub = *ptr;
/* for aggregates, check current rse, if not found then check
the sub-rse */
if (sub->nod_type == nod_aggregate) {
if ((stream == (USHORT) sub->nod_arg[e_rel_stream]) ||
(stream_in_rse(stream, (RSE) sub->nod_arg[e_agg_rse])))
return TRUE; /* do not mark as variant */
}
if ((sub->nod_type == nod_relation) &&
(stream == (USHORT) sub->nod_arg[e_rel_stream]))
return TRUE; /* do not mark as variant */
}
return FALSE; /* mark this rse as variant */
}