/* * 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): ______________________________________. * * *____________________________________________________________ * */ #include "firebird.h" #include #include "../jrd/common.h" #include "../jrd/ibase.h" //#include "../jrd/license.h" #include "../alice/alice.h" #include "../alice/alice_meta.h" #include "../jrd/gds_proto.h" #include "../include/fb_exception.h" #include "../common/classes/alloc.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 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 USHORT snarf_blob(ISC_QUAD*, USHORT, TEXT*); /* 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(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) { TEXT buffer[1024]; TEXT* bigger_buffer = 0; AliceGlobals* tdgbl = AliceGlobals::getSpecific(); const TEXT* p = buffer; const USHORT length = snarf_blob(blob_id, (USHORT) sizeof(buffer), buffer); if (length) { p = bigger_buffer = (TEXT *) gds__alloc((SLONG) length); if (!p) { tdgbl->status[0] = isc_arg_gds; tdgbl->status[1] = isc_virmemexh; tdgbl->status[2] = isc_arg_end; ALICE_print_status(tdgbl->status); return NULL; } snarf_blob(blob_id, length, bigger_buffer); } tdr* trans = NULL; alice_str* host_site = NULL; alice_str* database_path = NULL; // 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. 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) { 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 the * size of the blob if the passed buffer * is not big enough. */ static USHORT snarf_blob(ISC_QUAD* blob_id, USHORT buffer_length, TEXT* buffer) { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); if (buffer_length) buffer[0] = 0; if (buffer_length > 1) buffer[1] = 0; FB_API_HANDLE blob = 0; if (isc_open_blob(gds_status, &DB, &gds_trans, &blob, blob_id)) { ALICE_print_status(gds_status); return 0; } // get the blob into the buffer, if it fits TEXT* ptr = buffer; const TEXT* const end = buffer + buffer_length; for (;;) { if (ptr >= end) break; if (!(buffer_length = end - ptr)) break; USHORT returned_length; ISC_STATUS status = isc_get_segment(gds_status, &blob, &returned_length, buffer_length, ptr); if (status && status != isc_segment) break; ptr += returned_length; } // snarf failed, get length of blob for retry if (!buffer_length) { for (;;) { USHORT returned_length; ISC_STATUS status = isc_get_segment(gds_status, &blob, &returned_length, buffer_length, buffer); if (status && status != isc_segment) break; buffer_length += returned_length; } } else buffer_length = 0; isc_close_blob(gds_status, &blob); *ptr = 0; return buffer_length; }