8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-30 19:23:03 +01:00
firebird-mirror/src/alice/alice_meta.epp
robocop 484c6ec372 Cleanup, const correctness, variables in scope, etc.
Having done my best to merge everyone else's changes, I hope the tree can be build with whatever other tools != MSVC6.
2003-09-25 11:49:12 +00:00

510 lines
11 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): ______________________________________.
*
*
*____________________________________________________________
*
* $Id: alice_meta.epp,v 1.21 2003-09-25 11:48:57 robocop 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 "../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 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
*/
struct rfr_tab_t {
TEXT *relation;
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(ISC_STATUS* 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 = 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 = 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;
TGBL tdgbl = GET_THREAD_DATA;
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((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 = 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)
{
TGBL tdgbl = GET_THREAD_DATA;
TEXT* p = *ptr;
USHORT length = (USHORT) *p++;
STR string = FB_NEW_RPT(*tdgbl->ALICE_default_pool, length + 1) 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;
TGBL tdgbl = GET_THREAD_DATA;
// Look for desired fields in system relations
FRBRD* req = NULL;
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;
}
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;
TGBL tdgbl = GET_THREAD_DATA;
TEXT* 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);
}
TDR trans = NULL;
STR host_site = NULL, database_path = NULL;
// skip version number
p++;
TDR ptr;
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 = 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;
ISC_STATUS status;
TGBL tdgbl = GET_THREAD_DATA;
if (buffer_length)
buffer[0] = 0;
if (buffer_length > 1)
buffer[1] = 0;
FRBRD* 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
TEXT* ptr = buffer;
TEXT* const 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;
}