8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-01 00:43:03 +01:00
firebird-mirror/src/qli/meta.epp
2009-07-17 08:57:13 +00:00

3714 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(qli_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(qli_dbb*, qli_fld*, qli_symbol*);
static void delete_fields(qli_rel*);
static ISC_STATUS detach(ISC_STATUS *, qli_dbb*);
static void execute_dynamic_ddl(qli_dbb*, qli_rlb*);
static int field_length(USHORT, USHORT);
static void get_database_type(qli_dbb*);
static TEXT* get_query_header(qli_dbb*, ISC_QUAD&);
static void install(qli_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 release_request(qli_dbb* db, FB_API_HANDLE& request_handle);
static void rollback_update(qli_dbb*);
static void set_capabilities(qli_dbb*);
static qli_dbb* setup_update(qli_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 }, Unused
{ "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( qli_dbb* database, qli_fld* variable, const qli_name* 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( qli_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];
qli_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) ? TRUE : FALSE;
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;
qli_name** ptr = (qli_name**) 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
}
qli_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;
qli_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;
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( qli_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);
qli_dbb* next = NULL;
for (qli_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( qli_dbb* database, qli_name* 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;
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( qli_dbb* database, qli_name* 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;
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( qli_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;
qli_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( qli_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((qli_frb*) function->fun_symbol);
ALLQ_release((qli_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((qli_frb*) dbb->dbb_symbol);
}
int count = 0;
for (qli_dbb** ptr = &QLI_databases; *ptr; ptr = &(*ptr)->dbb_next)
{
if (*ptr == dbb)
{
*ptr = dbb->dbb_next;
ALLQ_release((qli_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(qli_dbb* db,
const SCHAR* relation_name,
const SCHAR* index_name,
SCHAR* const buffer,
size_t bufsize)
{
/**************************************
*
* 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)
{
for (p = fb_utils::exact_name(IDX.RDB$INDEX_NAME); *p;) {
*b++ = *p++;
}
*b++ = ' ';
*b++ = '(';
}
p = fb_utils::exact_name(SEG.RDB$FIELD_NAME);
if (p + strlen(p) + 2 > buffer + bufsize)
break;
while (*p) {
*b++ = *p++;
}
*b++ = ' ';
END_FOR
ON_ERROR
ERRQ_database_error(NULL, gds_status);
END_ERROR;
release_request(db, request_handle);
// back up over the last space and finish off
b--;
*b++ = ')';
*b++ = 0;
}
#endif
void MET_meta_commit( qli_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( qli_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(qli_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);
}
else if (!gds_trans)
{
// otherwise make one more effort to start the transaction
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( qli_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(nErr, 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.
*
**************************************/
qli_name* name = (qli_name*) node->syn_arg[s_mfi_name];
qli_dbb* database = (qli_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) ? TRUE : FALSE;
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.
*
**************************************/
qli_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, const bool 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);
qli_dbb* temp = (qli_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);
}
// It does absolutely nothing inside the engine these days: ignored.
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;
qli_dbb* dbb = NULL;
for (ptr = node->syn_arg; ptr < end; ptr++)
{
dbb = (qli_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, (qli_dbb*) *ptr2);
ERRQ_database_error(dbb, status_vector);
}
// Databases are up and running. Install each.
for (ptr = node->syn_arg; ptr < end; ptr++)
install((qli_dbb*) *ptr);
}
void MET_shutdown()
{
/**************************************
*
* M E T _ s h u t d o w n
*
**************************************
*
* Functional description
* Shut down all attached databases.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
for (qli_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;
qli_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;
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];
qli_dbb* database = setup_update(relation->rel_database);
relation->rel_database = database;
qli_symbol* symbol = relation->rel_symbol;
qli_rlb* rlb = NULL;
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, qli_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.
*
**************************************/
qli_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.
*
**************************************/
CHECK_RLB(rlb);
qli_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);
}
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;
qli_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.
*
**************************************/
qli_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(qli_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;
req1 = req2 = 0;
FB_API_HANDLE s_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;
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);
release_request(source->rel_database, req1);
release_request(source->rel_database, req2);
}
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 = 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)
{
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);
}
release_request(source->rel_database, req1);
release_request(target->rel_database, req2);
release_request(source->rel_database, req3);
}
static void define_global_field( qli_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((qli_frb*) field->fld_name);
if (field->fld_query_name)
ALLQ_release((qli_frb*) field->fld_query_name);
ALLQ_release((qli_frb*) field);
}
relation->rel_flags &= ~REL_fields;
}
static ISC_STATUS detach(ISC_STATUS* status_vector, qli_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( qli_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 = rlb->rlb_data - 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( qli_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);
const UCHAR* p = buffer;
while (*p != isc_info_end && p < buffer + sizeof(buffer))
{
UCHAR item = *p++;
const 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; // Not used
++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( qli_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;
const char* const end = header + sizeof(header) - 3;
// CVC: No bounds check here: it's assumed that 1024 is enough, but "p"
// might overflow "header" eventually.
for (;;)
{
if (p >= end)
break;
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 && p < end);
else
{
*p++ = '"';
while (*q && p < end)
*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( qli_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;
qli_dbb* new_dbb = (qli_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.
qli_name* name = (qli_name*) 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;
release_request(new_dbb, request);
// 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);
qli_fun* function = (qli_fun*) ALLOCP(type_fun);
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;
release_request(new_dbb, request);
release_request(new_dbb, request2);
}
}
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.
*
**************************************/
fb_assert(length > 0);
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;
memcpy(p, string, 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]);
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;
qli_name* 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 = (qli_name*) 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 = (qli_name*) 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;
QLI_validate_desc(constant->con_desc);
switch (dtype)
{
case dtype_text:
while (l--)
*p++ = BLR_BYTE;
break;
case dtype_short:
*(SSHORT*) p = gds__vax_integer(blr, l);
blr += l;
break;
case dtype_long:
*(SLONG*) p = gds__vax_integer(blr, l);
blr += l;
break;
}
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;
qli_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((qli_frb*) field->fld_name);
if (symbol = field->fld_query_name) {
ALLQ_release((qli_frb*) symbol);
}
ALLQ_release((qli_frb*) field);
}
ALLQ_release((qli_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 release_request(qli_dbb* db, FB_API_HANDLE& request_handle)
{
if (request_handle)
{
if (isc_release_request(gds_status, &request_handle))
ERRQ_database_error(db, gds_status);
}
}
static void rollback_update( qli_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( qli_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;
}
release_request(database, req);
}
static qli_dbb* setup_update( qli_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];
qli_dbb* database = setup_update(relation->rel_database);
relation->rel_database = database;
const TEXT* relation_name = relation->rel_symbol->sym_string;
qli_rlb* rlb = NULL;
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];
qli_name** name = (qli_name**) names->syn_arg;
for (qli_name** const end = name + names->syn_count; name < end; name++)
{
if (fields->syn_count)
{
qli_name** field = (qli_name**) fields->syn_arg;
qli_name** 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.
*
**************************************/
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);
}