8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 05:23:02 +01:00
firebird-mirror/src/qli/meta.epp
2007-04-11 16:05:40 +00:00

3647 lines
89 KiB
Plaintext

/*
* PROGRAM: JRD Command Oriented Query Language
* MODULE: meta.epp
* DESCRIPTION: Meta-data interface
*
* 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): ______________________________________.
*/
#include "firebird.h"
#include <stdio.h>
#include <string.h>
#include "../qli/dtr.h"
#include "../qli/compile.h"
#include "../qli/exe.h"
//#include "../jrd/license.h"
#include "../jrd/flags.h"
#include "../jrd/ibase.h"
#include "../qli/reqs.h"
#include "../qli/all_proto.h"
#include "../qli/err_proto.h"
#include "../qli/gener_proto.h"
#include "../qli/hsh_proto.h"
#include "../qli/meta_proto.h"
#include "../gpre/prett_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_f_proto.h"
#include "../jrd/utl_proto.h"
#include "../common/utils_proto.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/classes/UserBlob.h"
using MsgFormat::SafeArg;
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
DATABASE DB = FILENAME "yachts.lnk";
DATABASE DB1 = FILENAME "yachts.lnk";
static void add_field(qli_rel*, qli_fld*, USHORT);
static void add_sql_field(qli_rel*, qli_fld*, USHORT, qli_rlb*);
static void blob_copy(qli_rlb*, qli_rel*, ISC_QUAD&);
static void change_field(qli_rel*, qli_fld*);
static bool check_global_field(DBB, qli_fld*, const TEXT*);
static bool check_relation(qli_rel*);
static void clone_fields(qli_rel*, qli_rel*);
static void clone_global_fields(qli_rel*, qli_rel*);
static void define_global_field(DBB, qli_fld*, qli_symbol*);
static void delete_fields(qli_rel*);
static ISC_STATUS detach(ISC_STATUS *, DBB);
static void execute_dynamic_ddl(DBB, qli_rlb*);
static int field_length(USHORT, USHORT);
static void get_database_type(DBB);
static TEXT *get_query_header(DBB, ISC_QUAD&);
static void install(dbb*);
static qli_syntax* make_node(NOD_T, USHORT);
static TEXT *make_string(TEXT*, SSHORT);
static qli_symbol* make_symbol(TEXT *, SSHORT);
static qli_const* missing_value(ISC_QUAD&, qli_symbol*);
static qli_syntax* parse_blr(UCHAR **, qli_symbol*);
static qli_syntax* parse_blr_blob(ISC_QUAD&, qli_symbol*);
static void purge_relation(qli_rel*);
static void put_dyn_string(qli_rlb*, const TEXT*);
static void rollback_update(DBB);
static void set_capabilities(DBB);
static DBB setup_update(DBB);
static void sql_grant_revoke(qli_syntax*, USHORT);
static void stuff_priv(qli_rlb*, USHORT, const TEXT*, USHORT, const TEXT*, const TEXT*);
static int truncate_string(TEXT*);
static const UCHAR tpb[] = { isc_tpb_version1, isc_tpb_write };
static const UCHAR db_info[] = { isc_info_implementation, isc_info_end };
static UCHAR global_parm_buffer[256];
/*
static const UCHAR dpb_trace [] = { isc_dpb_version1, isc_dpb_trace, 1, 1 };
static const UCHAR dpb_num_buffers [ ] = { isc_dpb_version1, isc_dpb_num_buffers, 1, 1 };
*/
#define STUFF_STRING(str) put_dyn_string (rlb, str)
#define BLR_BYTE *blr++
static const USHORT blr_dtypes[] =
{
0,
blr_text,
blr_text,
blr_varying,
0,
0,
0,
0,
blr_short,
blr_long,
blr_quad,
blr_float,
blr_double,
blr_double,
blr_sql_date,
blr_sql_time,
blr_timestamp,
blr_blob,
0,
blr_int64
};
/*
table used to determine capabilities, checking for specific
fields in system relations
*/
struct rfr_tab_t {
const TEXT* relation;
const TEXT* field;
int bit_mask;
};
static const rfr_tab_t rfr_table[] =
{
{ "RDB$INDICES", "RDB$INDEX_INACTIVE", DBB_cap_idx_inactive },
/* OBSOLETE - 1996-Aug-06 David Schnepper
{ "RDB$RELATIONS", "RDB$STORE_TRIGGER", DBB_cap_triggers },
*/
{ "RDB$RELATIONS", "RDB$EXTERNAL_FILE", DBB_cap_extern_file },
{ "RDB$SECURITY_CLASSES", "RDB$SECURITY_CLASS", DBB_cap_security },
{ "RDB$FILES", "RDB$FILE_NAME", DBB_cap_files },
{ "RDB$FUNCTIONS", "RDB$FUNCTION_NAME", DBB_cap_functions },
{ "RDB$TRIGGERS", "RDB$TRIGGER_BLR", DBB_cap_new_triggers },
{ "RDB$CONSTRAINTS", "RDB$CONSTRAINT_NAME", DBB_cap_single_trans },
{ "RDB$FILES", "RDB$SHADOW_NUMBER", DBB_cap_shadowing },
{ "RDB$TYPES", "RDB$TYPE_NAME", DBB_cap_types },
{ "RDB$FIELDS", "RDB$DIMENSIONS", DBB_cap_dimensions },
{ "RDB$FIELDS", "RDB$EXTERNAL_TYPE", DBB_cap_external_type },
{ "RDB$RELATION_FIELDS", "RDB$SYSTEM_FLAG", DBB_cap_rfr_sys_flag },
{ "RDB$FILTERS", "RDB$FUNCTION_NAME", DBB_cap_filters },
{ "RDB$INDICES", "RDB$INDEX_TYPE", DBB_cap_index_type },
{ 0, 0, 0 }
};
bool MET_declare( DBB database, qli_fld* variable, const nam* field_name)
{
/**************************************
*
* M E T _ d e c l a r e
*
**************************************
*
* Functional description
* Find a global field referenced in a DECLARE.
*
**************************************/
if (!database)
database = QLI_databases;
for (; database; database = database->dbb_next) {
MET_meta_transaction(database, false);
if (check_global_field(database, variable, field_name->nam_string))
return true;
}
return false;
}
void MET_define_field( DBB database, qli_fld* field)
{
/**************************************
*
* M E T _ d e f i n e _ f i e l d
*
**************************************
*
* Functional description
* Define a new global field.
*
**************************************/
database = setup_update(database);
if (check_global_field(database, NULL, field->fld_name->sym_string)) {
rollback_update(database);
ERRQ_print_error(233, field->fld_name->sym_string);
// Msg233 global field %s already exists
}
define_global_field(database, field, field->fld_name);
MET_meta_commit(database);
}
void MET_define_index( qli_syntax* node)
{
/**************************************
*
* M E T _ d e f i n e _ i n d e x
*
**************************************
*
* Functional description
* Define a new index.
*
**************************************/
qli_symbol* symbol = (qli_symbol*) node->syn_arg[s_dfi_name];
qli_rel* relation = (qli_rel*) node->syn_arg[s_dfi_relation];
qli_syntax* fields = node->syn_arg[s_dfi_fields];
DBB database = setup_update(relation->rel_database);
if (relation->rel_flags & REL_view)
IBERROR(234); // Msg234 Can't define an index in a view
FOR(REQUEST_HANDLE database->dbb_requests[REQ_def_index1])
X IN DB.RDB$INDICES WITH X.RDB$INDEX_NAME EQ symbol->sym_string
ERRQ_print_error(235, symbol->sym_string);
// Msg235 Index %s already exists
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
STORE(REQUEST_HANDLE database->dbb_requests[REQ_def_index2])
X IN DB.RDB$INDICES
strcpy(X.RDB$RELATION_NAME, relation->rel_symbol->sym_string);
strcpy(X.RDB$INDEX_NAME, symbol->sym_string);
X.RDB$UNIQUE_FLAG = (node->syn_flags & s_dfi_flag_unique);
X.RDB$INDEX_INACTIVE = (node->syn_flags & s_dfi_flag_inactive);
X.RDB$SEGMENT_COUNT = fields->syn_count;
X.RDB$INDEX_TYPE =
(node->syn_flags & s_dfi_flag_descending) ? TRUE : FALSE;
END_STORE
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
/* at this point force garbage collection of any index segments left over
from previous failures. The system transaction (which might notice them
later does NOT recognize rolled back stuff! Should someone have actually
left an orphanned segment around, kill that too.
*/
FOR(REQUEST_HANDLE database->dbb_requests[REQ_scan_index])
X IN DB.RDB$INDEX_SEGMENTS WITH X.RDB$INDEX_NAME = symbol->sym_string
ERASE X
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
NAM* ptr = (NAM*) fields->syn_arg;
for (int position = 0; position < fields->syn_count; ptr++, position++)
{
MET_fields(relation);
bool present = false;
for (qli_fld* field = relation->rel_fields; field;
field = field->fld_next)
{
if (!(strcmp((*ptr)->nam_string, field->fld_name->sym_string))) {
present = true;
break;
}
}
if (!present) {
rollback_update(database);
ERRQ_print_error(236, SafeArg() << (*ptr)->nam_string <<
relation->rel_symbol->sym_string);
// Msg236 Field %s does not occur in relation %s
}
setup_update(database);
STORE(REQUEST_HANDLE database->dbb_requests[REQ_def_index3])
X IN DB.RDB$INDEX_SEGMENTS
strcpy(X.RDB$INDEX_NAME, symbol->sym_string);
strcpy(X.RDB$FIELD_NAME, (*ptr)->nam_string);
X.RDB$FIELD_POSITION = position;
END_STORE
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
}
MET_meta_commit(database);
}
void MET_define_relation( qli_rel* relation, qli_rel* source)
{
/**************************************
*
* M E T _ d e f i n e _ r e l a t i o n
*
**************************************
*
* Functional description
* Define a new relation. There may
* be field definitions here or we may
* copy the field definitions from somebody
* else.
*
**************************************/
qli_symbol* symbol = relation->rel_symbol;
if (source) {
source->rel_database = setup_update(source->rel_database);
if (!(check_relation(source)))
ERRQ_print_error(483, source->rel_symbol->sym_string);
// Msg 483 Relation %s does not exist
}
DBB database = setup_update(relation->rel_database);
relation->rel_database = database;
if (check_relation(relation))
ERRQ_print_error(237, symbol->sym_string); // Msg237 Relation %s already exists
STORE(REQUEST_HANDLE database->dbb_requests[REQ_store_relation])
X IN DB.RDB$RELATIONS
strcpy(X.RDB$RELATION_NAME, symbol->sym_string);
END_STORE
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
if (source)
clone_fields(relation, source);
else {
USHORT position = 1;
for (qli_fld* field = relation->rel_fields; field;
field = field->fld_next, position++)
{
add_field(relation, field, position);
}
}
MET_meta_commit(database);
relation = (qli_rel*) ALLOCP(type_rel);
relation->rel_next = database->dbb_relations;
database->dbb_relations = relation;
relation->rel_database = database;
// Go back and pick up relation id
setup_update(database);
FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_id])
X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string
relation->rel_id = X.RDB$RELATION_ID;
symbol = make_symbol(X.RDB$RELATION_NAME, sizeof(X.RDB$RELATION_NAME));
symbol->sym_type = SYM_relation;
symbol->sym_object = (BLK) relation;
relation->rel_symbol = symbol;
HSH_insert(symbol);
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
delete_fields(relation);
MET_fields(relation);
}
void MET_define_sql_relation( qli_rel* relation)
{
/**************************************
*
* M E T _ d e f i n e _ s q l _ r e l a t i o n
*
**************************************
*
* Functional description
* Define a new SQL relation, using dynamic ddl.
*
**************************************/
qli_symbol* symbol = relation->rel_symbol;
DBB database = setup_update(relation->rel_database);
relation->rel_database = database;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_def])
X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string
ERRQ_print_error(237, symbol->sym_string);
// Msg237 Relation %s already exists
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
qli_rlb* rlb = NULL;
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_begin);
STUFF(isc_dyn_def_rel);
STUFF_STRING(symbol->sym_string);
STUFF(isc_dyn_rel_sql_protection);
STUFF_WORD(2);
STUFF_WORD(1);
USHORT position = 1;
for (qli_fld* field = relation->rel_fields; field;
field = field->fld_next, position++)
{
add_sql_field(relation, field, position, rlb);
}
STUFF(isc_dyn_end);
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
execute_dynamic_ddl(database, rlb);
MET_meta_commit(database);
relation = (qli_rel*) ALLOCP(type_rel);
relation->rel_next = database->dbb_relations;
database->dbb_relations = relation;
relation->rel_database = database;
// Go back and pick up relation id
setup_update(database);
FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_id])
X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string
relation->rel_id = X.RDB$RELATION_ID;
symbol =
make_symbol(X.RDB$RELATION_NAME, sizeof(X.RDB$RELATION_NAME));
symbol->sym_type = SYM_relation;
symbol->sym_object = (BLK) relation;
relation->rel_symbol = symbol;
HSH_insert(symbol);
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
delete_fields(relation);
MET_fields(relation);
}
void MET_delete_database( DBB dbb)
{
/**************************************
*
* M E T _ d e l e t e _ d a t a b a s e
*
**************************************
*
* Functional description
* Drop an existing database, and all its files,
* and finish any copies we have active.
*
**************************************/
/* generate a database parameter block to
include the user/password, if necessary */
const TEXT* user;
USHORT user_length;
if (dbb->dbb_user) {
user = (TEXT *) dbb->dbb_user->con_data;
user_length = dbb->dbb_user->con_desc.dsc_length;
}
else {
user = QLI_default_user;
user_length = strlen(QLI_default_user);
}
const TEXT* password;
USHORT password_length;
if (dbb->dbb_password) {
password = (TEXT *) dbb->dbb_password->con_data;
password_length = dbb->dbb_user->con_desc.dsc_length;
}
else {
password = QLI_default_password;
password_length = strlen(QLI_default_password);
}
Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
if (user_length) {
dpb.insertString(isc_dpb_user_name, user, user_length);
}
if (password_length) {
dpb.insertString(isc_dpb_password, password, password_length);
}
ISC_STATUS_ARRAY status_vector;
if (isc_attach_database(status_vector, 0, dbb->dbb_filename,
&dbb->dbb_handle, dpb.getBufferLength(),
reinterpret_cast<const char*>(dpb.getBuffer())))
ERRQ_database_error(dbb, status_vector);
qli_lls* stack = NULL;
gds_trans = MET_transaction(nod_start_trans, dbb);
DB = dbb->dbb_handle;
FOR F IN DB.RDB$FILES SORTED BY F.RDB$FILE_START
qli_str* astring = (qli_str*) ALLOCDV(type_str, strlen(F.RDB$FILE_NAME));
strcpy(astring->str_data, F.RDB$FILE_NAME);
ALLQ_push((blk*) astring, &stack);
END_FOR
ON_ERROR
ERRQ_database_error(dbb, gds_status);
END_ERROR;
MET_transaction(nod_commit, dbb);
if (isc_detach_database(gds_status, &dbb->dbb_handle))
isc_print_status(gds_status);
DBB next = NULL;
for (DBB database = QLI_databases; database; database = next) {
next = database->dbb_next;
if (!strcmp(database->dbb_filename, dbb->dbb_filename))
MET_finish(database);
}
while (stack) {
qli_str* sstring = (qli_str*) ALLQ_pop(&stack);
if (unlink(sstring->str_data))
ERRQ_print_error(431, sstring->str_data);
// Msg431 Could not drop database file "%s"
}
if (unlink(dbb->dbb_filename))
ERRQ_print_error(431, dbb->dbb_filename);
// Msg431 Could not drop database file "%s"
}
void MET_delete_field( DBB database, NAM name)
{
/**************************************
*
* M E T _ d e l e t e _ f i e l d
*
**************************************
*
* Functional description
* Delete a global field.
*
**************************************/
database = setup_update(database);
USHORT count = 0;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_check_fld])
RFR IN DB.RDB$RELATION_FIELDS
WITH RFR.RDB$FIELD_SOURCE EQ name->nam_string
REDUCED TO RFR.RDB$RELATION_NAME
if (!count)
ERRQ_msg_put(238, name->nam_string); // Msg238 Field %s is in use in the following relations:
printf("\t%s\n", RFR.RDB$RELATION_NAME);
count++;
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
if (count)
ERRQ_print_error(239, SafeArg() << name->nam_string << database->dbb_filename);
// Msg239 Field %s is in use in database %s
FOR(REQUEST_HANDLE database->dbb_requests[REQ_erase_fld])
FLD IN DB.RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ name->nam_string
qli_rlb* rlb = NULL;
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_delete_dimensions);
STUFF_STRING(FLD.RDB$FIELD_NAME);
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
execute_dynamic_ddl(database, rlb);
ERASE FLD
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
count++;
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
if (!count)
ERRQ_print_error(240, SafeArg() << name->nam_string << database->dbb_filename);
// Msg240 Field %s is not defined in database %s
MET_meta_commit(database);
}
void MET_delete_index( DBB database, NAM name)
{
/**************************************
*
* M E T _ d e l e t e _ i n d e x
*
**************************************
*
* Functional description
* Delete an index.
*
**************************************/
database = setup_update(database);
USHORT count = 0;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_erase_index])
IDX IN DB.RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name->nam_string
count++;
ERASE IDX
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
if (!count)
ERRQ_print_error(241, SafeArg() << name->nam_string << database->dbb_filename);
// Msg241 Index %s is not defined in database %s
FOR(REQUEST_HANDLE database->dbb_requests[REQ_erase_segments])
SEG IN DB.RDB$INDEX_SEGMENTS WITH SEG.RDB$INDEX_NAME EQ name->nam_string
ERASE SEG
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
MET_meta_commit(database);
}
void MET_delete_relation( qli_rel* relation)
{
/**************************************
*
* M E T _ d e l e t e _ r e l a t i o n
*
**************************************
*
* Functional description
* Delete a relation.
*
**************************************/
// Pass the mess off to dynamic DDL and let it worry
qli_symbol* symbol = relation->rel_symbol;
qli_rlb* rlb = NULL;
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_delete_rel);
STUFF_STRING(symbol->sym_string);
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
setup_update(relation->rel_database);
execute_dynamic_ddl(relation->rel_database, rlb);
MET_meta_commit(relation->rel_database);
// Unlink and release various blocks
purge_relation(relation);
}
int MET_dimensions( DBB database, const TEXT* field_name)
{
/**************************************
*
* M E T _ d i m e n s i o n s
*
**************************************
*
* Functional description
* Determine if the field has any dimensions.
*
**************************************/
if (!(database->dbb_capabilities & DBB_cap_dimensions))
return 0;
USHORT dimensions = 0;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_fld_dimensions])
F IN DB.RDB$FIELDS WITH F.RDB$FIELD_NAME = field_name
dimensions = F.RDB$DIMENSIONS;
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
return dimensions;
}
void MET_fields( qli_rel* relation)
{
/**************************************
*
* M E T _ f i e l d s
*
**************************************
*
* Functional description
* Get all fields for a relation.
*
**************************************/
/* If we already have fetched the fields for the relation,
don't do it again. */
if (relation->rel_flags & REL_fields)
return;
DBB database = relation->rel_database;
MET_meta_transaction(database, false);
relation->rel_flags |= REL_fields;
qli_fld** ptr = &relation->rel_fields;
FOR(REQUEST_HANDLE database->dbb_field_request)
RFR IN DB.RDB$RELATION_FIELDS CROSS
RFL IN DB.RDB$FIELDS WITH
RFR.RDB$FIELD_SOURCE EQ RFL.RDB$FIELD_NAME AND
RFR.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string
SORTED BY RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_NAME;
if (RFR.RDB$FIELD_POSITION > relation->rel_max_field_pos)
relation->rel_max_field_pos = RFR.RDB$FIELD_POSITION;
qli_fld* field = (qli_fld*) ALLOCPV(type_fld, 0);
*ptr = field;
ptr = &field->fld_next;
field->fld_relation = relation;
qli_symbol* symbol = make_symbol(RFR.RDB$FIELD_NAME, sizeof(RFR.RDB$FIELD_NAME));
if (symbol) {
symbol->sym_object = (BLK) field;
symbol->sym_type = SYM_field;
field->fld_name = symbol;
}
if ((symbol = make_symbol(RFR.RDB$QUERY_NAME, sizeof(RFR.RDB$QUERY_NAME))) ||
(symbol = make_symbol(RFL.RDB$QUERY_NAME, sizeof(RFL.RDB$QUERY_NAME))))
{
symbol->sym_object = (BLK) field;
symbol->sym_type = SYM_field;
field->fld_query_name = symbol;
}
field->fld_scale = RFL.RDB$FIELD_SCALE;
field->fld_id = RFR.RDB$FIELD_ID;
if (RFL.RDB$SEGMENT_LENGTH.NULL)
field->fld_segment_length = 80;
else {
field->fld_segment_length =
((RFL.RDB$SEGMENT_LENGTH) < 256
&& (RFL.RDB$SEGMENT_LENGTH >
0)) ? RFL.RDB$SEGMENT_LENGTH : 255;
}
ISC_QUAD* blob = & RFR.RDB$QUERY_HEADER;
if (UserBlob::blobIsNull(*blob))
blob = & RFL.RDB$QUERY_HEADER;
if (!UserBlob::blobIsNull(*blob))
field->fld_query_header = get_query_header(database, *blob);
blob = & RFL.RDB$COMPUTED_BLR;
if (!UserBlob::blobIsNull(*blob))
field->fld_flags |= FLD_computed;
field->fld_dtype = MET_get_datatype(RFL.RDB$FIELD_TYPE);
field->fld_length =
field_length(field->fld_dtype, RFL.RDB$FIELD_LENGTH);
if (field->fld_dtype == dtype_varying)
field->fld_length += sizeof(SSHORT);
field->fld_sub_type = RFL.RDB$FIELD_SUB_TYPE;
field->fld_sub_type_missing = RFL.RDB$FIELD_SUB_TYPE.NULL;
if (!RFL.RDB$MISSING_VALUE.NULL)
field->fld_missing =
missing_value(RFL.RDB$MISSING_VALUE, field->fld_name);
if (!(field->fld_edit_string =
make_string(RFR.RDB$EDIT_STRING, sizeof(RFR.RDB$EDIT_STRING))))
field->fld_edit_string =
make_string(RFL.RDB$EDIT_STRING, sizeof(RFL.RDB$EDIT_STRING));
field->fld_validation =
parse_blr_blob(RFL.RDB$VALIDATION_BLR, field->fld_name);
if (MET_dimensions(database, RFL.RDB$FIELD_NAME) > 0)
field->fld_flags |= FLD_array;
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
}
void MET_finish( DBB dbb)
{
/**************************************
*
* M E T _ f i n i s h
*
**************************************
*
* Functional description
* Finish a database and release all associated blocks.
*
**************************************/
// Get rid of relation and field blocks.
qli_rel* relation;
while (relation = dbb->dbb_relations) {
purge_relation(relation);
}
// Get rid of any functions
qli_fun* function;
while (function = dbb->dbb_functions) {
dbb->dbb_functions = function->fun_next;
HSH_remove(function->fun_symbol);
ALLQ_release((FRB) function->fun_symbol);
ALLQ_release((FRB) function);
}
// Finally, actually close down database connection.
ISC_STATUS_ARRAY status_vector;
detach(status_vector, dbb);
if (dbb->dbb_symbol) {
HSH_remove(dbb->dbb_symbol);
ALLQ_release((FRB) dbb->dbb_symbol);
}
int count = 0;
for (DBB* ptr = &QLI_databases; *ptr; ptr = &(*ptr)->dbb_next)
{
if (*ptr == dbb)
{
*ptr = dbb->dbb_next;
ALLQ_release((FRB) dbb);
count++;
if (!*ptr) {
break;
}
}
}
if (!count)
ERRQ_bugcheck(231); // Msg231 database block not found for removal
if (status_vector[1])
ERRQ_database_error(NULL, status_vector);
}
int MET_get_datatype( USHORT blr_datatype)
{
/**************************************
*
* M E T _ g e t _ d a t a t y p e
*
**************************************
*
* Functional description
* Convert a blr datatype to a QLI dtype.
*
**************************************/
USHORT retvalue;
switch (blr_datatype)
{
case blr_text:
retvalue = dtype_text;
break;
case blr_varying:
retvalue = dtype_varying;
break;
case blr_cstring:
retvalue = dtype_cstring;
break;
case blr_short:
retvalue = dtype_short;
break;
case blr_long:
retvalue = dtype_long;
break;
case blr_quad:
retvalue = dtype_quad;
break;
case blr_float:
retvalue = dtype_real;
break;
case blr_double:
retvalue = dtype_double;
break;
case blr_timestamp:
retvalue = dtype_timestamp;
break;
case blr_sql_date:
retvalue = dtype_sql_date;
break;
case blr_sql_time:
retvalue = dtype_sql_time;
break;
case blr_blob:
retvalue = dtype_blob;
break;
case blr_int64:
retvalue = dtype_int64;
break;
default:
retvalue = dtype_unknown;
break;
}
return retvalue;
}
#ifdef DEV_BUILD
void MET_index_info(const SCHAR* relation_name,
const SCHAR* index_name,
SCHAR* buffer)
{
/**************************************
*
* M E T _ i n d e x _ i n f o
*
**************************************
*
* Functional description
* Get info about a particular index.
*
**************************************/
FB_API_HANDLE request_handle = 0;
SCHAR* b = buffer;
FOR(REQUEST_HANDLE request_handle)
IDX IN DB.RDB$INDICES CROSS
SEG IN DB.RDB$INDEX_SEGMENTS OVER
RDB$INDEX_NAME WITH
IDX.RDB$INDEX_NAME EQ index_name AND
IDX.RDB$RELATION_NAME EQ relation_name
SORTED BY SEG.RDB$FIELD_POSITION
const SCHAR* p;
if (b == buffer) {
// CVC: warning: not dialect 3 aware.
for (p = IDX.RDB$INDEX_NAME; *p && *p != ' ';) {
*b++ = *p++;
}
*b++ = ' ';
*b++ = '(';
}
// CVC: warning: not dialect 3 aware.
for (p = SEG.RDB$FIELD_NAME; *p && *p != ' ';) {
*b++ = *p++;
}
*b++ = ' ';
END_FOR
ON_ERROR
ERRQ_database_error(NULL, gds_status);
END_ERROR;
if (request_handle)
if (isc_release_request(gds_status, &request_handle))
ERRQ_database_error(NULL, gds_status);
// back up over the last space and finish off
b--;
*b++ = ')';
*b++ = 0;
}
#endif
void MET_meta_commit( DBB database)
{
/**************************************
*
* M E T _ m e t a _ c o m m i t
*
**************************************
*
* Functional description
* Commit the meta data lookup & update
* transaction.
*
**************************************/
if (database->dbb_capabilities & DBB_cap_multi_trans)
{
ISC_STATUS_ARRAY status_vector;
if (isc_commit_transaction(status_vector, &database->dbb_meta_trans)) {
rollback_update(database);
ERRQ_database_error(database, status_vector);
}
}
}
void MET_meta_rollback( DBB database)
{
/**************************************
*
* M E T _ m e t a _ r o l l b a c k
*
**************************************
*
* Functional description
* Rollback the metadata transaction,
* if there is one.
*
**************************************/
if (database->dbb_capabilities & DBB_cap_multi_trans)
rollback_update(database);
}
FB_API_HANDLE MET_meta_transaction(DBB database, bool update_flag)
{
/**************************************
*
* M E T _ m e t a _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Setup transaction for meta-data operation. Set up
* DB and gds_trans, and return meta-data transaction
* handle for yucks. If we're doing metadata updates,
* and lookups we'll use the metadat date transaction,
* unless this is an rdb database or gateway in which
* case, we'll use the only transacti.
*
* In any event, we will set up the met_transaction because
* it's widely used and somebody is going to forget to test
* that the database is multi_transaction before using it.
* This means that we have to be very careful about committing
* or rolling back, because this could affect user data.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
if (!database)
IBERROR(243); // Msg243 no active database for operation
FB_API_HANDLE transaction = (database->dbb_capabilities & DBB_cap_multi_trans) ?
database->dbb_meta_trans : 0;
DB = database->dbb_handle;
/* If we don't know whether or not the database can handle
multiple transactions, find out now */
if (!transaction &&
((database->dbb_capabilities & DBB_cap_multi_trans) ||
!(database->dbb_capabilities & DBB_cap_single_trans)))
{
if (isc_start_transaction(status_vector, &transaction, 1,
&database->dbb_handle, sizeof(tpb), tpb))
{
database->dbb_capabilities |= DBB_cap_single_trans;
}
else
{
database->dbb_capabilities |= DBB_cap_multi_trans;
}
}
// If we already have a meta-data transaction, there's more nothing to do
gds_trans = transaction;
// If we only support a single transaction, use the data transaction
if (!gds_trans && (database->dbb_capabilities & DBB_cap_single_trans))
{
if (update_flag)
IBERROR(244); // Msg244 Interactive metadata updates are not available on Rdb
if (!(gds_trans = database->dbb_transaction))
gds_trans = MET_transaction(nod_start_trans, database);
}
// otherwise make one more effort to start the transaction
else if (!gds_trans)
{
START_TRANSACTION
ON_ERROR
ERRQ_database_error(database, status_vector);
END_ERROR;
}
database->dbb_meta_trans = gds_trans;
return gds_trans;
}
void MET_modify_field( DBB database, qli_fld* field)
{
/**************************************
*
* M E T _ m o d i f y _ f i e l d
*
**************************************
*
* Functional description
* Modify an existing global field.
*
**************************************/
database = setup_update(database);
qli_symbol* field_name = field->fld_name;
SSHORT flag = 0;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_modify_fld])
X IN DB.RDB$FIELDS WITH X.RDB$FIELD_NAME EQ field_name->sym_string
if (field->fld_dtype &&
(X.RDB$FIELD_TYPE == blr_blob || blr_dtypes[field->fld_dtype] == blr_blob) &&
X.RDB$FIELD_TYPE != blr_dtypes[field->fld_dtype])
{
flag = -1;
}
else
{
flag = 1;
MODIFY X USING
if (field->fld_dtype) {
X.RDB$FIELD_TYPE = blr_dtypes[field->fld_dtype];
X.RDB$FIELD_SCALE = field->fld_scale;
X.RDB$FIELD_LENGTH = (field->fld_dtype == dtype_varying) ?
field->fld_length -
sizeof(SSHORT) : field->fld_length;
}
if (!field->fld_sub_type_missing) {
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
X.RDB$FIELD_SUB_TYPE = field->fld_sub_type;
}
qli_symbol* asymbol = field->fld_query_name;
if (asymbol) {
X.RDB$QUERY_NAME.NULL = FALSE;
strcpy(X.RDB$QUERY_NAME, asymbol->sym_string);
}
if (field->fld_edit_string) {
X.RDB$EDIT_STRING.NULL = FALSE;
strcpy(X.RDB$EDIT_STRING, field->fld_edit_string);
}
END_MODIFY
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
}
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
if (flag <= 0)
{
rollback_update(database);
// Msg245 global field %s is not defined
// Msg468 Datatype of field %s may not be changed to or from blob
const USHORT nErr = flag ? 468 : 245;
ERRQ_print_error(flag, field_name->sym_string);
}
MET_meta_commit(database);
// Now go back and re-fetch fields for affected databases
setup_update(database);
FOR(REQUEST_HANDLE database->dbb_requests[REQ_update_fld])
X IN DB.RDB$RELATION_FIELDS WITH X.RDB$FIELD_SOURCE EQ
field_name->sym_string REDUCED TO X.RDB$RELATION_NAME
const TEXT* p = X.RDB$RELATION_NAME;
while (*p && *p != ' ') {
p++;
}
qli_symbol* symbol = HSH_lookup(X.RDB$RELATION_NAME, p - X.RDB$RELATION_NAME);
for (; symbol; symbol = symbol->sym_homonym)
{
qli_rel* relation;
if (symbol->sym_type == SYM_relation &&
(relation = (qli_rel*) symbol->sym_object) &&
relation->rel_database == database)
{
delete_fields(relation);
MET_fields(relation);
}
}
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
}
void MET_modify_index( qli_syntax* node)
{
/**************************************
*
* M E T _ m o d i f y _ i n d e x
*
**************************************
*
* Functional description
* Change the changeable options
* of an index.
*
**************************************/
NAM name = (NAM) node->syn_arg[s_mfi_name];
DBB database = (DBB) node->syn_arg[s_mfi_database];
database = setup_update(database);
USHORT count = 0;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_mdf_index])
X IN DB.RDB$INDICES WITH X.RDB$INDEX_NAME = name->nam_string
MODIFY X USING
if (node->syn_flags & s_dfi_flag_selectivity)
X.RDB$UNIQUE_FLAG = (node->syn_flags & s_dfi_flag_unique);
if (node->syn_flags & s_dfi_flag_activity)
X.RDB$INDEX_INACTIVE =
(node->syn_flags & s_dfi_flag_inactive);
if (node->syn_flags & s_dfi_flag_order)
X.RDB$INDEX_TYPE =
(node->syn_flags & s_dfi_flag_descending) ? TRUE : FALSE;
if (node->syn_flags & s_dfi_flag_statistics)
X.RDB$STATISTICS = -1.0;
END_MODIFY
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
count++;
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
if (!count)
ERRQ_print_error(246, SafeArg() << name->nam_string << database->dbb_filename);
// Msg246 Index %s does not exist
MET_meta_commit(database);
}
void MET_modify_relation( qli_rel* relation, qli_fld* fields)
{
/**************************************
*
* M E T _ m o d i f y _ r e l a t i o n
*
**************************************
*
* Functional description
* Modify an existing relation.
*
**************************************/
DBB database = setup_update(relation->rel_database);
relation->rel_database = database;
qli_symbol* relation_name = relation->rel_symbol;
for (qli_fld* field = fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_drop) {
USHORT count = 0;
qli_symbol* field_name = field->fld_name;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_modify_rel])
X IN DB.RDB$RELATION_FIELDS WITH
X.RDB$FIELD_NAME EQ field_name->sym_string AND
X.RDB$RELATION_NAME EQ relation_name->sym_string
count++;
ERASE X
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
if (!count) {
rollback_update(database);
ERRQ_print_error(247, field_name->sym_string); // Msg247 field %s doesn't exist
}
}
else if (field->fld_flags & FLD_modify)
change_field(relation, field);
else
add_field(relation, field, 0);
}
MET_meta_commit(database);
setup_update(database);
delete_fields(relation);
MET_fields(relation);
}
void MET_ready( qli_syntax* node, USHORT create_flag)
{
/**************************************
*
* M E T _ r e a d y
*
**************************************
*
* Functional description
* Create or Ready one or more databases. If any
* fail, all fail. Fair enough?
*
**************************************/
Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
DBB temp = (DBB) node->syn_arg[0];
/* Only a SQL CREATE DATABASE will have a pagesize parameter,
so only looking at the first pointer is justified. */
SSHORT length;
const TEXT* lc_ctype = QLI_charset;
if (lc_ctype && (length = strlen(lc_ctype))) {
dpb.insertString(isc_dpb_lc_ctype, lc_ctype, length);
}
if (QLI_trace) {
dpb.insertByte(isc_dpb_trace, 1);
}
if (sw_buffers) {
dpb.insertInt(isc_dpb_num_buffers, ULONG(sw_buffers));
}
if (temp->dbb_pagesize) {
dpb.insertInt(isc_dpb_page_size, ULONG(temp->dbb_pagesize));
}
// get a username, if specified globally or on the command line
const TEXT* q;
if (temp->dbb_user) {
q = (TEXT *) temp->dbb_user->con_data;
length = temp->dbb_user->con_desc.dsc_length;
}
else {
q = QLI_default_user;
length = strlen(QLI_default_user);
}
if (length) {
dpb.insertString(isc_dpb_user_name, q, length);
}
// get a password, if specified
if (temp->dbb_password) {
q = (TEXT *) temp->dbb_password->con_data;
length = temp->dbb_password->con_desc.dsc_length;
}
else {
q = QLI_default_password;
length = strlen(QLI_default_password);
}
if (length) {
dpb.insertString(isc_dpb_password, q, length);
}
#ifdef TRUSTED_AUTH
if (QLI_trusted) {
dpb.insertTag(isc_dpb_trusted_auth);
}
#endif
const qli_syntax* const* const end = node->syn_arg + node->syn_count;
// Start by attaching all databases
ISC_STATUS_ARRAY status_vector;
qli_syntax** ptr;
DBB dbb = 0;
for (ptr = node->syn_arg; ptr < end; ptr++) {
dbb = (DBB) *ptr;
if (create_flag)
isc_create_database(status_vector, 0, dbb->dbb_filename,
&dbb->dbb_handle, dpb.getBufferLength(),
reinterpret_cast<const char*>(dpb.getBuffer()), 0);
else
isc_attach_database(status_vector, 0, dbb->dbb_filename,
&dbb->dbb_handle, dpb.getBufferLength(),
reinterpret_cast<const char*>(dpb.getBuffer()));
if (status_vector[1])
break;
}
// If any attach failed, cleanup and give up
if (ptr < end) {
for (qli_syntax** ptr2 = node->syn_arg; ptr2 < ptr; ptr2++)
detach(gds_status, (DBB) *ptr2);
ERRQ_database_error(dbb, status_vector);
}
// Databases are up and running. Install each.
for (ptr = node->syn_arg; ptr < end; ptr++)
install((DBB) *ptr);
}
void MET_shutdown(void)
{
/**************************************
*
* M E T _ s h u t d o w n
*
**************************************
*
* Functional description
* Shut down all attached databases.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
for (DBB dbb = QLI_databases; dbb; dbb = dbb->dbb_next)
if (detach(status_vector, dbb))
isc_print_status(status_vector);
}
void MET_sql_alter_table( qli_rel* relation, qli_fld* fields)
{
/**************************************
*
* M E T _ s q l _ a l t e r _ t a b l e
*
**************************************
*
* Functional description
* Alter table data based on a SQL metadata request
*
**************************************/
qli_symbol* symbol = relation->rel_symbol;
DBB database = setup_update(relation->rel_database);
relation->rel_database = database;
USHORT count = 0;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_def])
X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string
count++;
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
if (!count)
ERRQ_print_error(414, symbol->sym_string); // Msg237 Relation %s is lost
qli_rlb* rlb = NULL;
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_begin);
STUFF(isc_dyn_mod_rel);
STUFF_STRING(symbol->sym_string);
for (qli_fld* field = fields; field; field = field->fld_next) {
if (field->fld_flags & FLD_add)
add_sql_field(relation, field, 0, rlb);
else if (field->fld_flags & FLD_drop) {
STUFF(isc_dyn_delete_local_fld);
STUFF_STRING(field->fld_name->sym_string);
STUFF(isc_dyn_end);
}
}
STUFF(isc_dyn_end);
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
execute_dynamic_ddl(database, rlb);
MET_meta_commit(database);
setup_update(database);
delete_fields(relation);
MET_fields(relation);
}
void MET_sql_cr_view( qli_syntax* node)
{
/**************************************
*
* M E T _ s q l _ c r _ v i e w
*
**************************************
*
* Functional description
* Create a view from a SQL metadata request
*
**************************************/
qli_rel* relation = (qli_rel*) node->syn_arg[s_crv_name];
DBB database = setup_update(relation->rel_database);
relation->rel_database = database;
qli_symbol* symbol = relation->rel_symbol;
qli_rlb* rlb = NULL;
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_begin);
STUFF(isc_dyn_def_view);
STUFF_STRING(symbol->sym_string);
// The meat of the blr generation will go here
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
execute_dynamic_ddl(database, rlb);
MET_meta_commit(database);
}
void MET_sql_grant( qli_syntax* node)
{
/**************************************
*
* M E T _ s q l _ g r a n t
*
**************************************
*
* Functional description
* Grant access privilege(s) on a SQL table
*
**************************************/
sql_grant_revoke(node, isc_dyn_grant);
}
void MET_sql_revoke( qli_syntax* node)
{
/**************************************
*
* M E T _ s q l _ r e v o k e
*
**************************************
*
* Functional description
* Revoke access privilege(s) on a SQL table
*
**************************************/
sql_grant_revoke(node, isc_dyn_revoke);
}
FB_API_HANDLE MET_transaction( NOD_T node_type, DBB database)
{
/**************************************
*
* M E T _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Finish off a transaction with a commit, prepare, or rollback.
* For commit and rollback, start a new transaction.
*
* Multiple transaction coordination is handled
* by the caller. This is a singleminded routine.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
switch (node_type) {
case nod_commit:
isc_commit_transaction(status_vector, &database->dbb_transaction);
break;
case nod_rollback:
isc_rollback_transaction(status_vector, &database->dbb_transaction);
break;
case nod_prepare:
isc_prepare_transaction(status_vector, &database->dbb_transaction);
break;
case nod_start_trans:
isc_start_transaction(status_vector, &database->dbb_transaction, 1,
&database->dbb_handle, sizeof(tpb), tpb);
database->dbb_flags &= ~DBB_updates & ~DBB_prepared;
break;
}
if (status_vector[1])
ERRQ_database_error(database, status_vector);
return database->dbb_transaction;
}
static void add_field( qli_rel* relation, qli_fld* field, USHORT position)
{
/**************************************
*
* a d d _ f i e l d
*
**************************************
*
* Functional description
* Add a field to a relation. Do all sorts of sanity checks.
*
**************************************/
DBB database = relation->rel_database;
qli_symbol* relation_name = relation->rel_symbol;
qli_symbol* field_name = field->fld_name;
qli_symbol* global_field = field->fld_based;
if (!global_field)
global_field = field_name;
// Check to see if it already exits in relation
FOR(REQUEST_HANDLE database->dbb_requests[REQ_rfr_def])
X IN DB.RDB$RELATION_FIELDS WITH
X.RDB$RELATION_NAME EQ relation_name->sym_string AND
X.RDB$FIELD_NAME EQ field_name->sym_string
rollback_update(relation->rel_database);
ERRQ_print_error(251, SafeArg() << field_name->sym_string <<
relation_name->sym_string);
// Msg251 Field %s already exists in relation %s
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
// Check global field. Define it if it doesn't exist.
bool global_flag = false;
if (!check_global_field(database, field, global_field->sym_string)) {
global_flag = true;
define_global_field(database, field, global_field);
}
/* Finally, store into RFR. If we defined a global field, assume that
the query name and edit string will be inherited from there, otherwise
include them here */
STORE(REQUEST_HANDLE database->dbb_requests[REQ_store_rfr])
X IN DB.RDB$RELATION_FIELDS
strcpy(X.RDB$RELATION_NAME, relation_name->sym_string);
strcpy(X.RDB$FIELD_NAME, field_name->sym_string);
strcpy(X.RDB$FIELD_SOURCE, global_field->sym_string);
if (position) {
X.RDB$FIELD_POSITION.NULL = FALSE;
X.RDB$FIELD_POSITION = position;
}
else
X.RDB$FIELD_POSITION.NULL = TRUE;
const qli_symbol* symbol;
if (!global_flag && (symbol = field->fld_query_name)) {
X.RDB$QUERY_NAME.NULL = FALSE;
strcpy(X.RDB$QUERY_NAME, symbol->sym_string);
}
else
X.RDB$QUERY_NAME.NULL = TRUE;
if (!global_flag && (field->fld_edit_string)) {
X.RDB$EDIT_STRING.NULL = FALSE;
strcpy(X.RDB$EDIT_STRING, field->fld_edit_string);
}
else
X.RDB$EDIT_STRING.NULL = TRUE;
END_STORE
ON_ERROR
rollback_update(relation->rel_database);
ERRQ_database_error(relation->rel_database, gds_status);
END_ERROR;
}
static void add_sql_field( qli_rel* relation, qli_fld* field, USHORT position,
qli_rlb* rlb)
{
/**************************************
*
* a d d _ s q l _ f i e l d
*
**************************************
*
* Functional description
* Add a SQL field to a relation via dyn.
*
**************************************/
rlb = CHECK_RLB(rlb);
DBB database = relation->rel_database;
const qli_symbol* relation_name = relation->rel_symbol;
const qli_symbol* field_name = field->fld_name;
// Check to see if it already exists in relation
FOR(REQUEST_HANDLE database->dbb_requests[REQ_rfr_def])
X IN DB.RDB$RELATION_FIELDS WITH
X.RDB$RELATION_NAME EQ relation_name->sym_string AND
X.RDB$FIELD_NAME EQ field_name->sym_string
rollback_update(relation->rel_database);
ERRQ_print_error(251, SafeArg() << field_name->sym_string <<
relation_name->sym_string);
// Msg251 Field %s already exists in relation %s
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
STUFF(isc_dyn_def_sql_fld);
STUFF_STRING(field->fld_name->sym_string);
STUFF(isc_dyn_fld_type);
STUFF_WORD(2);
STUFF_WORD(blr_dtypes[field->fld_dtype]);
STUFF(isc_dyn_fld_length);
STUFF_WORD(2);
const USHORT l = (field->fld_dtype == dtype_varying)
? field->fld_length - sizeof(SSHORT) : field->fld_length;
STUFF_WORD(l);
STUFF(isc_dyn_fld_scale);
STUFF_WORD(2);
STUFF_WORD(field->fld_scale);
if (DTYPE_IS_EXACT(field->fld_dtype))
{
STUFF(isc_dyn_fld_precision);
STUFF_WORD(2);
STUFF_WORD(field->fld_precision);
STUFF(isc_dyn_fld_sub_type);
STUFF_WORD(2);
STUFF_WORD(field->fld_sub_type);
}
if (position) {
STUFF(isc_dyn_fld_position);
STUFF_WORD(2);
STUFF_WORD(position);
}
rlb = CHECK_RLB(rlb);
if (field->fld_flags & FLD_not_null) {
STUFF(isc_dyn_fld_validation_blr);
STUFF_WORD(8);
STUFF(blr_version4);
STUFF(blr_not);
STUFF(blr_missing);
STUFF(blr_fid);
STUFF(0);
STUFF(0);
STUFF(0);
STUFF(blr_eoc);
}
STUFF(isc_dyn_end);
}
static void blob_copy( qli_rlb* rlb, qli_rel* source, ISC_QUAD& source_blob_id)
{
/**************************************
*
* b l o b _ c o p y
*
**************************************
*
* Functional description
* Copy information from one blob to a request
* language block to stick it into a new definition.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
DBB source_dbb =
(source->rel_database) ? source->rel_database : QLI_databases;
DB = source_dbb->dbb_handle;
UserBlob source_blob(status_vector);
if (!source_blob.open(DB, source_dbb->dbb_meta_trans, source_blob_id))
{
rollback_update(source_dbb);
ERRQ_database_error(source_dbb, status_vector);
}
SLONG size, segment_count, max_segment;
if (!getBlobSize(source_blob, &size, &segment_count, &max_segment))
{
rollback_update(source_dbb);
ERRQ_database_error(source_dbb, status_vector);
}
TEXT fixed_buffer[4096];
TEXT* buffer;
USHORT buffer_length;
if (max_segment < static_cast<SLONG>(sizeof(fixed_buffer))) {
buffer_length = sizeof(fixed_buffer);
buffer = fixed_buffer;
}
else {
buffer_length = max_segment;
buffer = (TEXT *) gds__alloc((SLONG) buffer_length);
}
STUFF_WORD((USHORT) size);
size_t length;
while (source_blob.getSegment(buffer_length, buffer, length) && !source_blob.getCode())
{
while (rlb->rlb_data + length > rlb->rlb_limit)
rlb = GEN_rlb_extend(rlb);
const TEXT* p = buffer;
while (length--)
STUFF(*p++);
}
if (status_vector[1] != isc_segstr_eof) {
rollback_update(source_dbb);
if (buffer != fixed_buffer)
gds__free(buffer);
ERRQ_database_error(source_dbb, status_vector);
}
if (buffer != fixed_buffer)
gds__free(buffer);
if (!source_blob.close()) {
rollback_update(source_dbb);
ERRQ_database_error(source_dbb, status_vector);
}
//return TRUE;
}
static void change_field( qli_rel* relation, qli_fld* field)
{
/**************************************
*
* c h a n g e _ f i e l d
*
**************************************
*
* Functional description
* Change optional attributes of a field.
*
**************************************/
DBB database = relation->rel_database;
const qli_symbol* relation_name = relation->rel_symbol;
const qli_symbol* field_name = field->fld_name;
const qli_symbol* global_field = field->fld_based;
if (field->fld_dtype) {
rollback_update(database);
ERRQ_print_error(252);
// Msg252 datatype can not be changed locally
}
if (global_field
&&
!(check_global_field(database, NULL, global_field->sym_string)))
{
rollback_update(database);
ERRQ_print_error(253, global_field->sym_string);
// Msg253 global field %s does not exist
}
// Modify RFR
USHORT count = 0;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_mdf_rfr])
X IN DB.RDB$RELATION_FIELDS WITH
X.RDB$RELATION_NAME = relation_name->sym_string AND
X.RDB$FIELD_NAME = field_name->sym_string
count++;
MODIFY X USING
if (global_field)
strcpy(X.RDB$FIELD_SOURCE, global_field->sym_string);
const qli_symbol* symbol = field->fld_query_name;
if (symbol)
strcpy(X.RDB$QUERY_NAME, symbol->sym_string);
if (field->fld_edit_string)
strcpy(X.RDB$EDIT_STRING, field->fld_edit_string);
END_MODIFY
ON_ERROR
rollback_update(relation->rel_database);
ERRQ_database_error(relation->rel_database, gds_status);
END_ERROR;
END_FOR
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
if (!count) {
rollback_update(database);
ERRQ_print_error(254, SafeArg() << field_name->sym_string << relation_name->sym_string);
// Msg254 field %s not found in relation %s
}
}
static bool check_global_field(DBB database,
qli_fld* field,
const TEXT* name)
{
/**************************************
*
* c h e c k _ g l o b a l _ f i e l d
*
**************************************
*
* Functional description
* Given a name, check for the existence of a global field
* by that name. If a qli_fld block is supplied,
* check its characteristics against the global.
* If it is not fully defined, flesh it out from the global.
*
**************************************/
bool previously_defined = false;
FOR(REQUEST_HANDLE database->dbb_requests[REQ_field_def])
X IN DB.RDB$FIELDS WITH X.RDB$FIELD_NAME EQ name
if (field)
{
if (field->fld_dtype &&
(X.RDB$FIELD_TYPE != blr_dtypes[field->fld_dtype] ||
X.RDB$FIELD_LENGTH != static_cast<SLONG>(
((field->fld_dtype == dtype_varying) ? field->fld_length -
sizeof(SSHORT) : field->fld_length)) ||
X.RDB$FIELD_SCALE != field->fld_scale))
{
rollback_update(database);
ERRQ_print_error(255, name);
// Msg255 Datatype conflict with existing global field %s
}
field->fld_dtype = MET_get_datatype(X.RDB$FIELD_TYPE);
field->fld_length =
field_length(field->fld_dtype, X.RDB$FIELD_LENGTH);
if (field->fld_dtype == dtype_varying) {
field->fld_length += sizeof(SSHORT);
}
field->fld_scale = X.RDB$FIELD_SCALE;
field->fld_sub_type = X.RDB$FIELD_SUB_TYPE;
field->fld_sub_type_missing = X.RDB$FIELD_SUB_TYPE.NULL;
if (!field->fld_edit_string)
{
field->fld_edit_string =
make_string(X.RDB$EDIT_STRING, sizeof(X.RDB$EDIT_STRING));
}
if (!field->fld_query_name)
{
field->fld_query_name =
make_symbol(X.RDB$QUERY_NAME, sizeof(X.RDB$QUERY_NAME));
}
if (!field->fld_query_header)
{
ISC_QUAD& blob = X.RDB$QUERY_HEADER;
if (!UserBlob::blobIsNull(blob))
{
field->fld_query_header =
get_query_header(database, blob);
}
}
}
previously_defined = true;
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
return previously_defined;
}
static bool check_relation( qli_rel* relation)
{
/**************************************
*
* c h e c k _ r e l a t i o n
*
**************************************
*
* Functional description
* Check the existence of the named relation.
*
**************************************/
bool previously_defined = false;
FB_API_HANDLE spare = DB;
DB = relation->rel_database->dbb_handle;
FOR(REQUEST_HANDLE relation->rel_database->dbb_requests[REQ_relation_def]
TRANSACTION_HANDLE relation->rel_database->dbb_meta_trans)
X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string
previously_defined = true;
END_FOR
ON_ERROR
ERRQ_database_error(relation->rel_database, gds_status);
END_ERROR;
DB = spare;
return previously_defined;
}
static void clone_fields( qli_rel* target, qli_rel* source)
{
/**************************************
*
* c l o n e _ f i e l d s
*
**************************************
*
* Functional description
* Copy the RFR records for one relation
* into another. The target database is already
* setup for updates. If there is a different source
* database, get it ready too. Then make sure that
* the source relation actually exists.
*
* If it's two different databases, create the global
* fields that are missing in the target (except those
* that support computed fields), then create the local
* fields and during that pass create the computed
* fields and the SQL fields.
*
**************************************/
FB_API_HANDLE req1, req2;
FB_API_HANDLE t_trans, s_trans;
req1 = req2 = 0;
s_trans = t_trans = gds_trans;
DB = DB1 = target->rel_database->dbb_handle;
if (target->rel_database != source->rel_database) {
MET_meta_transaction(source->rel_database, false);
s_trans = gds_trans;
}
if (target->rel_database != source->rel_database)
clone_global_fields(target, source);
qli_rlb* rlb = NULL;
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_begin);
FOR(REQUEST_HANDLE req1
TRANSACTION_HANDLE s_trans)
Y IN DB.RDB$RELATION_FIELDS
CROSS F IN DB.RDB$FIELDS
WITH Y.RDB$RELATION_NAME = source->rel_symbol->sym_string
AND Y.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME
// If SQL made this field, create a new global field for it
if (F.RDB$COMPUTED_BLR.NULL &&
!fb_utils::implicit_domain(Y.RDB$FIELD_NAME) &&
fb_utils::implicit_domain(F.RDB$FIELD_NAME))
{
STUFF(isc_dyn_def_sql_fld);
STUFF_STRING(Y.RDB$FIELD_NAME);
STUFF(isc_dyn_fld_type);
STUFF_WORD(2);
STUFF_WORD(F.RDB$FIELD_TYPE);
STUFF(isc_dyn_fld_length);
STUFF_WORD(2);
STUFF_WORD(F.RDB$FIELD_LENGTH);
STUFF(isc_dyn_fld_scale);
STUFF_WORD(2);
STUFF_WORD(F.RDB$FIELD_SCALE);
if (!F.RDB$VALIDATION_BLR.NULL) {
STUFF(isc_dyn_fld_validation_blr);
blob_copy(rlb, source, F.RDB$VALIDATION_BLR);
}
}
else
{
STUFF(isc_dyn_def_local_fld);
STUFF_STRING(Y.RDB$FIELD_NAME);
STUFF(isc_dyn_fld_source);
STUFF_STRING(Y.RDB$FIELD_SOURCE);
}
STUFF(isc_dyn_rel_name);
STUFF_STRING(target->rel_symbol->sym_string);
if (!Y.RDB$FIELD_POSITION.NULL) {
STUFF(isc_dyn_fld_position);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$FIELD_POSITION);
}
if (!Y.RDB$QUERY_NAME.NULL) {
STUFF(isc_dyn_fld_query_name);
STUFF_STRING(Y.RDB$QUERY_NAME);
}
if (!Y.RDB$EDIT_STRING.NULL) {
STUFF(isc_dyn_fld_edit_string);
STUFF_STRING(Y.RDB$EDIT_STRING);
}
if (!Y.RDB$UPDATE_FLAG.NULL) {
STUFF(isc_dyn_update_flag);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$UPDATE_FLAG);
}
if (!Y.RDB$QUERY_HEADER.NULL) {
STUFF(isc_dyn_fld_query_header);
blob_copy(rlb, source, Y.RDB$QUERY_HEADER);
}
if (!Y.RDB$DESCRIPTION.NULL) {
STUFF(isc_dyn_description);
blob_copy(rlb, source, Y.RDB$DESCRIPTION);
}
if (!Y.RDB$DEFAULT_VALUE.NULL) {
STUFF(isc_dyn_fld_default_value);
blob_copy(rlb, source, Y.RDB$DEFAULT_VALUE);
}
if ((source->rel_database->dbb_capabilities & DBB_cap_rfr_sys_flag) &&
(target->rel_database->dbb_capabilities & DBB_cap_rfr_sys_flag))
FOR(REQUEST_HANDLE req2
TRANSACTION_HANDLE s_trans)
S_RFR IN DB.RDB$RELATION_FIELDS WITH
S_RFR.RDB$FIELD_NAME = Y.RDB$FIELD_NAME AND
S_RFR.RDB$RELATION_NAME = Y.RDB$RELATION_NAME
if (!S_RFR.RDB$SYSTEM_FLAG.NULL) {
STUFF(isc_dyn_system_flag);
STUFF_WORD(2);
STUFF_WORD(S_RFR.RDB$SYSTEM_FLAG);
}
END_FOR
ON_ERROR
rollback_update(target->rel_database);
ERRQ_database_error(source->rel_database, gds_status);
END_ERROR;
if (!F.RDB$COMPUTED_BLR.NULL)
{
STUFF(isc_dyn_fld_computed_blr);
blob_copy(rlb, source, F.RDB$COMPUTED_BLR);
STUFF(isc_dyn_fld_computed_source);
blob_copy(rlb, source, F.RDB$COMPUTED_SOURCE);
STUFF(isc_dyn_fld_type);
STUFF_WORD(2);
STUFF_WORD(F.RDB$FIELD_TYPE);
STUFF(isc_dyn_fld_length);
STUFF_WORD(2);
STUFF_WORD(F.RDB$FIELD_LENGTH);
if (!F.RDB$FIELD_SCALE.NULL) {
STUFF(isc_dyn_fld_scale);
STUFF_WORD(2);
STUFF_WORD(F.RDB$FIELD_SCALE);
}
if (!F.RDB$FIELD_SUB_TYPE.NULL) {
STUFF(isc_dyn_fld_sub_type);
STUFF_WORD(2);
STUFF_WORD(F.RDB$FIELD_SUB_TYPE);
}
}
STUFF(isc_dyn_end);
END_FOR
ON_ERROR
rollback_update(source->rel_database);
ERRQ_database_error(source->rel_database, gds_status);
END_ERROR;
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
execute_dynamic_ddl(target->rel_database, rlb);
if (req1)
if (isc_release_request(gds_status, &req1))
ERRQ_database_error(source->rel_database, gds_status);
if (req2)
if (isc_release_request(gds_status, &req2))
ERRQ_database_error(source->rel_database, gds_status);
//return TRUE;
}
static void clone_global_fields( qli_rel* target, qli_rel* source)
{
/**************************************
*
* c l o n e _ g l o b a l _ f i e l d s
*
**************************************
*
* Functional description
* Copy the global field definitions required by a relation
* from one database into another. Both databases are
* setup correctly. (Set up the handles again just because
* we're compulsive.)
*
* In this routine, as in clone fields, be careful to probe
* for our extensions rather than just using them so we
* can copy relations from V2 and from Rdb.
*
* Don't clone fields that already exist and have the
* correct datatype and length.
*
* Don't clone computed fields since they will be cloned
* later with the local field definitions. Don't clone
* SQL defined fields for the same reason.
*
**************************************/
FB_API_HANDLE req1, req2, req3;
req1 = req2 = req3 = 0;
FB_API_HANDLE s_trans = source->rel_database->dbb_meta_trans;
FB_API_HANDLE t_trans = target->rel_database->dbb_meta_trans;
DB = source->rel_database->dbb_handle;
DB1 = target->rel_database->dbb_handle;
qli_rlb* rlb = NULL;
bool first_field = true;
FOR(REQUEST_HANDLE req1
TRANSACTION_HANDLE s_trans)
RFR IN DB.RDB$RELATION_FIELDS CROSS
Y IN DB.RDB$FIELDS
WITH RFR.RDB$RELATION_NAME = source->rel_symbol->sym_string AND
Y.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE REDUCED TO Y.RDB$FIELD_NAME
if (!Y.RDB$COMPUTED_BLR.NULL ||
(!fb_utils::implicit_domain(RFR.RDB$FIELD_NAME) &&
fb_utils::implicit_domain(Y.RDB$FIELD_NAME)))
{
continue;
}
bool predefined = false;
FOR(REQUEST_HANDLE req2 TRANSACTION_HANDLE t_trans)
A IN DB1.RDB$FIELDS WITH A.RDB$FIELD_NAME = Y.RDB$FIELD_NAME
if ((A.RDB$FIELD_TYPE != Y.RDB$FIELD_TYPE) ||
(A.RDB$FIELD_LENGTH != Y.RDB$FIELD_LENGTH) ||
(A.RDB$FIELD_SCALE.NULL != Y.RDB$FIELD_SCALE.NULL) ||
((!Y.RDB$FIELD_SCALE.NULL) &&
(A.RDB$FIELD_SCALE != Y.RDB$FIELD_SCALE)) ||
(A.RDB$FIELD_SUB_TYPE.NULL != Y.RDB$FIELD_SUB_TYPE.NULL)
|| ((!Y.RDB$FIELD_SUB_TYPE.NULL)
&& (A.RDB$FIELD_SUB_TYPE != Y.RDB$FIELD_SUB_TYPE)))
{
TEXT* name = (TEXT*) ALLQ_malloc((SLONG) sizeof(Y.RDB$FIELD_NAME));
strcpy(name, Y.RDB$FIELD_NAME);
rollback_update(target->rel_database);
// CVC: When is this memory deallocated?
ERRQ_error(413, name);
// conflicting previous definition
}
else
predefined = true;
END_FOR
ON_ERROR
ERRQ_database_error(target->rel_database, gds_status);
END_ERROR;
if (predefined)
continue;
if (first_field) {
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_begin);
first_field = false;
}
STUFF(isc_dyn_def_global_fld);
STUFF_STRING(Y.RDB$FIELD_NAME);
STUFF(isc_dyn_fld_type);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$FIELD_TYPE);
STUFF(isc_dyn_fld_length);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$FIELD_LENGTH);
if (!Y.RDB$FIELD_SCALE.NULL) {
STUFF(isc_dyn_fld_scale);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$FIELD_SCALE);
}
if (!Y.RDB$FIELD_SUB_TYPE.NULL) {
STUFF(isc_dyn_fld_sub_type);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$FIELD_SUB_TYPE);
}
if (!Y.RDB$SEGMENT_LENGTH.NULL) {
STUFF(isc_dyn_fld_segment_length);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$SEGMENT_LENGTH);
}
if (!Y.RDB$SYSTEM_FLAG.NULL) {
STUFF(isc_dyn_system_flag);
STUFF_WORD(2);
STUFF_WORD(Y.RDB$SYSTEM_FLAG);
}
if (!Y.RDB$QUERY_NAME.NULL) {
STUFF(isc_dyn_fld_query_name);
STUFF_STRING(Y.RDB$QUERY_NAME);
}
if (!Y.RDB$EDIT_STRING.NULL) {
STUFF(isc_dyn_fld_edit_string);
STUFF_STRING(Y.RDB$EDIT_STRING);
}
if (!Y.RDB$MISSING_VALUE.NULL) {
STUFF(isc_dyn_fld_missing_value);
blob_copy(rlb, source, Y.RDB$MISSING_VALUE);
}
if (!Y.RDB$DEFAULT_VALUE.NULL) {
STUFF(isc_dyn_fld_default_value);
blob_copy(rlb, source, Y.RDB$DEFAULT_VALUE);
}
if (!Y.RDB$QUERY_HEADER.NULL) {
STUFF(isc_dyn_fld_query_header);
blob_copy(rlb, source, Y.RDB$QUERY_HEADER);
}
if (!Y.RDB$DESCRIPTION.NULL) {
STUFF(isc_dyn_description);
blob_copy(rlb, source, Y.RDB$DESCRIPTION);
}
if (!Y.RDB$VALIDATION_BLR.NULL) {
STUFF(isc_dyn_fld_validation_blr);
blob_copy(rlb, source, Y.RDB$VALIDATION_BLR);
}
if (!Y.RDB$VALIDATION_SOURCE.NULL) {
STUFF(isc_dyn_fld_validation_source);
blob_copy(rlb, source, Y.RDB$VALIDATION_SOURCE);
}
if ((target->rel_database->dbb_capabilities & DBB_cap_dimensions) &&
(source->rel_database->dbb_capabilities & DBB_cap_dimensions))
{
bool first_dimension = true;
FOR(REQUEST_HANDLE req3 TRANSACTION_HANDLE s_trans)
F IN DB.RDB$FIELDS CROSS
D IN DB.RDB$FIELD_DIMENSIONS
WITH F.RDB$FIELD_NAME = D.RDB$FIELD_NAME AND
F.RDB$FIELD_NAME = Y.RDB$FIELD_NAME
if (first_dimension) {
first_dimension = false;
STUFF(isc_dyn_fld_dimensions);
STUFF_WORD(2);
STUFF_WORD(F.RDB$DIMENSIONS);
}
STUFF(isc_dyn_def_dimension);
STUFF_WORD(2);
STUFF_WORD(D.RDB$DIMENSION);
if (!D.RDB$LOWER_BOUND.NULL) {
STUFF(isc_dyn_dim_lower);
STUFF_WORD(2);
STUFF_WORD(D.RDB$LOWER_BOUND);
}
if (!D.RDB$UPPER_BOUND.NULL) {
STUFF(isc_dyn_dim_upper);
STUFF_WORD(2);
STUFF_WORD(D.RDB$UPPER_BOUND);
}
STUFF(isc_dyn_end);
END_FOR
ON_ERROR
if (target->rel_database->dbb_meta_trans)
rollback_update(target->rel_database);
rollback_update(source->rel_database);
ERRQ_database_error(source->rel_database, gds_status);
END_ERROR;
}
STUFF(isc_dyn_end);
END_FOR
ON_ERROR
rollback_update(source->rel_database);
if (target->rel_database->dbb_meta_trans)
rollback_update(target->rel_database);
ERRQ_database_error(source->rel_database, gds_status);
END_ERROR;
if (rlb) {
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
execute_dynamic_ddl(target->rel_database, rlb);
}
if (req1)
if (isc_release_request(gds_status, &req1))
ERRQ_database_error(source->rel_database, gds_status);
if (req2)
if (isc_release_request(gds_status, &req2))
ERRQ_database_error(target->rel_database, gds_status);
if (req3)
if (isc_release_request(gds_status, &req3))
ERRQ_database_error(source->rel_database, gds_status);
//return TRUE;
}
static void define_global_field( DBB database, qli_fld* field, qli_symbol* name)
{
/**************************************
*
* d e f i n e _ g l o b a l _ f i e l d
*
**************************************
*
* Functional description
*
* Define a global field if we've got enough
* information.
*
**************************************/
if (!field->fld_dtype) {
rollback_update(database);
ERRQ_print_error(256, name->sym_string); // Msg256 No datatype specified for field %s
}
STORE(REQUEST_HANDLE database->dbb_requests[REQ_store_field])
X IN DB.RDB$FIELDS
strcpy(X.RDB$FIELD_NAME, name->sym_string);
X.RDB$FIELD_TYPE = blr_dtypes[field->fld_dtype];
X.RDB$FIELD_SCALE = field->fld_scale;
X.RDB$FIELD_LENGTH = (field->fld_dtype == dtype_varying) ?
field->fld_length - sizeof(SSHORT) : field->fld_length;
if (!field->fld_sub_type_missing) {
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
X.RDB$FIELD_SUB_TYPE = field->fld_sub_type;
}
else
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
const qli_symbol* symbol = field->fld_query_name;
if (symbol) {
X.RDB$QUERY_NAME.NULL = FALSE;
strcpy(X.RDB$QUERY_NAME, symbol->sym_string);
}
else
X.RDB$QUERY_NAME.NULL = TRUE;
if (field->fld_edit_string) {
X.RDB$EDIT_STRING.NULL = FALSE;
strcpy(X.RDB$EDIT_STRING, field->fld_edit_string);
}
else
X.RDB$EDIT_STRING.NULL = TRUE;
END_STORE
ON_ERROR
rollback_update(database);
ERRQ_database_error(database, gds_status);
END_ERROR;
}
static void delete_fields( qli_rel* relation)
{
/**************************************
*
* d e l e t e _ f i e l d s
*
**************************************
*
* Functional description
* Delete field definitions.
*
**************************************/
while (qli_fld* field = relation->rel_fields) {
relation->rel_fields = field->fld_next;
ALLQ_release((FRB) field->fld_name);
if (field->fld_query_name)
ALLQ_release((FRB) field->fld_query_name);
ALLQ_release((FRB) field);
}
relation->rel_flags &= ~REL_fields;
}
static ISC_STATUS detach(ISC_STATUS * status_vector,
DBB dbb)
{
/**************************************
*
* d e t a c h
*
**************************************
*
* Functional description
* Shut down a database. Return the status of the
* first failing call, if any.
*
**************************************/
if (!dbb->dbb_handle)
return FB_SUCCESS;
ISC_STATUS_ARRAY alt_vector;
ISC_STATUS* status = status_vector;
if (dbb->dbb_transaction)
if (isc_commit_transaction(status, &dbb->dbb_transaction))
status = alt_vector;
if (dbb->dbb_proc_trans && (dbb->dbb_capabilities & DBB_cap_multi_trans))
if (isc_commit_transaction(status, &dbb->dbb_proc_trans))
status = alt_vector;
if (dbb->dbb_meta_trans && (dbb->dbb_capabilities & DBB_cap_multi_trans))
if (isc_commit_transaction(status, &dbb->dbb_meta_trans))
status = alt_vector;
isc_detach_database(status, &dbb->dbb_handle);
return status_vector[1];
}
static void execute_dynamic_ddl( DBB database, qli_rlb* rlb)
{
/**************************************
*
* e x e c u t e _ d y n a m i c _ d d l
*
**************************************
*
* Functional description
* Execute a ddl command, for better or for worse.
*
**************************************/
const USHORT length = (UCHAR *) rlb->rlb_data - (UCHAR *) rlb->rlb_base;
if (QLI_blr)
PRETTY_print_dyn(rlb->rlb_base, NULL, NULL, 0);
if (isc_ddl(gds_status, &database->dbb_handle, &database->dbb_meta_trans,
length, (const char*) rlb->rlb_base))
{
rollback_update(database);
ERRQ_database_error(database, gds_status);
}
GEN_rlb_release (rlb);
}
static int field_length( USHORT dtype, USHORT length)
{
/**************************************
*
* f i e l d _ l e n g t h
*
**************************************
*
* Functional description
* Return implementation specific length for field.
*
**************************************/
switch (dtype) {
case dtype_short:
return sizeof(SSHORT);
case dtype_long:
return sizeof(SLONG);
case dtype_real:
return sizeof(float);
case dtype_double:
return sizeof(double);
case dtype_int64:
return sizeof(SINT64);
case dtype_sql_time:
case dtype_sql_date:
return sizeof(SLONG);
case dtype_timestamp:
case dtype_blob:
case dtype_quad:
return sizeof(ISC_QUAD);
}
return length;
}
static void get_database_type( DBB new_dbb)
{
/**************************************
*
* g e t _ d a t a b a s e _ t y p e
*
**************************************
*
* Functional description
* Ask what type of database this is.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
UCHAR buffer[1024];
isc_database_info(status_vector, &new_dbb->dbb_handle, sizeof(db_info),
reinterpret_cast<const char*>(db_info),
sizeof(buffer), (char*) buffer);
if (status_vector[1])
ERRQ_database_error(new_dbb, gds_status);
UCHAR* p = buffer;
while (*p != isc_info_end && p < buffer + sizeof(buffer)) {
UCHAR item = *p++;
USHORT l = gds__vax_integer(p, 2);
p += 2;
switch (item) {
case isc_info_implementation:
{
const UCHAR* q = p;
int count = *q++;
while (count--) {
new_dbb->dbb_type = *q++;
if (*q++ == isc_info_db_class_access)
break;
}
break;
}
default:
ERRQ_error(257);
// Msg257 database info call failed
}
p += l;
}
}
static TEXT *get_query_header( DBB database, ISC_QUAD& blob_id)
{
/**************************************
*
* g e t _ q u e r y _ h e a d e r
*
**************************************
*
* Functional description
* Pick up a query header for a field.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
TEXT header[1024], buffer[1024];
UserBlob blob(status_vector);
if (!blob.open(database->dbb_handle, gds_trans, blob_id))
{
ERRQ_database_error(database, status_vector);
}
TEXT* p = header;
// CVC: No bounds check here: it's assumed that 1024 is enough, but "p"
// might overflow "header" eventually.
for (;;) {
size_t length;
if (!blob.getSegment(sizeof(buffer), buffer, length))
break;
if (length && buffer[length - 1] == '\n')
--length;
buffer[length] = 0;
const TEXT* q = buffer;
if (*q == '"')
do {
*p++ = *q++;
} while (*q);
else {
*p++ = '"';
while (*q)
*p++ = *q++;
*p++ = '"';
}
}
if (!blob.close())
ERRQ_database_error(database, gds_status);
*p = 0;
if (!strcmp(header, "\" \"")) {
header[0] = '-';
header[1] = 0;
p = header + 1;
}
const USHORT len = p - header;
if (len)
return make_string(header, len);
return NULL;
}
static void install( dbb* old_dbb)
{
/**************************************
*
* i n s t a l l
*
**************************************
*
* Functional description
* Install a database. Scan relations, etc., make up relation
* blocks, etc, and copy the database block to someplace more
* permanent.
*
**************************************/
// Copy the database block to one out of the permanent pool
SSHORT l = old_dbb->dbb_filename_length;
DBB new_dbb = (DBB) ALLOCPV(type_dbb, l);
new_dbb->dbb_filename_length = l;
memcpy(new_dbb->dbb_filename, old_dbb->dbb_filename, l);
DB = new_dbb->dbb_handle = old_dbb->dbb_handle;
new_dbb->dbb_capabilities = old_dbb->dbb_capabilities;
new_dbb->dbb_next = QLI_databases;
QLI_databases = new_dbb;
// If a name was given, make up a symbol for the database.
nam* name = (nam*) old_dbb->dbb_symbol;
if (name) {
qli_symbol* asymbol = make_symbol(name->nam_string, name->nam_length);
new_dbb->dbb_symbol = asymbol;
asymbol->sym_type = SYM_database;
asymbol->sym_object = (BLK) new_dbb;
HSH_insert(asymbol, true);
}
gds_trans = MET_transaction(nod_start_trans, new_dbb);
/* Find out whose database this is so later we don't ask Rdb/VMS
or its pals to do any unnatural acts. While we're at it get
general capabilities */
get_database_type(new_dbb);
set_capabilities(new_dbb);
/* Get all relations in database. For each relation make up a
relation block, make up a symbol block, and insert the symbol
into the hash table. */
FB_API_HANDLE request = 0;
FOR(REQUEST_HANDLE request) X IN DB.RDB$RELATIONS
SORTED BY DESCENDING X.RDB$RELATION_NAME
qli_rel* relation = (qli_rel*) ALLOCP(type_rel);
relation->rel_next = new_dbb->dbb_relations;
new_dbb->dbb_relations = relation;
relation->rel_database = new_dbb;
relation->rel_id = X.RDB$RELATION_ID;
qli_symbol* rsymbol = make_symbol(X.RDB$RELATION_NAME, sizeof(X.RDB$RELATION_NAME));
rsymbol->sym_type = SYM_relation;
rsymbol->sym_object = (BLK) relation;
relation->rel_symbol = rsymbol;
HSH_insert(rsymbol);
if (strcmp(rsymbol->sym_string, "QLI$PROCEDURES") == 0)
new_dbb->dbb_flags |= DBB_procedures;
if (relation->rel_system_flag = X.RDB$SYSTEM_FLAG)
relation->rel_flags |= REL_system;
if (!X.RDB$VIEW_BLR.NULL)
relation->rel_flags |= REL_view;
END_FOR
ON_ERROR
ERRQ_database_error(new_dbb, gds_status);
END_ERROR;
if (request)
if (isc_release_request(gds_status, &request))
ERRQ_database_error(new_dbb, gds_status);
// Pick up functions, if appropriate
if (new_dbb->dbb_capabilities & DBB_cap_functions) {
FB_API_HANDLE request2 = 0;
FOR(REQUEST_HANDLE request) X IN DB.RDB$FUNCTIONS
qli_symbol* fsymbol = make_symbol(X.RDB$FUNCTION_NAME, sizeof(X.RDB$FUNCTION_NAME));
if (!fsymbol)
continue;
qli_fun* function = (qli_fun*) ALLOCPV(type_fun, 0);
function->fun_next = new_dbb->dbb_functions;
new_dbb->dbb_functions = function;
function->fun_symbol = fsymbol;
function->fun_database = new_dbb;
FOR(REQUEST_HANDLE request2) Y IN DB.RDB$FUNCTION_ARGUMENTS WITH
Y.RDB$FUNCTION_NAME EQ X.RDB$FUNCTION_NAME AND
Y.RDB$ARGUMENT_POSITION EQ X.RDB$RETURN_ARGUMENT
function->fun_return.dsc_dtype = MET_get_datatype(Y.RDB$FIELD_TYPE);
function->fun_return.dsc_length =
field_length(function->fun_return.dsc_dtype,
Y.RDB$FIELD_LENGTH);
function->fun_return.dsc_scale = Y.RDB$FIELD_SCALE;
END_FOR
ON_ERROR
ERRQ_database_error(new_dbb, gds_status);
END_ERROR;
if (!function->fun_return.dsc_dtype) {
function->fun_return.dsc_dtype = dtype_text;
function->fun_return.dsc_length = 20;
}
fsymbol->sym_type = SYM_function;
fsymbol->sym_object = (BLK) function;
HSH_insert(fsymbol);
// Insert another symbol if there is a function query name
qli_symbol* symbol2 = make_symbol(X.RDB$QUERY_NAME, sizeof(X.RDB$QUERY_NAME));
if (symbol2) {
function->fun_query_name = symbol2;
symbol2->sym_type = SYM_function;
symbol2->sym_object = (BLK) function;
HSH_insert(symbol2, true);
}
END_FOR
ON_ERROR
ERRQ_database_error(new_dbb, gds_status);
END_ERROR;
if (request)
if (isc_release_request(gds_status, &request))
ERRQ_database_error(new_dbb, gds_status);
if (request2)
if (isc_release_request(gds_status, &request2))
ERRQ_database_error(new_dbb, gds_status);
}
}
static qli_syntax* make_node( NOD_T type, USHORT count)
{
/**************************************
*
* m a k e _ n o d e
*
**************************************
*
* Functional description
* Generate a syntax node.
*
**************************************/
qli_syntax* node = (qli_syntax*) ALLOCPV(type_syn, count);
node->syn_count = count;
node->syn_type = type;
return node;
}
static TEXT *make_string( TEXT* string, SSHORT length)
{
/**************************************
*
* m a k e _ s t r i n g
*
**************************************
*
* Functional description
* Make up a permanent string from a large string. Chop off
* trailing blanks. If there's nothing left, return NULL.
*
**************************************/
string[length] = 0;
if (!(length = truncate_string(string)))
return NULL;
qli_str* str = (qli_str*) ALLOCPV(type_str, length + 1);
TEXT* p = str->str_data;
do {
*p++ = *string++;
} while (--length);
*p = 0;
return str->str_data;
}
static qli_symbol* make_symbol( TEXT* string, SSHORT length)
{
/**************************************
*
* m a k e _ s y m b o l
*
**************************************
*
* Functional description
* Make up a symbol from a string. If the string is all blanks,
* don't make up a symbol. Phooey.
*
**************************************/
if (!(length = truncate_string(string)))
return NULL;
qli_symbol* symbol = (qli_symbol*) ALLOCPV(type_sym, length);
symbol->sym_type = SYM_relation;
symbol->sym_length = length;
TEXT* p = symbol->sym_name;
symbol->sym_string = p;
do {
*p++ = *string++;
} while (--length);
return symbol;
}
static qli_const* missing_value( ISC_QUAD& blob_id, qli_symbol* symbol)
{
/**************************************
*
* m i s s i n g _ v a l u e
*
**************************************
*
* Functional description
* Get a missing value into a constant
* block for later reference.
*
**************************************/
qli_syntax* element = parse_blr_blob(blob_id, symbol);
if (element)
return ((qli_const*) element->syn_arg[0]);
else
return NULL;
}
static qli_syntax* parse_blr( UCHAR ** ptr, qli_symbol* symbol)
{
/**************************************
*
* p a r s e _ b l r
*
**************************************
*
* Functional description
* Parse a BLR expression.
*
**************************************/
qli_syntax* *arg;
qli_const* constant;
NOD_T operatr;
NAM name;
UCHAR *p;
SSHORT dtype, scale, length, l, op;
UCHAR* blr = *ptr;
SSHORT args = 2;
qli_syntax* node = NULL;
switch (op = BLR_BYTE) {
case blr_any:
case blr_unique:
case blr_eoc:
return NULL;
case blr_eql:
operatr = nod_eql;
break;
case blr_neq:
operatr = nod_neq;
break;
case blr_gtr:
operatr = nod_gtr;
break;
case blr_geq:
operatr = nod_geq;
break;
case blr_lss:
operatr = nod_lss;
break;
case blr_leq:
operatr = nod_leq;
break;
case blr_containing:
operatr = nod_containing;
break;
case blr_starting:
operatr = nod_starts;
break;
case blr_matching:
operatr = nod_matches;
break;
case blr_matching2:
operatr = nod_sleuth;
break;
case blr_like:
operatr = nod_like;
break;
case blr_and:
operatr = nod_and;
break;
case blr_or:
operatr = nod_or;
break;
case blr_add:
operatr = nod_add;
break;
case blr_subtract:
operatr = nod_subtract;
break;
case blr_multiply:
operatr = nod_multiply;
break;
case blr_divide:
operatr = nod_divide;
break;
case blr_upcase:
operatr = nod_upcase;
break;
case blr_lowcase:
operatr = nod_lowcase;
break;
case blr_null:
operatr = nod_null;
break;
case blr_between:
operatr = nod_between;
args = 3;
break;
case blr_not:
operatr = nod_not;
args = 1;
break;
case blr_negate:
operatr = nod_negate;
args = 1;
break;
case blr_missing:
operatr = nod_missing;
args = 1;
break;
case blr_fid:
BLR_BYTE;
blr += 2;
node = make_node(nod_field, 1);
name = (NAM) ALLOCPV(type_nam, symbol->sym_length);
node->syn_arg[0] = (qli_syntax*) name;
name->nam_length = symbol->sym_length;
strcpy(name->nam_string, symbol->sym_string);
break;
case blr_field:
BLR_BYTE;
length = BLR_BYTE;
node = make_node(nod_field, 1);
name = (NAM) ALLOCPV(type_nam, length);
node->syn_arg[0] = (qli_syntax*) name;
name->nam_length = length;
p = (UCHAR *) name->nam_string;
do {
*p++ = BLR_BYTE;
} while (--length);
break;
case blr_function:
length = BLR_BYTE;
node = make_node(nod_function, s_fun_count);
node->syn_arg[s_fun_function] = (qli_syntax*) HSH_lookup((SCHAR*) blr, length);
blr += length;
args = BLR_BYTE;
node->syn_arg[s_fun_args] = make_node(nod_list, args);
arg = node->syn_arg[s_fun_args]->syn_arg;
while (--args >= 0) {
if (!(*arg++ = parse_blr(&blr, symbol)))
return NULL;
}
break;
case blr_literal:
scale = 0;
switch (BLR_BYTE) {
case blr_text:
dtype = dtype_text;
length = l = gds__vax_integer(blr, 2);
blr += 2;
break;
case blr_text2:
dtype = dtype_text;
scale = gds__vax_integer(blr, 2);
blr += 2;
length = l = gds__vax_integer(blr, 2);
blr += 2;
break;
case blr_varying:
dtype = dtype_varying;
length = l = gds__vax_integer(blr, 2) + sizeof(USHORT);
blr += 2;
break;
case blr_varying2:
dtype = dtype_varying;
scale = gds__vax_integer(blr, 2);
blr += 2;
length = l = gds__vax_integer(blr, 2) + sizeof(USHORT);
blr += 2;
break;
case blr_short:
dtype = dtype_short;
l = 2;
length = sizeof(SSHORT);
scale = (int) BLR_BYTE;
break;
case blr_long:
dtype = dtype_long;
length = sizeof(SLONG);
l = 4;
scale = (int) BLR_BYTE;
break;
case blr_quad:
dtype = dtype_quad;
l = 8;
length = sizeof(ISC_QUAD);
scale = (int) BLR_BYTE;
break;
case blr_int64:
dtype = dtype_int64;
l = 8;
length = sizeof(SINT64);
scale = (int) BLR_BYTE;
break;
case blr_timestamp:
dtype = dtype_timestamp;
l = 8;
length = sizeof(ISC_QUAD);
break;
case blr_sql_date:
dtype = dtype_sql_date;
l = 4;
length = sizeof(SLONG);
break;
case blr_sql_time:
dtype = dtype_sql_time;
l = 4;
length = sizeof(ULONG);
break;
}
constant = (qli_const*) ALLOCPV(type_con, length);
constant->con_desc.dsc_dtype = dtype;
constant->con_desc.dsc_scale = scale;
constant->con_desc.dsc_length = length;
constant->con_desc.dsc_address = p = constant->con_data;
if (dtype == dtype_text)
while (l--)
*p++ = BLR_BYTE;
else if (dtype == dtype_short) {
*(SSHORT *) p = gds__vax_integer(blr, l);
blr += l;
}
else if (dtype == dtype_long) {
*(SLONG *) p = gds__vax_integer(blr, l);
blr += l;
}
node = make_node(nod_constant, 1);
node->syn_count = 0;
node->syn_arg[0] = (qli_syntax*) constant;
break;
default:
ERRQ_print_error(258, SafeArg() << op); // Msg258 don't understand BLR operatr %d
}
if (!node) {
node = make_node(operatr, args);
arg = node->syn_arg;
while (--args >= 0)
if (!(*arg++ = parse_blr(&blr, symbol)))
return NULL;
}
*ptr = blr;
return node;
}
static qli_syntax* parse_blr_blob(ISC_QUAD& blob_id, qli_symbol* symbol)
{
/**************************************
*
* p a r s e _ b l r _ b l o b
*
**************************************
*
* Functional description
* Gobble up a BLR blob, if possible.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
if (UserBlob::blobIsNull(blob_id))
return NULL;
UserBlob handle(status_vector);
if (!handle.open(DB, gds_trans, blob_id))
return NULL;
UCHAR buffer[1024];
UCHAR* ptr = buffer;
for (;;) {
size_t length = buffer + sizeof(buffer) - ptr;
if (!length)
break;
// The old code considered getting a portion of a segment a bug.
// Anyway, 1024 is not much to contain the BLR stream, even for validation.
if (!handle.getSegment(length, ptr, length) || handle.getCode())
break;
ptr += length;
}
if (!handle.close())
return NULL;
if (ptr == buffer)
return NULL;
ptr = buffer;
if (*ptr++ != blr_version4)
return NULL;
qli_syntax* node = parse_blr(&ptr, symbol);
if (*ptr != blr_eoc)
return NULL;
return node;
}
static void purge_relation(qli_rel* relation)
{
/**************************************
*
* p u r g e _ r e l a t i o n
*
**************************************
*
* Functional description
* Purge a relation out of internal data structures.
*
**************************************/
qli_symbol* symbol = relation->rel_symbol;
DBB database = relation->rel_database;
HSH_remove(symbol);
for (qli_rel** ptr = &database->dbb_relations; *ptr; ptr = &(*ptr)->rel_next)
{
if (*ptr == relation) {
*ptr = relation->rel_next;
break;
}
}
qli_fld* field;
while (field = relation->rel_fields) {
relation->rel_fields = field->fld_next;
ALLQ_release((FRB) field->fld_name);
if (symbol = field->fld_query_name) {
ALLQ_release((FRB) symbol);
}
ALLQ_release((FRB) field);
}
ALLQ_release((FRB) relation);
}
static void put_dyn_string( qli_rlb* rlb, const TEXT* string)
{
/**************************************
*
* p u t _ d y n _ s t r i n g
*
**************************************
*
* Functional description
* Move a string to a dynamic DDL command block.
*
**************************************/
const SSHORT l = strlen(string);
while ((SSHORT) (rlb->rlb_limit - rlb->rlb_data) < l) {
rlb = GEN_rlb_extend(rlb);
}
STUFF_WORD(l);
while (*string) {
STUFF(*string++);
}
CHECK_RLB(rlb);
}
static void rollback_update( DBB database)
{
/**************************************
*
* r o l l b a c k _ u p d a t e
*
**************************************
*
* Functional description
* Roll back a meta-data update.
*
**************************************/
if (gds_trans == database->dbb_meta_trans &&
(database->dbb_capabilities & DBB_cap_multi_trans))
{
ISC_STATUS_ARRAY alt_vector;
isc_rollback_transaction(alt_vector, &database->dbb_meta_trans);
// Note: No message given if rollback fails
}
gds_trans = 0;
}
static void set_capabilities( DBB database)
{
/**************************************
*
* s e t _ c a p a b i l i t i e s
*
**************************************
*
* Functional description
* Probe the database to determine
* what metadata capabilities it has.
* The table, rfr_table, in the static
* data declarations has the relation
* and field names, together with the
* capabilities bits.
*
**************************************/
FB_API_HANDLE req = 0;
// Look for desireable fields in system relations
for (const rfr_tab_t* rel_field_table = rfr_table; rel_field_table->relation;
rel_field_table++)
{
FOR(REQUEST_HANDLE req) x IN DB.RDB$RELATION_FIELDS
WITH x.RDB$RELATION_NAME = rel_field_table->relation
AND x.RDB$FIELD_NAME = rel_field_table->field
database->dbb_capabilities |= rel_field_table->bit_mask;
END_FOR
ON_ERROR
ERRQ_database_error(database, gds_status);
END_ERROR;
}
if (req)
{
if (isc_release_request(gds_status, &req))
ERRQ_database_error(database, gds_status);
}
}
static DBB setup_update( DBB database)
{
/**************************************
*
* s e t u p _ u p d a t e
*
**************************************
*
* Functional description
* Get ready to do a meta data update.
* If no database was specified, use the
* most recently readied one.
*
**************************************/
if (!database)
database = QLI_databases;
MET_meta_transaction(database, true);
return database;
}
static void sql_grant_revoke( qli_syntax* node, USHORT type)
{
/*****************************************************
*
* s q l _ g r a n t _ r e v o k e
*
*****************************************************
*
* Build a DYN string for a SQL GRANT or REVOKE statement
*
*****************************************************/
const USHORT privileges = (IPTR) node->syn_arg[s_grant_privileges];
qli_rel* relation = (qli_rel*) node->syn_arg[s_grant_relation];
DBB database = setup_update(relation->rel_database);
relation->rel_database = database;
const TEXT* relation_name = relation->rel_symbol->sym_string;
qli_rlb* rlb = NULL;
rlb = CHECK_RLB(rlb);
STUFF(isc_dyn_version_1);
STUFF(isc_dyn_begin);
// For each user create a separate grant string
// For each specified field create a separate grant string
qli_syntax* names = node->syn_arg[s_grant_users];
qli_syntax* fields = node->syn_arg[s_grant_fields];
NAM* name = (NAM*) names->syn_arg;
for (NAM* const end = name + names->syn_count;
name < end; name++)
{
if (fields->syn_count) {
NAM* field = (NAM *) fields->syn_arg;
NAM* const end_field = field + fields->syn_count;
for (; field < end_field; field++)
{
stuff_priv(rlb, type, relation_name, privileges,
(*name)->nam_string, (*field)->nam_string);
}
}
else
stuff_priv(rlb, type, relation_name, privileges,
(*name)->nam_string, NULL);
}
STUFF(isc_dyn_end);
STUFF(isc_dyn_eoc);
execute_dynamic_ddl(database, rlb);
MET_meta_commit(database);
}
static int truncate_string( TEXT* string)
{
/**************************************
*
* t r u n c a t e _ s t r i n g
*
**************************************
*
* Functional description
* Convert a blank filled string to
* a null terminated string without
* trailing blanks. Because some
* strings contain embedded blanks
* (e.g. query headers & edit strings)
* truncate from the back forward.
* Return the number of characters found,
* not including the terminating null.
*
**************************************/
const int len = fb_utils::name_length(string);
string[len] = 0;
return len;
}
static void stuff_priv(
qli_rlb* rlb,
USHORT type,
const TEXT* relation,
USHORT privileges, const TEXT* user, const TEXT* field)
{
/**************************************
*
* s t u f f _ p r i v
*
**************************************
*
* Functional description
* Stuff a privilege for grant/revoke.
*
**************************************/
rlb = CHECK_RLB(rlb);
STUFF(type);
// Stuff privileges first
USHORT priv_count = 0;
if (privileges & PRV_select)
priv_count++;
if (privileges & PRV_insert)
priv_count++;
if (privileges & PRV_delete)
priv_count++;
if (privileges & PRV_update)
priv_count++;
STUFF_WORD(priv_count);
if (privileges & PRV_select)
STUFF('S');
if (privileges & PRV_insert)
STUFF('I');
if (privileges & PRV_delete)
STUFF('D');
if (privileges & PRV_update)
STUFF('U');
STUFF(isc_dyn_rel_name);
STUFF_STRING(relation);
STUFF(isc_dyn_grant_user);
STUFF_STRING(user);
if (field) {
STUFF(isc_dyn_fld_name);
STUFF_STRING(field);
}
if (privileges & PRV_grant_option) {
STUFF(isc_dyn_grant_options);
STUFF_WORD(2);
STUFF_WORD(TRUE);
}
STUFF(isc_dyn_end);
}