/* *____________________________________________________________ * * 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 #include "../jrd/ibase.h" //#include "../jrd/license.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 // 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 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, 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; 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, 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(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); // 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; size_t len = blob_size; TEXT* ptr = buffer.getBuffer(blob_size + 1); while (len) { 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; }