mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-30 19:23:03 +01:00
b056a118b5
2. Reworked exceptions to make status_exception always hold correct status vector 3. Avoid use of RTTI
527 lines
12 KiB
Plaintext
527 lines
12 KiB
Plaintext
/*
|
|
* tab=4
|
|
*
|
|
*____________________________________________________________
|
|
*
|
|
* PROGRAM: Alice (All Else) Utility
|
|
* MODULE: alice_meta.epp
|
|
* DESCRIPTION: Metadata lookup 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 "../jrd/common.h"
|
|
#include "../jrd/ibase.h"
|
|
//#include "../jrd/license.h"
|
|
#include "../alice/alice.h"
|
|
#include "../alice/all.h"
|
|
#include "../alice/alice_meta.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#include "../jrd/thd.h"
|
|
#include "../include/fb_exception.h"
|
|
#include "../common/classes/alloc.h"
|
|
#include "../alice/alice_proto.h"
|
|
|
|
#include <string.h>
|
|
|
|
// For service APIs the follow DB handle is a value stored
|
|
// in thread data. This is also done for other statics generated by
|
|
// GPRE. This is to avoid multiple threading problems with module
|
|
// level statics.
|
|
|
|
DATABASE DB = STATIC FILENAME "yachts.lnk";
|
|
|
|
#define DB tdgbl->db_handle
|
|
#define gds_trans tdgbl->tr_handle
|
|
|
|
static alice_str* alloc_string(const TEXT**);
|
|
static USHORT get_capabilities(ISC_STATUS*);
|
|
static TDR get_description(ISC_QUAD*);
|
|
static void parse_fullpath(TDR);
|
|
static USHORT snarf_blob(ISC_QUAD*, USHORT, TEXT*);
|
|
|
|
|
|
/*
|
|
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$TRANSACTIONS", "RDB$TRANSACTION_DESCRIPTION", CAP_transactions },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static inline void return_error(const ISC_STATUS* user_status)
|
|
{
|
|
ALICE_print_status(gds_status);
|
|
Firebird::LongJump::raise();
|
|
}
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Disable WAL for the database forcibly.
|
|
* Drop all the entries from RDB$LOG_FILES
|
|
*/
|
|
|
|
void MET_disable_wal(ISC_STATUS* user_status, isc_db_handle handle)
|
|
{
|
|
FB_API_HANDLE request = 0;
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
if (!(DB = handle))
|
|
return;
|
|
|
|
START_TRANSACTION
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$LOG_FILES
|
|
ERASE X;
|
|
END_FOR
|
|
COMMIT
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
}
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Get the state of a transaction,
|
|
* assuming that the database has
|
|
* already been attached.
|
|
*/
|
|
|
|
void MET_get_state(ISC_STATUS* user_status, TDR trans)
|
|
{
|
|
FB_API_HANDLE request = 0;
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
if (!(DB = trans->tdr_db_handle) ||
|
|
!(trans->tdr_db_caps & CAP_transactions))
|
|
{
|
|
trans->tdr_state = TRA_unknown;
|
|
return;
|
|
}
|
|
|
|
START_TRANSACTION
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
TRA IN RDB$TRANSACTIONS WITH
|
|
TRA.RDB$TRANSACTION_ID = trans->tdr_id
|
|
trans->tdr_state = TRA.RDB$TRANSACTION_STATE;
|
|
END_FOR
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
|
|
isc_release_request(gds_status, &request);
|
|
if (gds_status[1]) {
|
|
return_error(user_status);
|
|
}
|
|
|
|
ROLLBACK
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
}
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Get the description of a transaction in
|
|
* limbo, including all associated transactions
|
|
* in other databases.
|
|
*/
|
|
|
|
TDR MET_get_transaction(ISC_STATUS* user_status, isc_db_handle handle, SLONG id)
|
|
{
|
|
FB_API_HANDLE request = 0;
|
|
TDR trans = NULL;
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
if (!(DB = handle))
|
|
return 0;
|
|
|
|
START_TRANSACTION
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
|
|
const USHORT capabilities = get_capabilities(user_status);
|
|
|
|
if (capabilities & CAP_transactions) {
|
|
FOR(REQUEST_HANDLE request)
|
|
TRA IN RDB$TRANSACTIONS WITH
|
|
TRA.RDB$TRANSACTION_ID = id AND
|
|
TRA.RDB$TRANSACTION_DESCRIPTION NOT MISSING
|
|
trans = get_description(&TRA.RDB$TRANSACTION_DESCRIPTION);
|
|
END_FOR
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
|
|
isc_release_request(gds_status, &request);
|
|
if (gds_status[1]) {
|
|
return_error(user_status);
|
|
}
|
|
}
|
|
|
|
ROLLBACK
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
|
|
if (trans)
|
|
trans->tdr_db_caps = capabilities;
|
|
|
|
return trans;
|
|
}
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Get the capabilities associated with
|
|
* the database for a particular transaction.
|
|
*/
|
|
|
|
void MET_set_capabilities(ISC_STATUS* user_status, TDR trans)
|
|
{
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
if (!(DB = trans->tdr_db_handle))
|
|
return;
|
|
|
|
START_TRANSACTION
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
|
|
trans->tdr_db_caps = get_capabilities(user_status);
|
|
|
|
ROLLBACK
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
}
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Eat a string with a byte-encoded length.
|
|
*/
|
|
|
|
static alice_str* alloc_string(const TEXT** ptr)
|
|
{
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
const TEXT* p = *ptr;
|
|
|
|
USHORT length = (USHORT) *p++;
|
|
alice_str* string = FB_NEW_RPT(*tdgbl->getDefaultPool(), length + 1) alice_str;
|
|
|
|
TEXT* q = (TEXT *) string->str_data;
|
|
while (length--) {
|
|
*q++ = *p++;
|
|
}
|
|
*q = 0;
|
|
|
|
*ptr = p;
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Get the capabilities associated with
|
|
* the database for a particular transaction.
|
|
*/
|
|
|
|
static USHORT get_capabilities(ISC_STATUS* user_status)
|
|
{
|
|
USHORT capabilities = CAP_none;
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
// Look for desired fields in system relations
|
|
FB_API_HANDLE req = 0;
|
|
|
|
for (const rfr_tab_t* rel_field_table = rfr_table; rel_field_table->relation;
|
|
rel_field_table++)
|
|
{
|
|
FOR(REQUEST_HANDLE req) x IN DB.RDB$RELATION_FIELDS
|
|
WITH x.RDB$RELATION_NAME = rel_field_table->relation
|
|
AND x.RDB$FIELD_NAME = rel_field_table->field
|
|
capabilities |= rel_field_table->bit_mask;
|
|
END_FOR
|
|
ON_ERROR
|
|
return_error(user_status);
|
|
END_ERROR;
|
|
}
|
|
|
|
isc_release_request(gds_status, &req);
|
|
if (gds_status[1]) {
|
|
return_error(user_status);
|
|
}
|
|
|
|
return capabilities;
|
|
}
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Get the description of a transaction in
|
|
* limbo, including all associated transactions
|
|
* in other databases.
|
|
*/
|
|
|
|
static TDR get_description(ISC_QUAD* blob_id)
|
|
{
|
|
TEXT buffer[1024];
|
|
TEXT* bigger_buffer = 0;
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
const TEXT* p = buffer;
|
|
const USHORT length = snarf_blob(blob_id, (USHORT) sizeof(buffer), buffer);
|
|
if (length) {
|
|
p = bigger_buffer = (TEXT *) gds__alloc((SLONG) length);
|
|
if (!p) {
|
|
tdgbl->status[0] = isc_arg_gds;
|
|
tdgbl->status[1] = isc_virmemexh;
|
|
tdgbl->status[2] = isc_arg_end;
|
|
|
|
ALICE_print_status(tdgbl->status);
|
|
return NULL;
|
|
}
|
|
snarf_blob(blob_id, length, bigger_buffer);
|
|
}
|
|
|
|
TDR trans = NULL;
|
|
alice_str* host_site = NULL;
|
|
alice_str* database_path = NULL;
|
|
|
|
// skip version number
|
|
++p;
|
|
|
|
TDR ptr = NULL; // silence uninitialized warning
|
|
SLONG id_length, id;
|
|
|
|
while (*p) {
|
|
switch (*p++) {
|
|
case TDR_HOST_SITE:
|
|
host_site = alloc_string(&p);
|
|
break;
|
|
|
|
case TDR_DATABASE_PATH:
|
|
database_path = alloc_string(&p);
|
|
break;
|
|
|
|
case TDR_TRANSACTION_ID:
|
|
id_length = *p++;
|
|
id = gds__vax_integer(reinterpret_cast<const UCHAR*>(p), id_length);
|
|
p += id_length;
|
|
if (!trans) {
|
|
trans = ptr = FB_NEW(*tdgbl->getDefaultPool()) tdr;
|
|
}
|
|
else {
|
|
ptr->tdr_next = FB_NEW(*tdgbl->getDefaultPool()) tdr;
|
|
ptr = ptr->tdr_next;
|
|
}
|
|
ptr->tdr_host_site = host_site;
|
|
ptr->tdr_fullpath = database_path;
|
|
parse_fullpath(ptr);
|
|
ptr->tdr_id = id;
|
|
database_path = NULL;
|
|
break;
|
|
|
|
default:
|
|
ALICE_print(108, 0, 0, 0, 0, 0);
|
|
// msg 108: Transaction description item unknown.
|
|
|
|
if (length) {
|
|
gds__free(bigger_buffer);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (length) {
|
|
gds__free(bigger_buffer);
|
|
}
|
|
|
|
return trans;
|
|
}
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Take apart a full remote path specification,
|
|
* finding the filename and the remote site.
|
|
*/
|
|
|
|
static void parse_fullpath(TDR trans)
|
|
{
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
// start at the end of the full pathname
|
|
|
|
const TEXT* p = (TEXT*) trans->tdr_fullpath->str_data;
|
|
const TEXT* const start = p;
|
|
while (*p)
|
|
p++;
|
|
const TEXT* const end = p;
|
|
|
|
// Check for a named pipes name - \\node\path\db or //node/path/db
|
|
while (p > start &&
|
|
!(*p == '/' && *(p - 1) == '/') &&
|
|
!(*p == '\\' && *(p - 1) == '\\'))
|
|
{
|
|
--p;
|
|
}
|
|
|
|
if (p > start) {
|
|
/* Increment p past slash, & search forward for end of node name */
|
|
p = p + 1;
|
|
const TEXT* q = p;
|
|
|
|
while (*q && *q != '/' && *q != '\\')
|
|
q++;
|
|
if (*q) {
|
|
trans->tdr_filename = q + 1;
|
|
|
|
trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->getDefaultPool(), q - p + 1) alice_str;
|
|
strncpy((char*) trans->tdr_remote_site->str_data, (char*) p, q - p);
|
|
trans->tdr_remote_site->str_data[q - p] = '\0';
|
|
}
|
|
}
|
|
else {
|
|
p = end;
|
|
|
|
/* If not named pipes, check the other protocols
|
|
* work backwards until we find a remote protocol specifier
|
|
*/
|
|
|
|
while (p >= start && (*p != '^' && *p != ':' && *p != '@'))
|
|
p--;
|
|
// dimitr: make sure that the remote path is parsed correctly
|
|
// for win32 servers, i.e. the drive separator is taken into account
|
|
if ((p - 2 >= start) && p[-2] == ':' && (p[0] == ':'))
|
|
p -= 2;
|
|
trans->tdr_filename = p + 1;
|
|
|
|
// now find the last remote node in the chain
|
|
|
|
while (p > start && (*p == ':' || *p == '^' || *p == '@'))
|
|
p--;
|
|
|
|
USHORT length = 0;
|
|
for (; p >= start && (*p != '^' && *p != ':' && *p != '@'); ++length)
|
|
--p;
|
|
++p;
|
|
|
|
if (length) {
|
|
trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->getDefaultPool(), length + 1) alice_str;
|
|
TEXT* q = (TEXT *) trans->tdr_remote_site->str_data;
|
|
while (length--)
|
|
*q++ = *p++;
|
|
*q = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* Get a blob into a buffer, returning the
|
|
* size of the blob if the passed buffer
|
|
* is not big enough.
|
|
*/
|
|
|
|
static USHORT snarf_blob(ISC_QUAD* blob_id,
|
|
USHORT buffer_length, TEXT* buffer)
|
|
{
|
|
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
|
|
|
|
if (buffer_length)
|
|
buffer[0] = 0;
|
|
if (buffer_length > 1)
|
|
buffer[1] = 0;
|
|
|
|
FB_API_HANDLE blob = 0;
|
|
if (isc_open_blob(gds_status, &DB, &gds_trans, &blob, blob_id))
|
|
{
|
|
ALICE_print_status(gds_status);
|
|
return 0;
|
|
}
|
|
|
|
// get the blob into the buffer, if it fits
|
|
|
|
USHORT returned_length;
|
|
|
|
TEXT* ptr = buffer;
|
|
const TEXT* const end = buffer + buffer_length;
|
|
for (;;) {
|
|
if (ptr >= end)
|
|
break;
|
|
if (!(buffer_length = end - ptr))
|
|
break;
|
|
ISC_STATUS status = isc_get_segment(gds_status, &blob, &returned_length,
|
|
buffer_length, ptr);
|
|
if (status && status != isc_segment)
|
|
break;
|
|
ptr += returned_length;
|
|
}
|
|
|
|
// snarf failed, get length of blob for retry
|
|
|
|
if (!buffer_length)
|
|
for (;;) {
|
|
ISC_STATUS status = isc_get_segment(gds_status, &blob, &returned_length,
|
|
buffer_length, buffer);
|
|
if (status && status != isc_segment)
|
|
break;
|
|
buffer_length += returned_length;
|
|
}
|
|
else
|
|
buffer_length = 0;
|
|
|
|
isc_close_blob(gds_status, &blob);
|
|
|
|
*ptr = 0;
|
|
|
|
return buffer_length;
|
|
}
|
|
|