mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-28 02:43:03 +01:00
08f9f20489
It is possible now to copy table contents between databases, insert bigints via qli and show works nearly as expected. Displaying bigints and evaluation of expressions containing bigints still doesn't work.
3751 lines
91 KiB
Plaintext
3751 lines
91 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"
|
|
|
|
#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 void wal_info(const UCHAR*, SLONG*, SCHAR*, SLONG*);
|
|
|
|
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 db_log_info[] =
|
|
{
|
|
isc_info_logfile,
|
|
isc_info_cur_logfile_name,
|
|
isc_info_cur_log_part_offset
|
|
};
|
|
|
|
/*
|
|
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, NULL, NULL, NULL, NULL); // 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, NULL, NULL, NULL, NULL);
|
|
// 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, (*ptr)->nam_string,
|
|
relation->rel_symbol->sym_string, NULL, NULL, NULL);
|
|
// 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, NULL, NULL,
|
|
NULL, NULL); // 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, NULL, NULL, NULL, NULL); // 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, NULL, NULL, NULL, NULL);
|
|
// 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 */
|
|
|
|
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);
|
|
}
|
|
|
|
UCHAR* p = global_parm_buffer;
|
|
UCHAR* const dpb = p;
|
|
*p++ = isc_dpb_version1;
|
|
|
|
if (user_length) {
|
|
*p++ = isc_dpb_user_name;
|
|
*p++ = user_length;
|
|
while (user_length--)
|
|
*p++ = *user++;
|
|
}
|
|
|
|
if (password_length) {
|
|
*p++ = isc_dpb_password;
|
|
*p++ = password_length;
|
|
while (password_length--)
|
|
*p++ = *password++;
|
|
}
|
|
|
|
USHORT dpb_length = p - global_parm_buffer;
|
|
|
|
if (dpb_length == 1)
|
|
dpb_length = 0;
|
|
|
|
ISC_STATUS_ARRAY status_vector;
|
|
if (isc_attach_database(status_vector, 0, dbb->dbb_filename,
|
|
&dbb->dbb_handle, dpb_length, (char*) dpb))
|
|
ERRQ_database_error(dbb, status_vector);
|
|
|
|
qli_lls* log_stack = NULL;
|
|
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;
|
|
|
|
// Get write ahead log information
|
|
|
|
TEXT db_info_buffer[512];
|
|
if (isc_database_info(status_vector, &DB, sizeof(db_log_info),
|
|
reinterpret_cast<const char*>(db_log_info),
|
|
sizeof(db_info_buffer),
|
|
db_info_buffer))
|
|
ERRQ_database_error(dbb, status_vector);
|
|
|
|
// extract info from buffer
|
|
|
|
TEXT cur_log[512];
|
|
SLONG part_offset, log = 0;
|
|
wal_info((const UCHAR*) db_info_buffer, &log, cur_log, &part_offset);
|
|
|
|
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, NULL, NULL, NULL, NULL);
|
|
// Msg431 Could not drop database file "%s"
|
|
}
|
|
|
|
while (log_stack) {
|
|
const qli_str* log_string = (qli_str*) ALLQ_pop(&log_stack);
|
|
unlink(log_string->str_data); // do not check for error
|
|
}
|
|
|
|
if (unlink(dbb->dbb_filename))
|
|
ERRQ_print_error(431, dbb->dbb_filename, NULL, NULL, NULL, NULL);
|
|
// 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, NULL, NULL, NULL, NULL); // 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, name->nam_string, database->dbb_filename, NULL,
|
|
NULL, NULL); // 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, name->nam_string, database->dbb_filename, NULL,
|
|
NULL, NULL); // 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, name->nam_string, database->dbb_filename, NULL,
|
|
NULL, NULL); // 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 (isNullBlob(blob))
|
|
blob = & RFL.RDB$QUERY_HEADER;
|
|
if (!isNullBlob(blob))
|
|
field->fld_query_header = get_query_header(database, blob);
|
|
|
|
blob = & RFL.RDB$COMPUTED_BLR;
|
|
if (!isNullBlob(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, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
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, name->nam_string, database->dbb_filename, NULL,
|
|
NULL, NULL); // 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, NULL, NULL,
|
|
NULL, NULL); // 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?
|
|
*
|
|
**************************************/
|
|
UCHAR* p = global_parm_buffer;
|
|
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. */
|
|
|
|
*p++ = isc_dpb_version1;
|
|
|
|
SSHORT length;
|
|
const TEXT* lc_ctype = QLI_charset;
|
|
if (lc_ctype && (length = strlen(lc_ctype))) {
|
|
*p++ = isc_dpb_lc_ctype;
|
|
*p++ = length;
|
|
while (*lc_ctype)
|
|
*p++ = *lc_ctype++;
|
|
}
|
|
|
|
if (QLI_trace) {
|
|
*p++ = isc_dpb_trace;
|
|
*p++ = 1;
|
|
*p++ = 1;
|
|
}
|
|
|
|
if (sw_buffers) {
|
|
*p++ = isc_dpb_num_buffers;
|
|
*p++ = 2;
|
|
*p++ = sw_buffers % 256;
|
|
*p++ = sw_buffers / 256;
|
|
}
|
|
|
|
if (temp->dbb_pagesize) {
|
|
*p++ = isc_dpb_page_size;
|
|
*p++ = 2;
|
|
*p++ = temp->dbb_pagesize % 256;
|
|
*p++ = temp->dbb_pagesize / 256;
|
|
}
|
|
|
|
/* 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) {
|
|
*p++ = isc_dpb_user_name;
|
|
*p++ = length;
|
|
while (length--)
|
|
*p++ = *q++;
|
|
}
|
|
|
|
// 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) {
|
|
*p++ = isc_dpb_password;
|
|
*p++ = length;
|
|
while (length--)
|
|
*p++ = *q++;
|
|
}
|
|
|
|
USHORT dpb_length = p - global_parm_buffer;
|
|
UCHAR* dpb;
|
|
if (dpb_length == 1) {
|
|
dpb = NULL;
|
|
dpb_length = 0;
|
|
}
|
|
else
|
|
dpb = global_parm_buffer;
|
|
|
|
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_length, (char*) dpb, 0);
|
|
else
|
|
isc_attach_database(status_vector, 0, dbb->dbb_filename,
|
|
&dbb->dbb_handle, dpb_length, (char*) dpb);
|
|
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, NULL, NULL, NULL, NULL); // 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, field_name->sym_string,
|
|
relation_name->sym_string, NULL, NULL, NULL);
|
|
// 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 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, field_name->sym_string,
|
|
relation_name->sym_string, NULL, NULL, NULL); // 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 (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;
|
|
FB_API_HANDLE source_blob = 0;
|
|
|
|
if (isc_open_blob(status_vector, &DB, &source_dbb->dbb_meta_trans,
|
|
&source_blob, source_blob_id))
|
|
{
|
|
rollback_update((DBB) DB);
|
|
ERRQ_database_error(source_dbb, status_vector);
|
|
}
|
|
|
|
SLONG size, segment_count, max_segment;
|
|
gds__blob_size(&source_blob, (SLONG*) &size, &segment_count, &max_segment);
|
|
|
|
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);
|
|
|
|
USHORT length;
|
|
while (!isc_get_segment(status_vector, &source_blob, &length,
|
|
buffer_length, buffer))
|
|
{
|
|
while (rlb->rlb_limit - rlb->rlb_data < length)
|
|
rlb = GEN_rlb_extend(rlb);
|
|
const TEXT* p = buffer;
|
|
while (length--)
|
|
STUFF(*p++);
|
|
}
|
|
|
|
if (status_vector[1] != isc_segstr_eof) {
|
|
rollback_update((DBB) DB);
|
|
if (buffer != fixed_buffer)
|
|
gds__free(buffer);
|
|
ERRQ_database_error(source_dbb, status_vector);
|
|
}
|
|
|
|
if (buffer != fixed_buffer)
|
|
gds__free(buffer);
|
|
|
|
if (isc_close_blob(status_vector, &source_blob)) {
|
|
rollback_update((DBB) DB);
|
|
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, NULL, NULL, NULL, NULL, NULL);
|
|
// 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, NULL, NULL, NULL, NULL);
|
|
// 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, field_name->sym_string,
|
|
relation_name->sym_string, NULL, NULL, NULL);
|
|
// 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, NULL, NULL, NULL, NULL); // 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 (!isNullBlob(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 &&
|
|
(strncmp("RDB$", Y.RDB$FIELD_NAME, 4)) &&
|
|
(!(strncmp("RDB$", F.RDB$FIELD_NAME, 4))))
|
|
{
|
|
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 ||
|
|
((strncmp("RDB$",RFR.RDB$FIELD_NAME,4)) &&
|
|
(!(strncmp("RDB$", Y.RDB$FIELD_NAME,4)))))
|
|
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, NULL, NULL, NULL, NULL);
|
|
// 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((DBB) source->rel_database->dbb_meta_trans);
|
|
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, NULL, NULL, NULL, NULL); // 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, NULL, NULL, NULL, NULL, NULL);
|
|
// 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];
|
|
FB_API_HANDLE blob = 0;
|
|
|
|
if (isc_open_blob(status_vector, &database->dbb_handle,
|
|
&gds_trans, &blob, 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 (;;) {
|
|
USHORT length;
|
|
ISC_STATUS status = isc_get_segment(status_vector, &blob, &length,
|
|
sizeof(buffer), buffer);
|
|
if (status && status != isc_segment)
|
|
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 (isc_close_blob(status_vector, &blob))
|
|
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;
|
|
TEXT* p = new_dbb->dbb_filename;
|
|
const TEXT* q = old_dbb->dbb_filename;
|
|
do {
|
|
*p++ = *q++;
|
|
} while (--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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
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_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, (TEXT*)(IPTR) op, NULL, NULL, NULL, NULL); // 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 (isNullBlob(blob_id))
|
|
return NULL;
|
|
|
|
FB_API_HANDLE handle = 0;
|
|
|
|
if (isc_open_blob(status_vector, &DB, &gds_trans, &handle, blob_id))
|
|
return NULL;
|
|
|
|
UCHAR buffer[1024];
|
|
UCHAR* ptr = buffer;
|
|
|
|
for (;;) {
|
|
USHORT length = buffer + sizeof(buffer) - ptr;
|
|
if (!length)
|
|
break;
|
|
if (isc_get_segment(status_vector, &handle, &length, length,
|
|
(char*) ptr))
|
|
break;
|
|
ptr += length;
|
|
}
|
|
|
|
if (isc_close_blob(status_vector, &handle))
|
|
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.
|
|
*
|
|
**************************************/
|
|
TEXT *p;
|
|
|
|
for (p = string; *p; p++);
|
|
|
|
for (p--; (p >= string) && (*p == ' '); p--);
|
|
|
|
*++p = 0;
|
|
|
|
return (p - string);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
static void wal_info(
|
|
const UCHAR* db_info_buffer,
|
|
SLONG* log, SCHAR* cur_log, SLONG* part_offset)
|
|
{
|
|
/**************************************
|
|
*
|
|
* w a l _ i n f o
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Extract wal name and log offset
|
|
*
|
|
**************************************/
|
|
*log = 0;
|
|
*part_offset = 0;
|
|
cur_log[0] = 0;
|
|
|
|
const UCHAR* p = db_info_buffer;
|
|
UCHAR item;
|
|
while ((item = *p++) != isc_info_end) {
|
|
const SLONG length = gds__vax_integer(p, 2);
|
|
p += 2;
|
|
switch (item) {
|
|
case isc_info_logfile:
|
|
*log = gds__vax_integer(p, length);
|
|
p += length;
|
|
break;
|
|
|
|
case isc_info_cur_logfile_name:
|
|
{
|
|
const UCHAR* d = p;
|
|
p += length;
|
|
const SLONG log_length = *d++;
|
|
memcpy(cur_log, d, log_length);
|
|
cur_log[log_length] = 0;
|
|
break;
|
|
}
|
|
|
|
case isc_info_cur_log_part_offset:
|
|
*part_offset = gds__vax_integer(p, length);
|
|
p += length;
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
|