8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 18:03:04 +01:00
firebird-mirror/src/dudley/extract.epp
2005-12-19 10:02:48 +00:00

2161 lines
59 KiB
Plaintext

/*
* PROGRAM: DUDLEY (ddl compiler)
* MODULE: extract.epp
* DESCRIPTION: definition extract routines
*
* 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 "../jrd/ibase.h"
#include "../dudley/ddl.h"
#include "../jrd/acl.h"
#include "../jrd/flags.h"
#include "../jrd/obj.h"
#include "../dudley/ddl_proto.h"
#include "../dudley/extra_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/gdsassert.h"
DATABASE DB = EXTERN FILENAME "yachts.lnk" RUNTIME dudleyGlob.DB_file_name;
static const char* const FOPEN_WRITE_TYPE = "w";
inline bool blob_null(const ISC_QUAD id)
{
return !id.gds_quad_low && !id.gds_quad_high;
}
struct view {
view *view_next;
TEXT view_name[1];
};
static void binary_to_ascii(SLONG, SSHORT, TEXT *);
static void decompile_blr_literal(const SCHAR*);
static void extract_acls(void);
static void extract_computed(const TEXT*, bool);
static void extract_database(const TEXT*);
static void extract_fields(void);
static void extract_filters(void);
static void extract_functions(void);
static void extract_func_args(const TEXT *);
static void extract_generators(void);
static void extract_grants(const TEXT *);
static void extract_indexes(void);
static void extract_relations(void);
static void extract_rfr(const TEXT*);
static void extract_security(void);
static void extract_triggers(void);
static void extract_trig_msgs(const TEXT *);
static void extract_view(const TEXT*, ISC_QUAD*, ISC_QUAD*, bool, bool, bool, bool*);
static void extract_views(void);
static void field_attributes(ISC_QUAD*, const TEXT*, ISC_QUAD*, const TEXT*, SSHORT);
static void format_acl(const SCHAR *);
static char name_trunc(const TEXT *, TEXT *);
static void print_blob(ISC_QUAD*, TEXT);
static void put_comment(USHORT);
static void put_error(USHORT, const TEXT*);
static void set_capabilities(void);
static void type_scalar(USHORT, SSHORT, USHORT, SSHORT, USHORT, const TEXT*);
static void view_fields(const TEXT*);
#ifdef NOT_USED_OR_REPLACED
static void wal_info(const UCHAR*, SSHORT*, SLONG*, SLONG*, SLONG*);
#endif
static FILE* output_file;
static const SCHAR db_items[] = { isc_info_page_size };
static const SCHAR db_info[] = {
isc_info_num_wal_buffers, isc_info_wal_buffer_size,
isc_info_wal_ckpt_length, isc_info_wal_grpc_wait_usecs
};
static const TEXT acl_privs[] = "?CGDRWP???";
static SLONG EXT_capabilities;
enum EXT_flags {
EXT_security = 1,
EXT_files = 2,
EXT_external = 4,
EXT_idx_inactive = 8,
EXT_triggers = 16, // Obsolete - 1996-Aug-05
EXT_context_name = 32,
EXT_db_description = 64,
EXT_v3 = 128, // rdb$dependencies, rdb$functions, rdb$filters, rdb$user_privileges
// rdb$triggers, rdb$types, rdb$indices.rdb$index_type
// rdb$fields.rdb$dimensions, rdb$files.rdb$shadow_number
// rdb$generators
EXT_v4 = 256
};
/* 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", EXT_idx_inactive },
/* Obsolete - 1996-Aug-05 David Schnepper
{ "RDB$RELATIONS", "RDB$STORE_TRIGGER", EXT_triggers },
*/
{ "RDB$RELATIONS", "RDB$EXTERNAL_FILE", EXT_external },
{ "RDB$SECURITY_CLASSES", "RDB$SECURITY_CLASS", EXT_security },
{ "RDB$FILES", "RDB$FILE_NAME", EXT_files },
{ "RDB$VIEW_RELATIONS", "RDB$CONTEXT_NAME", EXT_context_name },
{ "RDB$DATABASE", "RDB$DESCRIPTION", EXT_db_description },
{ "RDB$FUNCTIONS", "RDB$FUNCTION_NAME", EXT_v3 },
// { "RDB$LOG_FILES", "RDB$FILE_PARTITIONS", EXT_v4 },
{ 0, 0, 0 }
};
void DDL_ext(void)
{
/**************************************
*
* D D L _ e x t
*
**************************************
*
* Functional description
* Create an output text file and dump data
* definitions into it.
*
**************************************/
output_file = NULL;
if (*dudleyGlob.DDL_file_name) {
if (!(output_file = fopen(dudleyGlob.DDL_file_name, FOPEN_WRITE_TYPE))) {
DDL_err(248, dudleyGlob.DDL_file_name, NULL, NULL, NULL, NULL);
/* msg 248: ddl: can't open %s */
DDL_exit(FINI_ERROR);
}
}
else
output_file = stdout;
if (dudleyGlob.DDL_default_user || dudleyGlob.DDL_default_password)
READY DB USER dudleyGlob.DDL_default_user PASSWORD dudleyGlob.DDL_default_password;
else
READY DB;
/* the database version number is useful during extract - this goes
to stdout, not to the output_file */
if (dudleyGlob.DDL_version) {
DDL_msg_put(249, dudleyGlob.DB_file_name, NULL, NULL, NULL, NULL); /* msg 249: Version(s) for database \"%s\" */
isc_version(&DB, NULL, NULL);
}
START_TRANSACTION;
set_capabilities();
extract_database(dudleyGlob.DB_file_name);
if (EXT_capabilities & EXT_v3)
extract_functions();
extract_fields();
extract_relations();
extract_views();
extract_indexes();
if (EXT_capabilities & EXT_security) {
extract_security();
extract_acls();
}
if (EXT_capabilities & EXT_v3) {
extract_filters();
extract_generators();
extract_triggers();
}
fprintf(output_file, "\n");
fclose(output_file);
COMMIT;
FINISH;
}
static void binary_to_ascii(SLONG value, SSHORT scale, TEXT * buffer)
{
/**************************************
*
* b i n a r y _ t o _ a s c i i
*
**************************************
*
* Functional description
* Convert a binary number to ascii and
* return it in the user supplied buffer;
*
**************************************/
TEXT temp[32];
TEXT *p = buffer;
/* Handle non-negative scale factor in straightforward manner */
if (scale >= 0) {
sprintf(p, "%"SLONGFORMAT, value);
while (*p)
p++;
if (scale)
do
*p++ = '0';
while (--scale);
return;
}
if (value < 0) {
*p++ = '-';
value = -value;
}
TEXT* q;
for (q = temp; value || scale < 0;) {
*q++ = value % 10 + '0';
value /= 10;
if (!++scale) {
*q++ = '.';
if (!value)
*q++ = '0';
}
}
while (q > temp)
*p++ = *--q;
*p = 0;
}
static void decompile_blr_literal(const SCHAR* string)
{
/**************************************
*
* d e c o m p i l e _ b l r _ l i t e r a l
*
**************************************
*
* Functional description
* decompile a blr literal
* and write it to the output file.
*
**************************************/
SCHAR scale;
TEXT buffer[256];
SSHORT size;
SLONG value;
const UCHAR* p = reinterpret_cast<const UCHAR*>(string);
if ((*p++ != blr_version4) || (*p++ != blr_literal)) {
put_comment(250); /* msg 250: **** unable to decompile missing value *** */
return;
}
TEXT* b = buffer;
switch (*p++) {
case (blr_text):
size = gds__vax_integer(p, 2);
p += 2;
*b++ = '"';
while (size--)
*b++ = *p++;
*b++ = '"';
*b = 0;
break;
case (blr_long):
scale = *p++;
value = gds__vax_integer(p, 4);
binary_to_ascii(value, scale, buffer);
break;
case (blr_short):
scale = *p++;
value = gds__vax_integer(p, 2);
binary_to_ascii(value, scale, buffer);
break;
default:
gds__msg_format(NULL, DDL_MSG_FAC, 250, sizeof(buffer), buffer,
NULL, NULL, NULL, NULL, NULL);
/* msg 250: **** unable to decompile missing value *** */
}
fprintf(output_file, "%s", buffer);
}
static void extract_acls(void)
{
/**************************************
*
* e x t r a c t _ a c l s
*
**************************************
*
* Functional description
* Read security class records and
* and write them to the output file.
* For each security class record determine if there is
* any GRANT user privileges associated with it. If so,
* extract GRANT statement rather than a define security class
* statement.
*
**************************************/
TEXT s[32];
const TEXT* relname;
bool first = true;
FOR S IN RDB$SECURITY_CLASSES
if (first) {
put_comment(251); /* msg 251: \n\n\tSecurity Class Definitions / GRANT statements\t */
first = false;
}
USHORT count = 0;
/* if there's any rdb$user_privileges entry then we can use GRANT statement */
/* resort to parsing rdb$security_class for SQL$<relation-name> instead of
just checking for rdb$security_classes.rdb$relation_name since it's
possible that the security class was used on some other relation than
the one GRANTed ON */
// CVC: it's not clear how this works. Any relation with len(name) > 27
// will have its name truncated and any pair of relations whose first 27
// characters are the same will have a conflict, hence the latest to be
// defined or changed WRT rights will stomp over the other's settings, so
// both relations become tied to the same security class.
if (EXT_capabilities & EXT_v3) {
if (strncmp(S.RDB$SECURITY_CLASS, "SQL$", 4) == 0) {
strcpy(s, S.RDB$SECURITY_CLASS);
relname = &s[4];
if (EXT_capabilities & EXT_v4) {
FOR U IN RDB$USER_PRIVILEGES WITH
U.RDB$RELATION_NAME = relname AND
U.RDB$OBJECT_TYPE = obj_relation
count++;
END_FOR;
}
else {
FOR U IN RDB$USER_PRIVILEGES WITH U.RDB$RELATION_NAME = relname
count++;
END_FOR;
}
/* if there's no rdb$user_privileges entry, but we still find a
SQL$ security class, that's the relation owner's privilege list.
Ignore this as the new database created will have a new owner
and should start fresh with no security class defined for
owner */
if (!count)
continue;
}
}
if (count)
extract_grants(relname);
else {
fprintf(output_file, "\ndefine security_class %s",
S.RDB$SECURITY_CLASS);
if (!blob_null(S.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&S.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
if (!blob_null(S.RDB$ACL))
print_blob(&S.RDB$ACL, 'a');
fprintf(output_file, ";\n");
}
END_FOR;
}
static void extract_computed(const TEXT * relation_name, bool first)
{
/**************************************
*
* e x t r a c t _ c o m p u t e d
*
**************************************
*
* Functional description
* Read definitions of computed fields
* and write them to the output file.
*
* Check on the way by that we're not
* trying to expand a computed field
* that's been copied whole into a
* view. Meaning that if there is a
* view context, skip this field
*
**************************************/
TEXT s[32];
FOR RFR IN RDB$RELATION_FIELDS CROSS F IN RDB$FIELDS
WITH RFR.RDB$RELATION_NAME = relation_name
AND F.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE
AND F.RDB$COMPUTED_BLR NOT MISSING
SORTED BY RFR.RDB$FIELD_POSITION
if (RFR.RDB$VIEW_CONTEXT)
continue;
if (!first)
fprintf(output_file, ",\n");
first = false;
name_trunc(RFR.RDB$FIELD_NAME, s);
if (F.RDB$COMPUTED_SOURCE.NULL) {
put_error(252, s); /* msg 252: **** field %s can not be extracted, computed source missing *** */
continue;
}
fprintf(output_file, " %s", s);
strcpy(s, F.RDB$FIELD_NAME);
if (EXT_capabilities & EXT_v3) {
FOR FNDA IN RDB$FIELDS WITH FNDA.RDB$FIELD_NAME = s
type_scalar(FNDA.RDB$FIELD_TYPE, FNDA.RDB$FIELD_SCALE,
FNDA.RDB$FIELD_LENGTH,
FNDA.RDB$FIELD_SUB_TYPE, FNDA.RDB$DIMENSIONS, s);
END_FOR;
}
else {
FOR FNDA IN RDB$FIELDS WITH FNDA.RDB$FIELD_NAME = s
type_scalar(FNDA.RDB$FIELD_TYPE, FNDA.RDB$FIELD_SCALE,
FNDA.RDB$FIELD_LENGTH,
FNDA.RDB$FIELD_SUB_TYPE, 0, NULL);
END_FOR;
}
fprintf(output_file, " computed by (");
print_blob(&F.RDB$COMPUTED_SOURCE, 'u');
fprintf(output_file, ")");
if (!RFR.RDB$FIELD_POSITION.NULL)
fprintf(output_file, "\tposition %d", RFR.RDB$FIELD_POSITION);
field_attributes(&RFR.RDB$DESCRIPTION, RFR.RDB$QUERY_NAME,
&RFR.RDB$QUERY_HEADER, RFR.RDB$EDIT_STRING,
RFR.RDB$SYSTEM_FLAG);
END_FOR;
}
static void extract_database(const TEXT * DB_file_nameL)
{
/**************************************
*
* e x t r a c t _ d a t a b a s e
*
**************************************
*
* Functional description
* Get the essential database information
* and write it to the output file.
*
**************************************/
const int OneK = 1024;
TEXT s[32], buffer[256];
SLONG page_size;
ISC_STATUS_ARRAY status_vector;
fprintf(output_file, "define database \"%s\"", DB_file_nameL);
if (EXT_capabilities & EXT_security) {
FOR D IN RDB$DATABASE
if (!blob_null(D.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&D.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
if (!D.RDB$SECURITY_CLASS.NULL) {
name_trunc(D.RDB$SECURITY_CLASS, s);
if (*s)
fprintf(output_file, "\n\tsecurity_class %s", s);
}
END_FOR;
}
else if (EXT_capabilities & EXT_db_description)
FOR D IN RDB$DATABASE
if (!blob_null(D.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&D.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
END_FOR;
if (isc_database_info(status_vector, &DB, sizeof(db_items),
db_items, sizeof(buffer), buffer)) {
put_error(254, 0);
/* msg 254: ***isc_database_info failed*** */
gds__print_status(status_vector);
}
TEXT* d = buffer;
if (*d++ == isc_info_page_size) {
SSHORT length = gds__vax_integer(reinterpret_cast<const UCHAR*>(d), 2);
d += 2;
page_size = gds__vax_integer(reinterpret_cast<const UCHAR*>(d), length);
if (page_size)
fprintf(output_file, "\n\tpage_size %"SLONGFORMAT, page_size);
}
if (EXT_capabilities & EXT_files)
{
if (EXT_capabilities & EXT_v3) {
FOR F IN RDB$FILES WITH F.RDB$SHADOW_NUMBER MISSING OR
F.RDB$SHADOW_NUMBER EQ 0
name_trunc(F.RDB$FILE_NAME, buffer);
fprintf(output_file, "\n\tfile \"%s\" starting at page %ld",
buffer, F.RDB$FILE_START);
END_FOR;
}
else {
FOR F IN RDB$FILES
name_trunc(F.RDB$FILE_NAME, buffer);
fprintf(output_file, "\n\tfile \"%s\" starting at page %ld",
buffer, F.RDB$FILE_START);
END_FOR;
}
}
/* print log info. */
#ifdef NOT_USED_OR_REPLACED
// code commented in addition to ifdef to avoid warnings during compilation
/*
if (EXT_capabilities & EXT_v4) {
bool first_time = true;
bool need_close = false;
FOR F IN RDB$LOG_FILES
if (F.RDB$FILE_FLAGS & LOG_overflow)
continue;
if (first_time) {
first_time = false;
fprintf(output_file, "\n\tlogfile");
if (F.RDB$FILE_FLAGS & LOG_default)
break;
else if (F.RDB$FILE_FLAGS & LOG_serial)
fprintf(output_file, " base_name");
else {
need_close = true;
fprintf(output_file, "\n\t\t(");
}
fprintf(output_file, "\n\t\t\"%s\"", F.RDB$FILE_NAME);
}
else
fprintf(output_file, ",\n\t\t\"%s\"", F.RDB$FILE_NAME);
if (F.RDB$FILE_LENGTH)
fprintf(output_file, " size = %ld", F.RDB$FILE_LENGTH);
if (F.RDB$FILE_PARTITIONS)
fprintf(output_file, " partitions = %d",
F.RDB$FILE_PARTITIONS);
if (F.RDB$FILE_FLAGS & LOG_raw)
fprintf(output_file, " raw");
END_FOR;
if (need_close)
fprintf(output_file, "\n\t\t)");
FOR F IN RDB$LOG_FILES
if (!(F.RDB$FILE_FLAGS & LOG_overflow))
continue;
fprintf(output_file, "\n\toverflow");
fprintf(output_file, " \"%s\"", F.RDB$FILE_NAME);
if (F.RDB$FILE_LENGTH)
fprintf(output_file, " size = %ld", F.RDB$FILE_LENGTH);
if (F.RDB$FILE_PARTITIONS)
fprintf(output_file, " partitions = %d",
F.RDB$FILE_PARTITIONS);
END_FOR;
SCHAR db_info_buffer[64];
if (isc_database_info(gds_status, &DB, sizeof(db_info), db_info,
sizeof(db_info_buffer), db_info_buffer)) {
put_error(254, 0); // msg 254: ***isc_database_info failed***
gds__print_status(status_vector);
}
SSHORT num_buf = 0;
SLONG buf_size = 0, chkpt_len = 0, grp_commit= 0;
wal_info(reinterpret_cast<const UCHAR*>(db_info_buffer), &num_buf,
&buf_size, &chkpt_len, &grp_commit);
if (num_buf)
fprintf(output_file, "\n\tnum_log_buffers %d", num_buf);
if (buf_size)
fprintf(output_file, "\n\tlog_buffer_size %"SLONGFORMAT, buf_size);
if (chkpt_len)
fprintf(output_file, "\n\tcheck_point_length %"SLONGFORMAT,
chkpt_len / OneK);
if (grp_commit)
fprintf(output_file, "\n\tgroup_commit_wait_time %"SLONGFORMAT,
grp_commit);
}
*/
#endif
fprintf(output_file, ";\n");
if (EXT_capabilities & EXT_v3) {
bool any_shadows = false;
FOR F IN RDB$FILES WITH F.RDB$SHADOW_NUMBER GT 0
SORTED BY F.RDB$SHADOW_NUMBER, F.RDB$FILE_START
name_trunc(F.RDB$FILE_NAME, buffer);
if (F.RDB$FILE_START)
fprintf(output_file, "\n\tfile \"%s\" starting at page %ld",
buffer, F.RDB$FILE_START);
else
fprintf(output_file, "%s\ndefine shadow %d \"%s\"",
(any_shadows) ? ";\n" : "",
F.RDB$SHADOW_NUMBER,
buffer);
any_shadows = true;
END_FOR;
if (any_shadows)
fprintf(output_file, ";\n");
}
}
static void extract_fields(void)
{
/**************************************
*
* e x t r a c t _ f i e l d s
*
**************************************
*
* Functional description
* Read the global field definitions
* and write them to the output file.
*
**************************************/
TEXT s[32];
bool first = true;
FOR F IN RDB$FIELDS WITH
(F.RDB$SYSTEM_FLAG NE 1 OR F.RDB$SYSTEM_FLAG MISSING) AND
F.RDB$COMPUTED_BLR MISSING SORTED BY F.RDB$FIELD_NAME
if (first) {
put_comment(255); /*msg 255: \n\n\tGlobal Field Definitions\t */
first = false;
}
name_trunc(F.RDB$FIELD_NAME, s);
fprintf(output_file, "define field %s", s);
if (F.RDB$FIELD_TYPE == blr_blob) {
fprintf(output_file, " blob");
if (F.RDB$SEGMENT_LENGTH)
fprintf(output_file, " segment_length %u",
(USHORT) F.RDB$SEGMENT_LENGTH);
switch (F.RDB$FIELD_SUB_TYPE)
{
case 1:
fprintf(output_file, " sub_type text");
break;
case 2:
fprintf(output_file, " sub_type BLR");
break;
case 3:
fprintf(output_file, " sub_type ACL");
break;
default:
if (F.RDB$FIELD_SUB_TYPE)
fprintf(output_file, " sub_type %d", F.RDB$FIELD_SUB_TYPE);
break;
}
}
else {
strcpy(s, F.RDB$FIELD_NAME);
if (EXT_capabilities & EXT_v3) {
FOR FNDA IN RDB$FIELDS WITH FNDA.RDB$FIELD_NAME = s
type_scalar(FNDA.RDB$FIELD_TYPE, FNDA.RDB$FIELD_SCALE,
FNDA.RDB$FIELD_LENGTH,
FNDA.RDB$FIELD_SUB_TYPE, FNDA.RDB$DIMENSIONS, s);
END_FOR;
}
else {
FOR FNDA IN RDB$FIELDS WITH FNDA.RDB$FIELD_NAME = s
type_scalar(FNDA.RDB$FIELD_TYPE, FNDA.RDB$FIELD_SCALE,
FNDA.RDB$FIELD_LENGTH,
FNDA.RDB$FIELD_SUB_TYPE, 0, NULL);
END_FOR;
}
}
if (!blob_null(F.RDB$VALIDATION_SOURCE)) {
fprintf(output_file, "\n\tvalid if (");
print_blob(&F.RDB$VALIDATION_SOURCE, 'u');
fprintf(output_file, ")");
}
if (!blob_null(F.RDB$MISSING_VALUE)) {
fprintf(output_file, "\n\tmissing_value is ");
print_blob(&F.RDB$MISSING_VALUE, 'm');
}
/*
if (!blob_null (F.RDB$DEFAULT_VALUE))
default_value (&F.RDB$DEFAULT_VALUE);
*/
field_attributes(&F.RDB$DESCRIPTION, F.RDB$QUERY_NAME,
&F.RDB$QUERY_HEADER, F.RDB$EDIT_STRING,
F.RDB$SYSTEM_FLAG);
fprintf(output_file, ";\n");
END_FOR;
}
static void extract_filters(void)
{
/**************************************
*
* e x t r a c t _ f i l t e r s
*
**************************************
*
* Functional description
* Read filter definitions
* and write them to the output file.
*
**************************************/
TEXT s[32];
bool first = true;
FOR F IN RDB$FILTERS SORTED BY F.RDB$FUNCTION_NAME
if (first) {
put_comment(256); /* msg 256: \n\n\tFilter Definitions\t */
first = false;
}
name_trunc(F.RDB$FUNCTION_NAME, s);
fprintf(output_file, "\ndefine filter %s", s);
fprintf(output_file, "\n\tinput_type %d", F.RDB$INPUT_SUB_TYPE);
fprintf(output_file, "\n\toutput_type %d", F.RDB$OUTPUT_SUB_TYPE);
if (!F.RDB$MODULE_NAME.NULL && name_trunc(F.RDB$MODULE_NAME, s))
fprintf(output_file, "\n\tmodule_name '%s'", s);
name_trunc(F.RDB$ENTRYPOINT, s);
fprintf(output_file, "\n\tentry_point '%s'", s);
if (!blob_null(F.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&F.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
fprintf(output_file, ";\n");
END_FOR;
}
static void extract_functions(void)
{
/**************************************
*
* e x t r a c t _ f u n c t i o n s
*
**************************************
*
* Functional description
* Read function definitions
* and write them to the output file.
*
**************************************/
TEXT s[32];
bool first = true;
FOR FUNC IN RDB$FUNCTIONS SORTED BY FUNC.RDB$FUNCTION_NAME
if (first) {
put_comment(257); /* msg 257: \n\n\tFunction Definitions\t */
first = false;
}
name_trunc(FUNC.RDB$FUNCTION_NAME, s);
fprintf(output_file, "\ndefine function %s", s);
if (!FUNC.RDB$QUERY_NAME.NULL && name_trunc(FUNC.RDB$QUERY_NAME, s))
fprintf(output_file, "\n\tquery_name %s", s);
if (!FUNC.RDB$MODULE_NAME.NULL && name_trunc(FUNC.RDB$MODULE_NAME, s))
fprintf(output_file, "\n\tmodule_name '%s'", s);
name_trunc(FUNC.RDB$ENTRYPOINT, s);
fprintf(output_file, "\n\tentry_point '%s'", s);
if (!blob_null(FUNC.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&FUNC.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
extract_func_args(FUNC.RDB$FUNCTION_NAME);
fprintf(output_file, ";\n");
END_FOR;
}
static void extract_func_args(const TEXT * function_name)
{
/**************************************
*
* e x t r a c t _ f u n c _ a r g s
*
**************************************
*
* Functional description
* Read the argument descriptions for a function
* and write them to the output file.
*
**************************************/
bool first = true;
const TEXT* p = "";
FOR FARG IN RDB$FUNCTION_ARGUMENTS
WITH FARG.RDB$FUNCTION_NAME = function_name
SORTED BY FARG.RDB$ARGUMENT_POSITION
fprintf(output_file, "%s\n", p);
if (first) {
p = ",";
first = false;
}
fprintf(output_file, "\t\t");
if (FARG.RDB$FIELD_TYPE == blr_blob)
fprintf(output_file, " blob");
else {
type_scalar(FARG.RDB$FIELD_TYPE, FARG.RDB$FIELD_SCALE,
FARG.RDB$FIELD_LENGTH, FARG.RDB$FIELD_SUB_TYPE, 0, NULL);
}
fprintf(output_file, " by ");
if (FARG.RDB$MECHANISM == FUNCARG_mechanism_value)
fprintf(output_file, "value");
else if ((FARG.RDB$MECHANISM == FUNCARG_mechanism_reference) ||
(FARG.RDB$MECHANISM == FUNCARG_mechanism_blob_struc))
fprintf(output_file, "reference");
else if (FARG.RDB$MECHANISM == FUNCARG_mechanism_sc_array_desc)
fprintf(output_file, "scalar_array_descriptor");
FOR FUNC IN RDB$FUNCTIONS
WITH FUNC.RDB$FUNCTION_NAME = function_name
if (FARG.RDB$ARGUMENT_POSITION == FUNC.RDB$RETURN_ARGUMENT) {
if (FARG.RDB$ARGUMENT_POSITION)
fprintf(output_file, " return_argument");
else
fprintf(output_file, " return_value");
}
END_FOR;
END_FOR;
}
static void extract_generators(void)
{
/**************************************
*
* e x t r a c t _ g e n e r a t o r s
*
**************************************
*
* Functional description
* Get generator definitions from rdb$generators.
*
**************************************/
TEXT s[32];
bool first = true;
FOR G IN RDB$GENERATORS WITH
(G.RDB$SYSTEM_FLAG NE 1 OR G.RDB$SYSTEM_FLAG MISSING)
SORTED BY G.RDB$GENERATOR_NAME
if (first) {
put_comment(290); /* msg 290: \n\n\tGenerator Definitions\t */
first = false;
}
name_trunc(G.RDB$GENERATOR_NAME, s);
fprintf(output_file, "\ndefine generator %s;", s);
END_FOR;
}
static void extract_grants(const TEXT * relname)
{
/**************************************
*
* e x t r a c t _ g r a n t s
*
**************************************
*
* Functional description
* Build as many GRANT statements as needed
* for the relation from reading user privilge records.
*
**************************************/
TEXT relation_name[32], user_name[32], field_name[32];
const char* user_priv;
if (EXT_capabilities & EXT_v4) {
FOR U IN RDB$USER_PRIVILEGES WITH U.RDB$RELATION_NAME EQ relname
AND U.RDB$OBJECT_TYPE EQ obj_relation
SORTED BY U.RDB$USER, U.RDB$PRIVILEGE
switch (U.RDB$PRIVILEGE[0]) {
case 'S':
user_priv = "SELECT";
break;
case 'U':
user_priv = "UPDATE";
break;
case 'D':
user_priv = "DELETE";
break;
case 'I':
user_priv = "INSERT";
break;
case 'R':
/* None of the interfaces knows about the references privilege.
Ignore it for now. */
continue;
default:
user_priv = "**unknown**";
break;
}
fprintf(output_file, "\ngrant %s ", user_priv);
if (!U.RDB$FIELD_NAME.NULL) {
name_trunc(U.RDB$FIELD_NAME, field_name);
fprintf(output_file, "(%s) ", field_name);
}
name_trunc(U.RDB$RELATION_NAME, relation_name);
name_trunc(U.RDB$USER, user_name);
fprintf(output_file, "on %s to %s", relation_name, user_name);
if (U.RDB$GRANT_OPTION)
fprintf(output_file, " with grant option");
fprintf(output_file, ";\n");
END_FOR;
}
else {
FOR U IN RDB$USER_PRIVILEGES WITH U.RDB$RELATION_NAME EQ relname
SORTED BY U.RDB$USER, U.RDB$PRIVILEGE switch (U.RDB$PRIVILEGE[0]) {
case 'S':
user_priv = "SELECT";
break;
case 'U':
user_priv = "UPDATE";
break;
case 'D':
user_priv = "DELETE";
break;
case 'I':
user_priv = "INSERT";
break;
case 'R':
/* None of the interfaces knows about the references privilege.
Ignore it for now. */
continue;
default:
user_priv = "**unknown**";
break;
}
fprintf(output_file, "\ngrant %s ", user_priv);
if (!U.RDB$FIELD_NAME.NULL) {
name_trunc(U.RDB$FIELD_NAME, field_name);
fprintf(output_file, "(%s) ", field_name);
}
name_trunc(U.RDB$RELATION_NAME, relation_name);
name_trunc(U.RDB$USER, user_name);
fprintf(output_file, "on %s to %s", relation_name, user_name);
if (U.RDB$GRANT_OPTION)
fprintf(output_file, " with grant option");
fprintf(output_file, ";\n");
END_FOR;
}
}
static void extract_indexes(void)
{
/**************************************
*
* e x t r a c t _ i n d e x e s
*
**************************************
*
* Functional description
* Read definitions of indexes
* and write them to the output file.
*
**************************************/
TEXT index[32], relation[32], field[32];
const TEXT* p = "";
bool first = true;
/* this query gets all IDX which donot have the systemflag set and
further sorts them by realtion name and index name */
FOR I IN RDB$INDICES CROSS R IN RDB$RELATIONS
OVER RDB$RELATION_NAME
WITH(R.RDB$SYSTEM_FLAG NE 1 OR R.RDB$SYSTEM_FLAG MISSING)
SORTED BY I.RDB$RELATION_NAME, I.RDB$INDEX_NAME
if (first) {
put_comment(291); /* msg 291: \n\n\tIndex Definitions\t */
first = false;
}
if (!I.RDB$UNIQUE_FLAG.NULL && I.RDB$UNIQUE_FLAG == 1)
p = "unique";
else
p = "";
name_trunc(I.RDB$INDEX_NAME, index);
name_trunc(I.RDB$RELATION_NAME, relation);
fprintf(output_file, "\ndefine index %s for %s %s",
index, relation, p);
if (!blob_null(I.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&I.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
if (EXT_capabilities & EXT_idx_inactive)
FOR ACT IN RDB$INDICES WITH ACT.RDB$INDEX_NAME = index
if (ACT.RDB$INDEX_INACTIVE)
fprintf(output_file, "\ninactive");
END_FOR;
if (EXT_capabilities & EXT_v3)
FOR ACT IN RDB$INDICES WITH ACT.RDB$INDEX_NAME = index
if (ACT.RDB$INDEX_TYPE)
fprintf(output_file, "\ndescending");
END_FOR;
p = "";
FOR I_S IN RDB$INDEX_SEGMENTS WITH
I_S.RDB$INDEX_NAME = I.RDB$INDEX_NAME
SORTED BY I_S.RDB$FIELD_POSITION
name_trunc(I_S.RDB$FIELD_NAME, field);
fprintf(output_file, "%s\n\t%s", p, field);
p = ",";
END_FOR;
fprintf(output_file, ";\n");
END_FOR;
}
static void extract_relations(void)
{
/**************************************
*
* e x t r a c t _ r e l a t i o n s
*
**************************************
*
* Functional description
* Read relation definitions
* and write them to the output file.
*
**************************************/
TEXT s[256];
bool first = true;
if (EXT_capabilities & EXT_external) {
FOR R IN RDB$RELATIONS WITH
(R.RDB$SYSTEM_FLAG NE 1 OR R.RDB$SYSTEM_FLAG MISSING) AND
R.RDB$VIEW_BLR MISSING SORTED BY R.RDB$RELATION_NAME
if (first) {
put_comment(258); /* msg 258: \n\n\tRelation Definitions\t */
first = false;
}
name_trunc(R.RDB$RELATION_NAME, s);
fprintf(output_file, "\ndefine relation %s", s);
if (!R.RDB$EXTERNAL_FILE.NULL && name_trunc(R.RDB$EXTERNAL_FILE, s))
fprintf(output_file, "\n\texternal_file \"%s\"", s);
if (R.RDB$SYSTEM_FLAG)
fprintf(output_file, "\n\tsystem_flag %d", R.RDB$SYSTEM_FLAG);
if (!blob_null(R.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&R.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
extract_rfr(R.RDB$RELATION_NAME);
fprintf(output_file, ";\n");
END_FOR;
}
else {
FOR R IN RDB$RELATIONS WITH
(R.RDB$SYSTEM_FLAG NE 1 OR R.RDB$SYSTEM_FLAG MISSING) AND
R.RDB$VIEW_BLR MISSING SORTED BY R.RDB$RELATION_NAME
if (first) {
put_comment(258); /* msg 258: \n\n\tRelation Definitions\t */
first = false;
}
name_trunc(R.RDB$RELATION_NAME, s);
fprintf(output_file, "\ndefine relation %s", s);
if (R.RDB$SYSTEM_FLAG)
fprintf(output_file, "\n\tsystem_flag %d", R.RDB$SYSTEM_FLAG);
if (!blob_null(R.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&R.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
extract_rfr(R.RDB$RELATION_NAME);
fprintf(output_file, ";\n");
END_FOR;
}
}
static void extract_rfr(const TEXT * relation_name)
{
/**************************************
*
* e x t r a c t _ r f r
*
**************************************
*
* Functional description
* Read the field descriptions for a relation
* and write them to the output file.
*
**************************************/
TEXT s[32];
bool first = true;
const TEXT* p = "";
FOR RFR IN RDB$RELATION_FIELDS CROSS F IN RDB$FIELDS
WITH RFR.RDB$RELATION_NAME = relation_name
AND F.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE
AND F.RDB$COMPUTED_BLR MISSING
SORTED BY RFR.RDB$FIELD_POSITION
fprintf(output_file, "%s\n", p);
if (first) {
p = ",";
first = false;
}
name_trunc(RFR.RDB$FIELD_NAME, s);
fprintf(output_file, " %s", s);
if (strcmp(RFR.RDB$FIELD_NAME, RFR.RDB$FIELD_SOURCE)) {
name_trunc(RFR.RDB$FIELD_SOURCE, s);
fprintf(output_file, " based on %s", s);
}
if (!RFR.RDB$FIELD_POSITION.NULL)
fprintf(output_file, "\tposition %d", RFR.RDB$FIELD_POSITION);
field_attributes(&RFR.RDB$DESCRIPTION, RFR.RDB$QUERY_NAME,
&RFR.RDB$QUERY_HEADER, RFR.RDB$EDIT_STRING,
RFR.RDB$SYSTEM_FLAG);
END_FOR;
extract_computed(relation_name, first);
}
static void extract_security(void)
{
/**************************************
*
* e x t r a c t _ s e c u r i t y
*
**************************************
*
* Functional description
* Get security class information for
* relations, views, and fields.
*
**************************************/
TEXT r[32], s[32], f[32], rel1[32], rel2[32];
bool first = true;
FOR R IN RDB$RELATIONS
bool first_field = true;
bool modify = false;
name_trunc(R.RDB$RELATION_NAME, r);
const TEXT* p = (R.RDB$VIEW_BLR.NULL) ? "relation" : "view";
if (!R.RDB$SECURITY_CLASS.NULL && name_trunc(R.RDB$SECURITY_CLASS, s))
{
/* skip modify relation/view add security class if we have just a stub -
i.e. security_class is 'SQL$..' and has no rdb$user_privilege entry and
relation_name matches 'SQL$relation_name' */
/* doesn't hurt to extract even if redundant with GRANT statement - i.e.
security_class is 'SQL$..' and has rdb$user_privilege entries */
/* extract if security_class is 'SQL$..' but relation_name doesn't match
'SQL$relation_name' */
/* extract if security_class doesn't start with 'SQL$' */
if (EXT_capabilities & EXT_v3) {
if (strncmp(R.RDB$SECURITY_CLASS, "SQL$", 4) == 0) {
int count = 0;
name_trunc(&R.RDB$SECURITY_CLASS[4], rel1);
name_trunc(R.RDB$RELATION_NAME, rel2);
if (strcmp(rel1, rel2) == 0) {
FOR U IN RDB$USER_PRIVILEGES
WITH U.RDB$RELATION_NAME EQ R.RDB$RELATION_NAME
count++;
END_FOR;
}
else
count = 1;
if (!count)
continue;
}
}
if (first) {
put_comment(259); /* msg 259: \n\tAdd Security Classes to Defined Objects\t\n */
first = false;
}
fprintf(output_file, "\nmodify %s %s\n\tsecurity_class %s", p, r,
s);
modify = true;
}
FOR RFR IN RDB$RELATION_FIELDS
WITH RFR.RDB$RELATION_NAME EQ R.RDB$RELATION_NAME AND
RFR.RDB$SECURITY_CLASS NOT MISSING
name_trunc(RFR.RDB$FIELD_NAME, f);
if (name_trunc(RFR.RDB$SECURITY_CLASS, s)) {
if (first) {
put_comment(259); /* msg 259: \n\tAdd Security Classes to Defined Objects\t\n */
first = false;
}
if (!modify) {
fprintf(output_file, "\nmodify %s %s", p, r);
modify = true;
}
if (!first_field)
fprintf(output_file, ",");
first_field = false;
fprintf(output_file, "\n modify field %s security_class %s", f,
s);
}
END_FOR;
if (modify)
fprintf(output_file, ";\n");
END_FOR;
}
static void extract_triggers(void)
{
/**************************************
*
* e x t r a c t _ t r i g g e r s
*
**************************************
*
* Functional description
* Get trigger definitions from
* rdb$triggers for each relation.
*
**************************************/
TEXT s[32], st[32];
bool first = true;
FOR T IN RDB$TRIGGERS WITH
(T.RDB$SYSTEM_FLAG NE 1 OR T.RDB$SYSTEM_FLAG MISSING)
SORTED BY T.RDB$TRIGGER_NAME
if (first) {
put_comment(260); /* msg 260: \n\n\tTrigger Definitions\t */
first = false;
}
name_trunc(T.RDB$TRIGGER_NAME, st);
fprintf(output_file, "\n\ndefine trigger %s", st);
name_trunc(T.RDB$RELATION_NAME, s);
fprintf(output_file, " for %s\n", s);
if (T.RDB$TRIGGER_INACTIVE)
fprintf(output_file, "\tinactive\n");
switch (T.RDB$TRIGGER_TYPE) {
case (SSHORT) trg_store:
fprintf(output_file, "\tpre store");
break;
case (SSHORT) trg_post_store:
fprintf(output_file, "\tpost store");
break;
case (SSHORT) trg_modify:
fprintf(output_file, "\tpre modify");
break;
case (SSHORT) trg_post_modify:
fprintf(output_file, "\tpost modify");
break;
case (SSHORT) trg_pre_erase:
fprintf(output_file, "\tpre erase");
break;
case (SSHORT) trg_erase:
fprintf(output_file, "\tpost erase");
break;
default:
put_comment(261); /* msg 261: ***** trigger type not understood **** */
}
fprintf(output_file, " %d:\n", T.RDB$TRIGGER_SEQUENCE);
if (T.RDB$TRIGGER_SOURCE.NULL) {
put_error(262, st);
/* msg 262: **** trigger source for trigger %s must be recreated **** */
}
else {
print_blob(&T.RDB$TRIGGER_SOURCE, 'u');
}
fprintf(output_file, "\n\tend_trigger");
if (!blob_null(T.RDB$DESCRIPTION)) {
fprintf(output_file, "\n\t{");
print_blob(&T.RDB$DESCRIPTION, 'u');
fprintf(output_file, "}");
}
extract_trig_msgs(T.RDB$TRIGGER_NAME);
fprintf(output_file, ";");
END_FOR;
}
static void extract_trig_msgs(const TEXT * trigger_name)
{
/**************************************
*
* e x t r a c t _ t r i g _ m s g s
*
**************************************
*
* Functional description
* Read the trigger messages as part of the trigger definition
* and write them to the output file.
*
**************************************/
bool first = true;
const TEXT* p = " ";
/* delimit message with single quote by default */
TEXT quote_char = 39;
FOR TM IN RDB$TRIGGER_MESSAGES
WITH TM.RDB$TRIGGER_NAME = trigger_name
SORTED BY TM.RDB$MESSAGE_NUMBER
/* If message has a ' in it, delimit with " instead */
if (strchr(TM.RDB$MESSAGE, 39) != NULL)
quote_char = 34;
fprintf(output_file, "%s\n", p);
if (first) {
p = ",";
first = false;
}
fprintf(output_file, "\tmessage ");
fprintf(output_file, " %d: ", TM.RDB$MESSAGE_NUMBER);
fprintf(output_file, " %c%s%c", quote_char, TM.RDB$MESSAGE,
quote_char);
END_FOR;
}
static void extract_view(const TEXT * rel_name,
ISC_QUAD* source,
ISC_QUAD* description,
bool source_null,
bool desc_null,
bool system_flag,
bool * first)
{
/**************************************
*
* e x t r a c t _ v i e w
*
**************************************
*
* Functional description
* Get definitions of a view and its fields
* and write it to the output file.
*
**************************************/
TEXT s[32];
if (*first) {
*first = false;
put_comment(266); /* msg 266: \n\n\tView Definitions\t */
}
name_trunc(rel_name, s);
fprintf(output_file, "\ndefine view %s of ", s);
if (source_null) {
put_error(267, s); /* msg 267: **** view definition %s must be recreated **** */
return;
}
print_blob(source, 'u');
if (system_flag)
fprintf(output_file, "\n\tsystem_flag %d", system_flag);
if (!desc_null) {
fprintf(output_file, "\t{");
print_blob(description, 'u');
fprintf(output_file, "}");
}
view_fields(rel_name);
fprintf(output_file, ";\n");
}
static void extract_views(void)
{
/**************************************
*
* e x t r a c t _ v i e w s
*
**************************************
*
* Functional description
* Get definitions of views and their fields
* and write it to the output file.
*
**************************************/
bool source_null;
bool desc_null;
TEXT s[32];
bool first = true;
if (!(EXT_capabilities & EXT_v3)) {
/* In V2 we have no choice but to output views in the order they appear */
FOR V IN RDB$RELATIONS WITH
(V.RDB$SYSTEM_FLAG NE 1 OR V.RDB$SYSTEM_FLAG MISSING) AND
V.RDB$VIEW_BLR NOT MISSING
SORTED BY V.RDB$RELATION_NAME
source_null = (V.RDB$VIEW_SOURCE.NULL);
desc_null = (blob_null(V.RDB$DESCRIPTION));
extract_view(V.RDB$RELATION_NAME, &V.RDB$VIEW_SOURCE,
&V.RDB$DESCRIPTION, source_null, desc_null,
(V.RDB$SYSTEM_FLAG), &first);
END_FOR;
return;
}
// In V3 there are dependencies that can help us to output the views in
// the correct order. But first let's just save the names of the views.
view* view_list = NULL;
FOR V IN RDB$RELATIONS WITH
(V.RDB$SYSTEM_FLAG NE 1 OR V.RDB$SYSTEM_FLAG MISSING) AND
V.RDB$VIEW_BLR NOT MISSING
SORTED BY DESC V.RDB$RELATION_NAME
name_trunc(V.RDB$RELATION_NAME, s);
view* viewp = (view*) DDL_alloc(sizeof(view) + strlen(s));
strcpy(viewp->view_name, s);
viewp->view_next = view_list;
view_list = viewp;
END_FOR;
if (!view_list)
return;
/* For each view that was found, look for any views in the list that it is
dependent on. When a view is found that isn't dependent on any views
still in the list, dump it out and remove it from the list. Continue
going through the list until nothing more can be done. */
bool did_any;
do {
did_any = false;
for (view **view_ptr = &view_list; *view_ptr;) {
bool do_this_one = true;
const TEXT* view_name = (*view_ptr)->view_name;
// fields of rdb$dependencies have been renamed for V4.0
// This means if we are extracting a pre V3 definition, views
// will not be spit out in the optimal order. */
if (EXT_capabilities & EXT_v4) {
FOR D IN RDB$DEPENDENCIES WITH
D.RDB$DEPENDENT_NAME EQ view_name AND
D.RDB$DEPENDENT_TYPE EQ 0 AND
D.RDB$DEPENDED_ON_TYPE EQ 0
name_trunc(D.RDB$DEPENDED_ON_NAME, s);
for (view* viewp = view_list; viewp; viewp = viewp->view_next)
if (!strcmp(s, viewp->view_name)) {
do_this_one = false;
break;
}
END_FOR;
}
if (do_this_one) {
FOR V IN RDB$RELATIONS WITH
V.RDB$RELATION_NAME EQ view_name
source_null = (V.RDB$VIEW_SOURCE.NULL);
desc_null = (blob_null(V.RDB$DESCRIPTION));
extract_view(V.RDB$RELATION_NAME, &V.RDB$VIEW_SOURCE,
&V.RDB$DESCRIPTION, source_null, desc_null,
(V.RDB$SYSTEM_FLAG), &first);
END_FOR;
did_any = true;
*view_ptr = (*view_ptr)->view_next;
}
else
view_ptr = &(*view_ptr)->view_next;
}
} while (did_any);
// If any views remain, we can't figure out an order so just dump them
for (view* viewp = view_list; viewp; viewp = viewp->view_next) {
FOR V IN RDB$RELATIONS WITH
V.RDB$RELATION_NAME EQ viewp->view_name
source_null = (V.RDB$VIEW_SOURCE.NULL);
desc_null = (blob_null(V.RDB$DESCRIPTION));
extract_view(V.RDB$RELATION_NAME, &V.RDB$VIEW_SOURCE,
&V.RDB$DESCRIPTION, source_null, desc_null,
(V.RDB$SYSTEM_FLAG), &first);
END_FOR;
}
}
static void field_attributes(ISC_QUAD* descr,
const TEXT* query_name,
ISC_QUAD* query_header,
const TEXT* edit_string, SSHORT system_flag)
{
/**************************************
*
* f i e l d _ a t t r i b u t e s
*
**************************************
*
* Functional description
* Print miscellaneous field attributes.
*
**************************************/
TEXT s[256];
if (descr && (descr->gds_quad_low || descr->gds_quad_high)) {
fprintf(output_file, "\n\t{");
print_blob(descr, 'u');
fprintf(output_file, "}");
}
if (system_flag)
fprintf(output_file, "\n\tsystem_flag %d", system_flag);
if (query_name && name_trunc(query_name, s))
fprintf(output_file, "\n\tquery_name %s", s);
if (query_header && (query_header->gds_quad_low || query_header->gds_quad_high))
{
fprintf(output_file, "\n\tquery_header ");
print_blob(query_header, 'h');
}
if (edit_string && name_trunc(edit_string, s))
if (strchr(s, '"'))
fprintf(output_file, "\n\tedit_string \'%s\'", s);
else
fprintf(output_file, "\n\tedit_string \"%s\"", s);
}
static void format_acl(const SCHAR * string)
{
/**************************************
*
* f o r m a t _ a c l
*
**************************************
*
* Functional description
* Format an access control list.
*
**************************************/
SCHAR c;
TEXT view_name[32], group[32], user[32], person[41],
project[41], organization[41], node[41], temp[40];
int l;
const SCHAR* p = string;
bool first = true;
if (*p++ != ACL_version)
put_comment(268); /* msg 268: ***** ACL not understood ***** */
else
fprintf(output_file, "\n");
while (*p)
switch (*p++) {
case ACL_id_list:
*view_name = *user = *group = '\0';
*person = *project = *organization = *node = '\0';
if (!first)
fprintf(output_file, ",\n");
else
first = false;
fputc('\t', output_file);
while (c = *p++) {
switch (c) {
case id_view:
l = *p++;
strncpy(view_name, p, l);
view_name[l] = 0;
break;
case id_group:
l = *p++;
strncpy(group, p, l);
group[l] = 0;
break;
case id_user:
l = *p++;
strncpy(user, p, l);
user[l] = 0;
break;
case id_person:
l = *p++;
strncpy(person, p, l);
person[l] = 0;
break;
case id_project:
l = *p++;
strncpy(temp, p, l);
temp[l] = 0;
sprintf(project, ".%s", temp);
break;
case id_organization:
l = *p++;
strncpy(temp, p, l);
temp[l] = 0;
sprintf(organization, ".%s", temp);
break;
case id_node:
l = *p++;
strncpy(temp, p, l);
temp[l] = 0;
sprintf(node, ".%s", temp);
break;
default:
// l not assigned
fb_assert(false);
return;
}
p += l;
}
/*
Here we try to decompose the identifier. Views are easy, they're just views.
If its not a view, it may be a user/group pair or just the user or just the group.
Those are operating system dependant. Otherwise, its an Apollo identifier.
*/
if (*view_name)
fprintf(output_file, "view %s ", view_name);
else
fprintf(output_file, "<%s,%s> ",
(*group) ? group : (TEXT *) "*",
(*user) ? user : (TEXT *) "*");
break;
case ACL_priv_list:
while (c = *p++)
fputc(acl_privs[static_cast<UCHAR>(c)], output_file);
break;
}
}
static char name_trunc(const TEXT * in, TEXT * out)
{
/**************************************
*
* n a m e _ t r u n c
*
**************************************
*
* Functional description
* Copy a name (or other single word)
* to the output string, elmininating
* trailing blanks.
* CVC: This function truncates at the first blank. Not suited for dialect 3.
*
**************************************/
if (!in || *in == ' ')
return 0;
const TEXT* const start = out;
while (*in && *in != ' ')
*out++ = *in++;
*out = 0;
return *start;
}
static void print_blob(ISC_QUAD* blob_id, TEXT type)
{
/**************************************
*
* p r i n t _ b l o b
*
**************************************
*
* Functional description
* Read the contents of an blob
* and write them to the output file.
* Blobs are characterized as "u" for
* unspecified, "m" for missing values
* to decode, "h" meaning query
* headers that need formatting and "a"
* for acls.
*
**************************************/
TEXT buffer[4096];
SCHAR blr_buffer[2048], *q;
SCHAR *p = NULL; // silence non initialized warning
SSHORT length;
ISC_STATUS_ARRAY status_vector;
ISC_STATUS status;
UCHAR bpb_buffer[20], *bpb, *r;
FB_API_HANDLE blob = 0;
// This logic was never finished: bpb_length (6th param) is always zero,
// so this call might be reduced to isc_open_blob().
bpb = r = bpb_buffer;
if (isc_open_blob2(status_vector, &DB, &gds_trans, &blob,
blob_id, (USHORT) (r - bpb),
bpb))
{
gds__print_status(status_vector);
DDL_err(269, NULL, NULL, NULL, NULL, NULL);
/* msg 269: isc_open_blob failed */
return;
}
bool first = true;
if (type == 'm' || type == 'a') {
p = blr_buffer;
*blr_buffer = 0;
}
while (!(status = isc_get_segment(status_vector, &blob, (USHORT*) &length,
(USHORT) (sizeof(buffer) - 1),
buffer)) || status == isc_segment)
{
buffer[length] = 0;
switch (type) {
case 'h':
if (q = (SCHAR *) strchr(buffer, '\n')) {
p = buffer;
do {
*q = 0;
fprintf(output_file, "\"%s\" / ", p);
p = q + 1;
} while (q = (SCHAR *) strchr(p, '\n'));
fprintf(output_file, "\"%s\"", p);
first = false;
break;
}
if (!first)
fprintf(output_file, " / ");
first = false;
fprintf(output_file, "\"%s\"", buffer);
break;
case 'a':
case 'm':
q = (SCHAR *) buffer;
while (length--)
*p++ = *q++;
break;
case 'u':
for (p = (SCHAR *) buffer; *p; p++)
putc(*p, output_file);
break;
default:
put_comment(270); /* msg 270: ***** blob option not understood **** */
}
}
if (status_vector[1] != isc_segstr_eof) {
gds__print_status(status_vector);
DDL_err(271, NULL, NULL, NULL, NULL, NULL); /* msg 271: isc_get_segment failed */
return;
}
if (isc_close_blob(status_vector, &blob)) {
gds__print_status(status_vector);
DDL_err(272, NULL, NULL, NULL, NULL, NULL); /* msg 272: isc_close_blob failed */
return;
}
if (type == 'm')
decompile_blr_literal(blr_buffer);
if (type == 'a')
format_acl(blr_buffer);
}
static void put_comment(USHORT number)
{
/**************************************
*
* p u t _ c o m m e n t
*
**************************************
*
* Functional description
* Retrieve a message from the error file,
* format it, and print it in the output file
* but not on the screen
*
**************************************/
TEXT message[256];
gds__msg_format(NULL, DDL_MSG_FAC, number, sizeof(message), message,
NULL, NULL, NULL, NULL, NULL);
fprintf(output_file, "%s\n\n", message);
}
static void put_error(USHORT number, const TEXT * arg1)
{
/**************************************
*
* p u t _ e r r o r
*
**************************************
*
* Functional description
* Retrieve a message from the error file,
* format it, and print it in the output file
* and on the screen
*
**************************************/
TEXT message[256];
gds__msg_format(NULL, DDL_MSG_FAC, number, sizeof(message), message,
arg1, NULL, NULL, NULL, NULL);
fprintf(output_file, "%s\n\n", message);
printf("%s\n", message);
}
static void set_capabilities(void)
{
/**************************************
*
* s e t _ c a p a b i l i t i e s
*
**************************************
*
* Functional description
* set the capabilities bits for the
* database being extracted to avoid
* unpleasantness later.
*
**************************************/
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 RDB$RELATION_FIELDS
WITH x.RDB$RELATION_NAME = rel_field_table->relation
AND x.RDB$FIELD_NAME = rel_field_table->field
EXT_capabilities |= rel_field_table->bit_mask;
END_FOR;
}
isc_release_request(gds_status, &req);
}
static void type_scalar(USHORT dtype,
SSHORT scale,
USHORT length,
SSHORT subtype, USHORT dim, const TEXT * field_name)
{
/**************************************
*
* t y p e _ s c a l a r
*
**************************************
*
* Functional description
* Finish out the kernel field definition of a
* non-blob type field.
*
**************************************/
switch (dtype) {
case blr_cstring:
fprintf(output_file, " cstring [%d]", length);
break;
case blr_text:
fprintf(output_file, " char [%d]", length);
break;
case blr_varying:
fprintf(output_file, " varying [%d]", length);
break;
case blr_short:
fprintf(output_file, " short");
break;
case blr_long:
fprintf(output_file, " long");
break;
case blr_quad:
fprintf(output_file, " quad");
break;
case blr_timestamp:
fprintf(output_file, " date");
break;
case blr_sql_date:
fprintf(output_file, " date sub_type sql_date");
break;
case blr_sql_time:
fprintf(output_file, " date sub_type sql_time");
break;
case blr_float:
fprintf(output_file, " float");
break;
case blr_double:
fprintf(output_file, " double");
break;
case blr_int64:
fprintf(output_file, " integer64");
break;
default:
fprintf(output_file, " UNKNOWN");
break;
}
if (dim) {
TEXT c = '(';
FOR D IN RDB$FIELD_DIMENSIONS WITH
D.RDB$FIELD_NAME EQ field_name
SORTED BY D.RDB$DIMENSION
if (D.RDB$LOWER_BOUND == 1)
fprintf(output_file, "%c%ld", c, D.RDB$UPPER_BOUND);
else
fprintf(output_file, "%c%ld:%ld", c, D.RDB$LOWER_BOUND,
D.RDB$UPPER_BOUND);
c = ',';
END_FOR;
fprintf(output_file, ")");
}
if (scale)
fprintf(output_file, " scale %d", scale);
if (subtype && (dtype == blr_text || dtype == blr_varying
|| dtype == blr_cstring))
{
if (subtype == 1)
fprintf(output_file, " sub_type fixed");
else
fprintf(output_file, " sub_type %d", subtype);
}
}
static void view_fields(const TEXT * view_name)
{
/**************************************
*
* v i e w _ f i e l d s
*
**************************************
*
* Functional description
* Write out the view fields, including
* such oddities as context variables.
*
**************************************/
TEXT s[32], context[32], temp[32];
bool first = true;
context[0] = 0;
FOR RFR IN RDB$RELATION_FIELDS CROSS
VR IN RDB$VIEW_RELATIONS
WITH RFR.RDB$RELATION_NAME = view_name
AND RFR.RDB$VIEW_CONTEXT != 0 AND
VR.RDB$VIEW_NAME = RFR.RDB$RELATION_NAME AND
VR.RDB$VIEW_CONTEXT = RFR.RDB$VIEW_CONTEXT
SORTED BY RFR.RDB$FIELD_POSITION
if (!first)
fprintf(output_file, ",\n");
else
first = false;
name_trunc(RFR.RDB$FIELD_NAME, s);
if (EXT_capabilities & EXT_context_name) {
FOR VR2 IN RDB$VIEW_RELATIONS WITH
VR2.RDB$VIEW_NAME = VR.RDB$VIEW_NAME AND
VR2.RDB$VIEW_CONTEXT = VR.RDB$VIEW_CONTEXT
name_trunc(VR2.RDB$CONTEXT_NAME, context);
END_FOR;
if (!(strcmp(RFR.RDB$FIELD_NAME, RFR.RDB$BASE_FIELD)))
fprintf(output_file, " %s.%s", context, s);
else {
name_trunc(RFR.RDB$BASE_FIELD, temp);
fprintf(output_file, " %s FROM %s.%s", s, context, temp);
}
if (!RFR.RDB$FIELD_POSITION.NULL)
fprintf(output_file, "\tposition %d", RFR.RDB$FIELD_POSITION);
field_attributes(&RFR.RDB$DESCRIPTION, RFR.RDB$QUERY_NAME,
&RFR.RDB$QUERY_HEADER, RFR.RDB$EDIT_STRING,
RFR.RDB$SYSTEM_FLAG);
}
END_FOR;
extract_computed(view_name, first);
}
#ifdef NOT_USED_OR_REPLACED
static void wal_info(const UCHAR* db_info_buffer,
SSHORT* num_buf,
SLONG* buf_size, SLONG* chkpt_len, SLONG* grp_commit)
{
/**************************************
*
* w a l _ i n f o
*
**************************************
*
* Functional description
*
**************************************/
UCHAR item;
const UCHAR* p = db_info_buffer;
while ((item = *p++) != isc_info_end) {
USHORT length = gds__vax_integer(p, 2);
p += 2;
switch (item) {
case isc_info_num_wal_buffers:
*num_buf = gds__vax_integer(p, length);
break;
case isc_info_wal_buffer_size:
*buf_size = gds__vax_integer(p, length);
break;
case isc_info_wal_ckpt_length:
*chkpt_len = gds__vax_integer(p, length);
break;
case isc_info_wal_grpc_wait_usecs:
*grp_commit = gds__vax_integer(p, length);
break;
default:
;
}
p += length;
}
}
#endif