8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 08:03:03 +01:00
firebird-mirror/src/alice/alice_meta.epp

500 lines
11 KiB
Plaintext

/*
*____________________________________________________________
*
* 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/ibase.h"
#include "../alice/alice.h"
#include "../alice/alice_meta.h"
#include "../yvalve/gds_proto.h"
#include "fb_exception.h"
#include "../common/classes/alloc.h"
#include "../common/classes/array.h"
#include "../common/classes/UserBlob.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
typedef Firebird::HalfStaticArray<TEXT, 1024> TextBuffer;
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 bool snarf_blob(ISC_QUAD*, TextBuffer&);
// 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(true, 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, TraNumber 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;
const USHORT length = (USHORT) *p++;
alice_str* string = FB_NEW_RPT(*tdgbl->getDefaultPool(), length + 1) alice_str;
memcpy(string->str_data, p, length);
string->str_data[length] = 0;
*ptr = p + length;
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)
{
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
TextBuffer buffer;
if (UserBlob::blobIsNull(*blob_id))
return NULL;
if (!snarf_blob(blob_id, buffer))
{
ALICE_print_status(true, gds_status);
return NULL;
}
if (buffer.getCount() < 2)
return NULL;
tdr* trans = NULL;
alice_str* host_site = NULL;
alice_str* database_path = NULL;
const TEXT* p = buffer.begin();
// skip version number
++p;
tdr* ptr = NULL; // silence uninitialized warning
SLONG id_length;
TraNumber 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 = isc_portable_integer(reinterpret_cast<const UCHAR*>(p), id_length);
p += id_length;
if (!trans) {
trans = ptr = FB_NEW_POOL(*tdgbl->getDefaultPool()) tdr;
}
else
{
ptr->tdr_next = FB_NEW_POOL(*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);
// msg 108: Transaction description item unknown.
return NULL;
}
}
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 false if error happens
*/
static bool snarf_blob(ISC_QUAD* blob_id, TextBuffer& buffer)
{
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
UserBlob blob(gds_status);
if (!blob.open(DB, gds_trans, *blob_id))
return false;
SLONG blob_size;
if (!getBlobSize(blob, &blob_size, NULL, NULL))
return false;
FB_SIZE_T len = blob_size;
TEXT* ptr = buffer.getBuffer(blob_size + 1);
while (len)
{
FB_SIZE_T real_len;
if (len && blob.getSegment(len, ptr, real_len))
{
len -= real_len;
ptr += real_len;
}
const ISC_STATUS err = blob.getCode();
if (err == 0 || err == isc_segment)
continue;
if (err == isc_segstr_eof)
break;
return false;
}
blob.close();
*ptr = 0;
buffer.resize(ptr - buffer.begin() + 1);
return true;
}