8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 17:23:03 +01:00
firebird-mirror/src/gpre/gpre_meta.epp

2258 lines
59 KiB
Plaintext
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* tab=4
*____________________________________________________________
*
* PROGRAM: C preprocessor
2001-12-24 03:51:06 +01:00
* MODULE: gpre_meta.epp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Meta data interface to system
*
* 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): ______________________________________.
*
*
*____________________________________________________________
*
2004-05-03 01:06:37 +02:00
* $Id: gpre_meta.epp,v 1.41 2004-05-02 23:04:17 skidder Exp $
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../gpre/gpre.h"
#include "../jrd/license.h"
#include "../gpre/parse.h"
#include "../jrd/intl.h"
#include "../gpre/gpre_proto.h"
#include "../gpre/hsh_proto.h"
#include "../gpre/jrdme_proto.h"
#include "../gpre/gpre_meta.h"
#include "../gpre/msc_proto.h"
#include "../gpre/par_proto.h"
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
const int MAX_USER_LENGTH = 33;
const int MAX_PASSWORD_LENGTH = 33;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
DATABASE DB = FILENAME "yachts.lnk" RUNTIME database->dbb_filename;
2001-05-23 15:26:42 +02:00
extern enum lang_t sw_language;
extern bool sw_cstring;
2001-05-23 15:26:42 +02:00
extern DBB isc_databases;
static const UCHAR blr_bpb[] = {
isc_bpb_version1,
isc_bpb_source_type, 1, isc_blob_blr,
isc_bpb_target_type, 1, isc_blob_blr
2001-05-23 15:26:42 +02:00
};
#ifdef SCROLLABLE_CURSORS
2003-11-08 17:40:17 +01:00
static const SCHAR db_version_info[] = { isc_info_base_level };
2001-05-23 15:26:42 +02:00
#endif
static SLONG array_size(gpre_fld*);
static void get_array(dbb*, const TEXT*, gpre_fld*);
2003-10-07 11:58:26 +02:00
static bool get_intl_char_subtype(SSHORT*, const UCHAR*, USHORT, dbb*);
static bool resolve_charset_and_collation(SSHORT*, const UCHAR*, const UCHAR*);
static int symbol_length(const TEXT*);
#ifdef NOT_USED_OR_REPLACED
2003-10-07 11:58:26 +02:00
static int upcase(const TEXT*, TEXT*);
#endif
2001-05-23 15:26:42 +02:00
/*____________________________________________________________
*
* Lookup a field by name in a context.
* If found, return field block. If not, return NULL.
*/
gpre_fld* MET_context_field( gpre_ctx* context, const char* string)
2001-05-23 15:26:42 +02:00
{
if (context->ctx_relation) {
2003-10-07 11:58:26 +02:00
return MET_field(context->ctx_relation, string);
2001-05-23 15:26:42 +02:00
}
if (!context->ctx_procedure) {
return NULL;
}
2003-10-07 11:58:26 +02:00
SCHAR name[NAME_SIZE];
2001-05-23 15:26:42 +02:00
strcpy(name, string);
gpre_prc* procedure = context->ctx_procedure;
2001-05-23 15:26:42 +02:00
/* At this point the procedure should have been scanned, so all
* its fields are in the symbol table.
*/
gpre_fld* field = NULL;
for (gpre_sym* symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym) {
2001-05-23 15:26:42 +02:00
if (symbol->sym_type == SYM_field &&
(field = (gpre_fld*) symbol->sym_object) &&
2001-05-23 15:26:42 +02:00
field->fld_procedure == procedure)
{
return field;
}
}
return field;
}
/*____________________________________________________________
*
* Initialize meta data access to database. If the
* database can't be opened, return false.
2001-05-23 15:26:42 +02:00
*/
bool MET_database(dbb* database, bool print_version)
2001-05-23 15:26:42 +02:00
{
2003-10-07 11:58:26 +02:00
static const UCHAR sql_version_info[] =
{
isc_info_base_level,
2001-05-23 15:26:42 +02:00
isc_info_ods_version,
isc_info_db_sql_dialect,
isc_info_end
};
/*
** Each info item requested will return
**
** 1 byte for the info item tag
** 2 bytes for the length of the information that follows
** 1 to 4 bytes of integer information
**
** isc_info_end will not have a 2-byte length - which gives us
** some padding in the buffer.
*/
UCHAR sql_buffer[sizeof(sql_version_info) * (1 + 2 + 4)];
#ifndef REQUESTER
if (sw_language == lang_internal) {
2003-09-05 12:14:08 +02:00
JRDMET_init(database);
return true;
2001-05-23 15:26:42 +02:00
}
#endif
2004-05-03 01:06:37 +02:00
DB = 0;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
if (!database->dbb_filename) {
2001-05-23 15:26:42 +02:00
CPR_error("No database specified");
return false;
2001-05-23 15:26:42 +02:00
}
/* generate a dpb for the attach from the specified
* compiletime user name and password
*/
2003-10-07 11:58:26 +02:00
SCHAR dpb[MAX_PASSWORD_LENGTH + MAX_USER_LENGTH + 5];
SCHAR* d = dpb;
2003-09-05 12:14:08 +02:00
if (database->dbb_c_user || database->dbb_c_password) {
2003-11-08 17:40:17 +01:00
*d++ = isc_dpb_version1;
2003-10-07 11:58:26 +02:00
const SCHAR* p = database->dbb_c_user;
if (p) {
2003-11-08 17:40:17 +01:00
*d++ = isc_dpb_user_name;
2001-05-23 15:26:42 +02:00
*d++ = strlen(p);
while (*p)
*d++ = *p++;
}
2003-10-07 11:58:26 +02:00
p = database->dbb_c_password;
if (p) {
2003-11-08 17:40:17 +01:00
*d++ = isc_dpb_password;
2001-05-23 15:26:42 +02:00
*d++ = strlen(p);
while (*p)
*d++ = *p++;
}
}
2003-11-08 17:40:17 +01:00
if (isc_attach_database
2003-09-05 12:14:08 +02:00
(gds_status, 0, database->dbb_filename, &DB, d - &dpb[0], dpb)) {
/* We failed to attach, try in read only mode just in case
if (d == dpb)
2003-11-08 17:40:17 +01:00
*d++ = isc_dpb_version1;
*d++ = isc_dpb_set_db_readonly;
*d++ = 1;
*d++ = TRUE;
2003-11-08 17:40:17 +01:00
if (isc_attach_database
2003-09-05 12:14:08 +02:00
(gds_status, 0, database->dbb_filename, &DB, d - &dpb[0], dpb)) {
2003-08-30 04:12:44 +02:00
*/
2003-11-08 17:40:17 +01:00
isc_print_status(gds_status);
return false;
//}
2001-05-23 15:26:42 +02:00
}
2003-09-05 12:14:08 +02:00
database->dbb_handle = DB;
2001-05-23 15:26:42 +02:00
if (sw_version && print_version) {
2004-04-29 00:36:29 +02:00
printf(" Version(s) for database \"%s\"\n", database->dbb_filename);
2003-11-08 17:40:17 +01:00
isc_version(&DB, NULL, NULL);
2001-05-23 15:26:42 +02:00
}
sw_ods_version = 0;
sw_server_version = 0;
if (isc_database_info(isc_status, &DB, sizeof(sql_version_info),
reinterpret_cast<const char*>(sql_version_info), sizeof(sql_buffer),
reinterpret_cast<char*>(sql_buffer)))
{
2003-11-08 17:40:17 +01:00
isc_print_status(isc_status);
return false;
2001-05-23 15:26:42 +02:00
}
2003-10-07 11:58:26 +02:00
const UCHAR* ptr = sql_buffer;
2001-05-23 15:26:42 +02:00
while (*ptr != isc_info_end) {
2003-10-07 11:58:26 +02:00
const UCHAR item = *ptr++;
const USHORT length = isc_vax_integer((char*)ptr, sizeof(USHORT));
2001-05-23 15:26:42 +02:00
ptr += sizeof(USHORT);
2003-10-07 11:58:26 +02:00
2001-05-23 15:26:42 +02:00
switch (item) {
case isc_info_base_level:
sw_server_version = (USHORT) ptr[1];
break;
case isc_info_ods_version:
sw_ods_version = isc_vax_integer((char*)ptr, length);
2001-05-23 15:26:42 +02:00
break;
case isc_info_db_sql_dialect:
compiletime_db_dialect = isc_vax_integer((char*)ptr, length);
2001-05-23 15:26:42 +02:00
break;
2003-10-07 11:58:26 +02:00
2001-05-23 15:26:42 +02:00
case isc_info_error:
/* Error indicates that one of the option
was not understood by thr server. Make sure it was
isc_info_db_sql_dialect and assume
that the database dialect is SQL_DIALECT_V5
*/
if ((sw_server_version != 0) && (sw_ods_version != 0))
compiletime_db_dialect = SQL_DIALECT_V5;
else
2004-04-29 00:36:29 +02:00
printf("Internal error: Unexpected isc_info_value %d\n",
2001-05-23 15:26:42 +02:00
item);
break;
2003-10-07 11:58:26 +02:00
2001-05-23 15:26:42 +02:00
default:
2004-04-29 00:36:29 +02:00
printf("Internal error: Unexpected isc_info_value %d\n", item);
2001-05-23 15:26:42 +02:00
break;
}
ptr += length;
}
if (!dialect_specified)
sw_sql_dialect = compiletime_db_dialect;
if (sw_ods_version < 10) {
if (sw_sql_dialect != compiletime_db_dialect) {
char warn_mesg[100];
sprintf(warn_mesg,
"Pre 6.0 database. Cannot use dialect %d, Resetting to %d\n",
sw_sql_dialect, compiletime_db_dialect);
CPR_warn(warn_mesg);
}
sw_sql_dialect = compiletime_db_dialect;
}
else if (sw_sql_dialect != compiletime_db_dialect) {
char warn_mesg[100];
sprintf(warn_mesg,
"Client dialect set to %d. Compiletime database dialect is %d\n",
sw_sql_dialect, compiletime_db_dialect);
CPR_warn(warn_mesg);
}
#ifdef DEBUG
if (sw_version && print_version)
2004-04-29 00:36:29 +02:00
printf("Gpre Start up values. \
2001-05-23 15:26:42 +02:00
\n\tServer Ver : %d\
\n\tCompile time db Ver : %d\
\n\tCompile time db Sql Dialect : %d\
2003-10-07 11:58:26 +02:00
\n\tClient Sql dialect set to : %d\n\n",
sw_server_version, sw_ods_version, compiletime_db_dialect, sw_sql_dialect);
2001-05-23 15:26:42 +02:00
#endif
#ifdef SCROLLABLE_CURSORS
2001-12-24 03:51:06 +01:00
SCHAR buffer[16];
// get the base level of the engine
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
if (isc_database_info(gds_status, &DB, sizeof(db_version_info),
2001-05-23 15:26:42 +02:00
db_version_info, sizeof(buffer), buffer)) {
2003-11-08 17:40:17 +01:00
isc_print_status(gds_status);
return false;
2001-05-23 15:26:42 +02:00
}
/* this seems like a lot of rigamarole to read one info item,
* but it provides for easy extensibility in case we need
* more info items later
*/
2003-10-07 11:58:26 +02:00
const SCHAR* data = buffer;
2003-11-08 17:40:17 +01:00
while (*data != isc_info_end) {
2003-10-07 11:58:26 +02:00
const SCHAR item = *data++;
const USHORT l = isc_vax_integer(data, sizeof(USHORT));
data += sizeof(USHORT);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
switch (item) {
2001-05-23 15:26:42 +02:00
/* This flag indicates the version level of the engine
itself, so we can tell what capabilities the engine
code itself (as opposed to the on-disk structure).
Apparently the base level up to now indicated the major
version number, but for 4.1 the base level is being
incremented, so the base level indicates an engine version
as follows:
1 == v1.x
2 == v2.x
3 == v3.x
4 == v4.0 only
5 == v4.1
Note: this info item is so old it apparently uses an
archaic format, not a standard vax integer format.
*/
2003-11-08 17:40:17 +01:00
case isc_info_base_level:
2003-09-05 12:14:08 +02:00
database->dbb_base_level = (USHORT) data[1];
2001-05-23 15:26:42 +02:00
break;
default:
;
}
data += l;
}
#endif
return true;
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Lookup a domain by name.
* Initialize the size of the field.
*/
bool MET_domain_lookup(gpre_req* request, gpre_fld* field, const char* string)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE];
bool found = false;
2001-05-23 15:26:42 +02:00
strcpy(name, string);
/* Lookup domain. If we find it in the hash table, and it is not the
* field we a currently looking at, use it. Else look it up from the
* database.
*/
for (gpre_sym* symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym)
2003-10-07 11:58:26 +02:00
{
gpre_fld* d_field;
2001-05-23 15:26:42 +02:00
if ((symbol->sym_type == SYM_field) &&
((d_field = (gpre_fld*) symbol->sym_object) && (d_field != field)))
2003-10-07 11:58:26 +02:00
{
2001-05-23 15:26:42 +02:00
field->fld_length = d_field->fld_length;
field->fld_scale = d_field->fld_scale;
field->fld_sub_type = d_field->fld_sub_type;
field->fld_dtype = d_field->fld_dtype;
field->fld_ttype = d_field->fld_ttype;
field->fld_charset_id = d_field->fld_charset_id;
field->fld_collate_id = d_field->fld_collate_id;
field->fld_char_length = d_field->fld_char_length;
return true;
2001-05-23 15:26:42 +02:00
}
2003-10-07 11:58:26 +02:00
}
2001-05-23 15:26:42 +02:00
if (!request)
return false;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
dbb* database = request->req_database;
2003-09-05 12:14:08 +02:00
if (database->dbb_flags & DBB_sqlca)
return false;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
if (!(database->dbb_flags & DBB_v3)) {
FOR(REQUEST_HANDLE database->dbb_domain_request)
F IN RDB$FIELDS WITH F.RDB$FIELD_NAME EQ name
2003-10-07 11:58:26 +02:00
found = true;
field->fld_length = F.RDB$FIELD_LENGTH;
field->fld_scale = F.RDB$FIELD_SCALE;
field->fld_sub_type = F.RDB$FIELD_SUB_TYPE;
field->fld_dtype = MET_get_dtype(F.RDB$FIELD_TYPE, F.RDB$FIELD_SUB_TYPE,
&field->fld_length);
switch (field->fld_dtype) {
case dtype_text:
case dtype_cstring:
field->fld_flags |= FLD_text;
break;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
case dtype_blob:
field->fld_flags |= FLD_blob;
field->fld_seg_length = F.RDB$SEGMENT_LENGTH;
break;
}
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
if (field->fld_dtype <= dtype_any_text) {
if (!F.RDB$CHARACTER_LENGTH.NULL)
field->fld_char_length = F.RDB$CHARACTER_LENGTH;
if (!F.RDB$CHARACTER_SET_ID.NULL)
field->fld_charset_id = F.RDB$CHARACTER_SET_ID;
if (!F.RDB$COLLATION_ID.NULL)
field->fld_collate_id = F.RDB$COLLATION_ID;
}
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
field->fld_ttype = INTL_CS_COLL_TO_TTYPE(field->fld_charset_id,
field->fld_collate_id);
2001-05-23 15:26:42 +02:00
END_FOR;
}
else {
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_domain_request)
F IN RDB$FIELDS WITH F.RDB$FIELD_NAME EQ name
2003-10-07 11:58:26 +02:00
found = true;
field->fld_length = F.RDB$FIELD_LENGTH;
field->fld_scale = F.RDB$FIELD_SCALE;
field->fld_sub_type = F.RDB$FIELD_SUB_TYPE;
field->fld_dtype = MET_get_dtype(F.RDB$FIELD_TYPE, F.RDB$FIELD_SUB_TYPE,
&field->fld_length);
switch (field->fld_dtype) {
case dtype_text:
case dtype_cstring:
field->fld_flags |= FLD_text;
break;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
case dtype_blob:
field->fld_flags |= FLD_blob;
field->fld_seg_length = F.RDB$SEGMENT_LENGTH;
break;
}
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
field->fld_ttype = INTL_CS_COLL_TO_TTYPE(field->fld_charset_id,
field->fld_collate_id);
2001-05-23 15:26:42 +02:00
END_FOR;
}
return found;
}
/*____________________________________________________________
*
* Gets the default value for a domain of an existing table
*/
bool MET_get_domain_default(dbb* database,
const TEXT* domain_name,
TEXT* buffer,
USHORT buff_length)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE];
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vect;
2001-05-23 15:26:42 +02:00
strcpy(name, domain_name);
2003-09-05 12:14:08 +02:00
if (database == NULL)
return false;
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
if ((database->dbb_handle == 0) && !MET_database(database, false))
2001-05-23 15:26:42 +02:00
CPR_exit(FINI_ERROR);
2004-05-03 01:06:37 +02:00
fb_assert(database->dbb_transaction == 0);
FB_API_HANDLE gds_trans = 0;
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
FB_API_HANDLE DB = database->dbb_handle; // Overrides global DB
2001-05-23 15:26:42 +02:00
START_TRANSACTION;
2003-10-07 11:58:26 +02:00
bool has_default = false;
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_domain_request)
GLOB_FLD IN RDB$FIELDS WITH
GLOB_FLD.RDB$FIELD_NAME EQ name
if (!GLOB_FLD.RDB$DEFAULT_VALUE.NULL) {
ISC_QUAD* blob_id = &GLOB_FLD.RDB$DEFAULT_VALUE;
2003-10-07 11:58:26 +02:00
// open the blob
2004-05-03 01:06:37 +02:00
isc_blob_handle blob_handle = 0;
2003-10-07 11:58:26 +02:00
ISC_STATUS stat = isc_open_blob2(status_vect, &DB, &gds_trans,
&blob_handle, blob_id, sizeof(blr_bpb),
blr_bpb);
2003-10-07 11:58:26 +02:00
if (stat) {
2003-11-08 17:40:17 +01:00
isc_print_status(status_vect);
2001-05-23 15:26:42 +02:00
CPR_exit(FINI_ERROR);
2003-10-07 11:58:26 +02:00
}
// fetch segments. Assume buffer is big enough.
2003-10-07 11:58:26 +02:00
TEXT* ptr_in_buffer = buffer;
while (true) {
SSHORT length;
stat = isc_get_segment(status_vect, &blob_handle, (USHORT*) &length,
buff_length, ptr_in_buffer);
ptr_in_buffer = ptr_in_buffer + length;
buff_length = buff_length - length;
if (!stat)
continue;
2003-11-08 17:40:17 +01:00
else if (stat == isc_segstr_eof) {
// null terminate the buffer
2003-10-07 11:58:26 +02:00
*ptr_in_buffer = 0;
break;
}
else
CPR_exit(FINI_ERROR);
}
isc_close_blob(status_vect, &blob_handle);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
/* the default string must be of the form:
blr_version4 blr_literal ..... blr_eoc OR
blr_version4 blr_null blr_eoc OR
blr_version4 blr_user_name blr_eoc */
2003-11-04 00:59:24 +01:00
fb_assert(buffer[0] == blr_version4 || buffer[0] == blr_version5);
fb_assert(buffer[1] == blr_literal ||
2003-10-07 11:58:26 +02:00
buffer[1] == blr_null || buffer[1] == blr_user_name);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
has_default = true;
}
else { // default not found
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
if (sw_sql_dialect > SQL_DIALECT_V5)
buffer[0] = blr_version5;
else
buffer[0] = blr_version4;
buffer[1] = blr_eoc;
}
END_FOR
COMMIT;
return has_default;
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Gets the default value for a column of an existing table.
* Will check the default for the column of the table, if that is
* not present, will check for the default of the relevant domain
*
* The default blr is returned in buffer. The blr is of the form
* blr_version4 blr_literal ..... blr_eoc
*
* Reads the system tables RDB$FIELDS and RDB$RELATION_FIELDS.
*/
bool MET_get_column_default(const gpre_rel* relation,
2003-10-07 11:58:26 +02:00
const TEXT* column_name,
TEXT* buffer,
USHORT buff_length)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE];
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vect;
2001-05-23 15:26:42 +02:00
strcpy(name, column_name);
2003-10-07 11:58:26 +02:00
dbb* database = relation->rel_database;
if (database == NULL)
return false;
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
if ((database->dbb_handle == 0) && !MET_database(database, false))
2001-05-23 15:26:42 +02:00
CPR_exit(FINI_ERROR);
2004-05-03 01:06:37 +02:00
fb_assert(database->dbb_transaction == 0);
FB_API_HANDLE gds_trans = 0;
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
FB_API_HANDLE DB = database->dbb_handle; // Overrides global DB
2001-05-23 15:26:42 +02:00
START_TRANSACTION;
2003-10-07 11:58:26 +02:00
bool has_default = false;
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_field_request)
2001-05-23 15:26:42 +02:00
RFR IN RDB$RELATION_FIELDS CROSS F IN RDB$FIELDS WITH
RFR.RDB$FIELD_SOURCE EQ F.RDB$FIELD_NAME AND
RFR.RDB$FIELD_NAME EQ name AND
RFR.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string
2003-10-07 11:58:26 +02:00
ISC_QUAD* blob_id;
2001-05-23 15:26:42 +02:00
if (!RFR.RDB$DEFAULT_VALUE.NULL) {
2003-10-07 11:58:26 +02:00
blob_id = &RFR.RDB$DEFAULT_VALUE;
has_default = true;
}
else if (!F.RDB$DEFAULT_VALUE.NULL) {
blob_id = &F.RDB$DEFAULT_VALUE;
has_default = true;
2001-05-23 15:26:42 +02:00
}
2003-10-07 11:58:26 +02:00
if (has_default) {
// open the blob
2004-05-03 01:06:37 +02:00
isc_blob_handle blob_handle = 0;
2003-10-07 11:58:26 +02:00
ISC_STATUS stat = isc_open_blob2(status_vect, &DB, &gds_trans,
&blob_handle, blob_id, sizeof(blr_bpb),
blr_bpb);
2003-10-07 11:58:26 +02:00
if (stat) {
2003-11-08 17:40:17 +01:00
isc_print_status(status_vect);
2001-05-23 15:26:42 +02:00
CPR_exit(FINI_ERROR);
2003-10-07 11:58:26 +02:00
}
2001-05-23 15:26:42 +02:00
// fetch segments. Assuming here that the buffer is big enough.
2003-10-07 11:58:26 +02:00
TEXT* ptr_in_buffer = buffer;
while (true) {
SSHORT length;
stat = isc_get_segment(status_vect, &blob_handle,
(USHORT*)&length, buff_length, ptr_in_buffer);
ptr_in_buffer = ptr_in_buffer + length;
buff_length = buff_length - length;
if (!stat)
continue;
2003-11-08 17:40:17 +01:00
else if (stat == isc_segstr_eof) {
// null terminate the buffer
2003-10-07 11:58:26 +02:00
*ptr_in_buffer = 0;
break;
}
else
CPR_exit(FINI_ERROR);
}
isc_close_blob(status_vect, &blob_handle);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
/* the default string must be of the form:
blr_version4 blr_literal ..... blr_eoc OR
blr_version4 blr_null blr_eoc OR
blr_version4 blr_user_name blr_eoc */
2003-11-04 00:59:24 +01:00
fb_assert(buffer[0] == blr_version4 || buffer[0] == blr_version5);
fb_assert(buffer[1] == blr_literal ||
2003-10-07 11:58:26 +02:00
buffer[1] == blr_null || buffer[1] == blr_user_name);
}
else {
if (sw_sql_dialect > SQL_DIALECT_V5)
buffer[0] = blr_version5;
else
buffer[0] = blr_version4;
buffer[1] = blr_eoc;
}
2001-05-23 15:26:42 +02:00
END_FOR;
COMMIT;
return has_default;
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Lookup the fields for the primary key
* index on a relation, returning a list
* of the fields.
*/
2004-02-02 12:02:12 +01:00
gpre_lls* MET_get_primary_key(dbb* database, const TEXT* relation_name)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE];
strcpy(name, relation_name);
2003-09-05 12:14:08 +02:00
if (database == NULL)
2001-05-23 15:26:42 +02:00
return NULL;
2004-05-03 01:06:37 +02:00
if ((database->dbb_handle == 0) && !MET_database(database, false))
2001-05-23 15:26:42 +02:00
CPR_exit(FINI_ERROR);
2004-05-03 01:06:37 +02:00
fb_assert(database->dbb_transaction == 0);
FB_API_HANDLE gds_trans = 0;
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
FB_API_HANDLE DB = database->dbb_handle; // Overrides global DB
2001-05-23 15:26:42 +02:00
START_TRANSACTION;
2004-02-02 12:02:12 +01:00
gpre_lls* fields = NULL;
gpre_lls** ptr_fields = &fields;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_primary_key_request)
X IN RDB$INDICES CROSS
2002-09-10 10:07:16 +02:00
Y IN RDB$INDEX_SEGMENTS
OVER RDB$INDEX_NAME CROSS
Z IN RDB$RELATION_CONSTRAINTS
2001-05-23 15:26:42 +02:00
OVER RDB$INDEX_NAME
2002-09-10 20:30:52 +02:00
WITH Z.RDB$RELATION_NAME EQ name
AND Z.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY"
2001-05-23 15:26:42 +02:00
SORTED BY Y.RDB$FIELD_POSITION
2003-10-07 11:58:26 +02:00
2003-10-15 00:22:32 +02:00
STR field_name = (str*) MSC_string(Y.RDB$FIELD_NAME);
2003-10-07 11:58:26 +02:00
// Strip off any trailing spaces from field name
// CVC: This code stops at the first space, even in the middle.
TEXT* tmp = (TEXT*) field_name;
while (*tmp && *tmp != ' ')
*tmp++;
*tmp = '\0';
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) field_name, ptr_fields);
2003-10-07 11:58:26 +02:00
ptr_fields = &(*ptr_fields)->lls_next;
END_FOR
COMMIT;
2001-05-23 15:26:42 +02:00
return fields;
}
/*____________________________________________________________
*
* Lookup a field by name in a relation.
* If found, return field block. If not, return NULL.
*/
gpre_fld* MET_field(gpre_rel* relation, const char* string)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE];
strcpy(name, string);
2003-10-07 11:58:26 +02:00
const SSHORT length = strlen(name);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
// Lookup field. If we find it, nifty. If not, look it up in the
// database.
2001-05-23 15:26:42 +02:00
gpre_fld* field = NULL;
gpre_sym* symbol;
2001-05-23 15:26:42 +02:00
for (symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym)
if (symbol->sym_type == SYM_keyword &&
2003-10-07 11:58:26 +02:00
symbol->sym_keyword == (int) KW_DBKEY)
{
return relation->rel_dbkey;
}
2001-05-23 15:26:42 +02:00
else if (symbol->sym_type == SYM_field &&
(field = (gpre_fld*) symbol->sym_object) &&
2003-10-07 11:58:26 +02:00
field->fld_relation == relation)
{
return field;
}
2001-05-23 15:26:42 +02:00
if (sw_language == lang_internal)
return NULL;
2003-10-07 11:58:26 +02:00
dbb* database = relation->rel_database;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
if (database->dbb_flags & DBB_sqlca)
2001-05-23 15:26:42 +02:00
return NULL;
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
field = NULL;
2003-09-05 12:14:08 +02:00
if (!(database->dbb_flags & DBB_v3)) {
FOR(REQUEST_HANDLE database->dbb_field_request)
2001-05-23 15:26:42 +02:00
RFR IN RDB$RELATION_FIELDS CROSS F IN RDB$FIELDS WITH
RFR.RDB$FIELD_SOURCE EQ F.RDB$FIELD_NAME AND
RFR.RDB$FIELD_NAME EQ name AND
RFR.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string
field = (gpre_fld*) MSC_alloc(FLD_LEN);
2003-10-07 11:58:26 +02:00
field->fld_relation = relation;
field->fld_next = relation->rel_fields;
relation->rel_fields = field;
field->fld_id = RFR.RDB$FIELD_ID;
field->fld_length = F.RDB$FIELD_LENGTH;
field->fld_scale = F.RDB$FIELD_SCALE;
field->fld_position = RFR.RDB$FIELD_POSITION;
field->fld_sub_type = F.RDB$FIELD_SUB_TYPE;
field->fld_dtype =
MET_get_dtype(F.RDB$FIELD_TYPE, F.RDB$FIELD_SUB_TYPE,
2001-05-23 15:26:42 +02:00
&field->fld_length);
2003-10-07 11:58:26 +02:00
switch (field->fld_dtype) {
case dtype_text:
case dtype_cstring:
field->fld_flags |= FLD_text;
break;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
case dtype_blob:
field->fld_flags |= FLD_blob;
field->fld_seg_length = F.RDB$SEGMENT_LENGTH;
break;
}
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
if (F.RDB$DIMENSIONS && !(database->dbb_flags & DBB_no_arrays))
get_array(database, F.RDB$FIELD_NAME, field);
if ((field->fld_dtype <= dtype_any_text)
|| (field->fld_dtype == dtype_blob))
{
if (!F.RDB$CHARACTER_LENGTH.NULL)
field->fld_char_length = F.RDB$CHARACTER_LENGTH;
if (!F.RDB$CHARACTER_SET_ID.NULL)
field->fld_charset_id = F.RDB$CHARACTER_SET_ID;
if (!RFR.RDB$COLLATION_ID.NULL)
field->fld_collate_id = RFR.RDB$COLLATION_ID;
else if (!F.RDB$COLLATION_ID.NULL)
field->fld_collate_id = F.RDB$COLLATION_ID;
}
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
field->fld_ttype =
INTL_CS_COLL_TO_TTYPE(field->fld_charset_id,
field->fld_collate_id);
field->fld_symbol = symbol =
MSC_symbol(SYM_field, name, length, (gpre_ctx*) field);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
field->fld_global = symbol =
MSC_symbol(SYM_field, RFR.RDB$FIELD_SOURCE,
symbol_length(RFR.RDB$FIELD_SOURCE), (gpre_ctx*) field);
2001-05-23 15:26:42 +02:00
END_FOR;
}
else {
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_field_request)
2001-05-23 15:26:42 +02:00
RFR IN RDB$RELATION_FIELDS CROSS F IN RDB$FIELDS WITH
RFR.RDB$FIELD_SOURCE EQ F.RDB$FIELD_NAME AND
RFR.RDB$FIELD_NAME EQ name AND
RFR.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string
field = (gpre_fld*) MSC_alloc(FLD_LEN);
2003-10-07 11:58:26 +02:00
field->fld_relation = relation;
field->fld_next = relation->rel_fields;
relation->rel_fields = field;
field->fld_id = RFR.RDB$FIELD_ID;
field->fld_length = F.RDB$FIELD_LENGTH;
field->fld_scale = F.RDB$FIELD_SCALE;
field->fld_position = RFR.RDB$FIELD_POSITION;
field->fld_sub_type = F.RDB$FIELD_SUB_TYPE;
field->fld_dtype =
MET_get_dtype(F.RDB$FIELD_TYPE, F.RDB$FIELD_SUB_TYPE,
&field->fld_length);
switch (field->fld_dtype) {
case dtype_text:
case dtype_cstring:
field->fld_flags |= FLD_text;
break;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
case dtype_blob:
field->fld_flags |= FLD_blob;
field->fld_seg_length = F.RDB$SEGMENT_LENGTH;
break;
}
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
if (!(database->dbb_flags & DBB_no_arrays))
get_array(database, F.RDB$FIELD_NAME, field);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
field->fld_ttype =
INTL_CS_COLL_TO_TTYPE(field->fld_charset_id,
field->fld_collate_id);
field->fld_symbol = symbol =
MSC_symbol(SYM_field, name, length, (gpre_ctx*) field);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
field->fld_global = symbol =
MSC_symbol(SYM_field, RFR.RDB$FIELD_SOURCE,
symbol_length(RFR.RDB$FIELD_SOURCE), (gpre_ctx*) field);
2001-05-23 15:26:42 +02:00
END_FOR;
}
return field;
}
/*____________________________________________________________
*
* Return a list of the fields in a relation
*/
GPRE_NOD MET_fields(gpre_ctx* context)
2001-05-23 15:26:42 +02:00
{
gpre_fld* field;
2003-10-07 11:58:26 +02:00
GPRE_NOD field_node;
2001-05-23 15:26:42 +02:00
REF reference;
gpre_prc* procedure = context->ctx_procedure;
2003-10-07 11:58:26 +02:00
if (procedure) {
2003-10-15 00:22:32 +02:00
GPRE_NOD node = MSC_node(nod_list, procedure->prc_out_count);
2003-10-07 11:58:26 +02:00
//count = 0;
2001-05-23 15:26:42 +02:00
for (field = procedure->prc_outputs; field; field = field->fld_next) {
2003-10-15 03:18:01 +02:00
reference = (REF) MSC_alloc(REF_LEN);
2001-05-23 15:26:42 +02:00
reference->ref_field = field;
reference->ref_context = context;
2002-11-11 20:19:43 +01:00
field_node = MSC_unary(nod_field, (GPRE_NOD) reference);
2001-05-23 15:26:42 +02:00
node->nod_arg[field->fld_position] = field_node;
2003-10-07 11:58:26 +02:00
//count++;
2001-05-23 15:26:42 +02:00
}
return node;
}
gpre_rel* relation = context->ctx_relation;
2001-05-23 15:26:42 +02:00
if (relation->rel_meta) {
2003-10-07 11:58:26 +02:00
int count = 0;
for (field = relation->rel_fields; field; field = field->fld_next) {
++count;
}
2003-10-15 00:22:32 +02:00
GPRE_NOD anode = MSC_node(nod_list, count);
2003-10-07 11:58:26 +02:00
//count = 0;
2001-05-23 15:26:42 +02:00
for (field = relation->rel_fields; field; field = field->fld_next) {
2003-10-15 03:18:01 +02:00
reference = (REF) MSC_alloc(REF_LEN);
2001-05-23 15:26:42 +02:00
reference->ref_field = field;
reference->ref_context = context;
2002-11-11 20:19:43 +01:00
field_node = MSC_unary(nod_field, (GPRE_NOD) reference);
2003-10-07 11:58:26 +02:00
anode->nod_arg[field->fld_position] = field_node;
//count++;
2001-05-23 15:26:42 +02:00
}
2003-10-07 11:58:26 +02:00
return anode;
2001-05-23 15:26:42 +02:00
}
if (sw_language == lang_internal)
return NULL;
2003-10-07 11:58:26 +02:00
dbb* database = relation->rel_database;
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
int count = 0;
2004-02-02 12:02:12 +01:00
gpre_lls* stack = NULL;
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_flds_request)
RFR IN RDB$RELATION_FIELDS CROSS GLOB_FLD IN RDB$FIELDS WITH
RFR.RDB$FIELD_SOURCE EQ GLOB_FLD.RDB$FIELD_NAME AND
2001-05-23 15:26:42 +02:00
RFR.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string
SORTED BY RFR.RDB$FIELD_POSITION
2003-10-07 11:58:26 +02:00
TEXT* p;
// This code stops at the first blank.
2001-05-23 15:26:42 +02:00
for (p = RFR.RDB$FIELD_NAME; *p && *p != ' '; p++);
2003-10-07 11:58:26 +02:00
*p = 0;
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) MET_field(relation, RFR.RDB$FIELD_NAME), &stack);
2003-10-07 11:58:26 +02:00
count++;
2001-05-23 15:26:42 +02:00
END_FOR;
2003-10-15 00:22:32 +02:00
GPRE_NOD node = MSC_node(nod_list, count);
2001-05-23 15:26:42 +02:00
while (stack) {
field = (gpre_fld*) MSC_pop(&stack);
2003-10-15 03:18:01 +02:00
reference = (REF) MSC_alloc(REF_LEN);
2001-05-23 15:26:42 +02:00
reference->ref_field = field;
reference->ref_context = context;
2002-11-11 20:19:43 +01:00
field_node = MSC_unary(nod_field, (GPRE_NOD) reference);
2001-05-23 15:26:42 +02:00
node->nod_arg[--count] = field_node;
}
return node;
}
/*____________________________________________________________
*
* Shutdown all attached databases.
*/
2003-09-05 12:14:08 +02:00
void MET_fini(dbb* end)
2001-05-23 15:26:42 +02:00
{
2003-10-07 11:58:26 +02:00
for (dbb* database = isc_databases;
2003-09-05 12:14:08 +02:00
database && database != end;
database = database->dbb_next)
{
if (DB = database->dbb_handle) {
if (gds_trans = database->dbb_transaction)
2001-05-23 15:26:42 +02:00
COMMIT;
FINISH;
2004-05-03 01:06:37 +02:00
database->dbb_handle = 0;
database->dbb_transaction = 0;
database->dbb_field_request = 0;
database->dbb_flds_request = 0;
database->dbb_relation_request = 0;
database->dbb_procedure_request = 0;
database->dbb_udf_request = 0;
database->dbb_trigger_request = 0;
database->dbb_proc_prms_request = 0;
database->dbb_proc_prm_fld_request = 0;
database->dbb_index_request = 0;
database->dbb_type_request = 0;
database->dbb_array_request = 0;
database->dbb_dimension_request = 0;
database->dbb_domain_request = 0;
database->dbb_generator_request = 0;
database->dbb_view_request = 0;
database->dbb_primary_key_request = 0;
2001-05-23 15:26:42 +02:00
}
2003-09-05 12:14:08 +02:00
}
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Lookup a generator by name.
* If found, return string. If not, return NULL.
*/
const SCHAR* MET_generator(const TEXT* string, dbb* database)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE];
strcpy(name, string);
for (gpre_sym* symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym)
2001-05-23 15:26:42 +02:00
if ((symbol->sym_type == SYM_generator) &&
(database == (dbb*) (symbol->sym_object)))
{
return symbol->sym_string;
}
2001-05-23 15:26:42 +02:00
return NULL;
}
/*____________________________________________________________
*
* Compute internal datatype and length based on system relation field values.
*/
2003-10-07 11:58:26 +02:00
USHORT MET_get_dtype(USHORT blr_dtype, USHORT sub_type, USHORT* length)
2001-05-23 15:26:42 +02:00
{
2003-10-07 11:58:26 +02:00
USHORT dtype;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
USHORT l = *length;
2001-05-23 15:26:42 +02:00
switch (blr_dtype) {
case blr_varying:
case blr_text:
dtype = dtype_text;
2003-10-18 01:41:27 +02:00
if (sw_cstring && sub_type != dsc_text_type_fixed) {
2001-05-23 15:26:42 +02:00
++l;
dtype = dtype_cstring;
}
break;
case blr_cstring:
dtype = dtype_cstring;
++l;
break;
case blr_short:
dtype = dtype_short;
l = sizeof(SSHORT);
break;
case blr_long:
dtype = dtype_long;
l = sizeof(SLONG);
break;
case blr_quad:
dtype = dtype_quad;
l = sizeof(ISC_QUAD);
2001-05-23 15:26:42 +02:00
break;
case blr_float:
dtype = dtype_real;
l = sizeof(float);
break;
case blr_double:
dtype = dtype_double;
l = sizeof(double);
break;
case blr_blob:
dtype = dtype_blob;
l = sizeof(ISC_QUAD);
2001-05-23 15:26:42 +02:00
break;
/** Begin sql date/time/timestamp **/
case blr_sql_date:
dtype = dtype_sql_date;
l = sizeof(ISC_DATE);
break;
case blr_sql_time:
dtype = dtype_sql_time;
l = sizeof(ISC_TIME);
break;
case blr_timestamp:
dtype = dtype_timestamp;
l = sizeof(ISC_TIMESTAMP);
break;
/** Begin sql date/time/timestamp **/
case blr_int64:
dtype = dtype_int64;
l = sizeof(ISC_INT64);
break;
default:
CPR_error("datatype not supported");
}
*length = l;
return dtype;
}
/*____________________________________________________________
*
* Lookup a procedure (represented by a token) in a database.
* Return a procedure block (if name is found) or NULL.
*
* This function has been cloned into MET_get_udf
*/
gpre_prc* MET_get_procedure(dbb* database, const TEXT* string, const TEXT* owner_name)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE], owner[NAME_SIZE];
strcpy(name, string);
strcpy(owner, owner_name);
gpre_prc* procedure = NULL;
2001-05-23 15:26:42 +02:00
for (gpre_sym* symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym)
2001-05-23 15:26:42 +02:00
if (symbol->sym_type == SYM_procedure &&
(procedure = (gpre_prc*) symbol->sym_object) &&
2003-09-05 12:14:08 +02:00
procedure->prc_database == database &&
2001-05-23 15:26:42 +02:00
(!owner[0] ||
(procedure->prc_owner
2003-10-07 11:58:26 +02:00
&& !strcmp(owner, procedure->prc_owner->sym_string))))
{
break;
}
2001-05-23 15:26:42 +02:00
if (!procedure)
return NULL;
if (procedure->prc_flags & PRC_scanned)
return procedure;
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_procedure_request)
2001-05-23 15:26:42 +02:00
X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_ID = procedure->prc_id;
2003-10-07 11:58:26 +02:00
for (USHORT type = 0; type < 2; type++) {
USHORT count = 0;
gpre_fld** fld_list;
2001-05-23 15:26:42 +02:00
if (type)
fld_list = &procedure->prc_outputs;
else
fld_list = &procedure->prc_inputs;
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_proc_prms_request)
2001-05-23 15:26:42 +02:00
Y IN RDB$PROCEDURE_PARAMETERS WITH
Y.RDB$PROCEDURE_NAME EQ name AND
Y.RDB$PARAMETER_TYPE EQ type
2003-10-07 11:58:26 +02:00
SORTED BY DESCENDING Y.RDB$PARAMETER_NUMBER
count++;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
FOR(REQUEST_HANDLE database->dbb_proc_prm_fld_request)
F IN RDB$FIELDS WITH
Y.RDB$FIELD_SOURCE EQ F.RDB$FIELD_NAME
gpre_fld* field = (gpre_fld*) MSC_alloc(FLD_LEN);
2003-10-07 11:58:26 +02:00
field->fld_procedure = procedure;
field->fld_next = *fld_list;
*fld_list = field;
field->fld_position = Y.RDB$PARAMETER_NUMBER;
field->fld_length = F.RDB$FIELD_LENGTH;
field->fld_scale = F.RDB$FIELD_SCALE;
field->fld_sub_type = F.RDB$FIELD_SUB_TYPE;
field->fld_dtype = MET_get_dtype(F.RDB$FIELD_TYPE,
F.RDB$FIELD_SUB_TYPE,
&field->fld_length);
switch (field->fld_dtype) {
case dtype_text:
case dtype_cstring:
field->fld_flags |= FLD_text;
if (!F.RDB$CHARACTER_LENGTH.NULL)
field->fld_char_length = F.RDB$CHARACTER_LENGTH;
if (!F.RDB$CHARACTER_SET_ID.NULL)
field->fld_charset_id = F.RDB$CHARACTER_SET_ID;
if (!F.RDB$COLLATION_ID.NULL)
field->fld_collate_id = F.RDB$COLLATION_ID;
field->fld_ttype =
INTL_CS_COLL_TO_TTYPE(field->fld_charset_id,
field->fld_collate_id);
break;
case dtype_blob:
field->fld_flags |= FLD_blob;
field->fld_seg_length = F.RDB$SEGMENT_LENGTH;
if (!F.RDB$CHARACTER_SET_ID.NULL)
field->fld_charset_id = F.RDB$CHARACTER_SET_ID;
break;
}
field->fld_symbol = MSC_symbol(SYM_field,
Y.RDB$PARAMETER_NAME,
symbol_length(Y.RDB$PARAMETER_NAME),
(gpre_ctx*) field);
2003-10-07 11:58:26 +02:00
/* If output parameter, insert in symbol table as a
field. */
if (type)
HSH_insert(field->fld_symbol);
END_FOR;
2001-05-23 15:26:42 +02:00
END_FOR;
if (type)
procedure->prc_out_count = count;
else
procedure->prc_in_count = count;
}
END_FOR;
procedure->prc_flags |= PRC_scanned;
/* Generate a dummy relation to point to the procedure to use when procedure
* is used as a view.
*/
return procedure;
}
/*____________________________________________________________
*
* Lookup a relation (represented by a token) in a database.
* Return a relation block (if name is found) or NULL.
*/
gpre_rel* MET_get_relation(dbb* database, const TEXT* string, const TEXT* owner_name)
2001-05-23 15:26:42 +02:00
{
gpre_rel* relation;
2001-05-23 15:26:42 +02:00
SCHAR name[NAME_SIZE], owner[NAME_SIZE];
strcpy(name, string);
strcpy(owner, owner_name);
for (gpre_sym* symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym)
2001-05-23 15:26:42 +02:00
if (symbol->sym_type == SYM_relation &&
(relation = (gpre_rel*) symbol->sym_object) &&
2003-09-05 12:14:08 +02:00
relation->rel_database == database &&
2001-05-23 15:26:42 +02:00
(!owner[0] ||
(relation->rel_owner
2003-10-07 11:58:26 +02:00
&& !strcmp(owner, relation->rel_owner->sym_string))))
{
return relation;
}
2001-05-23 15:26:42 +02:00
return NULL;
}
/*____________________________________________________________
*
*/
INTLSYM MET_get_text_subtype(SSHORT ttype)
{
2003-10-07 11:58:26 +02:00
for (INTLSYM p = text_subtypes; p; p = p->intlsym_next)
2001-05-23 15:26:42 +02:00
if (p->intlsym_ttype == ttype)
return p;
return NULL;
}
/*____________________________________________________________
*
* Lookup a udf (represented by a token) in a database.
* Return a udf block (if name is found) or NULL.
*
* This function was cloned from MET_get_procedure
*/
udf* MET_get_udf(dbb* database, const TEXT* string)
2001-05-23 15:26:42 +02:00
{
SCHAR name[NAME_SIZE];
strcpy(name, string);
2003-10-07 11:58:26 +02:00
udf* the_udf = NULL;
for (gpre_sym* symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym)
2001-05-23 15:26:42 +02:00
if (symbol->sym_type == SYM_udf &&
2003-09-05 12:14:08 +02:00
(the_udf = (udf*) symbol->sym_object) && the_udf->udf_database == database)
2001-05-23 15:26:42 +02:00
break;
2003-09-05 12:14:08 +02:00
if (!the_udf)
2001-05-23 15:26:42 +02:00
return NULL;
2003-09-05 12:14:08 +02:00
if (the_udf->udf_flags & UDF_scanned)
return the_udf;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
if (database->dbb_flags & DBB_v3) {
// Version of V4 request without new V4 metadata
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_udf_request)
2001-05-23 15:26:42 +02:00
UDF_DEF IN RDB$FUNCTIONS CROSS
UDF_ARG IN RDB$FUNCTION_ARGUMENTS
WITH UDF_DEF.RDB$FUNCTION_NAME EQ name AND
UDF_DEF.RDB$FUNCTION_NAME EQ UDF_ARG.RDB$FUNCTION_NAME AND
UDF_DEF.RDB$RETURN_ARGUMENT != UDF_ARG.RDB$ARGUMENT_POSITION
SORTED BY DESCENDING UDF_ARG.RDB$ARGUMENT_POSITION;
gpre_fld* field = (gpre_fld*) MSC_alloc(FLD_LEN);
2003-10-07 11:58:26 +02:00
field->fld_next = the_udf->udf_inputs;
the_udf->udf_inputs = field;
the_udf->udf_args++;
field->fld_position = UDF_ARG.RDB$ARGUMENT_POSITION;
field->fld_length = UDF_ARG.RDB$FIELD_LENGTH;
field->fld_scale = UDF_ARG.RDB$FIELD_SCALE;
field->fld_sub_type = UDF_ARG.RDB$FIELD_SUB_TYPE;
field->fld_dtype = MET_get_dtype(UDF_ARG.RDB$FIELD_TYPE,
UDF_ARG.RDB$FIELD_SUB_TYPE,
&field->fld_length);
switch (field->fld_dtype) {
case dtype_text:
case dtype_cstring:
field->fld_flags |= FLD_text;
break;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
case dtype_blob:
field->fld_flags |= FLD_blob;
break;
}
2001-05-23 15:26:42 +02:00
END_FOR;
}
else {
// Same request as above, but with V4 metadata also fetched
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_udf_request)
2001-05-23 15:26:42 +02:00
UDF_DEF IN RDB$FUNCTIONS CROSS
UDF_ARG IN RDB$FUNCTION_ARGUMENTS
WITH UDF_DEF.RDB$FUNCTION_NAME EQ name AND
UDF_DEF.RDB$FUNCTION_NAME EQ UDF_ARG.RDB$FUNCTION_NAME AND
UDF_DEF.RDB$RETURN_ARGUMENT != UDF_ARG.RDB$ARGUMENT_POSITION
SORTED BY DESCENDING UDF_ARG.RDB$ARGUMENT_POSITION;
gpre_fld* field = (gpre_fld*) MSC_alloc(FLD_LEN);
2003-10-07 11:58:26 +02:00
field->fld_next = the_udf->udf_inputs;
the_udf->udf_inputs = field;
the_udf->udf_args++;
field->fld_position = UDF_ARG.RDB$ARGUMENT_POSITION;
field->fld_length = UDF_ARG.RDB$FIELD_LENGTH;
field->fld_scale = UDF_ARG.RDB$FIELD_SCALE;
field->fld_sub_type = UDF_ARG.RDB$FIELD_SUB_TYPE;
field->fld_dtype = MET_get_dtype(UDF_ARG.RDB$FIELD_TYPE,
UDF_ARG.RDB$FIELD_SUB_TYPE,
&field->fld_length);
switch (field->fld_dtype) {
case dtype_text:
case dtype_cstring:
field->fld_flags |= FLD_text;
if (!UDF_ARG.RDB$CHARACTER_SET_ID.NULL)
field->fld_charset_id = UDF_ARG.RDB$CHARACTER_SET_ID;
field->fld_ttype =
INTL_CS_COLL_TO_TTYPE(field->fld_charset_id,
field->fld_collate_id);
break;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
case dtype_blob:
field->fld_flags |= FLD_blob;
if (!UDF_ARG.RDB$CHARACTER_SET_ID.NULL)
field->fld_charset_id = UDF_ARG.RDB$CHARACTER_SET_ID;
break;
}
2001-05-23 15:26:42 +02:00
END_FOR;
}
2003-09-05 12:14:08 +02:00
the_udf->udf_flags |= UDF_scanned;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
return the_udf;
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Return relation if the passed view_name represents a
2001-05-23 15:26:42 +02:00
* view with the passed relation as a base table
* (the relation could be an alias).
*/
gpre_rel* MET_get_view_relation(gpre_req* request,
const char* view_name,
const char* relation_or_alias,
USHORT level)
2001-05-23 15:26:42 +02:00
{
2003-10-07 11:58:26 +02:00
dbb* database = request->req_database;
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
gpre_rel* relation = NULL;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_view_request, LEVEL level)
2001-05-23 15:26:42 +02:00
X IN RDB$VIEW_RELATIONS WITH
X.RDB$VIEW_NAME EQ view_name
2003-10-07 11:58:26 +02:00
TEXT* p;
// This code stops at the first blank.
2001-05-23 15:26:42 +02:00
for (p = X.RDB$CONTEXT_NAME; *p && *p != ' '; p++);
*p = 0;
2001-05-23 15:26:42 +02:00
for (p = X.RDB$RELATION_NAME; *p && *p != ' '; p++);
*p = 0;
2001-05-23 15:26:42 +02:00
if (!strcmp(X.RDB$RELATION_NAME, relation_or_alias) ||
!strcmp(X.RDB$CONTEXT_NAME, relation_or_alias))
{
return MET_get_relation(database, X.RDB$RELATION_NAME, "");
}
2001-05-23 15:26:42 +02:00
if (relation = MET_get_view_relation(request, X.RDB$RELATION_NAME,
relation_or_alias, level + 1))
{
return relation;
}
2001-05-23 15:26:42 +02:00
END_FOR;
return NULL;
}
/*____________________________________________________________
*
* Lookup an index for a database.
* Return an index block (if name is found) or NULL.
*/
2003-10-07 11:58:26 +02:00
IND MET_index(dbb* database, const TEXT* string)
2001-05-23 15:26:42 +02:00
{
IND index;
SCHAR name[NAME_SIZE];
strcpy(name, string);
2003-10-07 11:58:26 +02:00
const USHORT length = strlen(name);
2001-05-23 15:26:42 +02:00
gpre_sym* symbol;
2001-05-23 15:26:42 +02:00
for (symbol = HSH_lookup(name); symbol; symbol = symbol->sym_homonym)
if (symbol->sym_type == SYM_index &&
(index = (IND) symbol->sym_object) &&
2003-09-05 12:14:08 +02:00
index->ind_relation->rel_database == database)
2001-05-23 15:26:42 +02:00
return index;
if (sw_language == lang_internal)
return NULL;
2003-09-05 12:14:08 +02:00
if (database->dbb_flags & DBB_sqlca)
2001-05-23 15:26:42 +02:00
return NULL;
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
index = NULL;
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_index_request)
2001-05-23 15:26:42 +02:00
X IN RDB$INDICES WITH X.RDB$INDEX_NAME EQ name
2003-10-15 03:18:01 +02:00
index = (IND) MSC_alloc(IND_LEN);
index->ind_symbol = symbol = MSC_symbol(SYM_index, name, length, (gpre_ctx*) index);
2003-10-07 11:58:26 +02:00
index->ind_relation = MET_get_relation(database, X.RDB$RELATION_NAME, "");
HSH_insert(symbol);
2001-05-23 15:26:42 +02:00
END_FOR;
return index;
}
/*____________________________________________________________
*
2003-10-07 11:58:26 +02:00
* Load all of the relation names, procedure names
2001-05-23 15:26:42 +02:00
* and user defined function names
* into the symbol (hash) table.
2003-10-07 11:58:26 +02:00
* Includes also charsets and collations.
2001-05-23 15:26:42 +02:00
*/
2003-09-05 12:14:08 +02:00
void MET_load_hash_table(dbb* database)
2001-05-23 15:26:42 +02:00
{
/* If this is an internal ISC access method invocation, don't do any of this
* stuff
*/
if (sw_language == lang_internal)
return;
2003-09-05 12:14:08 +02:00
if (!database->dbb_handle)
if (!MET_database(database, false))
2001-05-23 15:26:42 +02:00
CPR_exit(FINI_ERROR);
if (database->dbb_transaction) {
// we must have already loaded this one
2001-05-23 15:26:42 +02:00
return;
}
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
gds_trans = 0;
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
2001-05-23 15:26:42 +02:00
START_TRANSACTION;
2003-09-05 12:14:08 +02:00
database->dbb_transaction = gds_trans;
2001-05-23 15:26:42 +02:00
// Determine if the database is V3.
2001-05-23 15:26:42 +02:00
bool post_v3_flag = false;
2004-05-03 01:06:37 +02:00
FB_API_HANDLE handle = 0;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME = 'RDB$PROCEDURES' AND
X.RDB$SYSTEM_FLAG = 1
post_v3_flag = true;
2001-05-23 15:26:42 +02:00
END_FOR;
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
if (!post_v3_flag)
2003-09-05 12:14:08 +02:00
database->dbb_flags |= DBB_v3;
2001-05-23 15:26:42 +02:00
gpre_sym* symbol;
2003-10-07 11:58:26 +02:00
TEXT* p;
SLONG length;
2001-05-23 15:26:42 +02:00
/* Pick up all relations (necessary to parse parts of the GDML grammar) */
2003-09-05 12:14:08 +02:00
if (database->dbb_flags & DBB_v3) {
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
2003-10-07 11:58:26 +02:00
X IN RDB$RELATIONS
gpre_rel* relation = (gpre_rel*) MSC_alloc(REL_LEN);
2003-10-07 11:58:26 +02:00
relation->rel_database = database;
relation->rel_next = database->dbb_relations;
database->dbb_relations = relation;
relation->rel_id = X.RDB$RELATION_ID;
relation->rel_symbol = symbol =
MSC_symbol(SYM_relation, X.RDB$RELATION_NAME,
symbol_length(X.RDB$RELATION_NAME), (gpre_ctx*) relation);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
const USHORT dbk_len = (X.RDB$DBKEY_LENGTH) ? X.RDB$DBKEY_LENGTH : 8;
gpre_fld* dbkey = MET_make_field("rdb$db_key", dtype_text,
2003-10-07 11:58:26 +02:00
dbk_len, false);
relation->rel_dbkey = dbkey;
dbkey->fld_flags |= FLD_dbkey | FLD_text | FLD_charset;
dbkey->fld_ttype = ttype_binary;
2001-05-23 15:26:42 +02:00
END_FOR;
}
else {
FOR(REQUEST_HANDLE handle)
2003-10-07 11:58:26 +02:00
X IN RDB$RELATIONS
gpre_rel* relation = (gpre_rel*) MSC_alloc(REL_LEN);
2003-10-07 11:58:26 +02:00
relation->rel_database = database;
relation->rel_next = database->dbb_relations;
database->dbb_relations = relation;
relation->rel_id = X.RDB$RELATION_ID;
relation->rel_symbol = symbol =
MSC_symbol(SYM_relation, X.RDB$RELATION_NAME,
symbol_length(X.RDB$RELATION_NAME), (gpre_ctx*) relation);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
const USHORT dbk_len = (X.RDB$DBKEY_LENGTH) ? X.RDB$DBKEY_LENGTH : 8;
gpre_fld* dbkey = MET_make_field("rdb$db_key", dtype_text,
2003-10-07 11:58:26 +02:00
dbk_len, false);
relation->rel_dbkey = dbkey;
dbkey->fld_flags |= FLD_dbkey | FLD_text | FLD_charset;
dbkey->fld_ttype = ttype_binary;
if (!X.RDB$OWNER_NAME.NULL
&& (length = symbol_length(X.RDB$OWNER_NAME)))
{
relation->rel_owner =
MSC_symbol(SYM_username, X.RDB$OWNER_NAME, length, NULL);
}
2001-05-23 15:26:42 +02:00
END_FOR;
}
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
/* Pick up all procedures (necessary to parse parts of the GDML grammar) */
FOR(REQUEST_HANDLE handle)
2003-10-07 11:58:26 +02:00
X IN RDB$PROCEDURES
gpre_prc* procedure = (gpre_prc*) MSC_alloc(REL_LEN);
2003-10-07 11:58:26 +02:00
procedure->prc_database = database;
procedure->prc_next = (gpre_prc*) database->dbb_procedures;
database->dbb_procedures = (gpre_rel*) procedure;
2003-10-07 11:58:26 +02:00
procedure->prc_id = X.RDB$PROCEDURE_ID;
procedure->prc_symbol = symbol =
MSC_symbol(SYM_procedure, X.RDB$PROCEDURE_NAME,
symbol_length(X.RDB$PROCEDURE_NAME), (gpre_ctx*) procedure);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
if (!X.RDB$OWNER_NAME.NULL && (length = symbol_length(X.RDB$OWNER_NAME)))
procedure->prc_owner =
MSC_symbol(SYM_username, X.RDB$OWNER_NAME, length, NULL);
END_FOR
ON_ERROR
// assume pre V4 database, no procedures
2003-10-07 11:58:26 +02:00
END_ERROR;
2001-05-23 15:26:42 +02:00
if (handle)
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
/* Pickup any user defined functions. If the database does not support udf's,
2001-05-23 15:26:42 +02:00
* this may fail
*/
2004-05-03 01:06:37 +02:00
FB_API_HANDLE handle2 = 0;
2003-10-07 11:58:26 +02:00
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
FUN IN RDB$FUNCTIONS CROSS ARG IN RDB$FUNCTION_ARGUMENTS WITH
FUN.RDB$FUNCTION_NAME EQ ARG.RDB$FUNCTION_NAME AND
FUN.RDB$RETURN_ARGUMENT EQ ARG.RDB$ARGUMENT_POSITION
p = FUN.RDB$FUNCTION_NAME;
2003-10-07 11:58:26 +02:00
length = symbol_length(p);
2001-05-23 15:26:42 +02:00
p[length] = 0;
2003-10-15 03:18:01 +02:00
udf* an_udf = (udf*) MSC_alloc(UDF_LEN + length);
2003-10-07 11:58:26 +02:00
strcpy(an_udf->udf_function, p);
an_udf->udf_database = database;
an_udf->udf_type = FUN.RDB$FUNCTION_TYPE;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
if (length = symbol_length(FUN.RDB$QUERY_NAME)) {
p = FUN.RDB$QUERY_NAME;
p[length] = 0;
}
an_udf->udf_symbol = symbol = MSC_symbol(SYM_udf, p, strlen(p), (gpre_ctx*) an_udf);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
an_udf->udf_length = ARG.RDB$FIELD_LENGTH;
an_udf->udf_scale = ARG.RDB$FIELD_SCALE;
an_udf->udf_sub_type = ARG.RDB$FIELD_SUB_TYPE;
an_udf->udf_dtype =
MET_get_dtype(ARG.RDB$FIELD_TYPE, ARG.RDB$FIELD_SUB_TYPE,
&an_udf->udf_length);
if (post_v3_flag) {
FOR(REQUEST_HANDLE handle2)
V4ARG IN RDB$FUNCTION_ARGUMENTS
CROSS CS IN RDB$CHARACTER_SETS
CROSS COLL IN RDB$COLLATIONS WITH
V4ARG.RDB$CHARACTER_SET_ID EQ CS.RDB$CHARACTER_SET_ID AND
COLL.RDB$COLLATION_NAME EQ CS.RDB$DEFAULT_COLLATE_NAME AND
V4ARG.RDB$CHARACTER_SET_ID NOT MISSING AND
V4ARG.RDB$FUNCTION_NAME EQ ARG.RDB$FUNCTION_NAME AND
V4ARG.RDB$ARGUMENT_POSITION EQ ARG.RDB$ARGUMENT_POSITION;
an_udf->udf_charset_id = V4ARG.RDB$CHARACTER_SET_ID;
an_udf->udf_ttype =
INTL_CS_COLL_TO_TTYPE(an_udf->udf_charset_id, COLL.RDB$COLLATION_ID);
END_FOR;
}
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
END_FOR
ON_ERROR
// Nothing
END_ERROR;
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
if (handle2)
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle2);
2001-05-23 15:26:42 +02:00
/* Pick up all Collation names, might have several collations
* for a given character set.
* There can also be several alias names for a character set.
*/
FOR(REQUEST_HANDLE handle)
CHARSET IN RDB$CHARACTER_SETS CROSS COLL IN RDB$COLLATIONS OVER
RDB$CHARACTER_SET_ID;
2003-10-07 11:58:26 +02:00
p = COLL.RDB$COLLATION_NAME;
length = symbol_length(p);
p[length] = 0;
2003-10-15 03:18:01 +02:00
INTLSYM iname = (INTLSYM) MSC_alloc(INTLSYM_LEN + length);
2003-10-07 11:58:26 +02:00
strcpy(iname->intlsym_name, p);
iname->intlsym_database = database;
iname->intlsym_symbol = symbol =
MSC_symbol(SYM_collate, p, strlen(p), (gpre_ctx*) iname);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
iname->intlsym_type = INTLSYM_collation;
iname->intlsym_flags = 0;
iname->intlsym_charset_id = COLL.RDB$CHARACTER_SET_ID;
iname->intlsym_collate_id = COLL.RDB$COLLATION_ID;
iname->intlsym_ttype =
INTL_CS_COLL_TO_TTYPE(iname->intlsym_charset_id,
iname->intlsym_collate_id);
iname->intlsym_bytes_per_char =
(CHARSET.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (CHARSET.
RDB$BYTES_PER_CHARACTER);
iname->intlsym_next = text_subtypes;
text_subtypes = iname;
FOR(REQUEST_HANDLE handle2)
TYPE IN RDB$TYPES
WITH TYPE.RDB$FIELD_NAME = "RDB$COLLATION_NAME"
AND TYPE.RDB$TYPE = COLL.RDB$COLLATION_ID
AND TYPE.RDB$TYPE_NAME != COLL.RDB$COLLATION_NAME;
p = TYPE.RDB$TYPE_NAME;
length = symbol_length(p);
p[length] = 0;
symbol = MSC_symbol(SYM_collate, p, length, (gpre_ctx*) iname);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
END_FOR
ON_ERROR
// Nothing
2001-05-23 15:26:42 +02:00
END_ERROR;
2003-10-07 11:58:26 +02:00
END_FOR
ON_ERROR
// assume pre V4 database, no collations
2003-10-07 11:58:26 +02:00
END_ERROR;
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
if (handle2)
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle2);
2001-05-23 15:26:42 +02:00
/* Now pick up all character set names - with the subtype set to
* the type of the default collation for the character set.
*/
FOR(REQUEST_HANDLE handle)
CHARSET IN RDB$CHARACTER_SETS CROSS COLL IN RDB$COLLATIONS
WITH CHARSET.RDB$DEFAULT_COLLATE_NAME EQ COLL.RDB$COLLATION_NAME;
2003-10-07 11:58:26 +02:00
p = CHARSET.RDB$CHARACTER_SET_NAME;
length = symbol_length(p);
p[length] = 0;
2003-10-15 03:18:01 +02:00
INTLSYM iname = (INTLSYM) MSC_alloc(INTLSYM_LEN + length);
2003-10-07 11:58:26 +02:00
strcpy(iname->intlsym_name, p);
iname->intlsym_database = database;
iname->intlsym_symbol = symbol =
MSC_symbol(SYM_charset, p, strlen(p), (gpre_ctx*) iname);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
iname->intlsym_type = INTLSYM_collation;
iname->intlsym_flags = 0;
iname->intlsym_charset_id = COLL.RDB$CHARACTER_SET_ID;
iname->intlsym_collate_id = COLL.RDB$COLLATION_ID;
iname->intlsym_ttype =
INTL_CS_COLL_TO_TTYPE(iname->intlsym_charset_id,
iname->intlsym_collate_id);
iname->intlsym_bytes_per_char =
(CHARSET.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (CHARSET.
RDB$BYTES_PER_CHARACTER);
FOR(REQUEST_HANDLE handle2)
TYPE IN RDB$TYPES
WITH TYPE.RDB$FIELD_NAME = "RDB$CHARACTER_SET_NAME"
AND TYPE.RDB$TYPE = CHARSET.RDB$CHARACTER_SET_ID
AND TYPE.RDB$TYPE_NAME != CHARSET.RDB$CHARACTER_SET_NAME;
p = TYPE.RDB$TYPE_NAME;
length = symbol_length(p);
p[length] = 0;
symbol = MSC_symbol(SYM_charset, p, length, (gpre_ctx*) iname);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
END_FOR
ON_ERROR
// Nothing
2001-05-23 15:26:42 +02:00
END_ERROR;
2003-10-07 11:58:26 +02:00
END_FOR
ON_ERROR
// assume pre V4 database, no character sets
2003-10-07 11:58:26 +02:00
END_ERROR;
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
if (handle2)
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle2);
2001-05-23 15:26:42 +02:00
// Pick up name of database default character set for SQL
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
FIRST 1 DBB IN RDB$DATABASE
WITH DBB.RDB$CHARACTER_SET_NAME NOT MISSING
p = DBB.RDB$CHARACTER_SET_NAME;
2003-10-07 11:58:26 +02:00
length = symbol_length(p);
p[length] = 0;
2003-10-15 03:18:01 +02:00
database->dbb_def_charset = (TEXT*) MSC_alloc(length + 1);
2003-10-07 11:58:26 +02:00
strcpy(database->dbb_def_charset, p);
if (!MSC_find_symbol(HSH_lookup(database->dbb_def_charset), SYM_charset))
CPR_warn("Default character set for database is not known");
END_FOR
ON_ERROR
// Assume V3 Db, no default charset
2003-10-07 11:58:26 +02:00
END_ERROR;
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
// Pick up all generators for the database
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
X IN RDB$GENERATORS
symbol = MSC_symbol(SYM_generator, X.RDB$GENERATOR_NAME,
symbol_length(X.RDB$GENERATOR_NAME), (gpre_ctx*) database);
2003-10-07 11:58:26 +02:00
HSH_insert(symbol);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
END_FOR
ON_ERROR
// Nothing
END_ERROR;
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &handle);
2001-05-23 15:26:42 +02:00
/* now that we have attached to the database, resolve the character set
* request (if any) (and if we can)
*/
2003-09-05 12:14:08 +02:00
if (database->dbb_c_lc_ctype) {
2001-05-23 15:26:42 +02:00
if (get_intl_char_subtype
2003-09-05 12:14:08 +02:00
(&database->dbb_char_subtype, (UCHAR*) database->dbb_c_lc_ctype,
strlen(database->dbb_c_lc_ctype), database))
2003-10-07 11:58:26 +02:00
{
2003-09-05 12:14:08 +02:00
database->dbb_know_subtype = 1;
2003-10-07 11:58:26 +02:00
}
2001-05-23 15:26:42 +02:00
else {
TEXT buffer[200];
sprintf(buffer, "Cannot recognize character set '%s'",
2003-09-05 12:14:08 +02:00
database->dbb_c_lc_ctype);
2001-05-23 15:26:42 +02:00
PAR_error(buffer);
}
2003-09-05 12:14:08 +02:00
sw_know_interp = database->dbb_know_subtype;
sw_interp = database->dbb_char_subtype;
2001-05-23 15:26:42 +02:00
}
}
/*____________________________________________________________
*
* Make a field symbol.
*/
gpre_fld* MET_make_field(const SCHAR* name,
SSHORT dtype,
SSHORT length,
bool insert_flag)
2001-05-23 15:26:42 +02:00
{
gpre_fld* field = (gpre_fld*) MSC_alloc(FLD_LEN);
2001-05-23 15:26:42 +02:00
field->fld_length = length;
field->fld_dtype = dtype;
gpre_sym* symbol = MSC_symbol(SYM_field, name, strlen(name), (gpre_ctx*) field);
2003-10-07 11:58:26 +02:00
field->fld_symbol = symbol;
2001-05-23 15:26:42 +02:00
if (insert_flag)
HSH_insert(symbol);
return field;
}
/*____________________________________________________________
*
* Make an index symbol.
*/
2003-10-07 11:58:26 +02:00
IND MET_make_index(const SCHAR* name)
2001-05-23 15:26:42 +02:00
{
2003-10-15 03:18:01 +02:00
IND index = (IND) MSC_alloc(IND_LEN);
index->ind_symbol = MSC_symbol(SYM_index, name, strlen(name), (gpre_ctx*) index);
2001-05-23 15:26:42 +02:00
return index;
}
/*____________________________________________________________
*
* Make an relation symbol.
*/
gpre_rel* MET_make_relation(const SCHAR* name)
2001-05-23 15:26:42 +02:00
{
gpre_rel* relation = (gpre_rel*) MSC_alloc(REL_LEN);
2001-05-23 15:26:42 +02:00
relation->rel_symbol =
MSC_symbol(SYM_relation, name, strlen(name), (gpre_ctx*) relation);
2001-05-23 15:26:42 +02:00
return relation;
}
/*____________________________________________________________
*
* Lookup a type name for a field.
*/
bool MET_type(gpre_fld* field,
2003-10-07 11:58:26 +02:00
const TEXT* string,
SSHORT* ptr)
2001-05-23 15:26:42 +02:00
{
field_type* type;
2001-05-23 15:26:42 +02:00
UCHAR buffer[32]; /* BASED ON RDB$TYPES.RDB$TYPE_NAME */
gpre_sym* symbol;
2001-05-23 15:26:42 +02:00
for (symbol = HSH_lookup(string); symbol; symbol = symbol->sym_homonym)
if (symbol->sym_type == SYM_type &&
(type = (field_type*) symbol->sym_object) &&
(!type->typ_field || type->typ_field == field))
{
2001-05-23 15:26:42 +02:00
*ptr = type->typ_value;
return true;
2001-05-23 15:26:42 +02:00
}
gpre_rel* relation = field->fld_relation;
2003-10-07 11:58:26 +02:00
dbb* database = relation->rel_database;
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
// Force the name to uppercase, using C locale rules for uppercasing
2003-10-07 11:58:26 +02:00
UCHAR* p;
2001-05-23 15:26:42 +02:00
for (p = buffer; *string && p < &buffer[sizeof(buffer) - 1];
2003-10-07 11:58:26 +02:00
++p, ++string)
{
*p = UPPER7(*string);
}
2001-05-23 15:26:42 +02:00
*p = '\0';
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_type_request)
2001-05-23 15:26:42 +02:00
X IN RDB$TYPES WITH X.RDB$FIELD_NAME EQ field->fld_global->sym_string AND
2003-10-07 11:58:26 +02:00
X.RDB$TYPE_NAME EQ buffer
type = (field_type*) MSC_alloc(TYP_LEN);
2001-05-23 15:26:42 +02:00
type->typ_field = field;
*ptr = type->typ_value = X.RDB$TYPE;
type->typ_symbol = symbol =
MSC_symbol(SYM_type, string, strlen(string), (gpre_ctx*) type);
2001-05-23 15:26:42 +02:00
HSH_insert(symbol);
return true;
2003-10-07 11:58:26 +02:00
END_FOR
ON_ERROR
// Nothing
END_ERROR;
2001-05-23 15:26:42 +02:00
return false;
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Lookup an index for a database.
*
* Return: true if the trigger exists
* false otherwise
2001-05-23 15:26:42 +02:00
*/
bool MET_trigger_exists(dbb* database,
2003-10-07 11:58:26 +02:00
const TEXT* trigger_name)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
char name[NAME_SIZE];
2001-05-23 15:26:42 +02:00
strcpy(name, trigger_name);
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_trigger_request)
TRIG IN RDB$TRIGGERS WITH TRIG.RDB$TRIGGER_NAME EQ name
return true;
2001-05-23 15:26:42 +02:00
END_FOR;
return false;
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Compute and return the size of the array.
*/
static SLONG array_size( gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
ary* array_block = field->fld_array_info;
2003-10-07 11:58:26 +02:00
SLONG count = field->fld_array->fld_length;
for (dim* dimension = array_block->ary_dimension; dimension;
2003-10-07 11:58:26 +02:00
dimension = dimension->dim_next)
{
count =
2001-05-23 15:26:42 +02:00
count * (dimension->dim_upper - dimension->dim_lower + 1);
2003-10-07 11:58:26 +02:00
}
2001-05-23 15:26:42 +02:00
return count;
}
/*____________________________________________________________
*
* See if field is array.
*/
static void get_array(dbb* database, const TEXT* field_name, gpre_fld* field)
2001-05-23 15:26:42 +02:00
{
ary* array_block = NULL;
2003-10-07 11:58:26 +02:00
USHORT field_dimensions = 0;
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_array_request)
2003-10-15 00:22:32 +02:00
2003-10-07 11:58:26 +02:00
F IN RDB$FIELDS WITH F.RDB$FIELD_NAME EQ field_name
if (F.RDB$DIMENSIONS) {
gpre_fld* sub_field = (gpre_fld*) MSC_alloc(FLD_LEN);
2003-10-07 11:58:26 +02:00
*sub_field = *field;
// CVC: beware, the above line is far from deep copy;
// there are only 13 non-pointer members in gpre_fld.
// The 14 pointer members share target locations with "field".
field->fld_array = sub_field;
field->fld_dtype = dtype_blob;
field->fld_flags |= FLD_blob;
field->fld_length = 8;
// CVC: Someone allocated the the max size inside the ary structure,
// so the dynamic length is irrelevant to create the array_block.
field_dimensions = F.RDB$DIMENSIONS;
fb_assert(field_dimensions <= MAX_ARRAY_DIMENSIONS);
fb_assert(field_dimensions > 0); // Sometimes, sys tables screw
2003-10-15 03:18:01 +02:00
field->fld_array_info = array_block = (ary*) MSC_alloc(ARY_LEN);
//(ary*) MSC_alloc(ARY_LEN(F.RDB$DIMENSIONS));
2003-10-07 11:58:26 +02:00
array_block->ary_dtype = sub_field->fld_dtype;
}
END_FOR
ON_ERROR {
2003-09-05 12:14:08 +02:00
database->dbb_flags |= DBB_no_arrays;
2001-05-23 15:26:42 +02:00
return;
}
END_ERROR;
if (!array_block)
return;
dim* last_dimension_block = 0;
2003-10-07 11:58:26 +02:00
USHORT dimension_counter = 0;
2003-09-05 12:14:08 +02:00
FOR(REQUEST_HANDLE database->dbb_dimension_request)
2001-05-23 15:26:42 +02:00
D IN RDB$FIELD_DIMENSIONS WITH D.RDB$FIELD_NAME EQ field_name
SORTED BY ASCENDING D.RDB$DIMENSION
array_block->ary_rpt[D.RDB$DIMENSION].ary_lower = D.RDB$LOWER_BOUND;
2003-10-07 11:58:26 +02:00
array_block->ary_rpt[D.RDB$DIMENSION].ary_upper = D.RDB$UPPER_BOUND;
dim* dimension_block = (dim*) MSC_alloc(DIM_LEN);
2003-10-07 11:58:26 +02:00
dimension_block->dim_number = D.RDB$DIMENSION;
dimension_block->dim_lower = D.RDB$LOWER_BOUND;
dimension_block->dim_upper = D.RDB$UPPER_BOUND;
if (D.RDB$DIMENSION != 0) {
last_dimension_block->dim_next = dimension_block;
dimension_block->dim_previous = last_dimension_block;
}
else
array_block->ary_dimension = dimension_block;
last_dimension_block = dimension_block;
++dimension_counter;
2001-05-23 15:26:42 +02:00
END_FOR;
2003-10-07 11:58:26 +02:00
// Check for lack of sync between RDB$FIELDS & RDB$FIELD_DIMENSIONS,
// it happened in the past due to buggy DYN code.
fb_assert(last_dimension_block);
fb_assert(dimension_counter == field_dimensions);
2001-05-23 15:26:42 +02:00
array_block->ary_dimension_count = last_dimension_block->dim_number + 1;
array_block->ary_size = array_size(field);
}
/*____________________________________________________________
*
* Character types can be specified as either:
* b) A POSIX style locale name "<collation>.<characterset>"
* or
* c) A simple <characterset> name (using default collation)
* d) A simple <collation> name (use charset for collation)
*
* Given an ASCII7 string which could be any of the above, try to
* resolve the name in the order b, c, d.
* b) is only tried iff the name contains a period.
* (in which case c) and d) are not tried).
*
* Return:
2003-10-07 11:58:26 +02:00
* true if no errors (and *id is set).
* false if the name could not be resolved.
2001-05-23 15:26:42 +02:00
*/
2003-10-07 11:58:26 +02:00
static bool get_intl_char_subtype(
SSHORT* id,
const UCHAR* name, USHORT length, dbb* database)
2001-05-23 15:26:42 +02:00
{
UCHAR buffer[32]; /* BASED ON RDB$COLLATION_NAME */
2003-11-04 00:59:24 +01:00
fb_assert(id != NULL);
fb_assert(name != NULL);
fb_assert(database != NULL);
2001-05-23 15:26:42 +02:00
2003-09-05 12:14:08 +02:00
DB = database->dbb_handle;
2001-05-23 15:26:42 +02:00
if (!DB)
2003-10-07 11:58:26 +02:00
return false;
2003-09-05 12:14:08 +02:00
gds_trans = database->dbb_transaction;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
const UCHAR* const end_name = name + length;
2001-05-23 15:26:42 +02:00
/* Force key to uppercase, following C locale rules for uppercasing
* At the same time, search for the first period in the string (if any)
*/
2003-10-07 11:58:26 +02:00
UCHAR* period = NULL;
UCHAR* p;
for (p = buffer;
name < end_name && p < (buffer + sizeof(buffer) - 1);
p++, name++)
{
2001-05-23 15:26:42 +02:00
*p = UPPER7(*name);
if ((*p == '.') && !period)
period = p;
2003-10-07 11:58:26 +02:00
}
2001-05-23 15:26:42 +02:00
*p = 0;
/* Is there a period, separating collation name from character set? */
if (period) {
*period = 0;
2003-10-07 11:58:26 +02:00
return resolve_charset_and_collation(id, period + 1, buffer);
2001-05-23 15:26:42 +02:00
}
else {
/* Is it a character set name (implying charset default collation sequence) */
2003-10-07 11:58:26 +02:00
bool res = resolve_charset_and_collation(id, buffer, NULL);
2001-05-23 15:26:42 +02:00
if (!res) {
/* Is it a collation name (implying implementation-default character set) */
res = resolve_charset_and_collation(id, NULL, buffer);
}
2003-10-07 11:58:26 +02:00
return res;
2001-05-23 15:26:42 +02:00
}
}
/*____________________________________________________________
*
* Given ASCII7 name of charset & collation
* resolve the specification to a ttype (id) that implements
* it.
*
* Inputs:
* (charset)
* ASCII7z name of characterset.
* NULL (implying unspecified) means use the character set
* for defined for (collation).
*
* (collation)
* ASCII7z name of collation.
* NULL means use the default collation for (charset).
*
* Outputs:
* (*id)
* Set to subtype specified by this name.
*
* Return:
2003-10-07 11:58:26 +02:00
* true if no errors (and *id is set).
* false if either name not found.
2001-05-23 15:26:42 +02:00
* or if names found, but the collation isn't for the specified
* character set.
*/
2003-10-07 11:58:26 +02:00
static bool resolve_charset_and_collation(
SSHORT* id,
const UCHAR* charset,
const UCHAR* collation)
2001-05-23 15:26:42 +02:00
{
2003-11-04 00:59:24 +01:00
fb_assert(id != NULL);
2001-05-23 15:26:42 +02:00
if (!DB)
2003-10-07 11:58:26 +02:00
return false;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
bool found = false;
2004-05-03 01:06:37 +02:00
FB_API_HANDLE request = 0;
2003-10-07 11:58:26 +02:00
2001-05-23 15:26:42 +02:00
if (collation == NULL) {
if (charset == NULL) {
FOR(REQUEST_HANDLE request)
FIRST 1 DBB IN RDB$DATABASE
WITH DBB.RDB$CHARACTER_SET_NAME NOT MISSING
AND DBB.RDB$CHARACTER_SET_NAME != " ";
2003-10-07 11:58:26 +02:00
charset = (UCHAR*) DBB.RDB$CHARACTER_SET_NAME;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
END_FOR
ON_ERROR
// Assume V3 DB, without default character set
2003-10-07 11:58:26 +02:00
END_ERROR;
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &request);
2001-05-23 15:26:42 +02:00
if (charset == NULL)
2003-10-07 11:58:26 +02:00
charset = (UCHAR*) DEFAULT_CHARACTER_SET_NAME;
2001-05-23 15:26:42 +02:00
}
FOR(REQUEST_HANDLE request)
FIRST 1 CS IN RDB$CHARACTER_SETS
CROSS COLL IN RDB$COLLATIONS
CROSS TYPE IN RDB$TYPES
WITH TYPE.RDB$TYPE_NAME EQ charset
AND TYPE.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME"
AND TYPE.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID
AND CS.RDB$DEFAULT_COLLATE_NAME EQ COLL.RDB$COLLATION_NAME;
2003-10-07 11:58:26 +02:00
found = true;
*id = MAP_CHARSET_TO_TTYPE(CS.RDB$CHARACTER_SET_ID);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
END_FOR
ON_ERROR
// Nothing
END_ERROR;
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &request);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
return found;
2001-05-23 15:26:42 +02:00
}
else if (charset == NULL) {
FOR(REQUEST_HANDLE request)
FIRST 1 CS IN RDB$CHARACTER_SETS
CROSS COLL IN RDB$COLLATIONS
OVER RDB$CHARACTER_SET_ID
WITH COLL.RDB$COLLATION_NAME EQ collation;
2003-10-07 11:58:26 +02:00
found = true;
*id = MAP_CHARSET_TO_TTYPE(CS.RDB$CHARACTER_SET_ID);
END_FOR
ON_ERROR
// Nothing
END_ERROR;
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &request);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
return found;
2001-05-23 15:26:42 +02:00
}
FOR(REQUEST_HANDLE request)
FIRST 1 CS IN RDB$CHARACTER_SETS
CROSS COL IN RDB$COLLATIONS OVER RDB$CHARACTER_SET_ID
CROSS NAME2 IN RDB$TYPES
WITH COL.RDB$COLLATION_NAME EQ collation
AND NAME2.RDB$TYPE_NAME EQ charset
AND NAME2.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME"
AND NAME2.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID;
2003-10-07 11:58:26 +02:00
found = true;
*id = MAP_CHARSET_TO_TTYPE(CS.RDB$CHARACTER_SET_ID);
END_FOR
ON_ERROR
// Nothing
END_ERROR;
2001-05-23 15:26:42 +02:00
2003-11-08 17:40:17 +01:00
isc_release_request(gds_status, &request);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
return found;
2001-05-23 15:26:42 +02:00
}
/*____________________________________________________________
*
* Compute significant length of symbol.
*/
2003-10-07 11:58:26 +02:00
static int symbol_length(const TEXT* string)
2001-05-23 15:26:42 +02:00
{
2003-10-07 11:58:26 +02:00
const int len = strlen(string);
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
const TEXT* p = string + (len - 1);
2001-05-23 15:26:42 +02:00
for (; p >= string && *p == ' '; p--);
if (p < string)
return 0;
++p;
return p - string;
}
#ifdef NOT_USED_OR_REPLACED
2001-05-23 15:26:42 +02:00
/*____________________________________________________________
*
* Upcase a string into another string. Return
* length of string.
*/
2003-10-07 11:58:26 +02:00
static int upcase(const TEXT* from, TEXT* to)
2001-05-23 15:26:42 +02:00
{
2003-10-07 11:58:26 +02:00
TEXT c;
2001-05-23 15:26:42 +02:00
2003-10-07 11:58:26 +02:00
TEXT* p = to;
const TEXT* const end = to + NAME_SIZE;
2001-05-23 15:26:42 +02:00
while (p < end && (c = *from++)) {
*p++ = UPPER(c);
}
*p = 0;
return p - to;
}
#endif
2003-10-07 11:58:26 +02:00