8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 20:43:04 +01:00
firebird-mirror/src/dsql/ddl.cpp
asfernandes c737d6a807 Create ExprNode class hierarchy.
Convert CONCATENATE and aggregate functions to it.

Fix some bugs:
- select sum(n) over (order by n2), sum(n) over (), n, n2 from x1;
- select (select 1 from x1 b where list(1, a.n) = 1) from x1 a;
2010-02-13 20:29:29 +00:00

6202 lines
177 KiB
C++

/*
* PROGRAM: Dynamic SQL runtime support
* MODULE: ddl.cpp
* DESCRIPTION: Utilities for generating ddl
*
* 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.5.20 Claudio Valderrama: Stop null pointer that leads to a crash,
* caused by incomplete yacc syntax that allows ALTER DOMAIN dom SET;
*
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
* December 2001 Mike Nordell - Attempt to make it C++
*
* 2001.5.20 Claudio Valderrama: Stop null pointer that leads to a crash,
* caused by incomplete yacc syntax that allows ALTER DOMAIN dom SET;
* 2001.5.29 Claudio Valderrama: Check for view v/s relation in DROP
* command will stop a user that uses DROP VIEW and drops a table by
* accident and vice-versa.
* 2001.5.30 Claudio Valderrama: alter column should use 1..N for the
* position argument since the call comes from SQL DDL.
* 2001.6.27 Claudio Valderrama: DDL_resolve_intl_type() was adding 2 to the
* length of varchars instead of just checking that len+2<=MAX_COLUMN_SIZE.
* It required a minor change to put_field() where it was decremented, too.
* 2001.6.27 Claudio Valderrama: Finally stop users from invoking the same option
* several times when altering a domain. Specially dangerous with text data types.
* Ex: alter domain d type char(5) type varchar(5) default 'x' default 'y';
* Bear in mind that if DYN functions are addressed directly, this protection
* becomes a moot point.
* 2001.6.30 Claudio Valderrama: revert changes from 2001.6.26 because the code
* is called from several places and there are more functions, even in metd.c,
* playing the same nonsense game with the field's length, so it needs more
* careful examination. For now, the new checks in DYN_MOD should catch most anomalies.
* 2001.7.3 Claudio Valderrama: fix Firebird Bug #223059 with mismatch between number
* of declared fields for a VIEW and effective fields in the SELECT statement.
* 2001.07.22 Claudio Valderrama: minor fixes and improvements.
* 2001.08.18 Claudio Valderrama: RECREATE PROCEDURE.
* 2001.10.01 Claudio Valderrama: modify_privilege() should recognize that a ROLE can
* now be made an explicit grantee.
* 2001.10.08 Claudio Valderrama: implement fb_sysflag enum values for autogenerated
* non-system triggers so DFW can recognize them easily.
* 2001.10.26 Claudio Valderrama: added a call to the new METD_drop_function()
* in DDL_execute() so the metadata cache for udfs can be refreshed.
* 2001.12.06 Claudio Valderrama: DDL_resolve_intl_type should calculate field length
* 2002.08.04 Claudio Valderrama: allow declaring and defining variables at the same time
* 2002.08.04 Dmitry Yemanov: ALTER VIEW
* 2002.08.31 Dmitry Yemanov: allowed user-defined index names for PK/FK/UK constraints
* 2002.09.01 Dmitry Yemanov: RECREATE VIEW
* 2002.09.12 Nickolay Samofatov: fixed cached metadata errors
* 2004.01.16 Vlad Horsun: added support for default parameters and
* EXECUTE BLOCK statement
* Adriano dos Santos Fernandes
*/
#include "firebird.h"
#include "dyn_consts.h"
#include <stdio.h>
#include <string.h>
#include "../jrd/SysFunction.h"
#include "../common/classes/MetaName.h"
#include "../dsql/dsql.h"
#include "../dsql/node.h"
#include "../jrd/ibase.h"
#include "../jrd/intl.h"
#include "../jrd/intl_classes.h"
#include "../jrd/jrd.h"
#include "../jrd/flags.h"
#include "../jrd/constants.h"
#include "../dsql/errd_proto.h"
#include "../dsql/ddl_proto.h"
#include "../dsql/gen_proto.h"
#include "../dsql/make_proto.h"
#include "../dsql/metd_proto.h"
#include "../dsql/pass1_proto.h"
#include "../dsql/utld_proto.h"
#include "../jrd/intl_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/thread_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/vio_proto.h"
#include "../jrd/why_proto.h"
#include "../common/utils_proto.h"
#include "../dsql/DdlNodes.h"
#include "../common/StatusArg.h"
#ifdef DSQL_DEBUG
#include "../gpre/prett_proto.h"
#endif
using namespace Jrd;
using namespace Dsql;
using namespace Firebird;
const int BLOB_BUFFER_SIZE = 4096; // to read in blr blob for default values
static void assign_field_length(dsql_fld*, USHORT);
static void check_constraint(DsqlCompilerScratch*, dsql_nod*, bool);
static void check_one_call(USHORT*, SSHORT, const TEXT*);
static void create_view_triggers(DsqlCompilerScratch*, dsql_nod*, dsql_nod*);
static void define_computed(DsqlCompilerScratch*, dsql_nod*, dsql_fld*, dsql_nod*);
static void define_constraint_trigger(DsqlCompilerScratch*, dsql_nod*);
static void define_database(DsqlCompilerScratch*);
static bool define_default(DsqlCompilerScratch*, const dsql_nod*);
static void define_del_cascade_trg(DsqlCompilerScratch*, const dsql_nod*, const dsql_nod*,
const dsql_nod*, const char*, const char*);
//static void define_del_default_trg(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*, TEXT*, TEXT*);
static void define_dimensions(DsqlCompilerScratch*, const dsql_fld*);
static void define_domain(DsqlCompilerScratch*);
static void define_exception(DsqlCompilerScratch*, NOD_TYPE);
static void define_field(DsqlCompilerScratch*, dsql_nod*, SSHORT, const dsql_str*, const dsql_nod* pkcols);
static void define_filter(DsqlCompilerScratch*);
static SSHORT getBlobFilterSubType(DsqlCompilerScratch* dsqlScratch, const dsql_nod* node);
static void define_collation(DsqlCompilerScratch*);
static void define_role(DsqlCompilerScratch*);
static void define_index(DsqlCompilerScratch*);
static void define_rel_constraint(DsqlCompilerScratch*, dsql_nod*);
static void define_relation(DsqlCompilerScratch*);
static void define_set_null_trg(DsqlCompilerScratch*, const dsql_nod*, const dsql_nod*,
const dsql_nod*, const char*, const char*, bool);
static void define_set_default_trg(DsqlCompilerScratch*, const dsql_nod*, const dsql_nod*,
const dsql_nod*, const char*, const char*, bool);
static void define_shadow(DsqlCompilerScratch*);
static void define_udf(DsqlCompilerScratch*);
static void define_update_action(DsqlCompilerScratch*, dsql_nod**, dsql_nod**, dsql_nod*);
static void define_upd_cascade_trg(DsqlCompilerScratch*, const dsql_nod*, const dsql_nod*,
const dsql_nod*, const char*, const char*);
static void define_view(DsqlCompilerScratch*, NOD_TYPE);
static void define_view_trigger(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*);
static void delete_collation(DsqlCompilerScratch*);
static void delete_exception(DsqlCompilerScratch*, dsql_nod*, bool);
static void delete_relation_view(DsqlCompilerScratch*, dsql_nod*, bool);
static const dsql_nod* find_pk_columns(const dsql_nod* def_rel_elements);
static ULONG find_start_of_body(const dsql_str* string);
static void fix_default_source(dsql_str* string);
static void foreign_key(DsqlCompilerScratch*, dsql_nod*, const char* index_name);
static void generate_dyn(DsqlCompilerScratch*, dsql_nod*);
static void grant_revoke(DsqlCompilerScratch*);
static void make_index(DsqlCompilerScratch*, const dsql_nod*, const dsql_nod*, const char*);
static void make_index_trg_ref_int(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*,
const char*, const char*);
static void modify_database(DsqlCompilerScratch*);
static void modify_domain(DsqlCompilerScratch*);
static void modify_field(DsqlCompilerScratch*, dsql_nod*, const dsql_str*);
static void modify_index(DsqlCompilerScratch*);
static void modify_privilege(DsqlCompilerScratch* dsqlScratch, NOD_TYPE type, SSHORT option,
const UCHAR* privs, const dsql_nod* table,
const dsql_nod* user, const dsql_nod* grantor,
const dsql_str* field_name);
static char modify_privileges(DsqlCompilerScratch*, NOD_TYPE, SSHORT, const dsql_nod*,
const dsql_nod*, const dsql_nod*, const dsql_nod*);
static void modify_relation(DsqlCompilerScratch*);
static void modify_udf(DsqlCompilerScratch*);
static void modify_map(DsqlCompilerScratch*);
static void process_role_nm_list(DsqlCompilerScratch*, SSHORT, const dsql_nod*, const dsql_nod*, NOD_TYPE, const dsql_nod*);
static void put_descriptor(DsqlCompilerScratch*, const dsc*);
static void put_dtype(DsqlCompilerScratch*, const dsql_fld*, bool);
static void put_field(DsqlCompilerScratch*, dsql_fld*, bool);
static dsql_nod* replace_field_names(dsql_nod*, dsql_nod*, dsql_nod*, bool, const char*);
static void reset_context_stack(DsqlCompilerScratch*);
static void save_field(DsqlCompilerScratch*, const SCHAR*);
static void save_relation(DsqlCompilerScratch*, const dsql_str*);
static void set_statistics(DsqlCompilerScratch*);
static void stuff_default_blr(DsqlCompilerScratch*, const UCHAR*, USHORT);
static void stuff_matching_blr(DsqlCompiledStatement*, const dsql_nod*, const dsql_nod*);
static void stuff_trg_firing_cond(DsqlCompiledStatement*, const dsql_nod*);
static void set_nod_value_attributes(dsql_nod*, const dsql_fld*);
static void clearPermanentField (dsql_rel*, bool);
static void define_user(DsqlCompilerScratch*, UCHAR);
static void put_grantor(DsqlCompilerScratch* dsqlScratch, const dsql_nod* grantor);
static void post_607(const Arg::StatusVector& v);
static void put_user_grant(DsqlCompilerScratch* dsqlScratch, const dsql_nod* user);
enum trigger_type {
PRE_STORE_TRIGGER = 1,
POST_STORE_TRIGGER = 2,
PRE_MODIFY_TRIGGER = 3,
POST_MODIFY_TRIGGER = 4,
PRE_ERASE_TRIGGER = 5,
POST_ERASE_TRIGGER = 6
};
const int DEFAULT_BUFFER = 2048;
const int DEFAULT_BLOB_SEGMENT_SIZE = 80; // bytes
static const UCHAR nonnull_validation_blr[] =
{
blr_version5,
blr_not,
blr_missing,
blr_fid, 0, 0, 0,
blr_eoc
};
void DsqlCompiledStatement::append_raw_string(const char* string, USHORT len)
{
blrData.add(reinterpret_cast<const UCHAR*>(string), len);
}
void DsqlCompiledStatement::append_raw_string(const UCHAR* string, USHORT len)
{
blrData.add(string, len);
}
//
// Write out a string valued attribute. (Overload 2.)
//
void DsqlCompiledStatement::append_string(UCHAR verb, const MetaName& name)
{
append_string(verb, name.c_str(), name.length());
}
//
// Write out a string valued attribute. (Overload 3.)
//
void DsqlCompiledStatement::append_string(UCHAR verb, const string& name)
{
append_string(verb, name.c_str(), name.length());
}
void DDL_execute(dsql_req* request)
{
/**************************************
*
* D D L _ e x e c u t e
*
**************************************
*
* Functional description
* Call access method layered service DYN
* to interpret dyn string and perform
* metadata updates.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
const DsqlCompiledStatement* statement = request->getStatement();
#ifdef DSQL_DEBUG
if (DSQL_debug & 4)
{
dsql_trace("Output DYN string for DDL:");
PRETTY_print_dyn(statement->getBlrData().begin(), gds__trace_printer, NULL, 0);
}
#endif
// for delete & modify, get rid of the cached relation metadata
const dsql_str* string = NULL;
SYM_TYPE sym_type;
const dsql_nod* relation_node;
const NOD_TYPE type = statement->getDdlNode()->nod_type;
switch (type)
{
case nod_mod_relation:
case nod_redef_relation:
relation_node = statement->getDdlNode()->nod_arg[e_alt_name];
string = (dsql_str*) relation_node->nod_arg[e_rln_name];
// fall into
case nod_mod_view:
case nod_replace_view:
case nod_redef_view:
case nod_del_relation:
case nod_del_view:
if (!string)
string = (dsql_str*) statement->getDdlNode()->nod_arg[e_alt_name];
sym_type = SYM_relation;
METD_drop_relation(request->getTransaction(), string);
break;
case nod_del_collation:
// for delete, get rid of the cached collation metadata
string = (dsql_str*) statement->getDdlNode()->nod_arg[e_del_coll_name];
sym_type = SYM_intlsym_collation;
METD_drop_collation(request->getTransaction(), string);
break;
case nod_del_udf:
case nod_mod_udf:
// Signal UDF for obsolescence
string = (dsql_str*) statement->getDdlNode()->nod_arg[e_udf_name];
sym_type = SYM_udf;
METD_drop_function(request->getTransaction(), string, "");
break;
}
if (string)
MET_dsql_cache_release(tdbb, sym_type, string->str_data);
if (type == nod_class_stmtnode)
{
// run all statements under savepoint control
{ // scope
AutoSavePoint savePoint(tdbb, request->req_transaction);
reinterpret_cast<DdlNode*>(statement->getDdlNode()->nod_arg[0])->execute(tdbb,
request->req_transaction);
savePoint.release(); // everything is ok
}
JRD_autocommit_ddl(tdbb, request->req_transaction);
}
else
{
JRD_ddl(tdbb, /*request->getAttachment()->dbb_attachment,*/ request->req_transaction,
statement->getBlrData().getCount(), statement->getBlrData().begin(),
*statement->getSqlText());
}
}
void DDL_generate(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
{
/**************************************
*
* D D L _ g e n e r a t e
*
**************************************
*
* Functional description
* Generate the DYN string for a
* metadata update. Done during the
* prepare phase.
*
**************************************/
if (dsqlScratch->getAttachment()->dbb_read_only)
{
ERRD_post(Arg::Gds(isc_read_only_database));
return;
}
if (node->nod_type != nod_class_stmtnode)
dsqlScratch->getStatement()->append_uchar(isc_dyn_version_1);
generate_dyn(dsqlScratch, node);
if (node->nod_type != nod_class_stmtnode)
dsqlScratch->getStatement()->append_uchar(isc_dyn_eoc);
}
//
// Determine whether ids or names should be referenced
// when generating blr for fields and relations.
//
bool DDL_ids(const DsqlCompilerScratch* scratch)
{
return !scratch->getStatement()->getDdlNode();
}
//
// Emit blr that describes a descriptor.
// Note that this depends on the same stuff variant
// as used in gen.cpp
//
void DDL_put_field_dtype(DsqlCompilerScratch* dsqlScratch, const dsql_fld* field, bool use_subtype)
{
put_dtype(dsqlScratch, field, use_subtype);
}
//
// See the next function for description. This is only a
// wrapper that sets the last parameter to false to indicate
// we are creating a field, not modifying one.
//
void DDL_resolve_intl_type(DsqlCompilerScratch* dsqlScratch, dsql_fld* field, const dsql_str* collation_name)
{
DDL_resolve_intl_type2(dsqlScratch, field, collation_name, false);
}
void DDL_resolve_intl_type2(DsqlCompilerScratch* dsqlScratch,
dsql_fld* field,
const dsql_str* collation_name,
bool modifying)
{
/**************************************
*
* D D L _ r e s o l v e _ i n t l _ t y p e 2
*
**************************************
*
* Function
* If the field is defined with a character set or collation,
* resolve the names to a subtype now.
*
* Also resolve the field length & whatnot.
*
* If the field is being created, it will pick the db-wide charset
* and collation if not specified. If the field is being modified,
* since we don't allow changes to those attributes, we'll go and
* calculate the correct old lenth from the field itself so DYN
* can validate the change properly.
*
* For International text fields, this is a good time to calculate
* their actual size - when declared they were declared in
* lengths of CHARACTERs, not BYTES.
*
**************************************/
if (field->fld_type_of_name.hasData())
{
//statement->getAttachment()->dbb_database->checkOdsForDsql(ODS_11_1);
if (field->fld_type_of_table)
{
dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch,
field->fld_type_of_table);
const dsql_fld* fld = NULL;
if (relation)
{
const MetaName fieldName(field->fld_type_of_name);
for (fld = relation->rel_fields; fld; fld = fld->fld_next)
{
if (fieldName == fld->fld_name)
{
field->fld_dimensions = fld->fld_dimensions;
field->fld_source = fld->fld_source;
field->fld_length = fld->fld_length;
field->fld_scale = fld->fld_scale;
field->fld_sub_type = fld->fld_sub_type;
field->fld_character_set_id = fld->fld_character_set_id;
field->fld_collation_id = fld->fld_collation_id;
field->fld_character_length = fld->fld_character_length;
field->fld_flags = fld->fld_flags;
field->fld_dtype = fld->fld_dtype;
field->fld_seg_length = fld->fld_seg_length;
break;
}
}
}
if (!fld)
{
// column @1 does not exist in table/view @2
post_607(Arg::Gds(isc_dyn_column_does_not_exist) <<
Arg::Str(field->fld_type_of_name) <<
Arg::Str(field->fld_type_of_table->str_data));
}
}
else
{
if (!METD_get_domain(dsqlScratch->getTransaction(), field, field->fld_type_of_name.c_str()))
{
// Specified domain or source field does not exist
post_607(Arg::Gds(isc_dsql_domain_not_found) << Arg::Str(field->fld_type_of_name));
}
}
if (field->fld_dimensions != 0)
{
ERRD_post(Arg::Gds(isc_wish_list) <<
Arg::Gds(isc_random) <<
Arg::Str("Usage of domain or TYPE OF COLUMN of array type in PSQL"));
}
}
if ((field->fld_dtype > dtype_any_text) && field->fld_dtype != dtype_blob)
{
if (field->fld_character_set || collation_name || field->fld_flags & FLD_national)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_collation_requires_text));
}
return;
}
if (field->fld_dtype == dtype_blob)
{
if (field->fld_sub_type_name)
{
SSHORT blob_sub_type;
if (!METD_get_type(dsqlScratch->getTransaction(),
reinterpret_cast<const dsql_str*>(field->fld_sub_type_name),
"RDB$FIELD_SUB_TYPE", &blob_sub_type))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_dsql_blob_type_unknown) <<
Arg::Str(((dsql_str*) field->fld_sub_type_name)->str_data));
}
field->fld_sub_type = blob_sub_type;
}
if (field->fld_sub_type > isc_blob_text)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_subtype_for_internal_use));
}
if (field->fld_character_set && (field->fld_sub_type == isc_blob_untyped))
{
field->fld_sub_type = isc_blob_text;
}
if (field->fld_character_set && (field->fld_sub_type != isc_blob_text))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_collation_requires_text));
}
if (collation_name && (field->fld_sub_type != isc_blob_text))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_collation_requires_text));
}
if (field->fld_sub_type != isc_blob_text) {
return;
}
}
if (field->fld_character_set_id != 0 && !collation_name)
{
// This field has already been resolved once, and the collation
// hasn't changed. Therefore, no need to do it again.
return;
}
if (modifying)
{
#ifdef DEV_BUILD
const dsql_rel* relation = dsqlScratch->relation;
#endif
const dsql_fld* afield = field->fld_next;
USHORT bpc = 0;
while (afield)
{
// The first test is redundant.
if (afield != field && afield->fld_relation && afield->fld_name == field->fld_name)
{
fb_assert(afield->fld_relation == relation || !relation);
break;
}
afield = afield->fld_next;
}
if (afield)
{
field->fld_character_set_id = afield->fld_character_set_id;
bpc = METD_get_charset_bpc(dsqlScratch->getTransaction(), field->fld_character_set_id);
field->fld_collation_id = afield->fld_collation_id;
field->fld_ttype = afield->fld_ttype;
if (afield->fld_flags & FLD_national) {
field->fld_flags |= FLD_national;
}
else {
field->fld_flags &= ~FLD_national;
}
assign_field_length (field, bpc);
return;
}
}
if (!(field->fld_character_set || field->fld_character_set_id || // set if a domain
(field->fld_flags & FLD_national)))
{
// Attach the database default character set, if not otherwise specified
const dsql_str* dfl_charset = METD_get_default_charset(dsqlScratch->getTransaction());
if (dfl_charset)
{
field->fld_character_set = (dsql_nod*) dfl_charset;
}
else
{
// If field is not specified with NATIONAL, or CHARACTER SET
// treat it as a single-byte-per-character field of character set NONE.
assign_field_length(field, 1);
field->fld_ttype = 0;
if (!collation_name) {
return;
}
}
}
const char* charset_name = 0;
if (field->fld_flags & FLD_national) {
charset_name = NATIONAL_CHARACTER_SET;
}
else if (field->fld_character_set) {
charset_name = ((dsql_str*) field->fld_character_set)->str_data;
}
// Find an intlsym for any specified character set name & collation name
const dsql_intlsym* resolved_type = NULL;
if (charset_name)
{
const dsql_intlsym* resolved_charset =
METD_get_charset(dsqlScratch->getTransaction(), (USHORT) strlen(charset_name), charset_name);
// Error code -204 (IBM's DB2 manual) is close enough
if (!resolved_charset)
{
// specified character set not found
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_charset_not_found) << Arg::Str(charset_name));
}
field->fld_character_set_id = resolved_charset->intlsym_charset_id;
resolved_type = resolved_charset;
}
if (collation_name)
{
const dsql_intlsym* resolved_collation = METD_get_collation(dsqlScratch->getTransaction(),
collation_name, field->fld_character_set_id);
if (!resolved_collation)
{
MetaName charSetName;
if (charset_name)
charSetName = charset_name;
else
{
charSetName = METD_get_charset_name(dsqlScratch->getTransaction(),
field->fld_character_set_id);
}
// Specified collation not found
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_collation_not_found) << Arg::Str(collation_name->str_data) <<
Arg::Str(charSetName));
}
// If both specified, must be for same character set
// A "literal constant" must be handled (charset as ttype_dynamic)
resolved_type = resolved_collation;
if ((field->fld_character_set_id != resolved_type->intlsym_charset_id) &&
(field->fld_character_set_id != ttype_dynamic))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_collation_not_for_charset) << Arg::Str(collation_name->str_data));
}
field->fld_explicit_collation = true;
}
assign_field_length (field, resolved_type->intlsym_bytes_per_char);
field->fld_ttype = resolved_type->intlsym_ttype;
field->fld_character_set_id = resolved_type->intlsym_charset_id;
field->fld_collation_id = resolved_type->intlsym_collate_id;
}
static void assign_field_length(dsql_fld* field, USHORT bytes_per_char)
{
/**************************************
*
* a s s i g n _ f i e l d _ l e n g t h
*
**************************************
*
* Function
* We'll see if the field's length fits in the maximum
* allowed field, including charset and space for varchars.
* Either we raise an error or assign the field's length.
* If the charlen comes as zero, we do nothing, although we
* know that DYN, MET and DFW will blindly set field length
* to zero if they don't catch charlen or another condition.
*
**************************************/
if (field->fld_character_length)
{
ULONG field_length = (ULONG) bytes_per_char * field->fld_character_length;
if (field->fld_dtype == dtype_varying) {
field_length += sizeof(USHORT);
}
if (field_length > MAX_COLUMN_SIZE)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_imp_exc) <<
Arg::Gds(isc_field_name) << Arg::Str(field->fld_name));
}
field->fld_length = (USHORT) field_length;
}
}
bool DDL_is_array_or_blob(DsqlCompilerScratch* dsqlScratch, const dsql_nod* node)
{
/**************************************
*
* D D L _ i s _ a r r a y _ o r _ b l o b
*
**************************************
*
* Functional description
* Return true if there is an array or blob in expression, else false.
* Array and blob expressions have limited usefullness in a computed
* expression - so we detect it here to report a syntax error at
* definition time, rather than a runtime error at execution.
*
**************************************/
switch (node->nod_type)
{
case nod_class_exprnode:
return reinterpret_cast<ExprNode*>(node->nod_arg[0])->isArrayOrBlob(dsqlScratch);
case nod_gen_id:
case nod_gen_id2:
case nod_dbkey:
case nod_current_date:
case nod_current_time:
case nod_current_timestamp:
case nod_constant:
case nod_strlen:
case nod_null:
case nod_substr:
case nod_internal_info:
return false;
case nod_via:
return DDL_is_array_or_blob(dsqlScratch, node->nod_arg[e_via_value_1]);
case nod_map:
{
const dsql_map* map = (dsql_map*) node->nod_arg[e_map_map];
return DDL_is_array_or_blob(dsqlScratch, map->map_node);
}
case nod_upcase:
case nod_lowcase:
case nod_negate:
return DDL_is_array_or_blob(dsqlScratch, node->nod_arg[0]);
case nod_cast:
{
const dsql_fld* fld = (dsql_fld*) node->nod_arg[e_cast_target];
if (fld->fld_dtype == dtype_blob || fld->fld_dtype == dtype_array)
return true;
}
return DDL_is_array_or_blob(dsqlScratch, node->nod_arg[e_cast_source]);
case nod_add:
case nod_subtract:
case nod_multiply:
case nod_divide:
case nod_add2:
case nod_subtract2:
case nod_multiply2:
case nod_divide2:
if (DDL_is_array_or_blob(dsqlScratch, node->nod_arg[0])) {
return true;
}
return DDL_is_array_or_blob(dsqlScratch, node->nod_arg[1]);
case nod_alias:
return DDL_is_array_or_blob(dsqlScratch, node->nod_arg[e_alias_value]);
case nod_udf:
{
const dsql_udf* userFunc = (dsql_udf*) node->nod_arg[0];
if (userFunc->udf_dtype == dtype_blob || userFunc->udf_dtype == dtype_array)
return true;
}
// parameters to UDF don't need checking, a blob or array can be passed
return false;
case nod_sys_function:
{
const dsql_str* name = (dsql_str*) node->nod_arg[e_sysfunc_name];
dsql_nod* nodeArgs = node->nod_arg[e_sysfunc_args];
Array<const dsc*> args;
if (nodeArgs)
{
fb_assert(nodeArgs->nod_type == nod_list);
for (dsql_nod** p = nodeArgs->nod_arg;
p < nodeArgs->nod_arg + nodeArgs->nod_count; ++p)
{
MAKE_desc(dsqlScratch, &(*p)->nod_desc, *p, NULL);
args.add(&(*p)->nod_desc);
}
}
dsc desc;
DSqlDataTypeUtil(dsqlScratch).makeSysFunction(&desc, name->str_data, args.getCount(), args.begin());
return DTYPE_IS_BLOB_OR_QUAD(desc.dsc_dtype);
}
case nod_extract:
case nod_list:
{
const dsql_nod* const* const end = node->nod_arg + node->nod_count;
for (const dsql_nod* const* ptr = node->nod_arg; ptr < end; ++ptr)
{
if (DDL_is_array_or_blob(dsqlScratch, *ptr)) {
return true;
}
}
}
return false;
case nod_field:
case nod_coalesce:
case nod_simple_case:
case nod_searched_case:
if (node->nod_desc.dsc_dtype == dtype_blob || node->nod_desc.dsc_dtype == dtype_array)
{
return true;
}
return false;
case nod_trim:
return DDL_is_array_or_blob(dsqlScratch, node->nod_arg[e_trim_value]);
case nod_derived_field:
return DDL_is_array_or_blob(dsqlScratch, node->nod_arg[e_derived_field_value]);
default:
fb_assert(false);
return false;
}
}
static void check_constraint(DsqlCompilerScratch* dsqlScratch,
dsql_nod* element,
bool delete_trigger_required)
{
/**************************************
*
* c h e c k _ c o n s t r a i n t
*
**************************************
*
* Function
* Generate triggers to implement the CHECK
* clause, either at the field or table level.
*
**************************************/
dsql_nod* ddl_node = dsqlScratch->getStatement()->getDdlNode();
if (!element->nod_arg[e_cnstr_table]) {
element->nod_arg[e_cnstr_table] = ddl_node->nod_arg[e_drl_name];
}
// specify that the trigger should abort if the condition is not met
dsql_nod* list_node = MAKE_node(nod_list, 1);
element->nod_arg[e_cnstr_actions] = list_node;
list_node->nod_arg[0] = MAKE_node(nod_gdscode, 1);
dsql_nod** errorcode_node = &list_node->nod_arg[0]->nod_arg[0];
*errorcode_node = (dsql_nod*) MAKE_cstring("check_constraint");
// create the INSERT trigger
element->nod_arg[e_cnstr_type] = MAKE_const_slong(PRE_STORE_TRIGGER);
define_constraint_trigger(dsqlScratch, element);
// create the UPDATE trigger
element->nod_arg[e_cnstr_type] = MAKE_const_slong(PRE_MODIFY_TRIGGER);
define_constraint_trigger(dsqlScratch, element);
// create the DELETE trigger, if required
if (delete_trigger_required)
{
element->nod_arg[e_cnstr_type] = MAKE_const_slong(PRE_ERASE_TRIGGER);
define_constraint_trigger(dsqlScratch, element);
}
dsqlScratch->getStatement()->append_uchar(isc_dyn_end); // For CHECK constraint definition
}
static void check_one_call (USHORT* repetition_count, SSHORT pos, const TEXT* error_msg)
{
/**************************************
*
* c h e c k _ o n e _ c a l l
*
**************************************
*
* Function
* Ensure that each option in modify_domain() is called only once.
* This restriction cannot be enforced by the DSQL parser.
*
**************************************/
if (++repetition_count[pos] > 1)
{
ERRD_post (Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(error_msg));
}
}
static void create_view_triggers(DsqlCompilerScratch* dsqlScratch, dsql_nod* element, dsql_nod* items)
{
/**************************************
*
* c r e a t e _ v i e w _ t r i g g e r s
*
**************************************
*
* Function
* Generate triggers to implement the WITH CHECK OPTION
* clause for a VIEW
*
**************************************/
dsql_nod* ddl_node = dsqlScratch->getStatement()->getDdlNode();
if (!element->nod_arg[e_cnstr_table]) {
element->nod_arg[e_cnstr_table] = ddl_node->nod_arg[e_drl_name];
}
// specify that the trigger should abort if the condition is not met
dsql_nod* list_node = MAKE_node(nod_list, 1);
element->nod_arg[e_cnstr_actions] = list_node;
list_node->nod_arg[0] = MAKE_node(nod_gdscode, 1);
dsql_nod** errorcode_node = &list_node->nod_arg[0]->nod_arg[0];
*errorcode_node = (dsql_nod*) MAKE_cstring("check_constraint");
// create the UPDATE trigger
element->nod_arg[e_cnstr_type] = MAKE_const_slong(PRE_MODIFY_TRIGGER);
dsql_nod* base_and_node = 0;
dsql_nod* base_relation = 0;
define_update_action(dsqlScratch, &base_and_node, &base_relation, items);
fb_assert(base_and_node);
fb_assert(base_relation);
dsql_nod* rse = MAKE_node(nod_rse, e_rse_count);
rse->nod_arg[e_rse_boolean] = base_and_node;
dsql_nod* temp = MAKE_node(nod_list, 1);
rse->nod_arg[e_rse_streams] = temp;
temp->nod_arg[0] = base_relation;
define_view_trigger(dsqlScratch, element, rse, items);
// create the INSERT trigger
element->nod_arg[e_cnstr_type] = MAKE_const_slong(PRE_STORE_TRIGGER);
define_view_trigger(dsqlScratch, element, NULL, items);
dsqlScratch->getStatement()->append_uchar(isc_dyn_end); // For triggers definition
}
static void define_computed(DsqlCompilerScratch* dsqlScratch,
dsql_nod* relation_node,
dsql_fld* field,
dsql_nod* node)
{
/**************************************
*
* d e f i n e _ c o m p u t e d
*
**************************************
*
* Function
* Create the ddl to define a computed field
* or an expression index.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* const saved_ddl_node = statement->getDdlNode();
statement->setDdlNode(node);
// Get the table node & set up correct context
reset_context_stack(dsqlScratch);
dsc save_desc;
// Save the size of the field if it is specified
save_desc.dsc_dtype = 0;
if (field && field->fld_dtype)
{
fb_assert(field->fld_dtype <= MAX_UCHAR);
save_desc.dsc_dtype = (UCHAR) field->fld_dtype;
save_desc.dsc_length = field->fld_length;
fb_assert(field->fld_scale <= MAX_SCHAR);
save_desc.dsc_scale = (SCHAR) field->fld_scale;
save_desc.dsc_sub_type = field->fld_sub_type;
field->fld_dtype = 0;
field->fld_length = 0;
field->fld_scale = 0;
field->fld_sub_type = 0;
}
PASS1_make_context(dsqlScratch, relation_node);
dsql_nod* input = PASS1_node(dsqlScratch, node->nod_arg[e_cmp_expr]);
// check if array or blobs are used in expression
if (DDL_is_array_or_blob(dsqlScratch, input))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) << Arg::Gds(isc_dsql_no_array_computed));
}
// try to calculate size of the computed field. The calculated size
// may be ignored, but it will catch self references
dsc desc;
MAKE_desc(dsqlScratch, &desc, input, NULL);
// generate the blr expression
statement->begin_blr(isc_dyn_fld_computed_blr);
GEN_hidden_variables(dsqlScratch, true);
GEN_expr(dsqlScratch, input);
statement->end_blr();
if (save_desc.dsc_dtype)
{
// restore the field size/type overrides
field->fld_dtype = save_desc.dsc_dtype;
field->fld_length = save_desc.dsc_length;
field->fld_scale = save_desc.dsc_scale;
if (field->fld_dtype <= dtype_any_text)
{
field->fld_character_set_id = DSC_GET_CHARSET(&save_desc);
field->fld_collation_id= DSC_GET_COLLATE(&save_desc);
}
else
field->fld_sub_type = save_desc.dsc_sub_type;
}
else if (field)
{
// use size calculated
field->fld_dtype = desc.dsc_dtype;
field->fld_length = desc.dsc_length;
field->fld_scale = desc.dsc_scale;
if (field->fld_dtype <= dtype_any_text)
{
field->fld_character_set_id = DSC_GET_CHARSET(&desc);
field->fld_collation_id= DSC_GET_COLLATE(&desc);
}
else
field->fld_sub_type = desc.dsc_sub_type;
}
statement->setType(DsqlCompiledStatement::TYPE_DDL);
statement->setDdlNode(saved_ddl_node);
reset_context_stack(dsqlScratch);
// generate the source text
const dsql_str* source = (dsql_str*) node->nod_arg[e_cmp_text];
fb_assert(source->str_length <= MAX_USHORT);
statement->append_string(isc_dyn_fld_computed_source, source->str_data, (USHORT) source->str_length);
}
static void define_constraint_trigger(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
{
/**************************************
*
* d e f i n e _ c o n s t r a i n t _ t r i g g e r
*
**************************************
*
* Function
* Create the ddl to define or alter a constraint trigger.
* This is a SQL's check constraint.
*
**************************************/
// make the "define trigger" node the current statement ddl node so
// that generating of BLR will be appropriate for trigger
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* const saved_ddl_node = statement->getDdlNode();
statement->setDdlNode(node);
if (node->nod_type != nod_def_constraint)
{
return;
}
statement->append_string(isc_dyn_def_trigger, "", 0);
dsql_nod* relation_node = node->nod_arg[e_cnstr_table];
const dsql_str* relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name];
statement->append_string(isc_dyn_rel_name, relation_name->str_data,
(USHORT) relation_name->str_length);
const dsql_str* source = (dsql_str*) node->nod_arg[e_cnstr_source];
if (source)
{
fb_assert(source->str_length <= MAX_USHORT);
const ULONG j = find_start_of_body(source);
if (j < source->str_length)
{
statement->append_string(isc_dyn_trg_source, source->str_data + j, source->str_length - j);
}
}
statement->append_number(isc_dyn_trg_sequence, 0);
const dsql_nod* constant = node->nod_arg[e_cnstr_type];
if (constant)
{
const SSHORT type = (SSHORT) constant->getSlong();
statement->append_number(isc_dyn_trg_type, type);
}
statement->append_uchar(isc_dyn_sql_object);
// generate the trigger blr
if (node->nod_arg[e_cnstr_condition] && node->nod_arg[e_cnstr_actions])
{
statement->begin_blr(isc_dyn_trg_blr);
statement->append_uchar(blr_begin);
// create the "OLD" and "NEW" contexts for the trigger --
// the new one could be a dummy place holder to avoid resolving
// fields to that context but prevent relations referenced in
// the trigger actions from referencing the predefined "1" context
reset_context_stack(dsqlScratch);
// CVC: check_constraint() is the only caller and it always receives
// false for the delete_trigger_required flag. Hence, I thought I could
// disable the OLD context here to avoid "ambiguous field name" errors
// in pre_store and pre_modify triggers. Also, what sense can I make
// from NEW in pre_delete? However, we clash at JRD with "no current
// record for fetch operation".
relation_node->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT);
dsql_ctx* oldContext = PASS1_make_context(dsqlScratch, relation_node);
oldContext->ctx_flags |= CTX_system;
relation_node->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(NEW_CONTEXT);
dsql_ctx* newContext = PASS1_make_context(dsqlScratch, relation_node);
newContext->ctx_flags |= CTX_system;
// generate the condition for firing the trigger
dsql_nod* condition = MAKE_node(nod_not, 1);
condition->nod_arg[0] = node->nod_arg[e_cnstr_condition];
condition = PASS1_node(dsqlScratch, condition);
GEN_hidden_variables(dsqlScratch, false);
statement->append_uchar(blr_if);
GEN_expr(dsqlScratch, condition);
// generate the action statements for the trigger
dsql_nod* actions = node->nod_arg[e_cnstr_actions];
dsql_nod** ptr = actions->nod_arg;
for (const dsql_nod* const* const end = ptr + actions->nod_count; ptr < end; ptr++)
{
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, *ptr));
}
statement->append_uchar(blr_end); // of if (as there's no ELSE branch)
statement->append_uchar(blr_end); // of begin
statement->end_blr();
}
statement->append_number(isc_dyn_system_flag, fb_sysflag_check_constraint);
statement->append_uchar(isc_dyn_end);
// the statement type may have been set incorrectly when parsing
// the trigger actions, so reset it to reflect the fact that this
// is a data definition statement; also reset the ddl node
statement->setType(DsqlCompiledStatement::TYPE_DDL);
statement->setDdlNode(saved_ddl_node);
reset_context_stack(dsqlScratch);
}
static void define_database(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e f i n e _ d a t a b a s e
*
**************************************
*
* Function
* Create a database. Assumes that
* database is created elsewhere with
* initial options. Modify the
* database using DYN to add the remaining
* options.
*
**************************************/
SLONG start = 0;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* ddl_node = statement->getDdlNode();
statement->append_uchar(isc_dyn_mod_database);
// statement->append_number(isc_dyn_rel_sql_protection, 1);
const dsql_nod* elements = ddl_node->nod_arg[e_database_initial_desc];
if (elements)
{
const dsql_nod* const* ptr = elements->nod_arg;
for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ptr++)
{
const dsql_nod* element = *ptr;
switch (element->nod_type)
{
case nod_file_length:
start = (IPTR) element->nod_arg[0] + 1;
break;
default:
break;
}
}
}
const dsql_str* name;
const dsql_fil* file;
elements = ddl_node->nod_arg[e_database_rem_desc];
if (elements)
{
const dsql_nod* const* ptr = elements->nod_arg;
for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ptr++)
{
const dsql_nod* element = *ptr;
switch (element->nod_type)
{
case nod_difference_file:
statement->append_cstring(isc_dyn_def_difference,
((dsql_str*) element->nod_arg[0])->str_data);
break;
case nod_file_desc:
file = (dsql_fil*) element->nod_arg[0];
statement->append_cstring(isc_dyn_def_file, file->fil_name->str_data);
start = MAX(start, file->fil_start);
statement->append_file_start(start);
statement->append_file_length(file->fil_length);
statement->append_uchar(isc_dyn_end);
start += file->fil_length;
break;
case nod_dfl_charset:
name = (dsql_str*) element->nod_arg[0];
statement->append_cstring(isc_dyn_fld_character_set_name, name->str_data);
break;
case nod_dfl_collate:
name = (dsql_str*) element->nod_arg[0];
statement->append_cstring(isc_dyn_fld_collation, name->str_data);
break;
default:
break;
}
}
}
statement->append_uchar(isc_dyn_end);
}
static bool define_default(DsqlCompilerScratch* dsqlScratch, const dsql_nod* node)
{
/*****************************************
*
* d e f i n e _ d e f a u l t
*
*****************************************
*
* Function
* Define default value for domain/column.
* Returns whether it is specified as being NULL.
*
**************************************/
fb_assert(node && node->nod_type == nod_def_default);
dsql_nod* const value = PASS1_node(dsqlScratch, node->nod_arg[e_dft_default]);
fb_assert(value);
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->begin_blr(isc_dyn_fld_default_value);
GEN_hidden_variables(dsqlScratch, true);
GEN_expr(dsqlScratch, value);
statement->end_blr();
dsql_str* const string = (dsql_str*) node->nod_arg[e_dft_default_source];
fb_assert(string && string->str_length <= MAX_USHORT);
fix_default_source(string);
statement->append_string(isc_dyn_fld_default_source, string->str_data, string->str_length);
return (value->nod_type == nod_null);
}
static void define_del_cascade_trg( DsqlCompilerScratch* dsqlScratch,
const dsql_nod* element,
const dsql_nod* for_columns,
const dsql_nod* prim_columns,
const char* prim_rel_name,
const char* for_rel_name)
{
/*****************************************************
*
* d e f i n e _ d e l _ c a s c a d e _ t r g
*
*****************************************************
*
* Function
* define "on delete cascade" trigger (for referential integrity)
* along with its blr
*
*****************************************************/
if (element->nod_type != nod_foreign) {
return;
}
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
// stuff a trigger_name of size 0. So the dyn-parser will make one up.
statement->append_string(isc_dyn_def_trigger, "", 0);
statement->append_number(isc_dyn_trg_type, (SSHORT) POST_ERASE_TRIGGER);
statement->append_uchar(isc_dyn_sql_object);
statement->append_number(isc_dyn_trg_sequence, (SSHORT) 1);
statement->append_number(isc_dyn_trg_inactive, (SSHORT) 0);
statement->append_cstring(isc_dyn_rel_name, prim_rel_name);
// the trigger blr
statement->begin_blr(isc_dyn_trg_blr);
statement->append_uchar(blr_for);
statement->append_uchar(blr_rse);
// the context for the prim. key relation
statement->append_uchar(1);
statement->append_uchar(blr_relation);
statement->append_cstring(0, for_rel_name);
// the context for the foreign key relation
statement->append_uchar(2);
stuff_matching_blr(statement, for_columns, prim_columns);
statement->append_uchar(blr_erase);
statement->append_uchar(2);
statement->end_blr();
// end of the blr
statement->append_number(isc_dyn_system_flag, fb_sysflag_referential_constraint);
// no trg_source and no trg_description
statement->append_uchar(isc_dyn_end);
}
static void define_set_default_trg( DsqlCompilerScratch* dsqlScratch,
const dsql_nod* element,
const dsql_nod* for_columns,
const dsql_nod* prim_columns,
const char* prim_rel_name,
const char* for_rel_name,
bool on_upd_trg)
{
/*****************************************************
*
* d e f i n e _ s e t _ d e f a u l t _ t r g
*
*****************************************************
*
* Function
* define "on delete|update set default" trigger (for
* referential integrity) along with its blr
*
*****************************************************/
if (element->nod_type != nod_foreign) {
return;
}
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->generate_unnamed_trigger_beginning(on_upd_trg, prim_rel_name, prim_columns,
for_rel_name, for_columns);
UCHAR default_val[BLOB_BUFFER_SIZE];
USHORT num_fields = 0;
const dsql_nod* const* for_key_flds = for_columns->nod_arg;
const dsql_nod* ddl_node = statement->getDdlNode();
do {
// for every column in the foreign key ....
const dsql_str* for_key_fld_name_str = (dsql_str*) (*for_key_flds)->nod_arg[1];
statement->append_uchar(blr_assignment);
// here stuff the default value as blr_literal .... or blr_null
// if this col. does not have an applicable default
// the default is determined in many cases:
// (1) the info. for the column is in memory. (This is because
// the column is being created in this ddl statement)
// (1-a) the table has a column level default. We get this by
// searching the dsql parse tree starting from the ddl node.
// (1-b) the table does not have a column level default, but
// has a domain default. We get the domain name from the dsql
// parse tree and call METD_get_domain_default to read the
// default from the system tables.
// (2) The default-info for this column is not in memory (This is
// because this is an alter table ddl statement). The table
// already exists; therefore we get the column and/or domain
// default value from the system tables by calling:
// METD_get_col_default().
bool found_default = false;
bool search_for_default = true;
// search the parse tree to find the column
const dsql_nod* elem = ddl_node->nod_arg[e_drl_elements];
const dsql_nod* const* ptr = elem->nod_arg;
for (const dsql_nod* const* const end = ptr + elem->nod_count; ptr < end; ++ptr)
{
elem = *ptr;
if (elem->nod_type != nod_def_field) {
continue;
}
const dsql_fld* field = (dsql_fld*) elem->nod_arg[e_dfl_field];
if (field->fld_name != for_key_fld_name_str->str_data)
{
continue;
}
// Now, we have the right column in the parse tree. case (1) above
dsql_nod* default_node = elem->nod_arg[e_dfl_default];
if (default_node)
{
// case (1-a) above: there is a col. level default
fb_assert(default_node->nod_type == nod_def_default);
GEN_hidden_variables(dsqlScratch, true);
GEN_expr(dsqlScratch, default_node->nod_arg[e_dft_default]);
found_default = true;
search_for_default = false;
}
else
{
const TEXT* domain_name;
const dsql_str* domain_name_str;
const dsql_nod* tmp_node;
const dsql_nod* domain_node = elem->nod_arg[e_dfl_domain];
if (!domain_node ||
!(tmp_node = domain_node->nod_arg[e_dom_name]) ||
!(domain_name_str = (dsql_str*) tmp_node->nod_arg[e_fln_name]) ||
!(domain_name = domain_name_str->str_data))
{
break;
}
// case: (1-b): domain name is available. Column level default
// is not declared. so get the domain default
const USHORT def_len = METD_get_domain_default(dsqlScratch->getTransaction(),
domain_name, &found_default, default_val, sizeof(default_val));
search_for_default = false;
if (found_default)
{
stuff_default_blr(dsqlScratch, default_val, def_len);
}
else
{
// neither col level nor domain level default exists
statement->append_uchar(blr_null);
}
}
break;
}
if (search_for_default)
{
// case 2: see if the column/domain has already been created
const USHORT def_len = METD_get_col_default(dsqlScratch->getTransaction(), for_rel_name,
for_key_fld_name_str->str_data, &found_default, default_val, sizeof(default_val));
if (found_default) {
stuff_default_blr(dsqlScratch, default_val, def_len);
}
else {
statement->append_uchar(blr_null);
}
}
// the context for the foreign key relation
statement->append_uchar(blr_field);
statement->append_uchar(2);
statement->append_cstring(0, for_key_fld_name_str->str_data);
num_fields++;
for_key_flds++;
} while (num_fields < for_columns->nod_count);
statement->append_uchar(blr_end);
if (on_upd_trg) {
statement->append_uchars(blr_end, 3);
}
statement->end_blr();
statement->append_number(isc_dyn_system_flag, fb_sysflag_referential_constraint);
// no trg_source and no trg_description
statement->append_uchar(isc_dyn_end);
}
static void define_dimensions( DsqlCompilerScratch* dsqlScratch, const dsql_fld* field)
{
/*****************************************
*
* d e f i n e _ d i m e n s i o n s
*
*****************************************
*
* Function
* Define dimensions of an array
*
**************************************/
const dsql_nod* elements = field->fld_ranges;
const USHORT dims = elements->nod_count / 2;
if (dims > MAX_ARRAY_DIMENSIONS)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-604) << Arg::Gds(isc_dsql_max_arr_dim_exceeded));
}
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->append_number(isc_dyn_fld_dimensions, (SSHORT) dims);
SSHORT position = 0;
const dsql_nod* const* ptr = elements->nod_arg;
for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ++ptr, ++position)
{
statement->append_number(isc_dyn_def_dimension, position);
const dsql_nod* element = *ptr++;
statement->append_uchar(isc_dyn_dim_lower);
const SLONG lrange = element->getSlong();
statement->append_ulong_with_length(lrange);
element = *ptr;
statement->append_uchar(isc_dyn_dim_upper);
const SLONG hrange = element->getSlong();
statement->append_ulong_with_length(hrange);
statement->append_uchar(isc_dyn_end);
if (lrange >= hrange)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-604) << Arg::Gds(isc_dsql_arr_range_error));
}
}
}
static void define_domain(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e f i n e _ d o m a i n
*
**************************************
*
* Function
* Define a domain (global field)
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* element = statement->getDdlNode();
dsql_fld* field = (dsql_fld*) element->nod_arg[e_dom_name];
if (fb_utils::implicit_domain(field->fld_name.c_str()))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
Arg::Gds(isc_dsql_implicit_domain_name) << Arg::Str(field->fld_name));
}
statement->append_string(isc_dyn_def_global_fld, field->fld_name);
DDL_resolve_intl_type(dsqlScratch, field, (dsql_str*) element->nod_arg[e_dom_collate]);
put_field(dsqlScratch, field, false);
// check for a default value
dsql_nod* node = element->nod_arg[e_dom_default];
if (node)
{
define_default(dsqlScratch, node);
}
if (field->fld_ranges)
{
define_dimensions(dsqlScratch, field);
}
bool null_flag = false;
bool check_flag = false;
// check for constraints
node = element->nod_arg[e_dom_constraint];
if (node)
{
dsql_nod** ptr = node->nod_arg;
const dsql_nod* const* const end_ptr = ptr + node->nod_count;
for (; ptr < end_ptr; ++ptr)
{
if ((*ptr)->nod_type == nod_rel_constraint)
{
dsql_nod* node1 = (*ptr)->nod_arg[e_rct_type];
if (node1->nod_type == nod_null)
{
if (!null_flag)
{
statement->append_uchar(isc_dyn_fld_not_null);
null_flag = true;
}
else
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str("NOT NULL"));
}
}
else if (node1->nod_type == nod_def_constraint)
{
if (check_flag)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str("DOMAIN CHECK CONSTRAINT"));
}
check_flag = true;
const dsql_str* string = (dsql_str*) node1->nod_arg[e_cnstr_source];
if (string)
{
fb_assert(string->str_length <= MAX_USHORT);
statement->append_string(isc_dyn_fld_validation_source,
string->str_data, string->str_length);
}
statement->begin_blr(isc_dyn_fld_validation_blr);
// Set any VALUE nodes to the type of the domain being defined.
if (node1->nod_arg[e_cnstr_condition])
{
set_nod_value_attributes(node1->nod_arg[e_cnstr_condition], field);
}
// Increment the context level for this statement, so
// that the context number for any RSE generated for a
// SELECT within the CHECK clause will be greater than
// 0. In the environment of a domain check
// constraint, context number 0 is reserved for the
// "blr_fid, 0, 0, 0," which is emitted for a
// nod_dom_value, corresponding to an occurance of the
// VALUE keyword in the body of the check constraint.
// -- chrisj 1999-08-20
dsqlScratch->contextNumber++;
node = PASS1_node(dsqlScratch, node1->nod_arg[e_cnstr_condition]);
GEN_hidden_variables(dsqlScratch, true);
GEN_expr(dsqlScratch, node);
statement->end_blr();
}
}
}
}
statement->append_uchar(isc_dyn_end);
}
static void define_exception( DsqlCompilerScratch* dsqlScratch, NOD_TYPE op)
{
/**************************************
*
* d e f i n e _ e x c e p t i o n
*
**************************************
*
* Function
* Generate ddl to create an exception code.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* ddl_node = statement->getDdlNode();
const dsql_str* name = (dsql_str*) ddl_node->nod_arg[e_xcp_name];
switch (op)
{
case nod_replace_exception:
if (METD_get_exception(dsqlScratch->getTransaction(), name)) {
define_exception(dsqlScratch, nod_mod_exception);
}
else {
define_exception(dsqlScratch, nod_def_exception);
}
break;
case nod_def_exception:
case nod_redef_exception:
statement->append_cstring(isc_dyn_def_exception, name->str_data);
break;
case nod_mod_exception:
statement->append_cstring(isc_dyn_mod_exception, name->str_data);
break;
default:
fb_assert(false);
}
const dsql_str* text = (dsql_str*) ddl_node->nod_arg[e_xcp_text];
fb_assert(text->str_length <= MAX_USHORT);
statement->append_string(isc_dyn_xcp_msg, text->str_data, text->str_length);
statement->append_uchar(isc_dyn_end);
}
static void define_field(DsqlCompilerScratch* dsqlScratch,
dsql_nod* element, SSHORT position,
const dsql_str* relation_name,
const dsql_nod* pkcols)
{
/**************************************
*
* d e f i n e _ f i e l d
*
**************************************
*
* Function
* Define a field, either as part of a create
* table or an alter table statement.
*
**************************************/
dsql_fld* field = (dsql_fld*) element->nod_arg[e_dfl_field];
// add the field to the relation being defined for parsing purposes
bool permanent = false;
dsql_rel* relation = dsqlScratch->relation;
if (relation != NULL)
{
if (! (relation->rel_flags & REL_new_relation))
{
dsql_fld* perm_field = FB_NEW(dsqlScratch->getAttachment()->dbb_pool) dsql_fld(
dsqlScratch->getAttachment()->dbb_pool);
*perm_field = *field;
field = perm_field;
permanent = true;
}
field->fld_next = relation->rel_fields;
relation->rel_fields = field;
}
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
try {
const dsql_nod* domain_node = element->nod_arg[e_dfl_domain];
if (domain_node)
{
statement->append_string(isc_dyn_def_local_fld, field->fld_name);
const dsql_nod* node1 = domain_node->nod_arg[e_dom_name];
const dsql_str* domain_name = (dsql_str*) node1->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_fld_source, domain_name->str_data);
// Get the domain information
if (!METD_get_domain(dsqlScratch->getTransaction(), field, domain_name->str_data))
{
// Specified domain or source field does not exist
post_607(Arg::Gds(isc_dsql_domain_not_found) << Arg::Str(domain_name->str_data));
}
DDL_resolve_intl_type(dsqlScratch, field,
reinterpret_cast<const dsql_str*>(element->nod_arg[e_dfl_collate]));
if (element->nod_arg[e_dfl_collate]) {
statement->append_number(isc_dyn_fld_collation, field->fld_collation_id);
}
}
else
{
statement->append_string(isc_dyn_def_sql_fld, field->fld_name);
if (relation_name) {
statement->append_cstring(isc_dyn_rel_name, relation_name->str_data);
}
if (element->nod_arg[e_dfl_computed])
{
field->fld_flags |= FLD_computed;
dsql_nod* computed_node = element->nod_arg[e_dfl_computed];
define_computed(dsqlScratch, statement->getDdlNode()->nod_arg[e_drl_name], field,
computed_node);
}
DDL_resolve_intl_type(dsqlScratch, field,
reinterpret_cast<const dsql_str*>(element->nod_arg[e_dfl_collate]));
put_field(dsqlScratch, field, false);
}
if ((relation->rel_flags & REL_external) &&
(field->fld_dtype == dtype_blob || field->fld_dtype == dtype_array || field->fld_dimensions))
{
const char* typeName = (field->fld_dtype == dtype_blob ? "BLOB" : "ARRAY");
post_607(Arg::Gds(isc_dsql_type_not_supp_ext_tab) << Arg::Str(typeName) <<
Arg::Str(relation->rel_name) <<
Arg::Str(field->fld_name));
}
if (position != -1)
statement->append_number(isc_dyn_fld_position, position);
// check for a default value
bool default_null_flag = false;
dsql_nod* node = element->nod_arg[e_dfl_default];
if (node)
{
default_null_flag = define_default(dsqlScratch, node);
}
if (field->fld_ranges) {
define_dimensions(dsqlScratch, field);
}
// check for constraints
bool not_null_flag = false;
if (element->nod_arg[e_dfl_identity])
{
not_null_flag = true; // identity columns are implicitly not null
statement->append_uchar(isc_dyn_fld_identity);
}
// dimitr: store the final position of the vector to insert a not null
// item later, if required. This is a kind of a hack, but I see
// no other way to ensure that NOT NULL is properly understood
// everywhere in the column constraint definition. A better
// solution would be a special class which handles all append_*
// operations for BLR/DYN (we could store constraints in the
// separate object and then merge them into statement->getBlrData()), but
// this is for another day.
const size_t end = statement->getBlrData().getCount();
statement->append_uchar(isc_dyn_end);
if ( (node = element->nod_arg[e_dfl_constraint]) )
{
const dsql_nod* const* const end_ptr = node->nod_arg + node->nod_count;
for (dsql_nod** ptr = node->nod_arg; ptr < end_ptr; ++ptr)
{
if ((*ptr)->nod_type == nod_rel_constraint)
{
const dsql_str* string = (dsql_str*) (*ptr)->nod_arg[e_rct_name];
dsql_nod* node1 = (*ptr)->nod_arg[e_rct_type];
switch (node1->nod_type)
{
case nod_null:
case nod_primary:
if (default_null_flag)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_bad_default_value) <<
Arg::Gds(isc_invalid_clause) << Arg::Str("default null not null"));
}
if (!not_null_flag)
{
statement->append_cstring(isc_dyn_rel_constraint,
string && node1->nod_type == nod_null ? string->str_data : 0);
statement->append_uchar(isc_dyn_fld_not_null);
statement->append_uchar(isc_dyn_end);
not_null_flag = true;
}
if (node1->nod_type == nod_null)
break;
// nod_primary falls into
case nod_unique:
{
const char* constraint_name = string ? string->str_data : 0;
statement->append_cstring(isc_dyn_rel_constraint, constraint_name);
const dsql_nod* index = node1->nod_arg[e_pri_index];
fb_assert(index);
const char* index_name = constraint_name;
string = (dsql_str*) index->nod_arg[e_idx_name];
if (string)
index_name = string->str_data;
if (node1->nod_type == nod_primary)
statement->append_cstring(isc_dyn_def_primary_key, index_name);
else if (node1->nod_type == nod_unique)
statement->append_cstring(isc_dyn_def_unique, index_name);
statement->append_number(isc_dyn_idx_unique, 1);
if (index->nod_arg[e_idx_asc_dsc])
statement->append_number(isc_dyn_idx_type, 1);
statement->append_string(isc_dyn_fld_name, field->fld_name);
statement->append_uchar(isc_dyn_end);
}
break;
case nod_foreign:
{
const char* constraint_name = string ? string->str_data : 0;
statement->append_cstring(isc_dyn_rel_constraint, constraint_name);
foreign_key(dsqlScratch, node1, constraint_name);
}
break;
case nod_def_constraint:
statement->append_cstring(isc_dyn_rel_constraint, string ? string->str_data : 0);
check_constraint(dsqlScratch, node1, false); // No delete trigger
break;
}
}
}
}
if (not_null_flag)
{
// dimitr: insert a not null item right before column's isc_dyn_end
statement->getBlrData().insert(end, isc_dyn_fld_not_null);
}
else if (pkcols)
{
// Let's see if it appears in a "primary_key(a, b, c)" relation constraint.
for (int i = 0; i < pkcols->nod_count; ++i)
{
const dsql_str* pkfield_name = (dsql_str*) pkcols->nod_arg[i]->nod_arg[e_fln_name];
if (field->fld_name == pkfield_name->str_data)
{
statement->getBlrData().insert(end, isc_dyn_fld_not_null);
break;
}
}
}
} // try
catch (const Exception&)
{
clearPermanentField(relation, permanent);
throw;
}
clearPermanentField(relation, permanent);
}
static SSHORT getBlobFilterSubType(DsqlCompilerScratch* dsqlScratch, const dsql_nod* node)
{
/*******************************************
*
* g e t B l o b F i l t e r S u b T y p e
*
*******************************************
*
* Function
* get sub_type value from nod_constant.
*
**************************************/
fb_assert(node->nod_type == nod_constant);
switch (node->nod_desc.dsc_dtype)
{
case dtype_long:
return (SSHORT) node->getSlong();
case dtype_text:
break;
default:
fb_assert(false);
return 0;
}
// fall thru for dtype_text
const dsql_str* type_name = reinterpret_cast<const dsql_str*>(node->nod_arg[0]);
SSHORT blob_sub_type;
if (!METD_get_type(dsqlScratch->getTransaction(), type_name, "RDB$FIELD_SUB_TYPE", &blob_sub_type))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_datatype_err) <<
Arg::Gds(isc_dsql_blob_type_unknown) << Arg::Str(type_name->str_data));
}
return blob_sub_type;
}
static void define_filter(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e f i n e _ f i l t e r
*
**************************************
*
* Function
* define a filter to the database.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* filter_node = statement->getDdlNode();
const dsql_nod* const* ptr = filter_node->nod_arg;
statement->append_cstring(isc_dyn_def_filter, ((dsql_str*) (ptr[e_filter_name]))->str_data);
statement->append_number(isc_dyn_filter_in_subtype,
getBlobFilterSubType(dsqlScratch, ptr[e_filter_in_type]));
statement->append_number(isc_dyn_filter_out_subtype,
getBlobFilterSubType(dsqlScratch, ptr[e_filter_out_type]));
statement->append_cstring(isc_dyn_func_entry_point,
((dsql_str*) (ptr[e_filter_entry_pt]))->str_data);
statement->append_cstring(isc_dyn_func_module_name,
((dsql_str*) (ptr[e_filter_module]))->str_data);
statement->append_uchar(isc_dyn_end);
}
static void define_collation(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e f i n e _ c o l l a t i o n
*
**************************************
*
* Function
* Create a collation.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_str* coll_name = (dsql_str*) statement->getDdlNode()->nod_arg[e_def_coll_name];
const dsql_str* coll_for = (dsql_str*) statement->getDdlNode()->nod_arg[e_def_coll_for];
const dsql_nod* coll_from = statement->getDdlNode()->nod_arg[e_def_coll_from];
const dsql_nod* coll_attributes = statement->getDdlNode()->nod_arg[e_def_coll_attributes];
const dsql_nod* coll_specific_attributes =
PASS1_node(dsqlScratch, statement->getDdlNode()->nod_arg[e_def_coll_specific_attributes]);
const dsql_intlsym* resolved_charset =
METD_get_charset(dsqlScratch->getTransaction(), (USHORT) strlen(coll_for->str_data),
coll_for->str_data);
if (!resolved_charset)
{
// specified character set not found
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
Arg::Gds(isc_charset_not_found) << Arg::Str(coll_for->str_data));
}
if (coll_specific_attributes)
coll_specific_attributes = coll_specific_attributes->nod_arg[0];
statement->append_cstring(isc_dyn_def_collation, coll_name->str_data);
statement->append_number(isc_dyn_coll_for_charset, resolved_charset->intlsym_charset_id);
if (coll_from)
{
const dsql_str* coll_name = reinterpret_cast<dsql_str*>(coll_from->nod_arg[0]);
if (coll_from->nod_type == nod_collation_from)
{
const dsql_intlsym* resolved_collation = METD_get_collation(
dsqlScratch->getTransaction(), coll_name, resolved_charset->intlsym_charset_id);
if (!resolved_collation)
{
// Specified collation not found
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_collation_not_found) << Arg::Str(coll_name->str_data) <<
Arg::Str(resolved_charset->intlsym_name));
}
statement->append_number(isc_dyn_coll_from,
INTL_CS_COLL_TO_TTYPE(resolved_collation->intlsym_charset_id,
resolved_collation->intlsym_collate_id));
}
else if (coll_from->nod_type == nod_collation_from_external)
{
statement->append_cstring(isc_dyn_coll_from_external, coll_name->str_data);
}
else
fb_assert(false);
}
if (coll_attributes)
{
const dsql_nod* const* ptr = coll_attributes->nod_arg;
for (const dsql_nod* const* const end = ptr + coll_attributes->nod_count; ptr < end; ptr++)
{
const dsql_nod* attribute = *ptr;
switch (attribute->nod_type)
{
case nod_collation_attr:
statement->append_number(isc_dyn_coll_attribute, (IPTR) attribute->nod_arg[0]);
break;
default:
break;
}
}
}
if (coll_specific_attributes)
{
statement->append_number(isc_dyn_coll_specific_attributes_charset,
coll_specific_attributes->nod_desc.dsc_ttype());
statement->append_cstring(isc_dyn_coll_specific_attributes,
((dsql_str*)coll_specific_attributes->nod_arg[0])->str_data);
}
statement->append_uchar(isc_dyn_end);
}
static void define_index(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e f i n e _ i n d e x
*
**************************************
*
* Function
* Generate ddl to create an index.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->append_uchar(isc_dyn_begin);
const dsql_nod* ddl_node = statement->getDdlNode();
dsql_nod* relation_node = (dsql_nod*) ddl_node->nod_arg[e_idx_table];
const dsql_str* relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name];
dsql_nod* field_list = ddl_node->nod_arg[e_idx_fields];
const dsql_str* index_name = (dsql_str*) ddl_node->nod_arg[e_idx_name];
statement->append_cstring(isc_dyn_def_idx, index_name->str_data);
statement->append_cstring(isc_dyn_rel_name, relation_name->str_data);
// go through the fields list, making an index segment for each field,
// unless we have a computation, in which case generate an expression index
if (field_list->nod_type == nod_list)
{
const dsql_nod* const* ptr = field_list->nod_arg;
const dsql_nod* const* const end = ptr + field_list->nod_count;
for (; ptr < end; ptr++)
statement->append_cstring(isc_dyn_fld_name, ((dsql_str*) (*ptr)->nod_arg[1])->str_data);
}
else if (field_list->nod_type == nod_def_computed)
define_computed(dsqlScratch, relation_node, NULL, field_list);
// check for a unique index
if (ddl_node->nod_arg[e_idx_unique]) {
statement->append_number(isc_dyn_idx_unique, 1);
}
if (ddl_node->nod_arg[e_idx_asc_dsc]) {
statement->append_number(isc_dyn_idx_type, 1);
}
statement->append_uchar(isc_dyn_end); // of define index
statement->append_uchar(isc_dyn_end); // of begin
}
// *****************************************
// d e f i n e _ r e l _ c o n s t r a i n t
// *****************************************
// Define a constraint, either as part of a create
// table or an alter table statement.
//
static void define_rel_constraint(DsqlCompilerScratch* dsqlScratch, dsql_nod* element)
{
const dsql_str* string = (dsql_str*) element->nod_arg[e_rct_name];
const char* constraint_name = string ? string->str_data : 0;
dsqlScratch->getStatement()->append_cstring(isc_dyn_rel_constraint, constraint_name);
dsql_nod* node = element->nod_arg[e_rct_type];
switch (node->nod_type)
{
case nod_unique:
case nod_primary:
make_index(dsqlScratch, node, node->nod_arg[e_pri_columns], constraint_name);
break;
case nod_foreign:
foreign_key(dsqlScratch, node, constraint_name);
break;
case nod_def_constraint:
check_constraint(dsqlScratch, node, false); // false = No delete trigger
break;
default: // silence compiler
break;
}
}
static void define_relation(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e f i n e _ r e l a t i o n
*
**************************************
*
* Function
* Create an SQL table, relying on DYN to generate
* global fields for the local fields.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data(); // not used
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* ddl_node = statement->getDdlNode();
const dsql_nod* relation_node = ddl_node->nod_arg[e_drl_name];
const dsql_str* relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name];
statement->append_cstring(isc_dyn_def_rel, relation_name->str_data);
const dsql_str* external_file = (dsql_str*) ddl_node->nod_arg[e_drl_ext_file];
if (external_file)
{
statement->append_cstring(isc_dyn_rel_ext_file, external_file->str_data);
}
save_relation(dsqlScratch, relation_name);
if (external_file)
{
fb_assert(dsqlScratch->relation);
dsqlScratch->relation->rel_flags |= REL_external;
}
statement->append_number(isc_dyn_rel_sql_protection, 1);
switch (ddl_node->nod_flags)
{
case NOD_GLOBAL_TEMP_TABLE_PRESERVE_ROWS:
statement->append_number(isc_dyn_rel_temporary, isc_dyn_rel_temp_global_preserve);
break;
case NOD_GLOBAL_TEMP_TABLE_DELETE_ROWS:
statement->append_number(isc_dyn_rel_temporary, isc_dyn_rel_temp_global_delete);
break;
}
// now do the actual metadata definition
dsql_nod* elements = ddl_node->nod_arg[e_drl_elements];
const dsql_nod* pkcols = find_pk_columns(elements);
SSHORT position = 0;
dsql_nod** ptr = elements->nod_arg;
for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ptr++)
{
dsql_nod* element = *ptr;
switch (element->nod_type)
{
case nod_def_field:
define_field(dsqlScratch, element, position, relation_name, pkcols);
++position;
break;
case nod_rel_constraint:
define_rel_constraint(dsqlScratch, element);
break;
default:
break;
}
}
dsqlScratch->relation->rel_flags &= ~REL_creating;
statement->append_uchar(isc_dyn_end);
}
// ******************************
// d e f i n e _ r o l e
// ******************************
// Create a SQL role.
//
static void define_role(DsqlCompilerScratch* dsqlScratch)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_str* role_name = (dsql_str*) statement->getDdlNode()->nod_arg[0];
statement->append_cstring(isc_dyn_def_sql_role, role_name->str_data);
statement->append_uchar(isc_dyn_end);
}
static void define_set_null_trg(DsqlCompilerScratch* dsqlScratch,
const dsql_nod* element,
const dsql_nod* for_columns,
const dsql_nod* prim_columns,
const char* prim_rel_name,
const char* for_rel_name,
bool on_upd_trg)
{
/*****************************************************
*
* d e f i n e _ s e t _ n u l l _ t r g
*
*****************************************************
*
* Function
* define "on delete/update set null" trigger (for referential integrity)
* The trigger blr is the same for both the delete and update
* cases. Only difference is its TRIGGER_TYPE (ON DELETE or ON UPDATE)
* The on_upd_trg parameter == true is an update trigger.
*
*****************************************************/
if (element->nod_type != nod_foreign) {
return;
}
// count of foreign key columns
fb_assert(prim_columns->nod_count == for_columns->nod_count);
fb_assert(prim_columns->nod_count != 0);
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->generate_unnamed_trigger_beginning(on_upd_trg, prim_rel_name, prim_columns,
for_rel_name, for_columns);
USHORT num_fields = 0;
const dsql_nod* const* for_key_flds = for_columns->nod_arg;
do {
const dsql_str* for_key_fld_name_str = (dsql_str*) (*for_key_flds)->nod_arg[1];
statement->append_uchar(blr_assignment);
statement->append_uchar(blr_null);
statement->append_uchar(blr_field);
statement->append_uchar(2);
statement->append_cstring(0, for_key_fld_name_str->str_data);
num_fields++;
for_key_flds++;
} while (num_fields < for_columns->nod_count);
statement->append_uchar(blr_end);
if (on_upd_trg) {
statement->append_uchars(blr_end, 3);
}
statement->end_blr();
// end of the blr
statement->append_number(isc_dyn_system_flag, fb_sysflag_referential_constraint);
// no trg_source and no trg_description
statement->append_uchar(isc_dyn_end);
}
//
// create a shadow for the database
//
static void define_shadow(DsqlCompilerScratch* dsqlScratch)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* shadow_node = statement->getDdlNode();
const dsql_nod* const* ptr = shadow_node->nod_arg;
if (!ptr[e_shadow_number])
{
post_607(Arg::Gds(isc_dsql_shadow_number_err));
}
statement->append_number(isc_dyn_def_shadow, (SSHORT)(IPTR) (ptr[e_shadow_number]));
statement->append_cstring(isc_dyn_def_file, ((dsql_str*) (ptr[e_shadow_name]))->str_data);
statement->append_number(isc_dyn_shadow_man_auto, (SSHORT) ptr[e_shadow_man_auto]->getSlong());
statement->append_number(isc_dyn_shadow_conditional, (SSHORT) ptr[e_shadow_conditional]->getSlong());
statement->append_file_start(0);
SLONG length = (IPTR) ptr[e_shadow_length];
statement->append_file_length(length);
statement->append_uchar(isc_dyn_end);
const dsql_nod* elements = ptr[e_shadow_sec_files];
if (elements)
{
ptr = elements->nod_arg;
for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ++ptr)
{
const dsql_nod* element = *ptr;
const dsql_fil* file = (dsql_fil*) element->nod_arg[0];
statement->append_cstring(isc_dyn_def_file, file->fil_name->str_data);
if (!length && !file->fil_start)
{
// Preceding file did not specify length, so %s must include starting page number
post_607(Arg::Gds(isc_dsql_file_length_err) << Arg::Str(file->fil_name->str_data));
}
const SLONG start = file->fil_start;
statement->append_file_start(start);
length = file->fil_length;
statement->append_file_length(length);
statement->append_uchar(isc_dyn_end);
}
}
statement->append_uchar(isc_dyn_end);
}
static void define_udf(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e f i n e _ u d f
*
**************************************
*
* Function
* define a udf to the database.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
SSHORT position, blob_position = -1;
dsql_nod* udf_node = statement->getDdlNode();
dsql_nod* arguments = udf_node->nod_arg[e_udf_args];
dsql_nod** ptr = udf_node->nod_arg;
const char* udf_name = ((dsql_str*) (ptr[e_udf_name]))->str_data;
const dsql_str* func_entry_point_name = reinterpret_cast<dsql_str*>(ptr[e_udf_entry_pt]);
const dsql_str* func_module_name = reinterpret_cast<dsql_str*>(ptr[e_udf_module]);
statement->append_cstring(isc_dyn_def_function, udf_name);
statement->append_cstring(isc_dyn_func_entry_point, func_entry_point_name->str_data);
if (func_module_name)
statement->append_cstring(isc_dyn_func_module_name, func_module_name->str_data);
dsql_nod** ret_val_ptr = ptr[e_udf_return_value]->nod_arg;
dsql_fld* field = (dsql_fld*) ret_val_ptr[0];
if (field)
{
// CVC: This is case of "returns <type> [by value|reference]"
// Some data types can not be returned as value
if (((int) ret_val_ptr[1]->getSlong() == FUN_value) &&
(field->fld_dtype == dtype_text || field->fld_dtype == dtype_varying ||
field->fld_dtype == dtype_cstring || field->fld_dtype == dtype_blob ||
field->fld_dtype == dtype_timestamp))
{
// Return mode by value not allowed for this data type
post_607(Arg::Gds(isc_return_mode_err));
}
// For functions returning a blob, coerce return argument position to
// be the last parameter.
if (field->fld_dtype == dtype_blob)
{
blob_position = arguments ? arguments->nod_count + 1 : 1;
if (blob_position > MAX_UDF_ARGUMENTS)
{
// External functions can not have more than 10 parameters
// Or 9 if the function returns a BLOB
post_607(Arg::Gds(isc_extern_func_err));
}
statement->append_number(isc_dyn_func_return_argument, blob_position);
}
else
{
statement->append_number(isc_dyn_func_return_argument, (SSHORT) 0);
}
position = 0;
}
else
{
// CVC: This is case of "returns parameter <N>"
position = (SSHORT) ret_val_ptr[1]->getSlong();
// Function modifies an argument whose value is the function return value
if (!arguments || position > arguments->nod_count || position < 1)
{
post_607(Arg::Gds(isc_dsql_udf_return_pos_err) << //gds__extern_func_err,
Arg::Num(arguments ? arguments->nod_count : 0));
// CVC: We should devise new msg "position should be between 1 and #params";
// here it is: dsql_udf_return_pos_err
// External functions can not have more than 10 parameters
// Not strictly correct -- return position error
}
// We'll verify that SCALAR_ARRAY can't be used as a return type.
// The support for SCALAR_ARRAY is only for input parameters.
const dsql_nod* ret_arg = arguments->nod_arg[position - 1];
const dsql_nod* const* param_node = ret_arg->nod_arg;
if (param_node[e_udf_param_type])
{
const SSHORT arg_mechanism = (SSHORT) param_node[e_udf_param_type]->getSlong();
if (arg_mechanism == FUN_scalar_array)
post_607(Arg::Gds(isc_random) << Arg::Str("BY SCALAR_ARRAY can't be used as a return parameter"));
}
statement->append_number(isc_dyn_func_return_argument, position);
position = 1;
}
// Now define all the arguments
if (!position)
{
// CVC: This is case of "returns <type> [by value|reference]"
if (field->fld_dtype == dtype_blob)
{
// CVC: I need to test returning blobs by descriptor before allowing the
// change there. For now, I ignore the return type specification.
const bool free_it = ((SSHORT) ret_val_ptr[1]->getSlong() < 0);
statement->append_number(isc_dyn_def_function_arg, blob_position);
statement->append_number(isc_dyn_func_mechanism,
(SSHORT)(SLONG) ((free_it ? -1 : 1) * FUN_blob_struct));
// if we have the free_it set then the blob has to be freed on return
}
else
{
statement->append_number(isc_dyn_def_function_arg, (SSHORT) 0);
statement->append_number(isc_dyn_func_mechanism, (SSHORT) ret_val_ptr[1]->getSlong());
}
statement->append_cstring(isc_dyn_function_name, udf_name);
DDL_resolve_intl_type(dsqlScratch, field, NULL);
put_field(dsqlScratch, field, true);
statement->append_uchar(isc_dyn_end);
position = 1;
}
fb_assert(position == 1);
// CVC: This for all params, including the case of "returns parameter <N>"
if (arguments)
{
ptr = arguments->nod_arg;
for (const dsql_nod* const* const end = ptr + arguments->nod_count;
ptr < end; ptr++, position++)
{
if (position > MAX_UDF_ARGUMENTS)
{
// External functions can not have more than 10 parameters
post_607(Arg::Gds(isc_extern_func_err));
}
// field = (dsql_fld*) *ptr;
dsql_nod** param_node = (*ptr)->nod_arg;
field = (dsql_fld*) param_node[e_udf_param_field];
statement->append_number(isc_dyn_def_function_arg, position);
if (param_node[e_udf_param_type])
{
const SSHORT arg_mechanism = (SSHORT) param_node[e_udf_param_type]->getSlong();
statement->append_number(isc_dyn_func_mechanism, arg_mechanism);
}
else if (field->fld_dtype == dtype_blob) {
statement->append_number(isc_dyn_func_mechanism, (SSHORT) FUN_blob_struct);
}
else {
statement->append_number(isc_dyn_func_mechanism, (SSHORT) FUN_reference);
}
statement->append_cstring(isc_dyn_function_name, udf_name);
DDL_resolve_intl_type(dsqlScratch, field, NULL);
put_field(dsqlScratch, field, true);
statement->append_uchar(isc_dyn_end);
}
}
statement->append_uchar(isc_dyn_end);
}
static void define_update_action(DsqlCompilerScratch* dsqlScratch,
dsql_nod** base_and_node, dsql_nod** base_relation, dsql_nod* items)
{
/**************************************
*
* d e f i n e _ u p d a t e _ a c t i o n
*
**************************************
*
* Function
* Define an action statement which, given a view
* definition, will map an update to a record from
* a view of a single relation into the
* base relation.
*
**************************************/
dsql_nod* ddl_node = dsqlScratch->getStatement()->getDdlNode();
// check whether this is an updatable view definition
dsql_nod* select_node = NULL;
dsql_nod* select_expr = NULL;
dsql_nod* from_list = NULL;
if ((ddl_node->nod_type != nod_def_view && ddl_node->nod_type != nod_redef_view &&
ddl_node->nod_type != nod_replace_view && ddl_node->nod_type != nod_mod_view) ||
!(select_node = ddl_node->nod_arg[e_view_select]) ||
!(select_expr = select_node->nod_arg[e_sel_query_spec]) ||
!(from_list = select_expr->nod_arg[e_qry_from]) ||
from_list->nod_count != 1)
{
// The caller seems throwing proper errors for all the above conditions.
// But just in case it doesn't, here we have the final attempt to prevent the bad things.
fb_assert(false);
}
// use the relation referenced in the select statement for rse
dsql_nod* relation_node = MAKE_node(nod_relation_name, (int) e_rln_count);
relation_node->nod_arg[e_rln_name] = from_list->nod_arg[0]->nod_arg[e_rln_name];
relation_node->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(TEMP_CONTEXT);
*base_relation = relation_node;
// get the list of values and fields to compare to -- if there is
// no list of fields, get all fields in the base relation that
// are not computed
dsql_nod* values_node = ddl_node->nod_arg[e_view_fields];
dsql_nod* fields_node = select_expr->nod_arg[e_qry_list];
if (!fields_node)
{
const dsql_str* rel_name = reinterpret_cast<const dsql_str*>(relation_node->nod_arg[e_rln_name]);
const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, rel_name);
DsqlNodStack field_stack;
for (const dsql_fld* field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
field_stack.push(MAKE_field_name(field->fld_name.c_str()));
}
fields_node = MAKE_list(field_stack);
}
if (!values_node)
values_node = fields_node;
// generate the list of assignments to fields in the base relation
dsql_nod** ptr = fields_node->nod_arg;
const dsql_nod* const* const end = ptr + fields_node->nod_count;
dsql_nod** ptr2 = values_node->nod_arg;
const dsql_nod* const* const end2 = ptr2 + values_node->nod_count;
dsql_nod* and_node = MAKE_node(nod_and, (int) 2);
int and_arg = 0;
for (; (ptr < end) && (ptr2 < end2); ptr++, ptr2++)
{
dsql_nod* field_node = *ptr;
if (field_node->nod_type == nod_alias)
field_node = field_node->nod_arg[e_alias_value];
// generate the actual comparisons
if (field_node->nod_type == nod_field_name)
{
field_node->nod_arg[e_fln_context] = (dsql_nod*) MAKE_cstring(TEMP_CONTEXT);
// CVC: This code serves no purpose.
//dsql_nod* value_node = MAKE_node(nod_field_name, (int) e_fln_count);
//value_node->nod_arg[e_fln_name] = (*ptr2)->nod_arg[e_fln_name];
//value_node->nod_arg[e_fln_context] =
// (dsql_nod*) MAKE_cstring(NEW_CONTEXT);
dsql_nod* old_value_node = MAKE_node(nod_field_name, (int) e_fln_count);
old_value_node->nod_arg[e_fln_name] = (*ptr2)->nod_arg[e_fln_name];
old_value_node->nod_arg[e_fln_context] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT);
dsql_nod* eql_node = MAKE_node(nod_eql, (int) 2);
eql_node->nod_arg[0] = old_value_node;
eql_node->nod_arg[1] = field_node;
dsql_nod* anull_node = MAKE_node(nod_missing, 1);
anull_node->nod_arg[0] = old_value_node;
dsql_nod* bnull_node = MAKE_node(nod_missing, 1);
bnull_node->nod_arg[0] = field_node;
dsql_nod* iand_node = MAKE_node(nod_and, (int) 2);
iand_node->nod_arg[0] = anull_node;
iand_node->nod_arg[1] = bnull_node;
dsql_nod* or_node = MAKE_node(nod_or, (int) 2);
or_node->nod_arg[0] = eql_node;
or_node->nod_arg[1] = iand_node;
if (and_arg <= 1)
and_node->nod_arg[and_arg++] = or_node;
else
{
dsql_nod* old_and = and_node;
and_node = MAKE_node(nod_and, (int) 2);
and_node->nod_arg[0] = old_and;
and_node->nod_arg[1] = or_node;
}
}
}
if (and_arg <= 1)
{
and_node->nod_arg[and_arg] =
replace_field_names(select_expr->nod_arg[e_qry_where], items, NULL, false, TEMP_CONTEXT);
}
else
{
dsql_nod* old_and = and_node;
and_node = MAKE_node(nod_and, (int) 2);
and_node->nod_arg[0] = old_and;
and_node->nod_arg[1] =
replace_field_names(select_expr->nod_arg[e_qry_where], items, NULL, false, TEMP_CONTEXT);
}
*base_and_node = and_node;
}
static void define_upd_cascade_trg( DsqlCompilerScratch* dsqlScratch,
const dsql_nod* element,
const dsql_nod* for_columns,
const dsql_nod* prim_columns,
const char* prim_rel_name,
const char* for_rel_name)
{
/*****************************************************
*
* d e f i n e _ u p d _ c a s c a d e _ t r g
*
*****************************************************
*
* Function
* define "on update cascade" trigger (for referential integrity)
* along with the trigger blr.
*
*****************************************************/
if (element->nod_type != nod_foreign) {
return;
}
// count of foreign key columns
fb_assert(prim_columns->nod_count == for_columns->nod_count);
fb_assert(prim_columns->nod_count != 0);
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->generate_unnamed_trigger_beginning(true,
prim_rel_name,
prim_columns,
for_rel_name,
for_columns);
USHORT num_fields = 0;
const dsql_nod* const* for_key_flds = for_columns->nod_arg;
const dsql_nod* const* prim_key_flds = prim_columns->nod_arg;
do {
const dsql_str* for_key_fld_name_str = (dsql_str*) (*for_key_flds)->nod_arg[1];
const dsql_str* prim_key_fld_name_str = (dsql_str*) (*prim_key_flds)->nod_arg[1];
statement->append_uchar(blr_assignment);
statement->append_uchar(blr_field);
statement->append_uchar(1);
statement->append_cstring(0, prim_key_fld_name_str->str_data);
statement->append_uchar(blr_field);
statement->append_uchar(2);
statement->append_cstring(0, for_key_fld_name_str->str_data);
num_fields++;
prim_key_flds++;
for_key_flds++;
} while (num_fields < for_columns->nod_count);
statement->append_uchars(blr_end, 4);
statement->end_blr();
// end of the blr
statement->append_number(isc_dyn_system_flag, fb_sysflag_referential_constraint);
// no trg_source and no trg_description
statement->append_uchar(isc_dyn_end);
}
static void define_view(DsqlCompilerScratch* dsqlScratch, NOD_TYPE op)
{
/**************************************
*
* d e f i n e _ v i e w
*
**************************************
*
* Function
* Create the ddl to define a view, using a SELECT
* statement as the source of the view.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data(); // not used
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* node = statement->getDdlNode();
const dsql_str* view_name = (dsql_str*) node->nod_arg[e_view_name];
const dsql_rel* view_relation = NULL;
switch (op)
{
case nod_replace_view:
if (METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, view_name))
define_view(dsqlScratch, nod_mod_view);
else
define_view(dsqlScratch, nod_def_view);
return;
case nod_def_view:
case nod_redef_view:
statement->append_cstring(isc_dyn_def_view, view_name->str_data);
statement->append_number(isc_dyn_rel_sql_protection, 1);
save_relation(dsqlScratch, view_name);
break;
default: // op == nod_mod_view
statement->append_cstring(isc_dyn_mod_view, view_name->str_data);
view_relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, view_name);
if (!view_relation)
{
post_607(Arg::Gds(isc_dsql_view_not_found) << Arg::Str(view_name->str_data));
}
}
// compile the SELECT statement into a record selection expression,
// making sure to bump the context number since view contexts start
// at 1 (except for computed fields) -- note that calling PASS1_rse
// directly rather than PASS1_statement saves the context stack
reset_context_stack(dsqlScratch);
dsqlScratch->contextNumber++;
dsql_nod* select_expr = node->nod_arg[e_view_select];
select_expr->nod_flags |= NOD_SELECT_VIEW_FIELDS;
dsql_nod* rse = PASS1_rse(dsqlScratch, select_expr, NULL);
// store the blr and source string for the view definition
statement->begin_blr(isc_dyn_view_blr);
// ASF: Call GEN_hidden_variables could be a optimization for views to not have
// blr_dcl_variables inside RSE loops, but this is currently not possible because it will
// mix the variables from view fields and view body.
GEN_expr(dsqlScratch, rse);
statement->end_blr();
// Store source for view. gdef -e cannot cope with it.
// We need to add something to rdb$views to indicate source type.
// Source will be for documentation purposes.
const dsql_str* source = (dsql_str*) node->nod_arg[e_view_source];
fb_assert(source->str_length <= MAX_USHORT);
statement->append_string(isc_dyn_view_source, source->str_data, source->str_length);
// define the view source relations from the statement contexts & union contexts
while (dsqlScratch->derivedContext.hasData())
{
dsqlScratch->context->push(dsqlScratch->derivedContext.pop());
}
while (dsqlScratch->unionContext.hasData())
{
dsqlScratch->context->push(dsqlScratch->unionContext.pop());
}
for (DsqlContextStack::iterator temp(*dsqlScratch->context); temp.hasData(); ++temp)
{
const dsql_ctx* context = temp.object();
const dsql_rel* relation = context->ctx_relation;
const dsql_prc* procedure = context->ctx_procedure;
if (relation || procedure)
{
// ASF: Check disabled as seems to not be reason to prevent
// procedure usage in view. 2007-11-28
/*
if (procedure)
{
// Disallow procedure-based views
ERRD_post(Arg::Gds(isc_wish_list));
}
*/
const MetaName& name = relation ? relation->rel_name : procedure->prc_name.identifier;
statement->append_string(isc_dyn_view_relation, name);
statement->append_number(isc_dyn_view_context, context->ctx_context);
const char* str = context->ctx_alias ? context->ctx_alias : name.c_str();
const USHORT len = context->ctx_alias ? strlen(str) : name.length();
statement->append_string(isc_dyn_view_context_name, str, len);
ViewContextType ctxType;
if (relation)
{
if (!(relation->rel_flags & REL_view))
ctxType = VCT_TABLE;
else
ctxType = VCT_VIEW;
}
else //if (procedure)
{
ctxType = VCT_PROCEDURE;
if (procedure->prc_name.package.hasData())
statement->append_string(isc_dyn_pkg_name, procedure->prc_name.package);
}
statement->append_number(isc_dyn_view_context_type, (SSHORT) ctxType);
statement->append_uchar(isc_dyn_end);
}
}
// if there are field names defined for the view, match them in order
// with the items from the SELECT. Otherwise use all the fields from
// the rse node that was created from the select expression
const dsql_nod* const* ptr = NULL;
const dsql_nod* const* end = NULL;
const dsql_nod* view_fields = node->nod_arg[e_view_fields];
if (view_fields != NULL)
{
ptr = view_fields->nod_arg;
end = ptr + view_fields->nod_count;
}
const TEXT* field_string;
bool updatable = true;
SSHORT position = 0;
// go through the fields list, defining or modifying the local fields;
// if an expression is specified rather than a field, define
// a global field for the computed value as well
dsql_nod* items = rse->nod_arg[e_rse_items];
dsql_nod** i_ptr = items->nod_arg;
SortedArray<dsql_fld*> modified_fields;
for (const dsql_nod* const* const i_end = i_ptr + items->nod_count;
i_ptr < i_end; i_ptr++, position++)
{
dsql_nod* field_node = *i_ptr;
// determine the proper field name, replacing the default if necessary
const dsql_nod* name_node = field_node;
const dsql_str* alias_name = NULL;
while (name_node->nod_type == nod_alias ||
name_node->nod_type == nod_derived_field ||
name_node->nod_type == nod_map)
{
switch (name_node->nod_type)
{
case nod_alias:
if (!alias_name)
{
alias_name = (dsql_str*) name_node->nod_arg[e_alias_alias];
}
name_node = name_node->nod_arg[e_alias_value];
break;
case nod_derived_field:
if (!alias_name)
{
alias_name = (dsql_str*) name_node->nod_arg[e_derived_field_name];
}
name_node = name_node->nod_arg[e_derived_field_value];
break;
case nod_map:
{
const dsql_map* map = (dsql_map*) name_node->nod_arg[e_map_map];
name_node = map->map_node;
}
break;
default:
break;
}
}
const dsql_fld* name_field = NULL;
if (name_node->nod_type == nod_field)
name_field = (dsql_fld*) name_node->nod_arg[e_fld_field];
if (alias_name)
field_string = alias_name->str_data;
else if (name_field)
field_string = name_field->fld_name.c_str();
else
field_string = NULL;
// check if this is a field or an expression
if (field_node->nod_type == nod_alias)
field_node = field_node->nod_arg[e_alias_value];
const dsql_fld* field = NULL;
const dsql_ctx* context = NULL;
if (field_node->nod_type == nod_field)
{
field = (dsql_fld*) field_node->nod_arg[e_fld_field];
context = (dsql_ctx*) field_node->nod_arg[e_fld_context];
}
else
{
updatable = false;
}
// if this is an expression, check to make sure there is a name specified
if (!ptr && !field_string)
{
// must specify field name for view select expression
post_607(Arg::Gds(isc_specify_field_err));
}
// CVC: Small modification here to catch any mismatch between number of
// explicit field names in a view and number of fields in the select expression,
// see comment below. This closes Firebird Bug #223059.
if (ptr)
{
if (ptr < end)
{
const dsql_str* field_name = (dsql_str*) (*ptr)->nod_arg[1];
field_string = field_name->str_data;
}
ptr++;
}
// if not an expression, point to the proper base relation field,
// else make up an SQL field with generated global field for calculations
dsql_fld* rel_field = NULL;
if (view_relation) // if we're modifying a view
{
for (rel_field = view_relation->rel_fields; rel_field; rel_field = rel_field->fld_next)
{
if (rel_field->fld_name == field_string)
{
if (modified_fields.exist(rel_field))
{
// column @1 appears more than once in ALTER VIEW
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_col_more_than_once_view) << Arg::Str(field_string));
}
modified_fields.add(rel_field);
break;
}
}
}
// CVC: Not sure if something should be done now that isc_dyn_view_context is used here,
// but if alter view is going to work, maybe we need here the context type and package, too.
if (field)
{
if (rel_field) // modifying a view
{
statement->append_cstring(isc_dyn_mod_sql_fld, field_string);
statement->append_uchar(isc_dyn_del_computed);
}
else
statement->append_cstring(isc_dyn_def_local_fld, field_string);
statement->append_string(isc_dyn_fld_base_fld, field->fld_name);
if (field->fld_dtype <= dtype_any_text) {
statement->append_number(isc_dyn_fld_collation, field->fld_collation_id);
}
statement->append_number(isc_dyn_view_context, context->ctx_context);
}
else
{
if (rel_field) // modifying a view
{
statement->append_cstring(isc_dyn_mod_sql_fld, field_string);
statement->append_cstring(isc_dyn_fld_base_fld, "");
}
else
statement->append_cstring(isc_dyn_def_sql_fld, field_string);
MAKE_desc(dsqlScratch, &field_node->nod_desc, field_node, NULL);
put_descriptor(dsqlScratch, &field_node->nod_desc);
statement->begin_blr(isc_dyn_fld_computed_blr);
GEN_expr(dsqlScratch, field_node);
statement->end_blr();
statement->append_number(isc_dyn_view_context, (SSHORT) 0);
}
if (field_string)
save_field(dsqlScratch, field_string);
statement->append_number(isc_dyn_fld_position, position);
statement->append_uchar(isc_dyn_end);
}
// CVC: This message was not catching the case when
// #fields < items in select list, see comment above.
if (ptr != end)
{
// number of fields does not match select list
post_607(Arg::Gds(isc_num_field_err));
}
if (view_relation) // modifying a view
{
// delete the old fields not present in the new definition
for (dsql_fld* rel_field = view_relation->rel_fields; rel_field; rel_field = rel_field->fld_next)
{
if (!modified_fields.exist(rel_field))
{
statement->append_string(isc_dyn_delete_local_fld, rel_field->fld_name);
statement->append_uchar(isc_dyn_end);
}
}
}
// setup to define triggers for WITH CHECK OPTION
dsql_nod* check = node->nod_arg[e_view_check];
if (check)
{
if (!updatable)
{
// Only simple column names permitted for VIEW WITH CHECK OPTION
post_607(Arg::Gds(isc_col_name_err));
}
select_expr = select_expr->nod_arg[e_sel_query_spec];
if (select_expr->nod_type == nod_list)
{
// Only one table allowed for VIEW WITH CHECK OPTION
post_607(Arg::Gds(isc_table_view_err));
}
if (select_expr->nod_arg[e_qry_from]->nod_count != 1)
{
// Only one table allowed for VIEW WITH CHECK OPTION
post_607(Arg::Gds(isc_table_view_err));
}
if (!select_expr->nod_arg[e_qry_where])
{
// No where clause for VIEW WITH CHECK OPTION
post_607(Arg::Gds(isc_where_err));
}
if (select_expr->nod_arg[e_qry_distinct] || select_expr->nod_arg[e_qry_group] ||
select_expr->nod_arg[e_qry_having])
{
// DISTINCT, GROUP or HAVING not permitted for VIEW WITH CHECK OPTION
post_607(Arg::Gds(isc_distinct_err));
}
dsql_nod* relation_node = MAKE_node(nod_relation_name, e_rln_count);
// Warning: implicit const_cast
relation_node->nod_arg[e_rln_name] = (dsql_nod*) view_name;
check->nod_arg[e_cnstr_table] = relation_node;
check->nod_arg[e_cnstr_source] = (dsql_nod*) source;
// the condition for the trigger is the converse of the selection
// criteria for the view, suitably fixed up so that the fields in
// the view are referenced
check->nod_arg[e_cnstr_condition] = select_expr->nod_arg[e_qry_where];
// Define the triggers
create_view_triggers(dsqlScratch, check, rse->nod_arg[e_rse_items]);
}
statement->append_uchar(isc_dyn_end);
reset_context_stack(dsqlScratch);
}
static void define_view_trigger(DsqlCompilerScratch* dsqlScratch, dsql_nod* node, dsql_nod* rse,
dsql_nod* items)
{
/**************************************
*
* d e f i n e _ v i e w _ t r i g g e r
*
**************************************
*
* Function
* Create the ddl to define a trigger for a VIEW WITH CHECK OPTION.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* const saved_ddl_node = statement->getDdlNode();
dsql_nod* select_expr = saved_ddl_node->nod_arg[e_view_select];
select_expr = select_expr->nod_arg[e_sel_query_spec];
dsql_nod* view_fields = saved_ddl_node->nod_arg[e_view_fields];
// make the "define trigger" node the current statement ddl node so
// that generating of BLR will be appropriate for trigger
statement->setDdlNode(node);
dsql_nod* relation_node = NULL;
if (node->nod_type == nod_def_constraint)
{
statement->append_string(isc_dyn_def_trigger, "", 0);
relation_node = node->nod_arg[e_cnstr_table];
const dsql_str* relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name];
fb_assert(relation_name->str_length <= MAX_USHORT);
statement->append_string(isc_dyn_rel_name, relation_name->str_data, relation_name->str_length);
}
else
{
return;
}
statement->append_number(isc_dyn_trg_sequence, 0);
const dsql_nod* constant = node->nod_arg[e_cnstr_type];
USHORT trig_type;
if (constant)
{
trig_type = (USHORT) constant->getSlong();
statement->append_number(isc_dyn_trg_type, trig_type);
}
else
{
// If we don't have a trigger type assigned, then this is just a template
// definition for use with domains. The real triggers are defined when
// the domain is used.
trig_type = 0;
}
statement->append_uchar(isc_dyn_sql_object);
// generate the trigger blr
if (node->nod_arg[e_cnstr_condition] && node->nod_arg[e_cnstr_actions])
{
statement->begin_blr(isc_dyn_trg_blr);
statement->append_uchar(blr_begin);
// create the "OLD" and "NEW" contexts for the trigger --
// the new one could be a dummy place holder to avoid resolving
// fields to that context but prevent relations referenced in
// the trigger actions from referencing the predefined "1" context
dsql_ctx* sav_context = 0;
dsql_ctx* context = 0;
if (dsqlScratch->contextNumber)
{
// If an alias is specified for the single base table involved,
// save and then add the context
context = dsqlScratch->context->object();
if (context->ctx_alias)
{
MemoryPool& pool = *tdbb->getDefaultPool();
sav_context = FB_NEW(pool) dsql_ctx(pool);
*sav_context = *context;
}
}
reset_context_stack(dsqlScratch);
dsql_nod* temp_alias = relation_node->nod_arg[e_rln_alias];
relation_node->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT);
dsql_ctx* oldContext = PASS1_make_context(dsqlScratch, relation_node);
oldContext->ctx_flags |= CTX_system;
relation_node->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(NEW_CONTEXT);
dsql_ctx* newContext = PASS1_make_context(dsqlScratch, relation_node);
newContext->ctx_flags |= CTX_system;
relation_node->nod_arg[e_rln_alias] = temp_alias;
if (sav_context)
{
sav_context->ctx_context = dsqlScratch->contextNumber++;
context->ctx_scope_level = dsqlScratch->scopeLevel;
dsqlScratch->context->push(sav_context);
}
// generate the condition for firing the trigger
dsql_nod* condition;
if (trig_type == PRE_MODIFY_TRIGGER)
{
statement->append_uchar(blr_for);
dsql_nod* temp = rse->nod_arg[e_rse_streams];
temp->nod_arg[0] = PASS1_node(dsqlScratch, temp->nod_arg[0]);
temp = rse->nod_arg[e_rse_boolean];
rse->nod_arg[e_rse_boolean] = PASS1_node(dsqlScratch, temp);
GEN_expr(dsqlScratch, rse);
condition = replace_field_names(select_expr->nod_arg[e_qry_where], items,
view_fields, false, NEW_CONTEXT);
}
else if (trig_type == PRE_STORE_TRIGGER)
{
condition = replace_field_names(select_expr->nod_arg[e_qry_where], items,
view_fields, true, NEW_CONTEXT);
}
else {
fb_assert(false);
}
statement->append_uchar(blr_if);
GEN_expr(dsqlScratch, PASS1_node(dsqlScratch, condition));
statement->append_uchar(blr_begin);
statement->append_uchar(blr_end);
// generate the action statements for the trigger
dsql_nod* actions = node->nod_arg[e_cnstr_actions];
dsql_nod** ptr = actions->nod_arg;
for (const dsql_nod* const* const end = ptr + actions->nod_count; ptr < end; ptr++)
{
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, *ptr));
}
statement->append_uchar(blr_end); // of begin
statement->end_blr();
}
statement->append_number(isc_dyn_system_flag, fb_sysflag_view_check);
statement->append_uchar(isc_dyn_end);
// the statement type may have been set incorrectly when parsing
// the trigger actions, so reset it to reflect the fact that this
// is a data definition statement; also reset the ddl node
statement->setType(DsqlCompiledStatement::TYPE_DDL);
statement->setDdlNode(saved_ddl_node);
reset_context_stack(dsqlScratch);
}
static void delete_collation(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* d e l e t e _ c o l l a t i o n
*
**************************************
*
* Function
* Delete a collation.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_str* coll_name = (dsql_str*) statement->getDdlNode()->nod_arg[e_del_coll_name];
statement->append_cstring(isc_dyn_del_collation, coll_name->str_data);
statement->append_uchar(isc_dyn_end);
}
static void delete_exception (DsqlCompilerScratch* dsqlScratch, dsql_nod* node, bool silent_deletion)
{
/**************************************
*
* d e l e t e _ e x c e p t i o n
*
**************************************
*
* Function
* Do nothing and don't throw error if the exception doesn't exist
* and silent_deletion is true.
*
**************************************/
const dsql_str* string = (dsql_str*) node->nod_arg[0];
fb_assert(string);
if (node->nod_type == nod_redef_exception || silent_deletion)
{
if (!METD_get_exception(dsqlScratch->getTransaction(), string)) {
return;
}
}
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->append_cstring(isc_dyn_del_exception, string->str_data);
statement->append_uchar(isc_dyn_end);
}
static void delete_relation_view (DsqlCompilerScratch* dsqlScratch, dsql_nod* node, bool silent_deletion)
{
/**************************************
*
* d e l e t e _ r e l a t i o n _ v i e w
*
**************************************
*
* Function
* Check that DROP TABLE is dropping a table and that
* DROP VIEW is dropping a view.
* Do nothing and don't throw error if the table or view doesn't exist
* and silent_deletion is true.
* CVC: Created this function to not clutter generate_dyn().
*
**************************************/
const dsql_str* string = 0;
if (node->nod_type == nod_redef_relation)
{
dsql_nod* relation_node = node->nod_arg[e_alt_name];
fb_assert (relation_node);
string = (dsql_str*) relation_node->nod_arg[e_rln_name];
}
else {
string = (dsql_str*) node->nod_arg[e_alt_name];
}
fb_assert (string);
const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, string);
if (node->nod_type == nod_del_relation || node->nod_type == nod_redef_relation)
{
if (!relation && !silent_deletion || relation && (relation->rel_flags & REL_view))
{
post_607(Arg::Gds(isc_dsql_table_not_found) << Arg::Str(string->str_data));
}
}
else
{
// node->nod_type == nod_del_view, nod_redef_view
if (!relation && !silent_deletion || relation && !(relation->rel_flags & REL_view))
{
post_607(Arg::Gds(isc_dsql_view_not_found) << Arg::Str(string->str_data));
}
}
if (relation)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->append_cstring(isc_dyn_delete_rel, string->str_data);
statement->append_uchar(isc_dyn_end);
}
}
// f i n d _ p k _ c o l u m n s
//
// @brief Starting from the elements in a table definition, locate the PK columns
// if given in a separate table constraint declaration.
//
const dsql_nod* find_pk_columns(const dsql_nod* def_rel_elements)
{
for (int i = 0; i < def_rel_elements->nod_count; ++i)
{
const dsql_nod* element = def_rel_elements->nod_arg[i];
if (element->nod_type == nod_rel_constraint)
{
const dsql_nod* node = element->nod_arg[e_rct_type];
if (node->nod_type == nod_primary)
return node->nod_arg[e_pri_columns];
}
}
return 0;
}
// f i n d _ s t a r t _ o f _ b o d y
//
// @brief Find the start of a procedure body. Empty lines are irrelevant.
// @param string the source string to be searched.
//
static ULONG find_start_of_body(const dsql_str* string)
{
ULONG j = 0;
for (ULONG i = 0; i < string->str_length; ++i)
{
switch (string->str_data[i])
{
case '\n':
j = i + 1;
break;
case ' ':
case '\r':
case '\t':
break;
default:
return j;
}
}
return 0; // Something suspicious happened, better return zero than str_length.
}
/**
f i x _ d e f a u l t _ s o u r c e
@brief Get rid of newlines between DEFAULT and the value.
@param string the source text to be fixed if necessary.
**/
static void fix_default_source(dsql_str* string)
{
// CVC: I know this is not very brilliant, but some people are annoyed
// at this for years.
// We assume the first position is used by "default"
#ifdef DEV_BUILD
// Verify that assumption about "default"
const char* token = "default\0DEFAULT";
for (int t = 0; t < 7; ++t)
{
const char c = string->str_data[t];
if (c != token[t] && c != token[t + 8])
return; // something is screwed, skip town.
}
#endif
for (ULONG i = 7; i < string->str_length; ++i)
{
switch (string->str_data[i])
{
case ' ':
case '\n':
case '\r':
case '\t':
string->str_data[i] = ' ';
break;
default:
return;
}
}
}
static void foreign_key(DsqlCompilerScratch* dsqlScratch, dsql_nod* element, const char* index_name)
{
/**************************************
*
* f o r e i g n _ k e y
*
**************************************
*
* Function
* Generate ddl to create a foreign key
* constraint.
*
**************************************/
dsql_nod* columns1 = element->nod_arg[e_for_columns];
dsql_nod* relation2_node = element->nod_arg[e_for_reftable];
const dsql_str* relation2 = (dsql_str*) relation2_node->nod_arg[e_rln_name];
// If there is a referenced table name but no referenced field names, the
// primary key of the referenced table designates the referenced fields.
dsql_nod* columns2 = element->nod_arg[e_for_refcolumns];
if (!columns2)
{
element->nod_arg[e_for_refcolumns] = columns2 = METD_get_primary_key(
dsqlScratch->getTransaction(), relation2);
// If there is NEITHER an explicitly referenced field name, NOR does
// the referenced table have a primary key to serve as the implicitly
// referenced field, fail.
if (!columns2)
{
// "REFERENCES table" without "(column)" requires PRIMARY
// KEY on referenced table
post_607(Arg::Gds(isc_reftable_requires_pk));
}
}
if (columns2 && (columns1->nod_count != columns2->nod_count))
{
// foreign key field count does not match primary key
post_607(Arg::Gds(isc_key_field_count_err));
}
// define the foreign key index and the triggers that may be needed
// for referential integrity action.
make_index_trg_ref_int(dsqlScratch, element, columns1, element->nod_arg[e_for_refcolumns],
relation2->str_data, index_name);
}
static void generate_dyn(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
{
/**************************************
*
* g e n e r a t e _ d y n
*
**************************************
*
* Functional description
* Switch off the type of node to generate a
* DYN string.
*
**************************************/
const dsql_str* string;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->setDdlNode(node);
switch (node->nod_type)
{
case nod_def_domain:
define_domain(dsqlScratch);
break;
case nod_mod_domain:
modify_domain(dsqlScratch);
break;
case nod_def_index:
define_index(dsqlScratch);
break;
case nod_def_relation:
define_relation(dsqlScratch);
break;
case nod_redef_relation:
stuff(statement, isc_dyn_begin);
delete_relation_view(dsqlScratch, node, true); // silent.
define_relation (dsqlScratch);
stuff(statement, isc_dyn_end);
break;
case nod_mod_relation:
modify_relation(dsqlScratch);
break;
case nod_def_view:
case nod_mod_view:
case nod_replace_view:
define_view(dsqlScratch, node->nod_type);
break;
case nod_redef_view:
stuff(statement, isc_dyn_begin);
delete_relation_view(dsqlScratch, node, true); // silent.
define_view(dsqlScratch, node->nod_type);
stuff(statement, isc_dyn_end);
break;
case nod_def_exception:
case nod_mod_exception:
case nod_replace_exception:
define_exception(dsqlScratch, node->nod_type);
break;
case nod_redef_exception:
stuff(statement, isc_dyn_begin);
delete_exception(dsqlScratch, node, true); // silent
define_exception(dsqlScratch, node->nod_type);
stuff(statement, isc_dyn_end);
break;
case nod_del_exception:
delete_exception(dsqlScratch, node, false); // no silent
break;
case nod_def_constraint:
define_constraint_trigger(dsqlScratch, node);
break;
case nod_del_domain:
string = (dsql_str*) node->nod_arg[0];
statement->append_cstring(isc_dyn_delete_global_fld, string->str_data);
statement->append_uchar(isc_dyn_end);
break;
case nod_del_index:
string = (dsql_str*) node->nod_arg[0];
statement->append_cstring(isc_dyn_delete_idx, string->str_data);
statement->append_uchar(isc_dyn_end);
break;
// CVC: Handling drop table and drop view properly.
case nod_del_relation:
case nod_del_view:
delete_relation_view (dsqlScratch, node, false); // no silent.
break;
case nod_del_role:
string = (dsql_str*) node->nod_arg[0];
statement->append_cstring(isc_dyn_del_sql_role, string->str_data);
statement->append_uchar(isc_dyn_end);
break;
case nod_grant:
case nod_revoke:
grant_revoke(dsqlScratch);
break;
case nod_def_role:
define_role(dsqlScratch);
break;
case nod_def_filter:
define_filter(dsqlScratch);
break;
case nod_del_generator:
string = (dsql_str*) node->nod_arg[0];
statement->append_cstring(isc_dyn_delete_generator, string->str_data);
statement->append_uchar(isc_dyn_end);
break;
case nod_del_filter:
string = (dsql_str*) node->nod_arg[0];
statement->append_cstring(isc_dyn_delete_filter, string->str_data);
statement->append_uchar(isc_dyn_end);
break;
case nod_def_udf:
define_udf(dsqlScratch);
break;
case nod_del_udf:
string = (dsql_str*) node->nod_arg[0];
statement->append_cstring(isc_dyn_delete_function, string->str_data);
statement->append_uchar(isc_dyn_end);
break;
case nod_def_shadow:
define_shadow(dsqlScratch);
break;
case nod_del_shadow:
statement->append_number(isc_dyn_delete_shadow, (SSHORT) (IPTR) node->nod_arg[0]);
statement->append_uchar(isc_dyn_end);
break;
case nod_mod_database:
modify_database(dsqlScratch);
break;
case nod_def_database:
define_database(dsqlScratch);
break;
case nod_mod_index:
modify_index(dsqlScratch);
break;
case nod_set_statistics:
set_statistics(dsqlScratch);
break;
case nod_mod_udf:
modify_udf(dsqlScratch);
break;
case nod_mod_role:
modify_map(dsqlScratch);
break;
case nod_def_collation:
define_collation(dsqlScratch);
break;
case nod_del_collation:
delete_collation(dsqlScratch);
break;
case nod_add_user:
define_user(dsqlScratch, isc_dyn_user_add);
break;
case nod_mod_user:
define_user(dsqlScratch, isc_dyn_user_mod);
break;
case nod_del_user:
define_user(dsqlScratch, isc_dyn_user_del);
break;
default: // CVC: Shouldn't we complain here?
break;
}
}
static void grant_revoke(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* g r a n t _ r e v o k e
*
**************************************
*
* Functional description
* Build DYN string for GRANT/REVOKE statements
*
**************************************/
const dsql_nod* const* uptr;
const dsql_nod* const* uend;
SSHORT option = 0; // no grant/admin option
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* ddl_node = statement->getDdlNode();
const dsql_nod* privs = ddl_node->nod_arg[e_grant_privs];
const dsql_nod* table = ddl_node->nod_arg[e_grant_table];
if ((ddl_node->nod_type == nod_revoke) && !privs && !table) // ALL ON ALL
{
statement->append_uchar(isc_dyn_begin);
const dsql_nod* users = ddl_node->nod_arg[e_grant_users];
uend = users->nod_arg + users->nod_count;
for (uptr = users->nod_arg; uptr < uend; ++uptr)
{
statement->append_uchar(isc_dyn_revoke_all);
put_user_grant(dsqlScratch, *uptr);
statement->append_uchar(isc_dyn_end);
}
statement->append_uchar(isc_dyn_end);
return;
}
bool process_grant_role = false;
if (privs->nod_arg[0] != NULL)
{
if (privs->nod_arg[0]->nod_type == nod_role_name) {
process_grant_role = true;
}
}
statement->append_uchar(isc_dyn_begin);
if (!process_grant_role)
{
const dsql_nod* users = ddl_node->nod_arg[e_grant_users];
if (ddl_node->nod_arg[e_grant_grant]) {
option = 1; // with grant option
}
uend = users->nod_arg + users->nod_count;
for (uptr = users->nod_arg; uptr < uend; ++uptr)
{
modify_privileges(dsqlScratch, ddl_node->nod_type, option,
privs, table, *uptr, ddl_node->nod_arg[e_grant_grantor]);
}
}
else
{
const dsql_nod* role_list = ddl_node->nod_arg[0];
const dsql_nod* users = ddl_node->nod_arg[1];
if (ddl_node->nod_arg[3]) {
option = 2; // with admin option
}
const dsql_nod* const* role_end = role_list->nod_arg + role_list->nod_count;
for (const dsql_nod* const* role_ptr = role_list->nod_arg; role_ptr < role_end; ++role_ptr)
{
uend = users->nod_arg + users->nod_count;
for (uptr = users->nod_arg; uptr < uend; ++uptr)
{
process_role_nm_list(dsqlScratch, option, *uptr, *role_ptr,
ddl_node->nod_type, ddl_node->nod_arg[e_grant_grantor]);
}
}
}
statement->append_uchar(isc_dyn_end);
}
static void make_index( DsqlCompilerScratch* dsqlScratch,
const dsql_nod* element,
const dsql_nod* columns,
const char* index_name)
{
/**************************************
*
* m a k e _ i n d e x
*
**************************************
*
* Function
* Generate ddl to create an index for a unique
* or primary key constraint.
* This is not called for a foreign key constraint.
* The func. make_index_trf_ref_int handles foreign key constraint
*
**************************************/
// stuff either user-defined name or
// zero-length name, indicating that an index name
// should be generated
fb_assert(element->nod_type != nod_foreign);
const dsql_nod* index = element->nod_arg[e_pri_index];
fb_assert(index);
const dsql_str* string = (dsql_str*) index->nod_arg[e_idx_name];
if (string)
index_name = string->str_data;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (element->nod_type == nod_primary)
statement->append_cstring(isc_dyn_def_primary_key, index_name);
else if (element->nod_type == nod_unique)
statement->append_cstring(isc_dyn_def_unique, index_name);
statement->append_number(isc_dyn_idx_unique, 1);
if (index->nod_arg[e_idx_asc_dsc])
statement->append_number(isc_dyn_idx_type, 1);
const dsql_nod* const* ptr = columns->nod_arg;
for (const dsql_nod* const* const end = ptr + columns->nod_count; ptr < end; ++ptr)
{
const dsql_str* field_name = (dsql_str*) (*ptr)->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_fld_name, field_name->str_data);
}
statement->append_uchar(isc_dyn_end);
}
static void make_index_trg_ref_int( DsqlCompilerScratch* dsqlScratch,
dsql_nod* element,
dsql_nod* columns,
dsql_nod* referenced_columns,
const char* relation_name,
const char* index_name)
{
/******************************************************
*
* m a k e _ i n d e x _ t r g _ r e f _ i n t
*
******************************************************
*
* Function
* This is called only when the element->nod_type == nod_foreign_key
*
* o Generate ddl to create an index for a unique
* or primary key constraint.
* o Also make an index for the foreign key constraint
* o in the caase of foreign key, also generate an appropriate trigger for
* cascading referential integrity.
*
*
*****************************************************/
fb_assert(element->nod_type == nod_foreign)
// for_rel_name_str is the name of the relation
// on which the ddl operation is being done,
// in this case the foreign key table
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* ddl_node = statement->getDdlNode();
dsql_nod* for_rel_node = ddl_node->nod_arg[e_drl_name];
const dsql_str* for_rel_name_str = (dsql_str*) for_rel_node->nod_arg[e_rln_name];
// stuff either user-defined name or
// zero-length name, indicating that an index name
// should be generated
dsql_nod* index = element->nod_arg[e_for_index];
fb_assert(index);
const dsql_str* string = (dsql_str*) index->nod_arg[e_idx_name];
if (string)
{
index_name = string->str_data;
}
statement->append_cstring(isc_dyn_def_foreign_key, index_name);
if (index->nod_arg[e_idx_asc_dsc])
{
statement->append_number(isc_dyn_idx_type, 1);
}
if (element->nod_arg[e_for_action])
{
dsql_nod* nod_for_action = element->nod_arg[e_for_action];
fb_assert(nod_for_action->nod_type == nod_ref_upd_del);
dsql_nod* nod_ref_upd_action = nod_for_action->nod_arg[e_ref_upd];
if (nod_ref_upd_action)
{
fb_assert(nod_ref_upd_action->nod_type == nod_ref_trig_action);
statement->append_uchar(isc_dyn_foreign_key_update);
switch (nod_ref_upd_action->nod_flags)
{
case REF_ACTION_CASCADE:
statement->append_uchar(isc_dyn_foreign_key_cascade);
define_upd_cascade_trg(dsqlScratch, element, columns, referenced_columns,
relation_name, for_rel_name_str->str_data);
break;
case REF_ACTION_SET_DEFAULT:
statement->append_uchar(isc_dyn_foreign_key_default);
define_set_default_trg(dsqlScratch, element, columns, referenced_columns,
relation_name, for_rel_name_str->str_data,
true);
break;
case REF_ACTION_SET_NULL:
statement->append_uchar(isc_dyn_foreign_key_null);
define_set_null_trg(dsqlScratch, element, columns, referenced_columns,
relation_name, for_rel_name_str->str_data,
true);
break;
case REF_ACTION_NONE:
statement->append_uchar(isc_dyn_foreign_key_none);
break;
default:
fb_assert(0);
statement->append_uchar(isc_dyn_foreign_key_none); // just in case
break;
}
}
dsql_nod* nod_ref_del_action = nod_for_action->nod_arg[e_ref_del];
if (nod_ref_del_action)
{
fb_assert(nod_ref_del_action->nod_type == nod_ref_trig_action);
statement->append_uchar(isc_dyn_foreign_key_delete);
switch (nod_ref_del_action->nod_flags)
{
case REF_ACTION_CASCADE:
statement->append_uchar(isc_dyn_foreign_key_cascade);
define_del_cascade_trg(dsqlScratch, element, columns, referenced_columns,
relation_name, for_rel_name_str->str_data);
break;
case REF_ACTION_SET_DEFAULT:
statement->append_uchar(isc_dyn_foreign_key_default);
define_set_default_trg(dsqlScratch, element, columns, referenced_columns,
relation_name, for_rel_name_str->str_data,
false);
break;
case REF_ACTION_SET_NULL:
statement->append_uchar(isc_dyn_foreign_key_null);
define_set_null_trg(dsqlScratch, element, columns, referenced_columns,
relation_name, for_rel_name_str->str_data,
false);
break;
case REF_ACTION_NONE:
statement->append_uchar(isc_dyn_foreign_key_none);
break;
default:
fb_assert(0);
statement->append_uchar(isc_dyn_foreign_key_none); // just in case
break;
// Error
}
}
}
const dsql_nod* const* ptr = columns->nod_arg;
for (const dsql_nod* const* const end = ptr + columns->nod_count; ptr < end; ++ptr)
{
const dsql_str* field_name = (dsql_str*) (*ptr)->nod_arg[1];
statement->append_cstring(isc_dyn_fld_name, field_name->str_data);
}
statement->append_cstring(isc_dyn_idx_foreign_key, relation_name);
if (referenced_columns)
{
ptr = referenced_columns->nod_arg;
for (const dsql_nod* const* const end = ptr + referenced_columns->nod_count; ptr < end; ++ptr)
{
const dsql_str* field_name = (dsql_str*) (*ptr)->nod_arg[1];
statement->append_cstring(isc_dyn_idx_ref_column, field_name->str_data);
}
}
statement->append_uchar(isc_dyn_end);
}
static void modify_database( DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* m o d i f y _ d a t a b a s e
*
**************************************
*
* Function
* Modify a database.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* ddl_node = statement->getDdlNode();
statement->append_uchar(isc_dyn_mod_database);
// statement->append_number(isc_dyn_rel_sql_protection, 1);
bool drop_difference = false;
const dsql_nod* elements = ddl_node->nod_arg[e_adb_all];
const dsql_nod* const* end = elements->nod_arg + elements->nod_count;
const dsql_nod* const* ptr;
for (ptr = elements->nod_arg; ptr < end; ptr++)
{
const dsql_nod* element = *ptr;
if (element->nod_type == nod_drop_difference)
drop_difference = true;
}
if (drop_difference) {
statement->append_uchar(isc_dyn_drop_difference);
}
SLONG start = 0;
elements = ddl_node->nod_arg[e_adb_all];
end = elements->nod_arg + elements->nod_count;
for (ptr = elements->nod_arg; ptr < end; ptr++)
{
const dsql_fil* file;
const dsql_nod* element = *ptr;
switch (element->nod_type)
{
case nod_file_desc:
file = (dsql_fil*) element->nod_arg[0];
statement->append_cstring(isc_dyn_def_file, file->fil_name->str_data);
start = MAX(start, file->fil_start);
statement->append_file_start(start);
statement->append_file_length(file->fil_length);
statement->append_uchar(isc_dyn_end);
start += file->fil_length;
break;
case nod_difference_file:
statement->append_cstring(isc_dyn_def_difference,
((dsql_str*) element->nod_arg[0])->str_data);
break;
case nod_begin_backup:
statement->append_uchar(isc_dyn_begin_backup);
break;
case nod_end_backup:
statement->append_uchar(isc_dyn_end_backup);
break;
case nod_dfl_charset:
statement->append_cstring(isc_dyn_fld_character_set_name,
((dsql_str*) element->nod_arg[0])->str_data);
break;
default:
break;
}
}
statement->append_uchar(isc_dyn_end);
}
static void modify_domain( DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* m o d i f y _ d o m a i n
*
**************************************
*
* Function
* Alter an SQL domain.
*
**************************************/
dsql_str* string;
dsql_fld* field;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_fld local_field(statement->getPool());
// CVC: This array used with check_one_call to ensure each modification
// option is called only once. Enlarge it if the switch() below gets more
// cases.
USHORT repetition_count[6];
dsql_nod* ddl_node = statement->getDdlNode();
dsql_nod* domain_node = ddl_node->nod_arg[e_alt_dom_name];
const dsql_str* domain_name = (dsql_str*) domain_node->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_mod_global_fld, domain_name->str_data);
// Is MOVE_CLEAR enough for all platforms?
// MOVE_CLEAR (repetition_count, sizeof (repetition_count));
const USHORT rtop = FB_NELEM(repetition_count);
USHORT* p = repetition_count;
while (p < repetition_count + rtop) {
*p++ = 0;
}
dsql_nod* ops = ddl_node->nod_arg[e_alt_dom_ops];
dsql_nod** ptr = ops->nod_arg;
for (const dsql_nod* const* const end = ptr + ops->nod_count; ptr < end; ptr++)
{
dsql_nod* element = *ptr;
switch (element->nod_type)
{
case nod_def_default:
check_one_call(repetition_count, 0, "DOMAIN DEFAULT");
define_default(dsqlScratch, element);
break;
case nod_def_constraint:
check_one_call(repetition_count, 1, "DOMAIN CONSTRAINT");
statement->append_uchar(isc_dyn_single_validation);
statement->begin_blr(isc_dyn_fld_validation_blr);
// Get the attributes of the domain, and set any occurances of
// nod_dom_value (corresponding to the keyword VALUE) to the
// correct type, length, scale, etc.
if (!METD_get_domain(dsqlScratch->getTransaction(), &local_field, domain_name->str_data))
{
// Specified domain or source field does not exist
post_607(Arg::Gds(isc_dsql_domain_not_found) << Arg::Str(domain_name->str_data));
}
if (element->nod_arg[e_cnstr_condition])
set_nod_value_attributes(element->nod_arg[e_cnstr_condition], &local_field);
// Increment the context level for this statement, so that
// the context number for any RSE generated for a SELECT
// within the CHECK clause will be greater than 0. In the
// environment of a domain check constraint, context
// number 0 is reserved for the "blr_fid, 0, 0, 0," which
// is emitted for a nod_dom_value, corresponding to an
// occurance of the VALUE keyword in the body of the check
// constraint. -- chrisj 1999-08-20
dsqlScratch->contextNumber++;
{
dsql_nod* node = PASS1_node(dsqlScratch, element->nod_arg[e_cnstr_condition]);
GEN_hidden_variables(dsqlScratch, true);
GEN_expr(dsqlScratch, node);
}
statement->end_blr();
if ((string = (dsql_str*) element->nod_arg[e_cnstr_source]) != NULL)
{
fb_assert(string->str_length <= MAX_USHORT);
statement->append_string(isc_dyn_fld_validation_source,
string->str_data, string->str_length);
}
break;
case nod_mod_domain_type:
field = (dsql_fld*) element->nod_arg[e_mod_dom_new_dom_type];
DDL_resolve_intl_type(dsqlScratch, field, NULL);
put_field(dsqlScratch, field, false);
break;
case nod_field_name:
{
check_one_call(repetition_count, 3, "DOMAIN NAME");
const dsql_str* new_dom_name = (dsql_str*) element->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_fld_name, new_dom_name->str_data);
break;
}
case nod_delete_rel_constraint:
check_one_call(repetition_count, 4, "DOMAIN DROP CONSTRAINT");
statement->append_uchar(isc_dyn_del_validation);
break;
case nod_del_default:
check_one_call(repetition_count, 5, "DOMAIN DROP DEFAULT");
statement->append_uchar(isc_dyn_del_default);
break;
default:
break;
}
}
statement->append_uchar(isc_dyn_end);
}
static void modify_index( DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* m o d i f y _ i n d e x
*
**************************************
*
* Function
* Alter an index (only active or inactive for now)
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* ddl_node = statement->getDdlNode();
dsql_nod* index_node = ddl_node->nod_arg[e_alt_index];
const dsql_str* index_name = (dsql_str*) index_node->nod_arg[e_alt_idx_name];
statement->append_cstring(isc_dyn_mod_idx, index_name->str_data);
if (index_node->nod_type == nod_idx_active) {
statement->append_number(isc_dyn_idx_inactive, 0);
}
else if (index_node->nod_type == nod_idx_inactive) {
statement->append_number(isc_dyn_idx_inactive, 1);
}
statement->append_uchar(isc_dyn_end);
}
static void put_user_grant(DsqlCompilerScratch* dsqlScratch, const dsql_nod* user)
{
/**************************************
*
* p u t _ u s e r _ g r a n t
*
**************************************
*
* Functional description
* Stuff a user/role/obj option in grant/revoke
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_str* name = (dsql_str*) user->nod_arg[0];
switch (user->nod_type)
{
case nod_user_group: // GRANT priv ON tbl TO GROUP unix_group
statement->append_cstring(isc_dyn_grant_user_group, name->str_data);
break;
case nod_user_name:
if (user->nod_count == 2) {
statement->append_cstring(isc_dyn_grant_user_explicit, name->str_data);
}
else {
statement->append_cstring(isc_dyn_grant_user, name->str_data);
}
break;
case nod_package_obj:
statement->append_cstring(isc_dyn_grant_package, name->str_data);
break;
case nod_proc_obj:
statement->append_cstring(isc_dyn_grant_proc, name->str_data);
break;
case nod_func_obj:
statement->append_cstring(isc_dyn_grant_func, name->str_data);
break;
case nod_trig_obj:
statement->append_cstring(isc_dyn_grant_trig, name->str_data);
break;
case nod_view_obj:
statement->append_cstring(isc_dyn_grant_view, name->str_data);
break;
case nod_role_name:
statement->append_cstring(isc_dyn_grant_role, name->str_data);
break;
default:
// CVC: Here we should complain: DYN doesn't check parameters
// and it will write trash in rdb$user_privileges. We probably
// should complain in most cases when "name" is blank, too.
break;
}
}
static void modify_privilege(DsqlCompilerScratch* dsqlScratch,
NOD_TYPE type,
SSHORT option,
const UCHAR* privs,
const dsql_nod* table,
const dsql_nod* user,
const dsql_nod* grantor,
const dsql_str* field_name)
{
/**************************************
*
* m o d i f y _ p r i v i l e g e
*
**************************************
*
* Functional description
* Stuff a single grant/revoke verb and all its options.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (type == nod_grant) {
statement->append_uchar(isc_dyn_grant);
}
else {
statement->append_uchar(isc_dyn_revoke);
}
// stuff the privileges string
SSHORT priv_count = 0;
statement->append_ushort(0);
for (; *privs; privs++)
{
priv_count++;
statement->append_uchar(*privs);
}
UCHAR* dynsave = statement->getBlrData().end();
for (SSHORT i = priv_count + 2; i; i--) {
--dynsave;
}
*dynsave++ = (UCHAR) priv_count;
*dynsave = (UCHAR) (priv_count >> 8);
UCHAR dynVerb = 0;
switch (table->nod_type)
{
case nod_procedure_name:
dynVerb = isc_dyn_prc_name;
break;
case nod_function_name:
dynVerb = isc_dyn_fun_name;
break;
case nod_package_name:
dynVerb = isc_dyn_pkg_name;
break;
default:
dynVerb = isc_dyn_rel_name;
}
const dsql_str* name = (dsql_str*) table->nod_arg[0];
statement->append_cstring(dynVerb, name->str_data);
put_user_grant(dsqlScratch, user);
if (field_name) {
statement->append_cstring(isc_dyn_fld_name, field_name->str_data);
}
if (option)
{
statement->append_number(isc_dyn_grant_options, option);
}
put_grantor(dsqlScratch, grantor);
statement->append_uchar(isc_dyn_end);
}
static char modify_privileges(DsqlCompilerScratch* dsqlScratch,
NOD_TYPE type,
SSHORT option,
const dsql_nod* privs,
const dsql_nod* table,
const dsql_nod* user,
const dsql_nod* grantor)
{
/**************************************
*
* m o d i f y _ p r i v i l e g e s
*
**************************************
*
* Functional description
* Return a char indicating the privilege to be modified
*
**************************************/
char privileges[10];
const char* p = 0;
char* q;
const dsql_nod* fields;
const dsql_nod* const* ptr;
const dsql_nod* const* end;
switch (privs->nod_type)
{
case nod_all:
p = "A";
break;
case nod_select:
return 'S';
case nod_execute:
return 'X';
case nod_insert:
return 'I';
case nod_references:
case nod_update:
p = (privs->nod_type == nod_references) ? "R" : "U";
fields = privs->nod_arg[0];
if (!fields) {
return *p;
}
for (ptr = fields->nod_arg, end = ptr + fields->nod_count; ptr < end; ptr++)
{
modify_privilege(dsqlScratch, type, option,
reinterpret_cast<const UCHAR*>(p), table, user, grantor,
reinterpret_cast<dsql_str*>((*ptr)->nod_arg[1]));
}
return 0;
case nod_delete:
return 'D';
case nod_list:
p = q = privileges;
for (ptr = privs->nod_arg, end = ptr + privs->nod_count; ptr < end; ptr++)
{
*q = modify_privileges(dsqlScratch, type, option, *ptr, table, user, grantor);
if (*q) {
q++;
}
}
*q = 0;
break;
default:
break;
}
if (*p)
{
modify_privilege(dsqlScratch, type, option,
reinterpret_cast<const UCHAR*>(p), table, user, grantor, 0);
}
return 0;
}
static void modify_relation(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* m o d i f y _ r e l a t i o n
*
**************************************
*
* Function
* Alter an SQL table, relying on DYN to generate
* global fields for the local fields.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data(); // not used
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_nod* ddl_node = statement->getDdlNode();
dsql_nod* relation_node = ddl_node->nod_arg[e_alt_name];
const dsql_str* relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name];
statement->append_cstring(isc_dyn_mod_rel, relation_name->str_data);
save_relation(dsqlScratch, relation_name);
if (!dsqlScratch->relation)
{
TEXT linecol[64];
sprintf (linecol, "At line %d, column %d.",
(int) relation_node->nod_line, (int) relation_node->nod_column);
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_relation_err) <<
Arg::Gds(isc_random) << Arg::Str(relation_name->str_data) <<
Arg::Gds(isc_random) << Arg::Str(linecol));
}
// need to handle error that occur in generating dyn string.
// If there is an error, get rid of the cached data
try {
dsql_nod* ops = ddl_node->nod_arg[e_alt_ops];
dsql_nod** ptr = ops->nod_arg;
for (const dsql_nod* const* const end = ptr + ops->nod_count; ptr < end; ptr++)
{
const dsql_nod* field_node;
const dsql_str* field_name;
dsql_nod* element = *ptr;
switch (element->nod_type)
{
case nod_mod_field_name:
{
const dsql_nod* old_field = element->nod_arg[e_mod_fld_name_orig_name];
const dsql_str* old_field_name = (dsql_str*) old_field->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_mod_local_fld, old_field_name->str_data);
dsql_nod* new_field = element->nod_arg[e_mod_fld_name_new_name];
const dsql_str* new_field_name = (dsql_str*) new_field->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_rel_name, relation_name->str_data);
statement->append_cstring(isc_dyn_new_fld_name, new_field_name->str_data);
statement->append_uchar(isc_dyn_end);
break;
}
case nod_mod_field_null_flag:
field_node = element->nod_arg[e_mod_fld_null_flag_field];
field_name = (dsql_str*) field_node->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_mod_local_fld, field_name->str_data);
statement->append_cstring(isc_dyn_rel_name, relation_name->str_data);
if (element->nod_arg[e_mod_fld_null_flag_value]->getSlong())
statement->append_uchar(isc_dyn_fld_not_null);
else
statement->append_uchar(isc_dyn_fld_null);
statement->append_uchar(isc_dyn_end);
break;
case nod_mod_field_pos:
{
field_node = element->nod_arg[e_mod_fld_pos_orig_name];
field_name = (dsql_str*) field_node->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_mod_local_fld, field_name->str_data);
const dsql_nod* const_node = element->nod_arg[e_mod_fld_pos_new_position];
// CVC: Since now the parser accepts pos=1..N, let's subtract one here.
const SSHORT constant = (SSHORT) const_node->getSlong() - 1;
statement->append_cstring(isc_dyn_rel_name, relation_name->str_data);
statement->append_number(isc_dyn_fld_position, constant);
statement->append_uchar(isc_dyn_end);
break;
}
case nod_mod_field_type:
modify_field(dsqlScratch, element, relation_name);
break;
case nod_def_field:
define_field(dsqlScratch, element, (SSHORT) -1, relation_name, 0);
break;
case nod_del_field:
// Fix for bug 8054:
//
// [CASCADE | RESTRICT] syntax is available in IB4.5, but not
// required until v5.0.
//
// Option CASCADE causes an error :
// unsupported DSQL construct
//
// Option RESTRICT is default behaviour.
field_node = element->nod_arg[0];
field_name = (dsql_str*) field_node->nod_arg[e_fln_name];
if ((element->nod_arg[1])->nod_type == nod_cascade)
{
// Unsupported DSQL construct
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_construct_err));
}
fb_assert((element->nod_arg[1])->nod_type == nod_restrict);
statement->append_cstring(isc_dyn_delete_local_fld, field_name->str_data);
statement->append_uchar(isc_dyn_end);
break;
case nod_delete_rel_constraint:
field_name = (dsql_str*) element->nod_arg[0];
statement->append_cstring(isc_dyn_delete_rel_constraint, field_name->str_data);
break;
case nod_rel_constraint:
define_rel_constraint(dsqlScratch, element);
break;
default:
break;
}
}
statement->append_uchar(isc_dyn_end);
} // try
catch (const Exception&)
{
METD_drop_relation(dsqlScratch->getTransaction(), relation_name);
dsqlScratch->relation = 0;
throw;
}
}
// *******************
// m o d i f y _ u d f
// *******************
// Allow the user to change the entry point or module name.
// Useful when there are dependencies on the udf, so it cannot be dropped.
static void modify_udf(DsqlCompilerScratch* dsqlScratch)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* node = statement->getDdlNode();
fb_assert(node->nod_type == nod_mod_udf);
const dsql_str* obj_name = (dsql_str*) node->nod_arg[e_mod_udf_name];
if (!node->nod_arg[e_mod_udf_entry_pt] && !node->nod_arg[e_mod_udf_module])
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Unexpected end of command
Arg::Gds(isc_command_end_err2) << Arg::Num(node->nod_line) <<
Arg::Num(node->nod_column + obj_name->str_length));
// + strlen("FUNCTION")
}
statement->append_cstring(isc_dyn_mod_function, obj_name->str_data);
const dsql_str* entry_point_name = (dsql_str*) node->nod_arg[e_mod_udf_entry_pt];
if (entry_point_name)
statement->append_cstring(isc_dyn_func_entry_point, entry_point_name->str_data);
const dsql_str* module_name = (dsql_str*) node->nod_arg[e_mod_udf_module];
if (module_name)
statement->append_cstring(isc_dyn_func_module_name, module_name->str_data);
statement->append_uchar(isc_dyn_end);
}
// *******************
// m o d i f y _ m a p
// *******************
// Allow the user to establish/drop the mapping between OS security object and the role
static void modify_map(DsqlCompilerScratch* dsqlScratch)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* node = statement->getDdlNode();
fb_assert(node->nod_type == nod_mod_role);
const dsql_str* ds = (dsql_str*) node->nod_arg[e_mod_role_os_name];
fb_assert(ds ||
node->nod_arg[e_mod_role_action]->getSlong() == isc_dyn_automap_role ||
node->nod_arg[e_mod_role_action]->getSlong() == isc_dyn_autounmap_role);
statement->append_cstring(isc_dyn_mapping, ds ? ds->str_data : "");
ds = (dsql_str*) node->nod_arg[e_mod_role_db_name];
fb_assert(ds);
statement->append_cstring(node->nod_arg[e_mod_role_action]->getSlong(), ds->str_data);
statement->append_uchar(isc_dyn_end);
}
// *********************
// d e f i n e _ u s e r
// *********************
// Support SQL operator create/alter/drop user
static void define_user(DsqlCompilerScratch* dsqlScratch, UCHAR op)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->append_uchar(isc_dyn_user);
const dsql_nod* node = statement->getDdlNode();
int argCount = 0;
for (int i = 0; i < node->nod_count; ++i)
{
const dsql_str* ds = (dsql_str*) node->nod_arg[i];
if (! ds)
{
if (i == e_user_name || (i == e_user_passwd && op == isc_dyn_user_add))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Unexpected end of command
Arg::Gds(isc_command_end_err2) << Arg::Num(node->nod_line) <<
Arg::Num(node->nod_column));
}
continue;
}
++argCount;
switch (i)
{
case e_user_name:
statement->append_cstring(op, ds->str_data);
break;
case e_user_passwd:
statement->append_cstring(isc_dyn_user_passwd, ds->str_data);
break;
case e_user_first:
statement->append_cstring(isc_dyn_user_first, ds->str_data);
break;
case e_user_middle:
statement->append_cstring(isc_dyn_user_middle, ds->str_data);
break;
case e_user_last:
statement->append_cstring(isc_dyn_user_last, ds->str_data);
break;
case e_user_admin:
statement->append_cstring(isc_dyn_user_admin, ds->str_data);
break;
}
}
if (argCount < 2 && op != isc_dyn_user_del)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Unexpected end of command
Arg::Gds(isc_command_end_err2) << Arg::Num(node->nod_line) <<
Arg::Num(node->nod_column));
}
statement->append_uchar(isc_user_end);
statement->append_uchar(isc_dyn_end);
}
static void process_role_nm_list(DsqlCompilerScratch* dsqlScratch,
SSHORT option,
const dsql_nod* user_ptr,
const dsql_nod* role_ptr,
NOD_TYPE type,
const dsql_nod* grantor)
{
/**************************************
*
* p r o c e s s _ r o l e _ n m _ l i s t
*
**************************************
*
* Functional description
* Build req_blr for grant & revoke role stmt
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (type == nod_grant) {
statement->append_uchar(isc_dyn_grant);
}
else {
statement->append_uchar(isc_dyn_revoke);
}
statement->append_ushort(1);
statement->append_uchar('M');
const dsql_str* role_nm = (dsql_str*) role_ptr->nod_arg[0];
statement->append_cstring(isc_dyn_sql_role_name, role_nm->str_data);
const dsql_str* user_nm = (dsql_str*) user_ptr->nod_arg[0];
statement->append_cstring(isc_dyn_grant_user, user_nm->str_data);
if (option) {
statement->append_number(isc_dyn_grant_admin_options, option);
}
put_grantor(dsqlScratch, grantor);
statement->append_uchar(isc_dyn_end);
}
static void put_grantor(DsqlCompilerScratch* dsqlScratch, const dsql_nod* grantor)
{
/**************************************
*
* p u t _ g r a n t o r
*
**************************************
*
* Function
* Write out grantor for grant / revoke.
*
**************************************/
if (grantor)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
fb_assert(grantor->nod_type == nod_user_name);
const dsql_str* name = (const dsql_str*) grantor->nod_arg[0];
statement->append_cstring(isc_dyn_grant_grantor, name->str_data);
}
}
static void put_descriptor(DsqlCompilerScratch* dsqlScratch, const dsc* desc)
{
/**************************************
*
* p u t _ d e s c r i p t o r
*
**************************************
*
* Function
* Write out field description in ddl, given the
* input descriptor.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->append_number(isc_dyn_fld_type, blr_dtypes[desc->dsc_dtype]);
if (desc->dsc_dtype == dtype_varying) {
statement->append_number(isc_dyn_fld_length, (SSHORT) (desc->dsc_length - sizeof(USHORT)));
}
else {
statement->append_number(isc_dyn_fld_length, desc->dsc_length);
}
if (desc->dsc_dtype <= dtype_any_text)
{
statement->append_number(isc_dyn_fld_character_set, DSC_GET_CHARSET(desc));
statement->append_number(isc_dyn_fld_collation, DSC_GET_COLLATE(desc));
}
else if (desc->dsc_dtype == dtype_blob)
{
statement->append_number(isc_dyn_fld_sub_type, desc->dsc_sub_type);
if (desc->dsc_sub_type == isc_blob_text)
{
statement->append_number(isc_dyn_fld_character_set, desc->dsc_scale);
statement->append_number(isc_dyn_fld_collation, desc->dsc_flags >> 8); // BLOB collation
}
}
else
{
statement->append_number(isc_dyn_fld_sub_type, desc->dsc_sub_type);
statement->append_number(isc_dyn_fld_scale, desc->dsc_scale);
}
}
//
// Write out field data type
// Taking special care to declare international text.
//
static void put_dtype(DsqlCompilerScratch* dsqlScratch, const dsql_fld* field, bool use_subtype)
{
#ifdef DEV_BUILD
// Check if the field describes a known datatype
if (field->fld_dtype > FB_NELEM(blr_dtypes) || !blr_dtypes[field->fld_dtype])
{
SCHAR buffer[100];
sprintf(buffer, "Invalid dtype %d in put_dtype", field->fld_dtype);
ERRD_bugcheck(buffer);
}
#endif
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (field->fld_not_nullable)
statement->append_uchar(blr_not_nullable);
if (field->fld_type_of_name.hasData())
{
if (field->fld_type_of_table)
{
if (field->fld_explicit_collation)
{
statement->append_uchar(blr_column_name2);
statement->append_uchar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
statement->append_meta_string(field->fld_type_of_table->str_data);
statement->append_meta_string(field->fld_type_of_name.c_str());
statement->append_ushort(field->fld_ttype);
}
else
{
statement->append_uchar(blr_column_name);
statement->append_uchar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
statement->append_meta_string(field->fld_type_of_table->str_data);
statement->append_meta_string(field->fld_type_of_name.c_str());
}
}
else
{
if (field->fld_explicit_collation)
{
statement->append_uchar(blr_domain_name2);
statement->append_uchar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
statement->append_meta_string(field->fld_type_of_name.c_str());
statement->append_ushort(field->fld_ttype);
}
else
{
statement->append_uchar(blr_domain_name);
statement->append_uchar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
statement->append_meta_string(field->fld_type_of_name.c_str());
}
}
return;
}
switch (field->fld_dtype)
{
case dtype_cstring:
case dtype_text:
case dtype_varying:
case dtype_blob:
if (!use_subtype) {
statement->append_uchar(blr_dtypes[field->fld_dtype]);
}
else if (field->fld_dtype == dtype_varying)
{
statement->append_uchar(blr_varying2);
statement->append_ushort(field->fld_ttype);
}
else if (field->fld_dtype == dtype_cstring)
{
statement->append_uchar(blr_cstring2);
statement->append_ushort(field->fld_ttype);
}
else if (field->fld_dtype == dtype_blob)
{
statement->append_uchar(blr_blob2);
statement->append_ushort(field->fld_sub_type);
statement->append_ushort(field->fld_ttype);
}
else
{
statement->append_uchar(blr_text2);
statement->append_ushort(field->fld_ttype);
}
if (field->fld_dtype == dtype_varying) {
statement->append_ushort(field->fld_length - sizeof(USHORT));
}
else if (field->fld_dtype != dtype_blob) {
statement->append_ushort(field->fld_length);
}
break;
default:
statement->append_uchar(blr_dtypes[field->fld_dtype]);
if (DTYPE_IS_EXACT(field->fld_dtype) || (dtype_quad == field->fld_dtype))
{
statement->append_uchar(field->fld_scale);
}
}
}
static void put_field( DsqlCompilerScratch* dsqlScratch, dsql_fld* field, bool udf_flag)
{
/**************************************
*
* p u t _ f i e l d
*
**************************************
*
* Function
* Emit dyn which describes a field data type.
* This field could be a column, a procedure input,
* or a procedure output.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (field->fld_not_nullable)
statement->append_uchar(isc_dyn_fld_not_null);
if (field->fld_type_of_name.hasData())
{
if (field->fld_source.hasData())
{
statement->append_string(isc_dyn_fld_source, field->fld_source);
statement->append_string(isc_dyn_fld_name, field->fld_type_of_name);
statement->append_cstring(isc_dyn_rel_name, field->fld_type_of_table->str_data);
}
else
statement->append_string(isc_dyn_fld_source, field->fld_type_of_name);
if (field->fld_explicit_collation)
statement->append_number(isc_dyn_fld_collation, field->fld_collation_id);
return;
}
statement->append_number(isc_dyn_fld_type, blr_dtypes[field->fld_dtype]);
if (field->fld_dtype == dtype_blob)
{
statement->append_number(isc_dyn_fld_sub_type, field->fld_sub_type);
statement->append_number(isc_dyn_fld_scale, 0);
if (!udf_flag)
{
if (!field->fld_seg_length) {
field->fld_seg_length = DEFAULT_BLOB_SEGMENT_SIZE;
}
statement->append_number(isc_dyn_fld_segment_length, field->fld_seg_length);
}
else
{
statement->append_number(isc_dyn_fld_length, sizeof(ISC_QUAD));
}
if (field->fld_sub_type == isc_blob_text)
{
statement->append_number(isc_dyn_fld_character_set, field->fld_character_set_id);
statement->append_number(isc_dyn_fld_collation, field->fld_collation_id);
}
}
else if (field->fld_dtype <= dtype_any_text)
{
statement->append_number(isc_dyn_fld_sub_type, field->fld_sub_type);
statement->append_number(isc_dyn_fld_scale, 0);
if (field->fld_dtype == dtype_varying)
{
// CVC: Fix the assertion
fb_assert((field->fld_length) <= MAX_SSHORT);
statement->append_number(isc_dyn_fld_length,
(SSHORT) (field->fld_length - sizeof(USHORT)));
}
else
{
statement->append_number(isc_dyn_fld_length, field->fld_length);
}
statement->append_number(isc_dyn_fld_char_length, field->fld_character_length);
statement->append_number(isc_dyn_fld_character_set, field->fld_character_set_id);
if (!udf_flag)
statement->append_number(isc_dyn_fld_collation, field->fld_collation_id);
}
else
{
statement->append_number(isc_dyn_fld_scale, field->fld_scale);
statement->append_number(isc_dyn_fld_length, field->fld_length);
if (DTYPE_IS_EXACT(field->fld_dtype))
{
statement->append_number(isc_dyn_fld_precision, field->fld_precision);
statement->append_number(isc_dyn_fld_sub_type, field->fld_sub_type);
}
}
}
void DDL_put_local_variable( DsqlCompilerScratch* dsqlScratch, dsql_var* variable,
dsql_nod* host_param, const dsql_str* collation_name)
{
/**************************************
*
* D D L _ p u t _ l o c a l _ v a r i a b l e
*
**************************************
*
* Function
* Write out local variable field data type
*
**************************************/
dsql_fld* field = variable->var_field;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->append_uchar(blr_dcl_variable);
statement->append_ushort(variable->var_variable_number);
DDL_resolve_intl_type(dsqlScratch, field, collation_name);
//const USHORT dtype = field->fld_dtype;
put_dtype(dsqlScratch, field, true);
//field->fld_dtype = dtype;
// Check for a default value, borrowed from define_domain
dsql_nod* node = host_param ? host_param->nod_arg[e_dfl_default] : 0;
if (node || (!field->fld_full_domain && !field->fld_not_nullable))
{
statement->append_uchar(blr_assignment);
if (node)
{
fb_assert(node->nod_type == nod_def_default);
PsqlChanger psqlChanger(dsqlScratch, false);
node = PASS1_node(dsqlScratch, node->nod_arg[e_dft_default]);
GEN_expr(dsqlScratch, node);
}
else
{
// Initialize variable to NULL
statement->append_uchar(blr_null);
}
statement->append_uchar(blr_variable);
statement->append_ushort(variable->var_variable_number);
}
else
{
statement->append_uchar(blr_init_variable);
statement->append_ushort(variable->var_variable_number);
}
if (variable->var_name[0]) // Not a function return value
{
statement->put_debug_variable(variable->var_variable_number, variable->var_name);
}
++dsqlScratch->hiddenVarsNumber;
}
void DDL_put_local_variables(DsqlCompilerScratch* dsqlScratch, const dsql_nod* parameters,
SSHORT locals, Array<dsql_nod*>& variables)
{
/**************************************
*
* D D L _ p u t _ l o c a l _ v a r i a b l e s
*
**************************************
*
* Function
* Emit dyn for the local variables declared
* in a procedure or trigger.
*
**************************************/
if (parameters)
{
dsql_nod* const* ptr = parameters->nod_arg;
for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++)
{
dsql_nod* parameter = *ptr;
dsqlScratch->getStatement()->put_debug_src_info(parameter->nod_line, parameter->nod_column);
if (parameter->nod_type == nod_def_field)
{
dsql_fld* field = (dsql_fld*) parameter->nod_arg[e_dfl_field];
const dsql_nod* const* rest = ptr;
while (++rest != end)
{
if ((*rest)->nod_type == nod_def_field)
{
const dsql_fld* rest_field = (dsql_fld*) (*rest)->nod_arg[e_dfl_field];
if (field->fld_name == rest_field->fld_name)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(field->fld_name));
}
}
}
dsql_nod* var_node = MAKE_variable(field, field->fld_name.c_str(), VAR_local, 0, 0, locals);
variables.add(var_node);
dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable];
DDL_put_local_variable(dsqlScratch, variable, parameter,
reinterpret_cast<const dsql_str*>(parameter->nod_arg[e_dfl_collate]));
// Some field attributes are calculated inside
// DDL_put_local_variable(), so we reinitialize the
// descriptor
MAKE_desc_from_field(&var_node->nod_desc, field);
locals++;
}
else if (parameter->nod_type == nod_cursor)
{
PASS1_statement(dsqlScratch, parameter);
GEN_statement(dsqlScratch, parameter);
}
}
}
}
static dsql_nod* replace_field_names(dsql_nod* input,
dsql_nod* search_fields,
dsql_nod* replace_fields,
bool null_them,
const char* context_name)
{
/**************************************
*
* r e p l a c e _ f i e l d _ n a m e s
*
**************************************
*
* Function
* Given an input node tree, find any field name nodes
* and replace them according to the mapping provided.
* Used to create view WITH CHECK OPTION.
*
**************************************/
if (!input || input->getType() != dsql_type_nod) {
return input;
}
const dsql_nod* const* const endo = input->nod_arg + input->nod_count;
for (dsql_nod** ptr = input->nod_arg; ptr < endo; ++ptr)
{
if ((*ptr)->nod_type == nod_select_expr)
{
// No subqueries permitted for VIEW WITH CHECK OPTION
post_607(Arg::Gds(isc_subquery_err));
}
if ((*ptr)->nod_type == nod_field_name)
{
// found a field node, check if it needs to be replaced
const dsql_str* field_name = (dsql_str*) (*ptr)->nod_arg[e_fln_name];
dsql_nod** search = search_fields->nod_arg;
const dsql_nod* const* const end = search + search_fields->nod_count;
dsql_nod** replace = NULL;
if (replace_fields) {
replace = replace_fields->nod_arg;
}
bool found = false;
for (; search < end; search++, replace_fields ? replace++ : NULL)
{
const dsql_str* replace_name = 0;
if (replace_fields) {
replace_name = (dsql_str*) (*replace)->nod_arg[e_fln_name];
}
const dsql_nod* field_node = *search;
const dsql_fld* field = (dsql_fld*) field_node->nod_arg[e_fld_field];
if (field->fld_name == field_name->str_data)
{
found = true;
if (replace_fields) {
(*ptr)->nod_arg[e_fln_name] = (*replace)->nod_arg[e_fln_name];
}
(*ptr)->nod_arg[e_fln_context] = (dsql_nod*) MAKE_cstring(context_name);
}
if (null_them && replace_fields && !strcmp(field_name->str_data, replace_name->str_data))
{
found = true;
}
}
if (null_them && !found) {
(*ptr) = MAKE_node(nod_null, (int) 0);
}
}
else
{
// recursively go through the input tree
// looking for field name nodes
replace_field_names(*ptr, search_fields, replace_fields, null_them, context_name);
}
}
return input;
}
static void reset_context_stack(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* r e s e t _ c o n t e x t _ s t a c k
*
**************************************
*
* Function
* Get rid of any predefined contexts created
* for a view or trigger definition.
* Also reset hidden variables.
*
**************************************/
dsqlScratch->context->clear();
dsqlScratch->contextNumber = 0;
dsqlScratch->derivedContextNumber = 0;
dsqlScratch->hiddenVarsNumber = 0;
dsqlScratch->hiddenVars.clear();
}
static void save_field(DsqlCompilerScratch* dsqlScratch, const TEXT* field_name)
{
/**************************************
*
* s a v e _ f i e l d
*
**************************************
*
* Function
* Save the name of a field in the relation or view currently
* being defined. This is done to support definition
* of triggers which will depend on the metadata created
* in this statement.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
dsql_rel* relation = dsqlScratch->relation;
if (!relation) {
return;
}
MemoryPool& p = relation->rel_flags & REL_new_relation ?
*tdbb->getDefaultPool() : dsqlScratch->getAttachment()->dbb_pool;
dsql_fld* field = FB_NEW(p) dsql_fld(p);
field->fld_name = field_name;
field->fld_next = relation->rel_fields;
relation->rel_fields = field;
}
static void save_relation(DsqlCompilerScratch* dsqlScratch, const dsql_str* relation_name)
{
/**************************************
*
* s a v e _ r e l a t i o n
*
**************************************
*
* Function
* Save the name of the relation or view currently
* being defined. This is done to support definition
* of triggers which will depend on the metadata created
* in this statement.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_METADATA_SAVED)
return;
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_METADATA_SAVED;
const dsql_nod* ddl_node = statement->getDdlNode();
dsql_rel* relation;
if (ddl_node->nod_type == nod_mod_relation)
{
relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name);
}
else
{
MemoryPool& pool = *tdbb->getDefaultPool();
relation = FB_NEW(pool) dsql_rel(pool);
relation->rel_name = relation_name->str_data;
if (ddl_node->nod_type == nod_def_relation || ddl_node->nod_type == nod_redef_relation)
relation->rel_flags = REL_creating;
}
dsqlScratch->relation = relation;
}
static void set_statistics(DsqlCompilerScratch* dsqlScratch)
{
/**************************************
*
* s e t _ s t a t i s t i c s
*
**************************************
*
* Function
* Alter an index/.. statistics
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
const dsql_nod* ddl_node = statement->getDdlNode();
const dsql_str* index_name = (dsql_str*) ddl_node->nod_arg[e_stat_name];
statement->append_cstring(isc_dyn_mod_idx, index_name->str_data);
statement->append_uchar(isc_dyn_idx_statistic);
statement->append_uchar(isc_dyn_end);
}
static void stuff_default_blr(DsqlCompilerScratch* dsqlScratch, const UCHAR* default_buff, USHORT buff_size)
{
/********************************************
*
* s t u f f _ d e f a u l t _ b l r
*
********************************************
* Function:
* The default_blr is passed in default_buffer. It is of the form:
* blr_version4 blr_literal ..... blr_eoc.
* strip the blr_version4 and blr_eoc verbs and stuff the remaining
* blr in the blr stream in the statement.
*
*********************************************/
fb_assert((*default_buff == blr_version4) || (*default_buff == blr_version5));
USHORT i;
for (i = 1; i < buff_size - 1; ++i)
{
dsqlScratch->getStatement()->append_uchar(default_buff[i]);
}
fb_assert(default_buff[i] == blr_eoc);
}
static void stuff_matching_blr(DsqlCompiledStatement* statement, const dsql_nod* for_columns,
const dsql_nod* prim_columns)
{
/********************************************
*
* s t u f f _ m a t c h i n g _ b l r
*
********************************************
*
* Function
* Generate blr to express: foreign_key == primary_key
* ie., for_key.column_1 = prim_key.column_1 and
* for_key.column_2 = prim_key.column_2 and .... so on..
*
**************************************/
// count of foreign key columns
fb_assert(prim_columns->nod_count == for_columns->nod_count);
fb_assert(prim_columns->nod_count != 0);
statement->append_uchar(blr_boolean);
if (prim_columns->nod_count > 1) {
statement->append_uchar(blr_and);
}
USHORT num_fields = 0;
const dsql_nod* const* for_key_flds = for_columns->nod_arg;
const dsql_nod* const* prim_key_flds = prim_columns->nod_arg;
do {
statement->append_uchar(blr_eql);
const dsql_str* for_key_fld_name_str = (dsql_str*) (*for_key_flds)->nod_arg[1];
const dsql_str* prim_key_fld_name_str = (dsql_str*) (*prim_key_flds)->nod_arg[1];
statement->append_uchar(blr_field);
statement->append_uchar(2);
statement->append_cstring(0, for_key_fld_name_str->str_data);
statement->append_uchar(blr_field);
statement->append_uchar(0);
statement->append_cstring(0, prim_key_fld_name_str->str_data);
num_fields++;
if (prim_columns->nod_count - num_fields >= 2) {
statement->append_uchar(blr_and);
}
for_key_flds++;
prim_key_flds++;
} while (num_fields < for_columns->nod_count);
statement->append_uchar(blr_end);
}
static void stuff_trg_firing_cond(DsqlCompiledStatement* statement, const dsql_nod* prim_columns)
{
/********************************************
*
* s t u f f _ t r g _ f i r i n g _ c o n d
*
********************************************
*
* Function
* Generate blr to express: if (old.primary_key != new.primary_key).
* do a column by column comparison.
*
**************************************/
statement->append_uchar(blr_if);
if (prim_columns->nod_count > 1) {
statement->append_uchar(blr_or);
}
USHORT num_fields = 0;
const dsql_nod* const* prim_key_flds = prim_columns->nod_arg;
do {
statement->append_uchar(blr_neq);
const dsql_str* prim_key_fld_name_str = (dsql_str*) (*prim_key_flds)->nod_arg[1];
statement->append_uchar(blr_field);
statement->append_uchar(0);
statement->append_cstring(0, prim_key_fld_name_str->str_data);
statement->append_uchar(blr_field);
statement->append_uchar(1);
statement->append_cstring(0, prim_key_fld_name_str->str_data);
num_fields++;
if (prim_columns->nod_count - num_fields >= 2)
statement->append_uchar(blr_or);
prim_key_flds++;
} while (num_fields < prim_columns->nod_count);
}
static void modify_field(DsqlCompilerScratch* dsqlScratch, dsql_nod*element, const dsql_str* relation_name)
{
/**************************************
*
* m o d i f y _ f i e l d
*
**************************************
*
* Function
* Modify a field, as part of an alter table statement.
* Alter domain is handled in modify_domain.
*
**************************************/
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsql_fld* field = (dsql_fld*) element->nod_arg[e_mod_fld_type_field];
statement->append_string(isc_dyn_mod_sql_fld, field->fld_name);
// add the field to the relation being defined for parsing purposes
bool permanent = false;
dsql_rel* relation = dsqlScratch->relation;
if (relation != NULL)
{
if (! (relation->rel_flags & REL_new_relation))
{
dsql_fld* perm_field = FB_NEW(dsqlScratch->getAttachment()->dbb_pool)
dsql_fld(dsqlScratch->getAttachment()->dbb_pool);
*perm_field = *field;
field = perm_field;
permanent = true;
}
field->fld_next = relation->rel_fields;
relation->rel_fields = field;
}
try
{
dsql_nod* computedNod = element->nod_arg[e_mod_fld_type_computed];
if (computedNod)
{
reset_context_stack(dsqlScratch);
PASS1_make_context(dsqlScratch, statement->getDdlNode()->nod_arg[e_alt_name]);
dsql_str* computedSrc = (dsql_str*) computedNod->nod_arg[e_cmp_text];
fb_assert(computedSrc->str_length <= MAX_USHORT);
computedNod = PASS1_node(dsqlScratch, computedNod->nod_arg[e_cmp_expr]);
if (DDL_is_array_or_blob(dsqlScratch, computedNod))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
Arg::Gds(isc_dsql_no_array_computed));
}
statement->begin_blr(isc_dyn_fld_computed_blr);
GEN_hidden_variables(dsqlScratch, true);
GEN_expr(dsqlScratch, computedNod);
statement->end_blr();
statement->append_string(isc_dyn_fld_computed_source,
computedSrc->str_data, (USHORT) computedSrc->str_length);
reset_context_stack(dsqlScratch);
}
const dsql_nod* defNod = element->nod_arg[e_mod_fld_type_default];
if (defNod)
{
// We have the default or want to get rid of it.
if (defNod->nod_type == nod_def_default)
define_default(dsqlScratch, defNod);
else if (defNod->nod_type == nod_del_default)
statement->append_uchar(isc_dyn_del_default);
else
{
fb_assert(false);
}
}
else
{
// We have the type. Default and type/domain are exclusive for now.
dsql_nod* domain_node = element->nod_arg[e_mod_fld_type_dom_name];
if (domain_node)
{
const dsql_nod* node1 = domain_node->nod_arg[e_dom_name];
const dsql_str* domain_name = (dsql_str*) node1->nod_arg[e_fln_name];
statement->append_cstring(isc_dyn_fld_source, domain_name->str_data);
// Get the domain information
if (!METD_get_domain(dsqlScratch->getTransaction(), field, domain_name->str_data))
{
// Specified domain or source field does not exist
post_607(Arg::Gds(isc_dsql_domain_not_found) << Arg::Str(domain_name->str_data));
}
DDL_resolve_intl_type(dsqlScratch, field, NULL);
}
else
{
if (relation_name) {
statement->append_cstring(isc_dyn_rel_name, relation_name->str_data);
}
// If COMPUTED was specified but the type wasn't, we use the type of
// the computed expression.
if (computedNod && field->fld_dtype == dtype_unknown)
{
dsc desc;
MAKE_desc(dsqlScratch, &desc, computedNod, NULL);
field->fld_dtype = desc.dsc_dtype;
field->fld_length = desc.dsc_length;
field->fld_scale = desc.dsc_scale;
if (field->fld_dtype <= dtype_any_text)
{
field->fld_character_set_id = DSC_GET_CHARSET(&desc);
field->fld_collation_id= DSC_GET_COLLATE(&desc);
}
else
field->fld_sub_type = desc.dsc_sub_type;
}
else
DDL_resolve_intl_type2(dsqlScratch, field, NULL, true);
put_field(dsqlScratch, field, false);
}
}
statement->append_uchar(isc_dyn_end);
} // try
catch (const Exception&)
{
clearPermanentField(relation, permanent);
throw;
}
clearPermanentField(relation, permanent);
}
static void set_nod_value_attributes( dsql_nod* node, const dsql_fld* field)
{
/**************************************
*
* s e t _ n o d _ v a l u e _ a t t r i b u t e s
*
**************************************
*
* Function
* Examine all the children of the argument node:
* if any is a nod_dom_value, set its dtype, size, and scale
* to those of the field argument
*
**************************************/
for (ULONG child_number = 0; child_number < node->nod_count; ++child_number)
{
dsql_nod* child = node->nod_arg[child_number];
if (child && child->getType() == dsql_type_nod)
{
if (nod_dom_value == child->nod_type)
{
fb_assert(field->fld_dtype <= MAX_UCHAR);
child->nod_desc.dsc_dtype = (UCHAR) field->fld_dtype;
child->nod_desc.dsc_length = field->fld_length;
fb_assert(field->fld_scale <= MAX_SCHAR);
child->nod_desc.dsc_scale = (SCHAR) field->fld_scale;
}
else if ((nod_constant != child->nod_type) && (child->nod_count > 0))
{
// A nod_constant can have nod_arg entries which are not really
// pointers to other nodes, but rather integer values, so
// it is not safe to scan through its children. Fortunately,
// it cannot have a nod_dom_value as a child in any case, so
// we lose nothing by skipping it.
set_nod_value_attributes(child, field);
}
} // if it's a node
} // for (child_number ...
return;
}
// BEGIN dsql_req METHODS.
// Write out a string of blr as part of a ddl string,
// as in a view or computed field definition.
//
void DsqlCompiledStatement::begin_blr(UCHAR verb)
{
if (verb) {
append_uchar(verb);
}
baseOffset = blrData.getCount();
// put in a place marker for the size of the blr, since it is unknown
append_ushort(0);
append_uchar((flags & FLAG_BLR_VERSION4) ? blr_version4 : blr_version5);
}
// Complete the stuffing of a piece of
// blr by going back and inserting the length.
//
void DsqlCompiledStatement::end_blr()
{
append_uchar(blr_eoc);
// go back and stuff in the proper length
UCHAR* blr_base = &blrData[baseOffset];
const ULONG length = (blrData.getCount() - baseOffset) - 2;
if (length > 0xFFFF) {
ERRD_post(Arg::Gds(isc_too_big_blr) << Arg::Num(length) << Arg::Num(0xFFFF));
}
*blr_base++ = (UCHAR) length;
*blr_base = (UCHAR) (length >> 8);
}
void DsqlCompiledStatement::append_number(UCHAR verb, SSHORT number)
{
/**************************************
*
* Input
* blr_ptr: current position of blr being generated
* verb: blr byte of which number is an argument
* number: value to be written to blr
* Function
* Write out a numeric valued attribute.
*
**************************************/
if (verb) {
append_uchar(verb);
}
append_ushort_with_length(number);
}
//
// Write out a string valued attribute.
//
void DsqlCompiledStatement::append_cstring(UCHAR verb, const char* string)
{
const USHORT length = string ? strlen(string) : 0;
append_string(verb, string, length);
}
//
// Write out a string in metadata charset with one byte of length.
//
void DsqlCompiledStatement::append_meta_string(const char* string)
{
append_string(0, string, strlen(string));
}
//
// Write out a string valued attribute. (Overload 1.)
//
void DsqlCompiledStatement::append_string(UCHAR verb, const char* string, USHORT length)
{
// TMN: Doesn't this look pretty awkward? If we are given
// a verb, the length is a ushort, else it's uchar.
if (verb)
{
append_uchar(verb);
append_ushort(length);
}
else
{
fb_assert(length <= MAX_UCHAR);
append_uchar(length);
}
// CVC: I preserve this code but it's inconsistent: we first log the length
// then we check the null terminator. If we want this, we should recalculate the
// length and log the correct length instead.
/*
if (string)
{
for (; length-- && *string; string++) {
append_uchar(*string);
}
}
*/
if (string)
append_raw_string(string, length);
}
void DsqlCompiledStatement::append_uchars(UCHAR byte, int count)
{
for (int i = 0; i < count; ++i) {
append_uchar(byte);
}
}
void DsqlCompiledStatement::append_ushort_with_length(USHORT val)
{
// append an USHORT value, prepended with the USHORT length of an USHORT
append_ushort(2);
append_ushort(val);
}
void DsqlCompiledStatement::append_ulong_with_length(ULONG val)
{
// append an ULONG value, prepended with the USHORT length of an ULONG
append_ushort(4);
append_ulong(val);
}
void DsqlCompiledStatement::append_file_length(ULONG length)
{
append_uchar(isc_dyn_file_length);
append_ulong_with_length(length);
}
void DsqlCompiledStatement::append_file_start(ULONG start)
{
append_uchar(isc_dyn_file_start);
append_ulong_with_length(start);
}
//
// common code factored out
//
void DsqlCompiledStatement::generate_unnamed_trigger_beginning(bool on_update_trigger,
const char* prim_rel_name,
const dsql_nod* prim_columns,
const char* for_rel_name,
const dsql_nod* for_columns)
{
// no trigger name. It is generated by the engine
append_string(isc_dyn_def_trigger, "", 0);
append_number(isc_dyn_trg_type,
(SSHORT) (on_update_trigger ? POST_MODIFY_TRIGGER : POST_ERASE_TRIGGER));
append_uchar(isc_dyn_sql_object);
append_number(isc_dyn_trg_sequence, 1);
append_number(isc_dyn_trg_inactive, 0);
append_cstring(isc_dyn_rel_name, prim_rel_name);
// the trigger blr
begin_blr(isc_dyn_trg_blr);
// for ON UPDATE TRIGGER only: generate the trigger firing condition:
// if prim_key.old_value != prim_key.new value.
// Note that the key could consist of multiple columns
if (on_update_trigger)
{
stuff_trg_firing_cond(this, prim_columns);
append_uchars(blr_begin, 2);
}
append_uchar(blr_for);
append_uchar(blr_rse);
// the context for the prim. key relation
append_uchar(1);
append_uchar(blr_relation);
append_cstring(0, for_rel_name);
// the context for the foreign key relation
append_uchar(2);
// generate the blr for: foreign_key == primary_key
stuff_matching_blr(this, for_columns, prim_columns);
append_uchar(blr_modify);
append_uchar(2);
append_uchar(2);
append_uchar(blr_begin);
}
void DsqlCompiledStatement::beginDebug()
{
fb_assert(!debugData.getCount());
debugData.add(fb_dbg_version);
debugData.add(1);
}
void DsqlCompiledStatement::endDebug()
{
debugData.add(fb_dbg_end);
}
void DsqlCompiledStatement::put_debug_src_info(USHORT line, USHORT col)
{
debugData.add(fb_dbg_map_src2blr);
debugData.add(line);
debugData.add(line >> 8);
debugData.add(col);
debugData.add(col >> 8);
ULONG offset = (blrData.getCount() - baseOffset);
// for DDL statements we store BLR's length at the first 2 bytes
if ((type == DsqlCompiledStatement::TYPE_DDL || ddlNode) && !blockNode)
offset -= 2;
debugData.add(offset);
debugData.add(offset >> 8);
}
void DsqlCompiledStatement::put_debug_variable(USHORT number, const TEXT* name)
{
fb_assert(name);
debugData.add(fb_dbg_map_varname);
debugData.add(number);
debugData.add(number >> 8);
USHORT len = strlen(name);
if (len > MAX_UCHAR)
len = MAX_UCHAR;
debugData.add(len);
debugData.add(reinterpret_cast<const UCHAR*>(name), len);
}
void DsqlCompiledStatement::put_debug_argument(UCHAR type, USHORT number, const TEXT* name)
{
fb_assert(name);
debugData.add(fb_dbg_map_argument);
debugData.add(type);
debugData.add(number);
debugData.add(number >> 8);
USHORT len = strlen(name);
if (len > MAX_UCHAR)
len = MAX_UCHAR;
debugData.add(len);
debugData.add(reinterpret_cast<const UCHAR*>(name), len);
}
void DsqlCompiledStatement::append_debug_info()
{
endDebug();
const size_t len = blrData.getCount() + debugData.getCount();
if (len + 4 < MAX_USHORT)
{
append_uchar(isc_dyn_debug_info);
append_ushort(debugData.getCount());
append_raw_string(debugData.begin(), debugData.getCount());
}
}
//
// removes temporary pool pointers from field, stored in permanent cache
//
void clearPermanentField (dsql_rel* relation, bool perm)
{
if (relation && relation->rel_fields && perm)
{
relation->rel_fields->fld_procedure = 0;
relation->rel_fields->fld_ranges = 0;
relation->rel_fields->fld_character_set = 0;
relation->rel_fields->fld_sub_type_name = 0;
relation->rel_fields->fld_relation = relation;
}
}
//
// post very often used error - avoid code duplication
//
static void post_607(const Arg::StatusVector& v)
{
Arg::Gds err(isc_sqlerr);
err << Arg::Num(-607) << Arg::Gds(isc_dsql_command_err);
err.append(v);
ERRD_post(err);
}