mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-26 08:03:03 +01:00
500 lines
11 KiB
Plaintext
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;
|
|
}
|