/* * tab=4 * *____________________________________________________________ * * PROGRAM: Alice (All Else) Utility * MODULE: met.e * 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.5 2002-04-04 05:31:13 bellardo Exp $ */ #include "firebird.h" #include #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/memory/allocators.h" #include /* Transaction Description Record */ #define TDR_VERSION 1 #define TDR_HOST_SITE 1 #define TDR_DATABASE_PATH 2 #define TDR_TRANSACTION_ID 3 #define TDR_REMOTE_SITE 4 #define TDR_PROTOCOL 5 /* For service APIs the follow DB handle is #defined to be 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(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 */ typedef struct rfr_tab_t { TEXT *relation; TEXT *field; int bit_mask; } *RFR_TAB; static struct rfr_tab_t rfr_table[] = { { "RDB$TRANSACTIONS", "RDB$TRANSACTION_DESCRIPTION", CAP_transactions }, { 0, 0, 0 } }; #ifdef GUI_TOOLS #define RETURN_ERROR(user_status) \ { memcpy (user_status, gds_status, sizeof (gds_status)); \ Firebird::status_exception::raise(0); } #else #define RETURN_ERROR(user_status) \ { ALICE_print_status (gds_status); \ Firebird::status_exception::raise(0); } #endif /*____________________________________________________________ * * Disable WAL for the database forcibly. * Drop all the entries from RDB$LOG_FILES */ void MET_disable_wal(STATUS * user_status, isc_db_handle handle) { SLONG *request = NULL; TGBL tdgbl; 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(STATUS * user_status, TDR trans) { SLONG *request = NULL; TGBL tdgbl; 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, (void**) GDS_REF(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(STATUS * user_status, isc_db_handle handle, SLONG id) { SLONG *request = NULL; TDR trans = NULL; USHORT capabilities; TGBL tdgbl; tdgbl = GET_THREAD_DATA; if (!(DB = handle)) return 0; START_TRANSACTION ON_ERROR RETURN_ERROR(user_status) END_ERROR; 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, (void**) GDS_REF(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(STATUS * user_status, TDR trans) { TGBL tdgbl; 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) { TEXT *p, *q; USHORT length; STR string; TGBL tdgbl; tdgbl = GET_THREAD_DATA; p = *ptr; length = (USHORT) * p++; string = new(*tdgbl->ALICE_default_pool, length + 1) str; 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(STATUS * user_status) { ULONG *req; RFR_TAB rel_field_table; USHORT capabilities = CAP_none; TGBL tdgbl; tdgbl = GET_THREAD_DATA; req = NULL; /* Look for desired fields in system relations */ for (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, (void**) GDS_REF(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, *p; TDR trans, ptr; STR host_site, database_path; TGBL tdgbl; tdgbl = GET_THREAD_DATA; 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); } trans = NULL; host_site = database_path = NULL; /* skip version number */ p++; 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 = new(*tdgbl->ALICE_default_pool) tdr; else { ptr->tdr_next = 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; 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 = new(*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--; 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 = new(*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; TEXT *ptr, *end; STATUS status; int *blob; TGBL tdgbl; tdgbl = GET_THREAD_DATA; if (buffer_length) buffer[0] = 0; if (buffer_length > 1) buffer[1] = 0; blob = NULL; if (gds__open_blob(gds_status, (void**) GDS_REF(DB), (void**) GDS_REF(gds_trans), (void**) GDS_REF(blob), (GDS_QUAD*) GDS_VAL(blob_id))) { ALICE_print_status(gds_status); return 0; } /* get the blob into the buffer, if it fits */ ptr = buffer; end = buffer + buffer_length; for (;;) { if (ptr >= end) break; if (!(buffer_length = end - ptr)) break; status = gds__get_segment(gds_status, (void**) GDS_REF(blob), GDS_REF(returned_length), buffer_length, GDS_VAL(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, (void**) GDS_REF(blob), GDS_REF(returned_length), buffer_length, buffer); if (status && status != gds_segment) break; buffer_length += returned_length; } else buffer_length = 0; gds__close_blob(gds_status, (void**) GDS_REF(blob)); *ptr = 0; return buffer_length; }