8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 10:43:02 +01:00
firebird-mirror/src/alice/tdr.cpp

920 lines
23 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// PROGRAM: Alice (All Else) Utility
// MODULE: tdr.cpp
// DESCRIPTION: Routines for automated transaction recovery
//
2001-05-23 15:26:42 +02:00
// 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
//
2001-05-23 15:26:42 +02:00
// 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.
//
2001-05-23 15:26:42 +02:00
// The Original Code was created by Inprise Corporation
// and its predecessors. Portions created by Inprise Corporation are
// Copyright (C) Inprise Corporation.
//
2001-05-23 15:26:42 +02:00
// All Rights Reserved.
// Contributor(s): ______________________________________.
//
2001-05-23 15:26:42 +02:00
//
//____________________________________________________________
//
// $Id: tdr.cpp,v 1.37 2004-05-20 00:56:23 brodsom Exp $
//
// 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "Apollo" port
2001-05-23 15:26:42 +02:00
//
// 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix" port
//
//
2001-05-23 15:26:42 +02:00
#include "firebird.h"
2004-04-29 00:00:03 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
2003-11-08 00:09:04 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/common.h"
#include "../alice/alice.h"
#include "../alice/aliceswi.h"
#include "../alice/all.h"
#include "../alice/alice_proto.h"
#include "../alice/all_proto.h"
#include "../alice/alice_meta.h"
#include "../alice/tdr_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/svc_proto.h"
#include "../jrd/thd_proto.h"
static ULONG ask(void);
static void print_description(const tdr*);
2001-05-23 15:26:42 +02:00
static void reattach_database(TDR);
static void reattach_databases(TDR);
2004-05-03 01:06:37 +02:00
static bool reconnect(FB_API_HANDLE, SLONG, const TEXT*, ULONG);
2001-05-23 15:26:42 +02:00
const char* const NEWLINE = "\n";
2001-05-23 15:26:42 +02:00
2003-11-08 00:09:04 +01:00
static const UCHAR limbo_info[] = { isc_info_limbo, isc_info_end };
2001-05-23 15:26:42 +02:00
//
2001-05-23 15:26:42 +02:00
// The following routines are shared by the command line gfix and
// the windows server manager. These routines should not contain
2004-04-29 00:00:03 +02:00
// any direct screen I/O (i.e. printf/getc statements).
2001-05-23 15:26:42 +02:00
//
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// Determine the proper action to take
// based on the state of the various
// transactions.
//
2001-05-23 15:26:42 +02:00
USHORT TDR_analyze(const tdr* trans)
2001-05-23 15:26:42 +02:00
{
USHORT advice = TRA_none;
2001-05-23 15:26:42 +02:00
if (trans == NULL)
return TRA_none;
// if the tdr for the first transaction is missing,
// we can assume it was committed
2001-05-23 15:26:42 +02:00
USHORT state = trans->tdr_state;
2001-05-23 15:26:42 +02:00
if (state == TRA_none)
state = TRA_commit;
else if (state == TRA_unknown)
advice = TRA_unknown;
for (trans = trans->tdr_next; trans; trans = trans->tdr_next) {
switch (trans->tdr_state) {
// an explicitly committed transaction necessitates a check for the
// perverse case of a rollback, otherwise a commit if at all possible
2001-05-23 15:26:42 +02:00
case TRA_commit:
if (state == TRA_rollback) {
ALICE_print(105, 0, 0, 0, 0, 0);
// msg 105: Warning: Multidatabase transaction is in inconsistent state for recovery.
ALICE_print(106, reinterpret_cast<char*>(trans->tdr_id), 0,
0, 0, 0);
// msg 106: Transaction %ld was committed, but prior ones were rolled back.
2001-05-23 15:26:42 +02:00
return 0;
}
else
advice = TRA_commit;
break;
// a prepared transaction requires a commit if there are missing
// records up to now, otherwise only do something if somebody else
// already has
2001-05-23 15:26:42 +02:00
case TRA_limbo:
if (state == TRA_none)
advice = TRA_commit;
else if (state == TRA_commit)
advice = TRA_commit;
else if (state == TRA_rollback)
advice = TRA_rollback;
break;
// an explicitly rolled back transaction requires a rollback unless a
// transaction has committed or is assumed committed
2001-05-23 15:26:42 +02:00
case TRA_rollback:
if ((state == TRA_commit) || (state == TRA_none)) {
ALICE_print(105, 0, 0, 0, 0, 0);
// msg 105: Warning: Multidatabase transaction is in inconsistent state for recovery.
ALICE_print(107, reinterpret_cast<char*>(trans->tdr_id), 0,
0, 0, 0);
// msg 107: Transaction %ld was rolled back, but prior ones were committed.
2001-05-23 15:26:42 +02:00
return 0;
}
else
advice = TRA_rollback;
break;
// a missing TDR indicates a committed transaction if a limbo one hasn't
// been found yet, otherwise it implies that the transaction wasn't
// prepared
2001-05-23 15:26:42 +02:00
case TRA_none:
if (state == TRA_commit)
advice = TRA_commit;
else if (state == TRA_limbo)
advice = TRA_rollback;
break;
// specifically advise TRA_unknown to prevent assumption that all are
// in limbo
case TRA_unknown:
if (!advice)
advice = TRA_unknown;
break;
default:
ALICE_print(67, reinterpret_cast<char*>(trans->tdr_state), 0,
0, 0, 0); // msg 67: Transaction state %d not in valid range.
2001-05-23 15:26:42 +02:00
return 0;
}
}
return advice;
}
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// Attempt to attach a database with a given pathname.
//
2001-05-23 15:26:42 +02:00
2003-10-16 10:51:06 +02:00
bool TDR_attach_database(ISC_STATUS* status_vector,
2003-09-10 19:52:12 +02:00
TDR trans,
const TEXT* pathname)
2001-05-23 15:26:42 +02:00
{
UCHAR dpb[128];
Tgbl* tdgbl = ALICE_get_thread_data();
2001-05-23 15:26:42 +02:00
if (tdgbl->ALICE_data.ua_debug)
ALICE_print(68, pathname, 0, 0, 0, 0);
// msg 68: ATTACH_DATABASE: attempted attach of %s
2001-05-23 15:26:42 +02:00
UCHAR* d = dpb;
2001-05-23 15:26:42 +02:00
2003-11-08 00:09:04 +01:00
*d++ = isc_dpb_version1;
*d++ = isc_dpb_no_garbage_collect;
2001-05-23 15:26:42 +02:00
*d++ = 0;
*d++ = isc_dpb_gfix_attach;
*d++ = 0;
if (tdgbl->ALICE_data.ua_user) {
2003-11-08 00:09:04 +01:00
*d++ = isc_dpb_user_name;
*d++ = strlen(reinterpret_cast<const char*>(tdgbl->ALICE_data.ua_user));
for (const UCHAR* q = tdgbl->ALICE_data.ua_user; *q;)
2001-05-23 15:26:42 +02:00
*d++ = *q++;
}
if (tdgbl->ALICE_data.ua_password) {
if (!tdgbl->sw_service)
2003-11-08 00:09:04 +01:00
*d++ = isc_dpb_password;
2001-05-23 15:26:42 +02:00
else
2003-11-08 00:09:04 +01:00
*d++ = isc_dpb_password_enc;
2001-05-23 15:26:42 +02:00
*d++ =
strlen(reinterpret_cast<const char*>(tdgbl->ALICE_data.ua_password));
for (const UCHAR* q = tdgbl->ALICE_data.ua_password; *q;)
2001-05-23 15:26:42 +02:00
*d++ = *q++;
}
USHORT dpb_length = d - dpb;
2001-05-23 15:26:42 +02:00
if (dpb_length == 1)
dpb_length = 0;
2004-05-03 01:06:37 +02:00
trans->tdr_db_handle = 0;
2001-05-23 15:26:42 +02:00
2003-11-08 00:09:04 +01:00
isc_attach_database(status_vector, 0, pathname,
2003-08-30 04:02:36 +02:00
&trans->tdr_db_handle, dpb_length,
reinterpret_cast<char*>(dpb));
2001-05-23 15:26:42 +02:00
if (status_vector[1]) {
if (tdgbl->ALICE_data.ua_debug) {
ALICE_print(69, 0, 0, 0, 0, 0); // msg 69: failed
2001-05-23 15:26:42 +02:00
ALICE_print_status(status_vector);
}
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
}
MET_set_capabilities(status_vector, trans);
if (tdgbl->ALICE_data.ua_debug)
ALICE_print(70, 0, 0, 0, 0, 0); // msg 70: succeeded
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// Get the state of the various transactions
// in a multidatabase transaction.
//
2001-05-23 15:26:42 +02:00
void TDR_get_states(TDR trans)
{
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
2001-05-23 15:26:42 +02:00
for (TDR ptr = trans; ptr; ptr = ptr->tdr_next)
2001-05-23 15:26:42 +02:00
MET_get_state(status_vector, ptr);
}
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// Detach all databases associated with
// a multidatabase transaction.
//
2001-05-23 15:26:42 +02:00
void TDR_shutdown_databases(TDR trans)
{
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
2001-05-23 15:26:42 +02:00
for (TDR ptr = trans; ptr; ptr = ptr->tdr_next)
2003-11-08 00:09:04 +01:00
isc_detach_database(status_vector, &ptr->tdr_db_handle);
2001-05-23 15:26:42 +02:00
}
//
// The following routines are only for the command line utility.
2001-05-23 15:26:42 +02:00
// This should really be split into two files...
//
2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// List transaction stuck in limbo. If the prompt switch is set,
// prompt for commit, rollback, or leave well enough alone.
//
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
void TDR_list_limbo(FB_API_HANDLE handle, const TEXT* name, const ULONG switches)
2001-05-23 15:26:42 +02:00
{
UCHAR buffer[1024];
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
Tgbl* tdgbl = ALICE_get_thread_data();
2001-05-23 15:26:42 +02:00
2003-11-08 00:09:04 +01:00
if (isc_database_info(status_vector, &handle, sizeof(limbo_info),
reinterpret_cast<const char*>(limbo_info),
2001-05-23 15:26:42 +02:00
sizeof(buffer),
reinterpret_cast<char*>(buffer))) {
2001-05-23 15:26:42 +02:00
ALICE_print_status(status_vector);
return;
}
SLONG id;
TDR trans;
UCHAR* ptr = buffer;
bool flag = true;
2001-05-23 15:26:42 +02:00
while (flag) {
const USHORT item = *ptr++;
const USHORT length = (USHORT) gds__vax_integer(ptr, 2);
2001-05-23 15:26:42 +02:00
ptr += 2;
switch (item) {
2003-11-08 00:09:04 +01:00
case isc_info_limbo:
id = gds__vax_integer(ptr, length);
2001-05-23 15:26:42 +02:00
if (switches &
2003-09-10 19:52:12 +02:00
(sw_commit | sw_rollback | sw_two_phase | sw_prompt))
{
2001-05-23 15:26:42 +02:00
TDR_reconnect_multiple(handle, id, name, switches);
ptr += length;
break;
}
if (!tdgbl->sw_service_thd)
ALICE_print(71, reinterpret_cast<char*>(id), 0, 0, 0, 0);
// msg 71: Transaction %d is in limbo.
2001-05-23 15:26:42 +02:00
if (trans = MET_get_transaction(status_vector, handle, id)) {
#ifdef SUPERSERVER
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_multi_tra_id);
SVC_putc(tdgbl->service_blk, (UCHAR) id);
SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 8));
SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 16));
SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 24));
#endif
reattach_databases(trans);
TDR_get_states(trans);
TDR_shutdown_databases(trans);
print_description(trans);
}
#ifdef SUPERSERVER
else {
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_single_tra_id);
SVC_putc(tdgbl->service_blk, (UCHAR) id);
SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 8));
SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 16));
SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 24));
}
#endif
ptr += length;
break;
2003-11-08 00:09:04 +01:00
case isc_info_truncated:
2001-05-23 15:26:42 +02:00
if (!tdgbl->sw_service_thd)
ALICE_print(72, 0, 0, 0, 0, 0);
// msg 72: More limbo transactions than fit. Try again
2001-05-23 15:26:42 +02:00
2003-11-08 00:09:04 +01:00
case isc_info_end:
2003-09-10 19:52:12 +02:00
flag = false;
2001-05-23 15:26:42 +02:00
break;
default:
if (!tdgbl->sw_service_thd)
ALICE_print(73, reinterpret_cast<char*>(item), 0, 0, 0, 0);
// msg 73: Unrecognized info item %d
2001-05-23 15:26:42 +02:00
}
}
}
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// Check a transaction's TDR to see if it is
// a multi-database transaction. If so, commit
// or rollback according to the user's wishes.
// Object strongly if the transaction is in a
// state that would seem to preclude committing
// or rolling back, but essentially do what the
// user wants. Intelligence is assumed for the
2001-05-23 15:26:42 +02:00
// gfix user.
//
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
bool TDR_reconnect_multiple(FB_API_HANDLE handle,
2003-09-10 19:52:12 +02:00
SLONG id,
2003-10-16 10:51:06 +02:00
const TEXT* name,
2003-09-10 19:52:12 +02:00
ULONG switches)
2001-05-23 15:26:42 +02:00
{
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
2001-05-23 15:26:42 +02:00
// get the state of all the associated transactions
2001-05-23 15:26:42 +02:00
TDR trans = MET_get_transaction(status_vector, handle, id);
if (!trans)
2001-05-23 15:26:42 +02:00
return reconnect(handle, id, name, switches);
reattach_databases(trans);
TDR_get_states(trans);
// analyze what to do with them; if the advice contradicts the user's
// desire, make them confirm it; otherwise go with the flow.
2001-05-23 15:26:42 +02:00
2003-10-16 10:51:06 +02:00
const USHORT advice = TDR_analyze(trans);
2001-05-23 15:26:42 +02:00
if (!advice) {
print_description(trans);
switches = ask();
}
else {
switch (advice) {
case TRA_rollback:
if (switches & sw_commit) {
ALICE_print(74, reinterpret_cast<char*>(trans->tdr_id), 0,
0, 0, 0);
// msg 74: A commit of transaction %ld will violate two-phase commit.
2001-05-23 15:26:42 +02:00
print_description(trans);
switches = ask();
}
else if (switches & sw_rollback)
switches |= sw_rollback;
else if (switches & sw_two_phase)
switches |= sw_rollback;
else if (switches & sw_prompt) {
ALICE_print(75, reinterpret_cast<char*>(trans->tdr_id), 0,
0, 0, 0);
// msg 75: A rollback of transaction %ld is needed to preserve two-phase commit.
2001-05-23 15:26:42 +02:00
print_description(trans);
switches = ask();
}
break;
case TRA_commit:
if (switches & sw_rollback) {
ALICE_print(76, reinterpret_cast<char*>(trans->tdr_id), 0,
0, 0, 0);
// msg 76: Transaction %ld has already been partially committed.
ALICE_print(77, 0, 0, 0, 0, 0);
// msg 77: A rollback of this transaction will violate two-phase commit.
2001-05-23 15:26:42 +02:00
print_description(trans);
switches = ask();
}
else if (switches & sw_commit)
switches |= sw_commit;
else if (switches & sw_two_phase)
switches |= sw_commit;
2001-05-24 16:54:26 +02:00
else if (switches & sw_prompt)
{
ALICE_print(78, reinterpret_cast<char*>(trans->tdr_id), 0,
0, 0, 0);
// msg 78: Transaction %ld has been partially committed.
ALICE_print(79, 0, 0, 0, 0, 0);
// msg 79: A commit is necessary to preserve the two-phase commit.
2001-05-23 15:26:42 +02:00
print_description(trans);
switches = ask();
}
break;
case TRA_unknown:
ALICE_print(80, 0, 0, 0, 0, 0);
// msg 80: Insufficient information is available to determine
ALICE_print(81, reinterpret_cast<char*>(trans->tdr_id), 0, 0,
0, 0);
// msg 81: a proper action for transaction %ld.
2001-05-23 15:26:42 +02:00
print_description(trans);
switches = ask();
break;
default:
2001-05-24 16:54:26 +02:00
if (!(switches & (sw_commit | sw_rollback)))
{
ALICE_print(82, reinterpret_cast<char*>(trans->tdr_id), 0,
0, 0, 0);
// msg 82: Transaction %ld: All subtransactions have been prepared.
ALICE_print(83, 0, 0, 0, 0, 0);
// msg 83: Either commit or rollback is possible.
2001-05-23 15:26:42 +02:00
print_description(trans);
switches = ask();
}
}
}
bool error = false;
2003-10-16 10:51:06 +02:00
if (switches != (ULONG) -1)
2001-05-24 16:54:26 +02:00
{
// now do the required operation with all the subtransactions
2001-05-23 15:26:42 +02:00
if (switches & (sw_commit | sw_rollback))
2001-05-24 16:54:26 +02:00
{
for (TDR ptr = trans; ptr; ptr = ptr->tdr_next)
2001-05-24 16:54:26 +02:00
{
2001-05-23 15:26:42 +02:00
if (ptr->tdr_state == TRA_limbo)
2001-05-24 16:54:26 +02:00
{
reconnect(ptr->tdr_db_handle,
2001-05-23 15:26:42 +02:00
ptr->tdr_id, ptr->tdr_filename, switches);
2001-05-24 16:54:26 +02:00
}
}
}
2001-05-23 15:26:42 +02:00
}
2001-05-24 16:54:26 +02:00
else
{
ALICE_print(84, 0, 0, 0, 0, 0); // msg 84: unexpected end of input
2003-09-10 19:52:12 +02:00
error = true;
2001-05-23 15:26:42 +02:00
}
// shutdown all the databases for cleanliness' sake
2001-05-23 15:26:42 +02:00
TDR_shutdown_databases(trans);
return error;
}
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// format and print description of a transaction in
// limbo, including all associated transactions
// in other databases.
//
2001-05-23 15:26:42 +02:00
static void print_description(const tdr* trans)
2001-05-23 15:26:42 +02:00
{
Tgbl* tdgbl = ALICE_get_thread_data();
2001-05-23 15:26:42 +02:00
if (!trans)
2001-05-24 16:54:26 +02:00
{
2001-05-23 15:26:42 +02:00
return;
2001-05-24 16:54:26 +02:00
}
2001-05-23 15:26:42 +02:00
if (!tdgbl->sw_service_thd)
2001-05-24 16:54:26 +02:00
{
ALICE_print(92, 0, 0, 0, 0, 0); // msg 92: Multidatabase transaction:
2001-05-24 16:54:26 +02:00
}
2001-05-23 15:26:42 +02:00
bool prepared_seen = false;
for (const tdr* ptr = trans; ptr; ptr = ptr->tdr_next)
2001-05-24 16:54:26 +02:00
{
if (ptr->tdr_host_site)
{
const char* pszHostSize =
reinterpret_cast<const char*>(ptr->tdr_host_site->str_data);
2001-05-24 16:54:26 +02:00
2001-05-23 15:26:42 +02:00
#ifndef SUPERSERVER
// msg 93: Host Site: %s
2001-05-24 16:54:26 +02:00
ALICE_print(93, pszHostSize, 0, 0, 0, 0);
2001-05-23 15:26:42 +02:00
#else
2001-05-24 16:54:26 +02:00
const size_t nHostSiteLen = strlen(pszHostSize);
2001-05-23 15:26:42 +02:00
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_host_site);
2001-05-24 16:54:26 +02:00
SVC_putc(tdgbl->service_blk, (UCHAR) nHostSiteLen);
SVC_putc(tdgbl->service_blk, (UCHAR) (nHostSiteLen >> 8 ));
for (int i = 0; i < (int) nHostSiteLen; i++)
2001-05-24 16:54:26 +02:00
{
SVC_putc(tdgbl->service_blk, (UCHAR) pszHostSize[i]);
}
2001-05-23 15:26:42 +02:00
#endif
}
if (ptr->tdr_id)
2001-05-24 16:54:26 +02:00
{
2001-05-23 15:26:42 +02:00
#ifndef SUPERSERVER
// msg 94: Transaction %ld
2001-05-24 16:54:26 +02:00
ALICE_print(94, reinterpret_cast<char*>(ptr->tdr_id), 0, 0, 0, 0);
2001-05-23 15:26:42 +02:00
#else
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_id);
SVC_putc(tdgbl->service_blk, (UCHAR) ptr->tdr_id);
SVC_putc(tdgbl->service_blk, (UCHAR) (ptr->tdr_id >> 8));
SVC_putc(tdgbl->service_blk, (UCHAR) (ptr->tdr_id >> 16));
SVC_putc(tdgbl->service_blk, (UCHAR) (ptr->tdr_id >> 24));
#endif
2001-05-24 16:54:26 +02:00
}
2001-05-23 15:26:42 +02:00
2001-05-24 16:54:26 +02:00
switch (ptr->tdr_state)
{
2001-05-23 15:26:42 +02:00
case TRA_limbo:
#ifndef SUPERSERVER
ALICE_print(95, 0, 0, 0, 0, 0); // msg 95: has been prepared.
2001-05-23 15:26:42 +02:00
#else
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state);
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_limbo);
#endif
2003-09-10 19:52:12 +02:00
prepared_seen = true;
2001-05-23 15:26:42 +02:00
break;
case TRA_commit:
#ifndef SUPERSERVER
ALICE_print(96, 0, 0, 0, 0, 0); // msg 96: has been committed.
2001-05-23 15:26:42 +02:00
#else
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state);
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_commit);
#endif
break;
case TRA_rollback:
#ifndef SUPERSERVER
ALICE_print(97, 0, 0, 0, 0, 0); // msg 97: has been rolled back.
2001-05-23 15:26:42 +02:00
#else
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state);
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_rollback);
#endif
break;
case TRA_unknown:
#ifndef SUPERSERVER
ALICE_print(98, 0, 0, 0, 0, 0); // msg 98: is not available.
2001-05-23 15:26:42 +02:00
#else
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state);
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_unknown);
#endif
break;
default:
#ifndef SUPERSERVER
if (prepared_seen)
2001-05-24 16:54:26 +02:00
{
// msg 99: is not found, assumed not prepared.
2001-05-24 16:54:26 +02:00
ALICE_print(99, 0, 0, 0, 0, 0);
}
2001-05-23 15:26:42 +02:00
else
2001-05-24 16:54:26 +02:00
{
// msg 100: is not found, assumed to be committed.
2001-05-24 16:54:26 +02:00
ALICE_print(100, 0, 0, 0, 0, 0);
}
2001-05-23 15:26:42 +02:00
#endif
break;
}
2001-05-24 16:54:26 +02:00
if (ptr->tdr_remote_site)
{
const char* pszRemoteSite =
reinterpret_cast<const char*>(ptr->tdr_remote_site->str_data);
2001-05-24 16:54:26 +02:00
2001-05-23 15:26:42 +02:00
#ifndef SUPERSERVER
//msg 101: Remote Site: %s
2001-05-24 16:54:26 +02:00
ALICE_print(101, pszRemoteSite, 0, 0, 0, 0);
2001-05-23 15:26:42 +02:00
#else
2001-05-24 16:54:26 +02:00
const size_t nRemoteSiteLen = strlen(pszRemoteSite);
2001-05-23 15:26:42 +02:00
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_remote_site);
2001-05-24 16:54:26 +02:00
SVC_putc(tdgbl->service_blk, (UCHAR) nRemoteSiteLen);
SVC_putc(tdgbl->service_blk, (UCHAR) (nRemoteSiteLen >> 8));
for (int i = 0; i < (int) nRemoteSiteLen; i++)
2001-05-24 16:54:26 +02:00
{
SVC_putc(tdgbl->service_blk, (UCHAR) pszRemoteSite[i]);
2001-05-24 16:54:26 +02:00
}
2001-05-23 15:26:42 +02:00
#endif
}
2001-05-24 16:54:26 +02:00
if (ptr->tdr_fullpath)
{
const char* pszFullpath =
reinterpret_cast<const char*>(ptr->tdr_fullpath->str_data);
2001-05-24 16:54:26 +02:00
2001-05-23 15:26:42 +02:00
#ifndef SUPERSERVER
// msg 102: Database Path: %s
2001-05-24 16:54:26 +02:00
ALICE_print(102, pszFullpath, 0, 0, 0, 0);
2001-05-23 15:26:42 +02:00
#else
2001-05-24 16:54:26 +02:00
const size_t nFullpathLen = strlen(pszFullpath);
2001-05-23 15:26:42 +02:00
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_db_path);
2001-05-24 16:54:26 +02:00
SVC_putc(tdgbl->service_blk, (UCHAR) nFullpathLen);
SVC_putc(tdgbl->service_blk, (UCHAR) (nFullpathLen >> 8));
for (int i = 0; i < (int) nFullpathLen; i++)
2001-05-24 16:54:26 +02:00
{
SVC_putc(tdgbl->service_blk, (UCHAR) pszFullpath[i]);
}
2001-05-23 15:26:42 +02:00
#endif
}
}
// let the user know what the suggested action is
2001-05-23 15:26:42 +02:00
2001-05-24 16:54:26 +02:00
switch (TDR_analyze(trans))
{
2001-05-23 15:26:42 +02:00
case TRA_commit:
#ifndef SUPERSERVER
// msg 103: Automated recovery would commit this transaction.
2001-05-24 16:54:26 +02:00
ALICE_print(103, 0, 0, 0, 0, 0);
2001-05-23 15:26:42 +02:00
#else
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise);
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise_commit);
#endif
break;
case TRA_rollback:
#ifndef SUPERSERVER
// msg 104: Automated recovery would rollback this transaction.
2001-05-24 16:54:26 +02:00
ALICE_print(104, 0, 0, 0, 0, 0);
2001-05-23 15:26:42 +02:00
#else
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise);
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise_rollback);
#endif
break;
default:
#ifdef SUPERSERVER
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise);
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise_unknown);
#endif
break;
}
}
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// Ask the user whether to commit or rollback.
//
2001-05-23 15:26:42 +02:00
static ULONG ask(void)
{
UCHAR response[32];
char* const resp_ptr = reinterpret_cast<char*>(response);
Tgbl* tdgbl = ALICE_get_thread_data();
2001-05-23 15:26:42 +02:00
ULONG switches = 0;
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
while (true) {
ALICE_print(85, 0, 0, 0, 0, 0);
// msg 85: Commit, rollback, or neither (c, r, or n)?
2001-05-23 15:26:42 +02:00
if (tdgbl->sw_service)
2004-04-29 00:00:03 +02:00
putc('\001', stdout);
fflush(stdout);
int c;
UCHAR* p;
2004-04-29 00:00:03 +02:00
for (p = response; (c = getchar()) != '\n' && c != EOF;)
2001-05-23 15:26:42 +02:00
*p++ = c;
if (c == EOF && p == response)
2003-10-16 10:51:06 +02:00
return (ULONG) -1;
2001-05-23 15:26:42 +02:00
*p = 0;
ALICE_down_case(resp_ptr, resp_ptr, sizeof(response));
if (!strcmp(resp_ptr, "n") || !strcmp(resp_ptr, "c")
|| !strcmp(resp_ptr, "r"))
{
2001-05-23 15:26:42 +02:00
break;
}
2001-05-23 15:26:42 +02:00
}
if (response[0] == 'c')
switches |= sw_commit;
else if (response[0] == 'r')
switches |= sw_rollback;
return switches;
}
//____________________________________________________________
//
// Generate pathnames for a given database
2001-05-23 15:26:42 +02:00
// until the database is successfully attached.
//
2001-05-23 15:26:42 +02:00
static void reattach_database(TDR trans)
{
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
2003-10-16 10:51:06 +02:00
UCHAR buffer[1024];
// sizeof(buffer) - 1 => leave space for the terminator.
const UCHAR* const end = buffer + sizeof(buffer) - 1;
Tgbl* tdgbl = ALICE_get_thread_data();
2001-05-23 15:26:42 +02:00
ISC_get_host(reinterpret_cast<char*>(buffer), sizeof(buffer));
2001-05-23 15:26:42 +02:00
// if this is being run from the same host,
// try to reconnect using the same pathname
if (!strcmp(reinterpret_cast<const char*>(buffer),
reinterpret_cast<const char*>(trans->tdr_host_site->str_data)))
2003-09-10 19:52:12 +02:00
{
if (TDR_attach_database(status_vector, trans,
reinterpret_cast<char*>
(trans->tdr_fullpath->str_data)))
{
return;
}
}
else if (trans->tdr_host_site) {
2003-10-16 10:51:06 +02:00
// try going through the previous host with all available
// protocols, using chaining to try the same method of
// attachment originally used from that host
UCHAR* p = buffer;
const UCHAR* q = trans->tdr_host_site->str_data;
while (*q && p < end)
2001-05-23 15:26:42 +02:00
*p++ = *q++;
*p++ = ':';
2003-10-16 10:51:06 +02:00
q = trans->tdr_fullpath->str_data;
while (*q && p < end)
*p++ = *q++;
2003-10-16 10:51:06 +02:00
*p = 0;
2003-09-10 19:52:12 +02:00
if (TDR_attach_database(status_vector, trans,
reinterpret_cast<char*>(buffer)))
2003-09-10 19:52:12 +02:00
{
return;
}
2001-05-23 15:26:42 +02:00
}
// attaching using the old method didn't work;
// try attaching to the remote node directly
2001-05-23 15:26:42 +02:00
if (trans->tdr_remote_site) {
2003-10-16 10:51:06 +02:00
UCHAR* p = buffer;
const UCHAR* q = trans->tdr_remote_site->str_data;
while (*q && p < end)
2001-05-23 15:26:42 +02:00
*p++ = *q++;
*p++ = ':';
2003-10-16 10:51:06 +02:00
q = (UCHAR *) trans->tdr_filename;
while (*q && p < end)
*p++ = *q++;
2003-10-16 10:51:06 +02:00
*p = 0;
2003-09-10 19:52:12 +02:00
if (TDR_attach_database (status_vector, trans,
reinterpret_cast<char*>(buffer)))
2003-09-10 19:52:12 +02:00
{
return;
}
2001-05-23 15:26:42 +02:00
}
// we have failed to reattach; notify the user
// and let them try to succeed where we have failed
2001-05-23 15:26:42 +02:00
ALICE_print(86, reinterpret_cast<char*>(trans->tdr_id), 0, 0, 0, 0);
// msg 86: Could not reattach to database for transaction %ld.
ALICE_print(87, reinterpret_cast<char*>(trans->tdr_fullpath->str_data),
0, 0, 0, 0); // msg 87: Original path: %s
2001-05-23 15:26:42 +02:00
for (;;) {
ALICE_print(88, 0, 0, 0, 0, 0); // msg 88: Enter a valid path:
2003-10-16 10:51:06 +02:00
UCHAR* p = buffer;
2004-04-29 00:00:03 +02:00
while (p < end && (*p = getchar()) != '\n')
2003-10-16 10:51:06 +02:00
++p;
2001-05-23 15:26:42 +02:00
*p = 0;
if (!buffer[0])
break;
p = buffer;
while (*p == ' ')
2003-10-16 10:51:06 +02:00
++p;
2003-09-10 19:52:12 +02:00
if (TDR_attach_database(status_vector, trans,
2001-12-24 03:51:06 +01:00
reinterpret_cast<char*>(p)))
{
alice_str* string = FB_NEW_RPT(*tdgbl->ALICE_default_pool,
strlen(reinterpret_cast<const char*>(p)) + 1) alice_str;
2001-12-24 03:51:06 +01:00
strcpy(reinterpret_cast<char*>(string->str_data),
reinterpret_cast<const char*>(p));
string->str_length = strlen(reinterpret_cast<const char*>(p));
2001-05-23 15:26:42 +02:00
trans->tdr_fullpath = string;
trans->tdr_filename = (TEXT *) string->str_data;
return;
}
ALICE_print(89, 0, 0, 0, 0, 0); // msg 89: Attach unsuccessful.
2001-05-23 15:26:42 +02:00
}
}
//____________________________________________________________
//
// Attempt to locate all databases used in
2001-05-23 15:26:42 +02:00
// a multidatabase transaction.
//
2001-05-23 15:26:42 +02:00
static void reattach_databases(TDR trans)
{
for (TDR ptr = trans; ptr; ptr = ptr->tdr_next)
2001-05-23 15:26:42 +02:00
reattach_database(ptr);
}
//____________________________________________________________
//
2001-05-23 15:26:42 +02:00
// Commit or rollback a named transaction.
//
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
static bool reconnect(FB_API_HANDLE handle,
2003-09-10 19:52:12 +02:00
SLONG number,
2003-10-16 10:51:06 +02:00
const TEXT* name,
2003-09-10 19:52:12 +02:00
ULONG switches)
2001-05-23 15:26:42 +02:00
{
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
2001-05-23 15:26:42 +02:00
const SLONG id = gds__vax_integer(reinterpret_cast<const UCHAR*>(&number), 4);
2004-05-03 01:06:37 +02:00
FB_API_HANDLE transaction = 0;
2003-11-08 00:09:04 +01:00
if (isc_reconnect_transaction(status_vector, &handle, &transaction,
sizeof(id),
reinterpret_cast<const char*>(&id)))
{
2003-09-10 19:52:12 +02:00
ALICE_print(90, name, 0, 0, 0, 0);
// msg 90: failed to reconnect to a transaction in database %s
2001-05-23 15:26:42 +02:00
ALICE_print_status(status_vector);
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
if (!(switches & (sw_commit | sw_rollback))) {
ALICE_print(91, reinterpret_cast<char*>(number), 0, 0, 0, 0);
2003-09-10 19:52:12 +02:00
// msg 91: Transaction %ld:
2001-05-23 15:26:42 +02:00
switches = ask();
2003-10-16 10:51:06 +02:00
if (switches == (ULONG) -1) {
2003-09-10 19:52:12 +02:00
ALICE_print(84, 0, 0, 0, 0, 0);
// msg 84: unexpected end of input
return true;
2001-05-23 15:26:42 +02:00
}
}
if (switches & sw_commit)
2003-11-08 00:09:04 +01:00
isc_commit_transaction(status_vector, &transaction);
2001-05-23 15:26:42 +02:00
else if (switches & sw_rollback)
2003-11-08 00:09:04 +01:00
isc_rollback_transaction(status_vector, &transaction);
2001-05-23 15:26:42 +02:00
else
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (status_vector[1]) {
ALICE_print_status(status_vector);
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
}