mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-30 09:23:09 +01:00
1008 lines
25 KiB
C++
1008 lines
25 KiB
C++
//____________________________________________________________
|
|
//
|
|
// PROGRAM: Alice (All Else) Utility
|
|
// MODULE: tdr.cpp
|
|
// DESCRIPTION: Routines for automated transaction recovery
|
|
//
|
|
// 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: tdr.cpp,v 1.12 2003-01-12 18:16:49 alexpeshkoff Exp $
|
|
//
|
|
// 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "Apollo" port
|
|
//
|
|
// 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix" port
|
|
//
|
|
//
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include <string.h>
|
|
#include "../jrd/gds.h"
|
|
#include "../jrd/common.h"
|
|
#include "../alice/alice.h"
|
|
#include "../alice/aliceswi.h"
|
|
#include "../alice/all.h"
|
|
#include "../alice/alloc.h"
|
|
#include "../alice/info.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(TDR);
|
|
static void reattach_database(TDR);
|
|
static void reattach_databases(TDR);
|
|
static BOOLEAN reconnect(FRBRD *, SLONG, TEXT *, ULONG);
|
|
|
|
|
|
#ifdef GUI_TOOLS
|
|
#define NEWLINE "\r\n"
|
|
#else
|
|
#define NEWLINE "\n"
|
|
#endif
|
|
|
|
static UCHAR limbo_info[] = { gds_info_limbo, gds_info_end };
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// The following routines are shared by the command line gfix and
|
|
// the windows server manager. These routines should not contain
|
|
// any direct screen I/O (i.e. ib_printf/ib_getc statements).
|
|
//
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Determine the proper action to take
|
|
// based on the state of the various
|
|
// transactions.
|
|
//
|
|
|
|
USHORT TDR_analyze(TDR trans)
|
|
{
|
|
USHORT state, advice = TRA_none;
|
|
|
|
if (trans == NULL)
|
|
return TRA_none;
|
|
|
|
// if the tdr for the first transaction is missing,
|
|
// we can assume it was committed
|
|
|
|
state = trans->tdr_state;
|
|
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 */
|
|
|
|
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. */
|
|
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
|
|
|
|
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
|
|
|
|
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. */
|
|
|
|
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
|
|
|
|
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. */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return advice;
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Attempt to attach a database with a given pathname.
|
|
//
|
|
|
|
BOOLEAN TDR_attach_database(STATUS * status_vector,
|
|
TDR trans, TEXT * pathname)
|
|
{
|
|
UCHAR dpb[128], *d, *q;
|
|
USHORT dpb_length;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
#ifndef GUI_TOOLS
|
|
if (tdgbl->ALICE_data.ua_debug)
|
|
ALICE_print(68, pathname, 0, 0, 0, 0); /* msg 68: ATTACH_DATABASE: attempted attach of %s */
|
|
#endif
|
|
|
|
d = dpb;
|
|
|
|
*d++ = gds_dpb_version1;
|
|
*d++ = gds_dpb_no_garbage_collect;
|
|
*d++ = 0;
|
|
*d++ = isc_dpb_gfix_attach;
|
|
*d++ = 0;
|
|
|
|
if (tdgbl->ALICE_data.ua_user) {
|
|
*d++ = gds_dpb_user_name;
|
|
*d++ =
|
|
strlen(reinterpret_cast <
|
|
const char *>(tdgbl->ALICE_data.ua_user));
|
|
for (q = tdgbl->ALICE_data.ua_user; *q;)
|
|
*d++ = *q++;
|
|
}
|
|
|
|
if (tdgbl->ALICE_data.ua_password) {
|
|
if (!tdgbl->sw_service_thd)
|
|
*d++ = gds_dpb_password;
|
|
else
|
|
*d++ = gds_dpb_password_enc;
|
|
*d++ =
|
|
strlen(reinterpret_cast <
|
|
const char *>(tdgbl->ALICE_data.ua_password));
|
|
for (q = tdgbl->ALICE_data.ua_password; *q;)
|
|
*d++ = *q++;
|
|
}
|
|
|
|
dpb_length = d - dpb;
|
|
if (dpb_length == 1)
|
|
dpb_length = 0;
|
|
|
|
trans->tdr_db_handle = NULL;
|
|
|
|
gds__attach_database(status_vector,
|
|
0,
|
|
GDS_VAL(pathname),
|
|
(GDS_REF(trans->tdr_db_handle)), dpb_length,
|
|
reinterpret_cast < char *>(GDS_VAL(dpb)));
|
|
|
|
if (status_vector[1]) {
|
|
#ifndef GUI_TOOLS
|
|
if (tdgbl->ALICE_data.ua_debug) {
|
|
ALICE_print(69, 0, 0, 0, 0, 0); /* msg 69: failed */
|
|
ALICE_print_status(status_vector);
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
MET_set_capabilities(status_vector, trans);
|
|
|
|
#ifndef GUI_TOOLS
|
|
if (tdgbl->ALICE_data.ua_debug)
|
|
ALICE_print(70, 0, 0, 0, 0, 0); /* msg 70: succeeded */
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Get the state of the various transactions
|
|
// in a multidatabase transaction.
|
|
//
|
|
|
|
void TDR_get_states(TDR trans)
|
|
{
|
|
STATUS status_vector[ISC_STATUS_LENGTH];
|
|
TDR ptr;
|
|
|
|
for (ptr = trans; ptr; ptr = ptr->tdr_next)
|
|
MET_get_state(status_vector, ptr);
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Detach all databases associated with
|
|
// a multidatabase transaction.
|
|
//
|
|
|
|
void TDR_shutdown_databases(TDR trans)
|
|
{
|
|
TDR ptr;
|
|
STATUS status_vector[ISC_STATUS_LENGTH];
|
|
|
|
for (ptr = trans; ptr; ptr = ptr->tdr_next)
|
|
gds__detach_database(status_vector,
|
|
(GDS_REF(ptr->tdr_db_handle)));
|
|
}
|
|
|
|
|
|
|
|
#ifndef GUI_TOOLS
|
|
//
|
|
// The following routines are only for the command line utility.
|
|
// This should really be split into two files...
|
|
//
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// List transaction stuck in limbo. If the prompt switch is set,
|
|
// prompt for commit, rollback, or leave well enough alone.
|
|
//
|
|
|
|
void TDR_list_limbo(FRBRD *handle, TEXT * name, ULONG switches)
|
|
{
|
|
UCHAR buffer[1024], *ptr;
|
|
STATUS status_vector[ISC_STATUS_LENGTH];
|
|
SLONG id;
|
|
USHORT item, flag, length;
|
|
TDR trans;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
if (gds__database_info(status_vector,
|
|
(GDS_REF(handle)),
|
|
sizeof(limbo_info),
|
|
reinterpret_cast < char *>(limbo_info),
|
|
sizeof(buffer),
|
|
reinterpret_cast < char *>(buffer))) {
|
|
ALICE_print_status(status_vector);
|
|
return;
|
|
}
|
|
|
|
ptr = buffer;
|
|
flag = TRUE;
|
|
|
|
while (flag) {
|
|
item = *ptr++;
|
|
length = (USHORT) gds__vax_integer(ptr, 2);
|
|
ptr += 2;
|
|
switch (item) {
|
|
case gds_info_limbo:
|
|
id = gds__vax_integer(ptr, length);
|
|
if (switches &
|
|
(sw_commit | sw_rollback | sw_two_phase | sw_prompt)) {
|
|
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. */
|
|
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;
|
|
|
|
case gds_info_truncated:
|
|
if (!tdgbl->sw_service_thd)
|
|
ALICE_print(72, 0, 0, 0, 0, 0); /* msg 72: More limbo transactions than fit. Try again */
|
|
|
|
case gds_info_end:
|
|
flag = FALSE;
|
|
break;
|
|
|
|
default:
|
|
if (!tdgbl->sw_service_thd)
|
|
ALICE_print(73, reinterpret_cast < char *>(item), 0, 0, 0, 0); /* msg 73: Unrecognized info item %d */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// 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
|
|
// gfix user.
|
|
//
|
|
|
|
BOOLEAN TDR_reconnect_multiple(FRBRD *handle,
|
|
SLONG id, TEXT * name, ULONG switches)
|
|
{
|
|
TDR trans, ptr;
|
|
STATUS status_vector[ISC_STATUS_LENGTH];
|
|
USHORT advice;
|
|
BOOLEAN error = FALSE;
|
|
|
|
// get the state of all the associated transactions
|
|
|
|
if (!(trans = MET_get_transaction(status_vector, handle, id)))
|
|
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.
|
|
|
|
advice = TDR_analyze(trans);
|
|
|
|
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. */
|
|
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. */
|
|
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. */
|
|
print_description(trans);
|
|
switches = ask();
|
|
}
|
|
else if (switches & sw_commit)
|
|
switches |= sw_commit;
|
|
else if (switches & sw_two_phase)
|
|
switches |= sw_commit;
|
|
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. */
|
|
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. */
|
|
print_description(trans);
|
|
switches = ask();
|
|
break;
|
|
|
|
default:
|
|
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. */
|
|
print_description(trans);
|
|
switches = ask();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (switches != (ULONG) - 1)
|
|
{
|
|
/* now do the required operation with all the subtransactions */
|
|
|
|
if (switches & (sw_commit | sw_rollback))
|
|
{
|
|
for (ptr = trans; ptr; ptr = ptr->tdr_next)
|
|
{
|
|
if (ptr->tdr_state == TRA_limbo)
|
|
{
|
|
reconnect((ptr->tdr_db_handle),
|
|
ptr->tdr_id, ptr->tdr_filename, switches);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ALICE_print(84, 0, 0, 0, 0, 0); /* msg 84: unexpected end of input */
|
|
error = TRUE;
|
|
}
|
|
|
|
// shutdown all the databases for cleanliness' sake
|
|
|
|
TDR_shutdown_databases(trans);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// format and print description of a transaction in
|
|
// limbo, including all associated transactions
|
|
// in other databases.
|
|
//
|
|
|
|
static void print_description(TDR trans)
|
|
{
|
|
TDR ptr;
|
|
BOOLEAN prepared_seen;
|
|
|
|
#ifdef SUPERSERVER
|
|
int i;
|
|
#endif
|
|
|
|
TGBL tdgbl = GET_THREAD_DATA;
|
|
|
|
if (!trans)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!tdgbl->sw_service_thd)
|
|
{
|
|
ALICE_print(92, 0, 0, 0, 0, 0); /* msg 92: Multidatabase transaction: */
|
|
}
|
|
|
|
prepared_seen = FALSE;
|
|
for (ptr = trans; ptr; ptr = ptr->tdr_next)
|
|
{
|
|
if (ptr->tdr_host_site)
|
|
{
|
|
char* pszHostSize =
|
|
reinterpret_cast<char*>(ptr->tdr_host_site->str_data);
|
|
|
|
#ifndef SUPERSERVER
|
|
/* msg 93: Host Site: %s */
|
|
ALICE_print(93, pszHostSize, 0, 0, 0, 0);
|
|
#else
|
|
const size_t nHostSiteLen = strlen(pszHostSize);
|
|
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_host_site);
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) nHostSiteLen);
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) (nHostSiteLen >> 8 ));
|
|
for (i = 0; i < (int) nHostSiteLen; i++)
|
|
{
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) pszHostSize[i]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ptr->tdr_id)
|
|
{
|
|
#ifndef SUPERSERVER
|
|
/* msg 94: Transaction %ld */
|
|
ALICE_print(94, reinterpret_cast<char*>(ptr->tdr_id), 0, 0, 0, 0);
|
|
#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
|
|
}
|
|
|
|
switch (ptr->tdr_state)
|
|
{
|
|
case TRA_limbo:
|
|
#ifndef SUPERSERVER
|
|
ALICE_print(95, 0, 0, 0, 0, 0); /* msg 95: has been prepared. */
|
|
#else
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state);
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_limbo);
|
|
#endif
|
|
prepared_seen = TRUE;
|
|
break;
|
|
|
|
case TRA_commit:
|
|
#ifndef SUPERSERVER
|
|
ALICE_print(96, 0, 0, 0, 0, 0); /* msg 96: has been committed. */
|
|
#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. */
|
|
#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. */
|
|
#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)
|
|
{
|
|
/* msg 99: is not found, assumed not prepared. */
|
|
ALICE_print(99, 0, 0, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
/* msg 100: is not found, assumed to be committed. */
|
|
ALICE_print(100, 0, 0, 0, 0, 0);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (ptr->tdr_remote_site)
|
|
{
|
|
char* pszRemoteSite =
|
|
reinterpret_cast<char*>(ptr->tdr_remote_site->str_data);
|
|
|
|
#ifndef SUPERSERVER
|
|
/*msg 101: Remote Site: %s */
|
|
ALICE_print(101, pszRemoteSite, 0, 0, 0, 0);
|
|
#else
|
|
const size_t nRemoteSiteLen = strlen(pszRemoteSite);
|
|
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_remote_site);
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) nRemoteSiteLen);
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) (nRemoteSiteLen >> 8));
|
|
for (i = 0; i < (int) nRemoteSiteLen; i++)
|
|
{
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) nRemoteSiteLen);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ptr->tdr_fullpath)
|
|
{
|
|
char* pszFullpath =
|
|
reinterpret_cast<char*>(ptr->tdr_fullpath->str_data);
|
|
|
|
#ifndef SUPERSERVER
|
|
/* msg 102: Database Path: %s */
|
|
ALICE_print(102, pszFullpath, 0, 0, 0, 0);
|
|
#else
|
|
const size_t nFullpathLen = strlen(pszFullpath);
|
|
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_db_path);
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) nFullpathLen);
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) (nFullpathLen >> 8));
|
|
for (i = 0; i < (int) nFullpathLen; i++)
|
|
{
|
|
SVC_putc(tdgbl->service_blk, (UCHAR) pszFullpath[i]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
// let the user know what the suggested action is
|
|
|
|
switch (TDR_analyze(trans))
|
|
{
|
|
case TRA_commit:
|
|
#ifndef SUPERSERVER
|
|
/* msg 103: Automated recovery would commit this transaction. */
|
|
ALICE_print(103, 0, 0, 0, 0, 0);
|
|
#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. */
|
|
ALICE_print(104, 0, 0, 0, 0, 0);
|
|
#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;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Ask the user whether to commit or rollback.
|
|
//
|
|
|
|
static ULONG ask(void)
|
|
{
|
|
UCHAR response[32], *p;
|
|
int c;
|
|
ULONG switches;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
switches = 0;
|
|
|
|
while (TRUE) {
|
|
ALICE_print(85, 0, 0, 0, 0, 0); /* msg 85: Commit, rollback, or neither (c, r, or n)? */
|
|
if (tdgbl->sw_service)
|
|
ib_putc('\001', ib_stdout);
|
|
ib_fflush(ib_stdout);
|
|
for (p = response; (c = ib_getchar()) != '\n' && c != EOF;)
|
|
*p++ = c;
|
|
if (c == EOF && p == response)
|
|
return (ULONG) - 1;
|
|
*p = 0;
|
|
ALICE_down_case(reinterpret_cast < char *>(response),
|
|
reinterpret_cast < char *>(response));
|
|
if (!strcmp(reinterpret_cast < const char *>(response), "n")
|
|
|| !strcmp(reinterpret_cast < const char *>(response), "c")
|
|
|| !strcmp(reinterpret_cast < const char *>(response), "r"))
|
|
break;
|
|
}
|
|
|
|
if (response[0] == 'c')
|
|
switches |= sw_commit;
|
|
else if (response[0] == 'r')
|
|
switches |= sw_rollback;
|
|
|
|
return switches;
|
|
}
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Generate pathnames for a given database
|
|
// until the database is successfully attached.
|
|
//
|
|
|
|
static void reattach_database(TDR trans)
|
|
{
|
|
STATUS status_vector[ISC_STATUS_LENGTH];
|
|
UCHAR buffer[1024], *p, *q, *start;
|
|
USHORT protocols = 0;
|
|
STR string;
|
|
TGBL tdgbl;
|
|
|
|
tdgbl = GET_THREAD_DATA;
|
|
|
|
// determine what protocols are allowable for this OS
|
|
|
|
#ifdef VMS
|
|
protocols |= DECNET_PROTOCOL | VMS_TCP_PROTOCOL;
|
|
#else
|
|
protocols |= TCP_PROTOCOL;
|
|
#endif
|
|
|
|
ISC_get_host(reinterpret_cast < char *>(buffer), sizeof(buffer));
|
|
|
|
// 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)))
|
|
{
|
|
if (TDR_attach_database(status_vector, trans,
|
|
reinterpret_cast<char*>(trans->tdr_fullpath->str_data)))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// try going through the previous host with all available
|
|
// protocols, using chaining to try the same method of
|
|
// attachment originally used from that host
|
|
|
|
else if (trans->tdr_host_site) {
|
|
for (p = buffer, q = trans->tdr_host_site->str_data; *q;)
|
|
*p++ = *q++;
|
|
start = p;
|
|
|
|
if (protocols & DECNET_PROTOCOL) {
|
|
p = start;
|
|
*p++ = ':';
|
|
*p++ = ':';
|
|
for (q = trans->tdr_fullpath->str_data; *q;)
|
|
*p++ = *q++;
|
|
*q = 0;
|
|
if (TDR_attach_database
|
|
(status_vector, trans,
|
|
reinterpret_cast < char *>(buffer))) return;
|
|
}
|
|
|
|
if (protocols & VMS_TCP_PROTOCOL) {
|
|
p = start;
|
|
*p++ = '^';
|
|
for (q = trans->tdr_fullpath->str_data; *q;)
|
|
*p++ = *q++;
|
|
*q = 0;
|
|
if (TDR_attach_database
|
|
(status_vector, trans,
|
|
reinterpret_cast < char *>(buffer))) return;
|
|
}
|
|
|
|
if (protocols & TCP_PROTOCOL) {
|
|
p = start;
|
|
*p++ = ':';
|
|
for (q = trans->tdr_fullpath->str_data; *q;)
|
|
*p++ = *q++;
|
|
*q = 0;
|
|
if (TDR_attach_database
|
|
(status_vector, trans,
|
|
reinterpret_cast < char *>(buffer))) return;
|
|
}
|
|
}
|
|
|
|
// attaching using the old method didn't work;
|
|
// try attaching to the remote node directly
|
|
|
|
if (trans->tdr_remote_site) {
|
|
for (p = buffer, q = trans->tdr_remote_site->str_data; *q;)
|
|
*p++ = *q++;
|
|
start = p;
|
|
|
|
if (protocols & DECNET_PROTOCOL) {
|
|
p = start;
|
|
*p++ = ':';
|
|
*p++ = ':';
|
|
for (q = (UCHAR *) trans->tdr_filename; *q;)
|
|
*p++ = *q++;
|
|
*q = 0;
|
|
if (TDR_attach_database
|
|
(status_vector, trans,
|
|
reinterpret_cast < char *>(buffer))) return;
|
|
}
|
|
|
|
if (protocols & VMS_TCP_PROTOCOL) {
|
|
p = start;
|
|
*p++ = '^';
|
|
for (q = (UCHAR *) trans->tdr_filename; *q;)
|
|
*p++ = *q++;
|
|
*q = 0;
|
|
if (TDR_attach_database
|
|
(status_vector, trans,
|
|
reinterpret_cast < char *>(buffer))) return;
|
|
}
|
|
|
|
if (protocols & TCP_PROTOCOL) {
|
|
p = start;
|
|
*p++ = ':';
|
|
for (q = (UCHAR *) trans->tdr_filename; *q;)
|
|
*p++ = *q++;
|
|
*q = 0;
|
|
if (TDR_attach_database
|
|
(status_vector, trans,
|
|
reinterpret_cast < char *>(buffer))) return;
|
|
}
|
|
|
|
if (protocols & MSLAN_PROTOCOL) {
|
|
p = buffer;
|
|
*p++ = '\\';
|
|
*p++ = '\\';
|
|
for (q = trans->tdr_remote_site->str_data; *q;)
|
|
*p++ = *q++;
|
|
for (q = (UCHAR *) trans->tdr_filename; *q;)
|
|
*p++ = *q++;
|
|
*q = 0;
|
|
|
|
if (TDR_attach_database
|
|
(status_vector, trans,
|
|
reinterpret_cast < char *>(buffer))) return;
|
|
}
|
|
}
|
|
|
|
// we have failed to reattach; notify the user
|
|
// and let them try to succeed where we have failed
|
|
|
|
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 */
|
|
|
|
for (;;) {
|
|
ALICE_print(88, 0, 0, 0, 0, 0); /* msg 88: Enter a valid path: */
|
|
for (p = buffer; (*p = ib_getchar()) != '\n'; p++);
|
|
*p = 0;
|
|
if (!buffer[0])
|
|
break;
|
|
p = buffer;
|
|
while (*p == ' ')
|
|
*p++;
|
|
if (TDR_attach_database(status_vector,
|
|
trans,
|
|
reinterpret_cast<char*>(p)))
|
|
{
|
|
string = FB_NEW_RPT(*tdgbl->ALICE_default_pool,
|
|
strlen(reinterpret_cast<const char*>(p)) + 1) str;
|
|
strcpy(reinterpret_cast<char*>(string->str_data),
|
|
reinterpret_cast<const char*>(p));
|
|
string->str_length = strlen(reinterpret_cast<const char*>(p));
|
|
trans->tdr_fullpath = string;
|
|
trans->tdr_filename = (TEXT *) string->str_data;
|
|
return;
|
|
}
|
|
ALICE_print(89, 0, 0, 0, 0, 0); /* msg 89: Attach unsuccessful. */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Attempt to locate all databases used in
|
|
// a multidatabase transaction.
|
|
//
|
|
|
|
static void reattach_databases(TDR trans)
|
|
{
|
|
TDR ptr;
|
|
|
|
for (ptr = trans; ptr; ptr = ptr->tdr_next)
|
|
reattach_database(ptr);
|
|
}
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
//
|
|
// Commit or rollback a named transaction.
|
|
//
|
|
|
|
static BOOLEAN reconnect(FRBRD *handle,
|
|
SLONG number, TEXT * name, ULONG switches)
|
|
{
|
|
FRBRD *transaction;
|
|
SLONG id;
|
|
STATUS status_vector[ISC_STATUS_LENGTH];
|
|
|
|
id = gds__vax_integer((UCHAR *) & number, 4);
|
|
transaction = NULL;
|
|
if (gds__reconnect_transaction(status_vector,
|
|
(GDS_REF(handle)),
|
|
(GDS_REF(transaction)),
|
|
sizeof(id),
|
|
reinterpret_cast <char *>(GDS_REF(id)))) {
|
|
ALICE_print(90, name, 0, 0, 0, 0); /* msg 90: failed to reconnect to a transaction in database %s */
|
|
ALICE_print_status(status_vector);
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(switches & (sw_commit | sw_rollback))) {
|
|
ALICE_print(91, reinterpret_cast < char *>(number), 0, 0, 0, 0); /* msg 91: Transaction %ld: */
|
|
switches = ask();
|
|
if (switches == (ULONG) - 1) {
|
|
ALICE_print(84, 0, 0, 0, 0, 0); /* msg 84: unexpected end of input */
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (switches & sw_commit)
|
|
gds__commit_transaction(status_vector,
|
|
(GDS_REF(transaction)));
|
|
else if (switches & sw_rollback)
|
|
gds__rollback_transaction(status_vector,
|
|
(GDS_REF(transaction)));
|
|
else
|
|
return FALSE;
|
|
|
|
if (status_vector[1]) {
|
|
ALICE_print_status(status_vector);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif /* GUI_TOOLS */
|
|
|