mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-31 04:03:03 +01:00
507 lines
12 KiB
Plaintext
507 lines
12 KiB
Plaintext
/*
|
|
* tab=4
|
|
*
|
|
*____________________________________________________________
|
|
*
|
|
* PROGRAM: Alice (All Else) Utility
|
|
* MODULE: met.e
|
|
* 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): ______________________________________.
|
|
*
|
|
*
|
|
*____________________________________________________________
|
|
*
|
|
* $Id: alice_meta.epp,v 1.16 2003-09-15 12:50:39 dimitr Exp $
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <stdio.h>
|
|
#include "../jrd/common.h"
|
|
#include "../jrd/ibsetjmp.h"
|
|
#include "../jrd/gds.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_proto.h"
|
|
#include "../include/fb_exception.h"
|
|
#include "../common/classes/alloc.h"
|
|
|
|
#include <string.h>
|
|
|
|
/* Transaction Description Record */
|
|
|
|
#define TDR_VERSION 1
|
|
#define TDR_HOST_SITE 1
|
|
#define TDR_DATABASE_PATH 2
|
|
#define TDR_TRANSACTION_ID 3
|
|
#define TDR_REMOTE_SITE 4
|
|
#define TDR_PROTOCOL 5
|
|
|
|
/* For service APIs the follow DB handle is #defined to be 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 STR alloc_string(TEXT **);
|
|
static USHORT get_capabilities(ISC_STATUS *);
|
|
static TDR get_description(SLONG[2]);
|
|
static void parse_fullpath(TDR);
|
|
static USHORT snarf_blob(SLONG[2], USHORT, TEXT *);
|
|
|
|
|
|
/*
|
|
table used to determine capabilities, checking for specific
|
|
fields in system relations
|
|
*/
|
|
|
|
typedef struct rfr_tab_t {
|
|
TEXT *relation;
|
|
TEXT *field;
|
|
int bit_mask;
|
|
} *RFR_TAB;
|
|
|
|
static rfr_tab_t rfr_table[] = {
|
|
{ "RDB$TRANSACTIONS", "RDB$TRANSACTION_DESCRIPTION", CAP_transactions },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
#define RETURN_ERROR(user_status) \
|
|
{ ALICE_print_status (gds_status); \
|
|
Firebird::status_exception::raise(0); }
|
|
|
|
|
|
/*____________________________________________________________
|
|
*
|
|
* 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)
|
|
{
|
|
FRBRD *request = NULL;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
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)
|
|
{
|
|
FRBRD *request = NULL;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
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;
|
|
|
|
gds__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)
|
|
{
|
|
FRBRD *request = NULL;
|
|
TDR trans = NULL;
|
|
USHORT capabilities;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
if (!(DB = handle))
|
|
return 0;
|
|
|
|
START_TRANSACTION ON_ERROR RETURN_ERROR(user_status) END_ERROR;
|
|
|
|
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((SLONG*)&TRA.RDB$TRANSACTION_DESCRIPTION);
|
|
END_FOR ON_ERROR RETURN_ERROR(user_status) END_ERROR;
|
|
|
|
gds__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)
|
|
{
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
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 STR alloc_string(TEXT ** ptr)
|
|
{
|
|
TEXT *p, *q;
|
|
USHORT length;
|
|
STR string;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
p = *ptr;
|
|
|
|
length = (USHORT) * p++;
|
|
string = FB_NEW_RPT(*tdgbl->ALICE_default_pool, length + 1) str;
|
|
|
|
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)
|
|
{
|
|
FRBRD *req;
|
|
RFR_TAB rel_field_table;
|
|
USHORT capabilities = CAP_none;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
req = NULL;
|
|
|
|
/* Look for desired fields in system relations */
|
|
|
|
for (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;
|
|
}
|
|
|
|
gds__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(SLONG blob_id[2])
|
|
{
|
|
SLONG id_length, id;
|
|
USHORT length;
|
|
TEXT buffer[1024], *bigger_buffer, *p;
|
|
TDR trans, ptr;
|
|
STR host_site, database_path;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
p = buffer;
|
|
if (length = snarf_blob(blob_id, (USHORT) sizeof(buffer), buffer)) {
|
|
p = bigger_buffer = (TEXT *) gds__alloc((SLONG) length);
|
|
snarf_blob(blob_id, length, bigger_buffer);
|
|
}
|
|
|
|
trans = NULL;
|
|
host_site = database_path = NULL;
|
|
|
|
/* skip version number */
|
|
|
|
p++;
|
|
|
|
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((UCHAR*) p, id_length);
|
|
p += id_length;
|
|
if (!trans)
|
|
trans = ptr = FB_NEW(*tdgbl->ALICE_default_pool) tdr;
|
|
else {
|
|
ptr->tdr_next = FB_NEW(*tdgbl->ALICE_default_pool) 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)
|
|
{
|
|
TEXT *start, *p, *q, *end;
|
|
USHORT length;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
/* start at the end of the full pathname */
|
|
|
|
start = p = (TEXT*) trans->tdr_fullpath->str_data;
|
|
while (*p)
|
|
p++;
|
|
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;
|
|
q = p;
|
|
|
|
while (*q && *q != '/' && *q != '\\')
|
|
q++;
|
|
if (*q) {
|
|
trans->tdr_filename = q + 1;
|
|
|
|
trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->ALICE_default_pool, q - p + 1) 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--;
|
|
for (length = 0; p >= start && (*p != '^' && *p != ':' && *p != '@');
|
|
length++)
|
|
p--;
|
|
p++;
|
|
|
|
if (length) {
|
|
trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->ALICE_default_pool, length + 1) str;
|
|
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(SLONG blob_id[2],
|
|
USHORT buffer_length, TEXT * buffer)
|
|
{
|
|
USHORT returned_length;
|
|
TEXT *ptr, *end;
|
|
ISC_STATUS status;
|
|
FRBRD *blob;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
if (buffer_length)
|
|
buffer[0] = 0;
|
|
if (buffer_length > 1)
|
|
buffer[1] = 0;
|
|
|
|
blob = NULL;
|
|
if (gds__open_blob(gds_status, &DB, &gds_trans, &blob, (GDS_QUAD*) blob_id)) {
|
|
ALICE_print_status(gds_status);
|
|
return 0;
|
|
}
|
|
|
|
/* get the blob into the buffer, if it fits */
|
|
|
|
ptr = buffer;
|
|
end = buffer + buffer_length;
|
|
for (;;) {
|
|
if (ptr >= end)
|
|
break;
|
|
if (!(buffer_length = end - ptr))
|
|
break;
|
|
status = gds__get_segment(gds_status, &blob, &returned_length,
|
|
buffer_length, ptr);
|
|
if (status && status != gds_segment)
|
|
break;
|
|
ptr += returned_length;
|
|
}
|
|
|
|
/* snarf failed, get length of blob for retry */
|
|
|
|
if (!buffer_length)
|
|
for (;;) {
|
|
status = gds__get_segment(gds_status, &blob, &returned_length,
|
|
buffer_length, buffer);
|
|
if (status && status != gds_segment)
|
|
break;
|
|
buffer_length += returned_length;
|
|
}
|
|
else
|
|
buffer_length = 0;
|
|
|
|
gds__close_blob(gds_status, &blob);
|
|
|
|
*ptr = 0;
|
|
|
|
return buffer_length;
|
|
}
|