2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: JRD Access Method
|
2003-10-29 11:53:47 +01:00
|
|
|
* MODULE: tra.cpp
|
2001-05-23 15:26:42 +02:00
|
|
|
* DESCRIPTION: Transaction manager
|
|
|
|
*
|
|
|
|
* 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): ______________________________________.
|
2001-07-10 19:35:13 +02:00
|
|
|
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
|
|
|
|
* conditionals, as the engine now fully supports
|
|
|
|
* readonly databases.
|
2002-10-29 21:20:44 +01:00
|
|
|
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
|
2001-07-29 19:42:23 +02:00
|
|
|
#include "firebird.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include <string.h>
|
2004-03-22 12:38:23 +01:00
|
|
|
#include "../jrd/common.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/tra.h"
|
|
|
|
#include "../jrd/ods.h"
|
|
|
|
#include "../jrd/pag.h"
|
|
|
|
#include "../jrd/lck.h"
|
2003-11-08 17:40:17 +01:00
|
|
|
#include "../jrd/ibase.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/lls.h"
|
2003-11-30 22:04:18 +01:00
|
|
|
#include "../jrd/btr.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/req.h"
|
|
|
|
#include "../jrd/exe.h"
|
|
|
|
#include "../jrd/rse.h"
|
2006-08-07 18:39:21 +02:00
|
|
|
#include "../jrd/intl_classes.h"
|
2003-02-13 23:38:04 +01:00
|
|
|
#include "../jrd/jrd_pwd.h"
|
2008-01-16 13:22:11 +01:00
|
|
|
#include "../jrd/ThreadStart.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/blb_proto.h"
|
|
|
|
#include "../jrd/cch_proto.h"
|
|
|
|
#include "../jrd/cmp_proto.h"
|
|
|
|
#include "../jrd/dfw_proto.h"
|
|
|
|
#include "../jrd/dpm_proto.h"
|
|
|
|
#include "../jrd/err_proto.h"
|
|
|
|
#include "../jrd/exe_proto.h"
|
|
|
|
#include "../jrd/ext_proto.h"
|
|
|
|
#include "../jrd/gds_proto.h"
|
|
|
|
#include "../jrd/isc_proto.h"
|
|
|
|
#include "../jrd/lck_proto.h"
|
|
|
|
#include "../jrd/met_proto.h"
|
|
|
|
#include "../jrd/mov_proto.h"
|
|
|
|
#include "../jrd/rlck_proto.h"
|
2004-05-18 00:30:09 +02:00
|
|
|
#include "../jrd/thread_proto.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/tpc_proto.h"
|
|
|
|
#include "../jrd/tra_proto.h"
|
|
|
|
#include "../jrd/vio_proto.h"
|
|
|
|
#include "../jrd/enc_proto.h"
|
2003-06-01 18:22:47 +02:00
|
|
|
#include "../jrd/jrd_proto.h"
|
2004-12-09 20:19:47 +01:00
|
|
|
#include "../common/classes/ClumpletWriter.h"
|
2007-09-04 10:22:48 +02:00
|
|
|
#include "../common/classes/TriState.h"
|
2008-02-14 12:52:59 +01:00
|
|
|
#include "../common/utils_proto.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../lock/lock_proto.h"
|
2008-02-28 14:48:16 +01:00
|
|
|
#include "../dsql/dsql.h"
|
|
|
|
#include "../dsql/dsql_proto.h"
|
2008-03-12 08:33:12 +01:00
|
|
|
//#include "../common/classes/GenericMap.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2004-05-03 23:43:56 +02:00
|
|
|
const int DYN_MSG_FAC = 8;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-02-05 12:06:28 +01:00
|
|
|
static const SLONG MAX_TRA_NUMBER = MAX_SLONG;
|
2004-09-28 22:25:52 +02:00
|
|
|
|
2004-03-20 15:57:40 +01:00
|
|
|
using namespace Jrd;
|
|
|
|
using namespace Ods;
|
|
|
|
|
2004-11-09 13:59:37 +01:00
|
|
|
#ifdef GARBAGE_THREAD
|
2004-11-05 09:01:21 +01:00
|
|
|
#include "../jrd/isc_s_proto.h"
|
|
|
|
#endif
|
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
typedef Firebird::GenericMap<Firebird::Pair<Firebird::NonPooled<USHORT, UCHAR> > > RelationLockTypeMap;
|
|
|
|
|
|
|
|
|
2007-01-07 16:15:06 +01:00
|
|
|
static int blocking_ast_transaction(void*);
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef SUPERSERVER_V2
|
2004-03-11 06:04:26 +01:00
|
|
|
static SLONG bump_transaction_id(thread_db*, WIN *);
|
2001-05-23 15:26:42 +02:00
|
|
|
#else
|
2004-03-11 06:04:26 +01:00
|
|
|
static header_page* bump_transaction_id(thread_db*, WIN *);
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
2008-03-19 17:19:56 +01:00
|
|
|
static Lock* create_transaction_lock(thread_db* tdbb, void* object);
|
2006-01-25 13:20:57 +01:00
|
|
|
static void retain_context(thread_db*, jrd_tra*, bool, SSHORT);
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef VMS
|
2006-01-25 13:20:57 +01:00
|
|
|
static void compute_oldest_retaining(thread_db*, jrd_tra*, bool);
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
2008-03-12 08:33:12 +01:00
|
|
|
static void expand_view_lock(thread_db* tdbb, jrd_tra*, jrd_rel*, UCHAR lock_type,
|
|
|
|
const char* option_name, RelationLockTypeMap& lockmap, const int level);
|
2004-03-11 06:04:26 +01:00
|
|
|
static tx_inv_page* fetch_inventory_page(thread_db*, WIN *, SLONG, USHORT);
|
2008-03-12 08:33:12 +01:00
|
|
|
static const char* get_lockname_v3(const UCHAR lock);
|
2004-03-11 06:04:26 +01:00
|
|
|
static SLONG inventory_page(thread_db*, SLONG);
|
|
|
|
static SSHORT limbo_transaction(thread_db*, SLONG);
|
2006-10-07 12:53:01 +02:00
|
|
|
static void link_transaction(thread_db*, jrd_tra*);
|
2004-03-11 06:04:26 +01:00
|
|
|
static void restart_requests(thread_db*, jrd_tra*);
|
2003-03-01 19:04:21 +01:00
|
|
|
#ifdef SWEEP_THREAD
|
2004-03-28 11:10:30 +02:00
|
|
|
static void start_sweeper(thread_db*, Database*);
|
2004-06-08 15:41:08 +02:00
|
|
|
static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM);
|
2003-03-01 19:04:21 +01:00
|
|
|
#endif
|
2004-03-11 06:04:26 +01:00
|
|
|
static void transaction_options(thread_db*, jrd_tra*, const UCHAR*, USHORT);
|
2008-01-16 13:22:11 +01:00
|
|
|
static jrd_tra* transaction_start(thread_db* tdbb, jrd_tra* temp);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
static const UCHAR sweep_tpb[] =
|
|
|
|
{
|
|
|
|
isc_tpb_version1, isc_tpb_read,
|
2001-05-23 15:26:42 +02:00
|
|
|
isc_tpb_read_committed, isc_tpb_rec_version
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-10-08 18:03:37 +02:00
|
|
|
void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request)
|
|
|
|
{
|
2004-12-07 02:19:55 +01:00
|
|
|
// When request finishes normally transaction reference is not cleared.
|
|
|
|
// Then if afterwards request is restarted TRA_attach_request is called again.
|
|
|
|
if (request->req_transaction) {
|
2008-03-06 10:43:43 +01:00
|
|
|
if (request->req_transaction == transaction)
|
2004-12-07 02:19:55 +01:00
|
|
|
return;
|
|
|
|
TRA_detach_request(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(request->req_transaction == NULL);
|
|
|
|
fb_assert(request->req_tra_next == NULL);
|
|
|
|
fb_assert(request->req_tra_prev == NULL);
|
2007-01-07 16:15:06 +01:00
|
|
|
|
2004-12-07 02:19:55 +01:00
|
|
|
// Assign transaction reference
|
|
|
|
request->req_transaction = transaction;
|
|
|
|
|
|
|
|
// Add request to the doubly linked list
|
|
|
|
if (transaction->tra_requests) {
|
|
|
|
fb_assert(transaction->tra_requests->req_tra_prev == NULL);
|
|
|
|
transaction->tra_requests->req_tra_prev = request;
|
|
|
|
request->req_tra_next = transaction->tra_requests;
|
|
|
|
}
|
|
|
|
transaction->tra_requests = request;
|
|
|
|
}
|
|
|
|
|
2006-10-08 18:03:37 +02:00
|
|
|
void TRA_detach_request(Jrd::jrd_req* request)
|
|
|
|
{
|
|
|
|
if (!request->req_transaction)
|
|
|
|
return;
|
2004-12-07 02:19:55 +01:00
|
|
|
|
|
|
|
// Remove request from the doubly linked list
|
|
|
|
if (request->req_tra_next) {
|
|
|
|
fb_assert(request->req_tra_next->req_tra_prev == request);
|
|
|
|
request->req_tra_next->req_tra_prev = request->req_tra_prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request->req_tra_prev) {
|
|
|
|
fb_assert(request->req_tra_prev->req_tra_next == request);
|
|
|
|
request->req_tra_prev->req_tra_next = request->req_tra_next;
|
2008-03-06 10:43:43 +01:00
|
|
|
}
|
2006-04-06 10:18:53 +02:00
|
|
|
else {
|
2004-12-07 02:19:55 +01:00
|
|
|
fb_assert(request->req_transaction->tra_requests == request);
|
|
|
|
request->req_transaction->tra_requests = request->req_tra_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear references
|
|
|
|
request->req_transaction = NULL;
|
|
|
|
request->req_tra_next = NULL;
|
|
|
|
request->req_tra_prev = NULL;
|
|
|
|
}
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
bool TRA_active_transactions(thread_db* tdbb, Database* dbb)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ a c t i v e _ t r a n s a c t i o n s
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Determine if any transactions are active.
|
2004-02-20 07:43:27 +01:00
|
|
|
* Return true is active transactions; otherwise
|
|
|
|
* return false if no active transactions.
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
2008-01-26 14:17:19 +01:00
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifndef VMS
|
2008-01-26 14:17:19 +01:00
|
|
|
return ((LCK_query_data(tdbb, dbb->dbb_lock, LCK_tra, LCK_ANY)) ? true : false);
|
2001-05-23 15:26:42 +02:00
|
|
|
#else
|
|
|
|
|
|
|
|
/* Read header page and allocate transaction number. */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
ULONG number, oldest, active;
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef SUPERSERVER_V2
|
|
|
|
number = dbb->dbb_next_transaction;
|
|
|
|
oldest = dbb->dbb_oldest_transaction;
|
|
|
|
active = MAX(dbb->dbb_oldest_active, dbb->dbb_oldest_transaction);
|
|
|
|
#else
|
|
|
|
if (dbb->dbb_flags & DBB_read_only) {
|
|
|
|
number = dbb->dbb_next_transaction;
|
|
|
|
oldest = dbb->dbb_oldest_transaction;
|
|
|
|
active = MAX(dbb->dbb_oldest_active, dbb->dbb_oldest_transaction);
|
|
|
|
}
|
|
|
|
else {
|
2007-03-09 09:56:31 +01:00
|
|
|
WIN window(HEADER_PAGE_NUMBER);
|
2004-02-20 07:43:27 +01:00
|
|
|
const header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header);
|
2001-05-23 15:26:42 +02:00
|
|
|
number = header->hdr_next_transaction;
|
|
|
|
oldest = header->hdr_oldest_transaction;
|
|
|
|
active =
|
|
|
|
MAX(header->hdr_oldest_active, header->hdr_oldest_transaction);
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
}
|
|
|
|
#endif /* SUPERSERVER_V2 */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG base = oldest & ~TRA_MASK;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
jrd_tra* trans =
|
|
|
|
FB_NEW_RPT(*dbb->dbb_permanent, (number - base + TRA_MASK) / 4) jrd_tra();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Build transaction bitmap to scan for active transactions. */
|
|
|
|
|
|
|
|
TRA_get_inventory(tdbb, trans->tra_transactions, base, number);
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock temp_lock;
|
2001-05-23 15:26:42 +02:00
|
|
|
temp_lock.lck_dbb = dbb;
|
2004-02-20 07:43:27 +01:00
|
|
|
temp_lock.lck_object = trans;
|
2001-05-23 15:26:42 +02:00
|
|
|
temp_lock.lck_type = LCK_tra;
|
|
|
|
temp_lock.lck_owner_handle =
|
|
|
|
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_length = sizeof(SLONG);
|
|
|
|
|
|
|
|
for (; active <= number; active++) {
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG byte = TRANS_OFFSET(active - base);
|
|
|
|
const USHORT shift = TRANS_SHIFT(active);
|
|
|
|
const USHORT state = (trans->tra_transactions[byte] >> shift) & TRA_MASK;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (state == tra_active) {
|
|
|
|
temp_lock.lck_key.lck_long = active;
|
2008-01-26 14:17:19 +01:00
|
|
|
if (!LCK_lock(tdbb, &temp_lock, LCK_read, LCK_NO_WAIT)) {
|
2001-12-24 03:51:06 +01:00
|
|
|
delete trans;
|
2004-02-20 07:43:27 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
LCK_release(tdbb, &temp_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
delete trans;
|
2004-02-20 07:43:27 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_cleanup(thread_db* tdbb)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ c l e a n u p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* TRA_cleanup is called at startup while an exclusive lock is
|
|
|
|
* held on the database. Because we haven't started a transaction,
|
|
|
|
* and we have an exclusive lock on the db, any transactions marked
|
|
|
|
* as active on the transaction inventory pages are indeed dead.
|
|
|
|
* Mark them so.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* Return without cleaning up the TIP's for a ReadOnly database */
|
|
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* First, make damn sure there are no outstanding transactions */
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
for (Attachment* attachment = dbb->dbb_attachments; attachment;
|
2003-09-13 14:03:11 +02:00
|
|
|
attachment = attachment->att_next)
|
|
|
|
{
|
|
|
|
if (attachment->att_transactions)
|
2001-05-23 15:26:42 +02:00
|
|
|
return;
|
2003-09-13 14:03:11 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
const SLONG trans_per_tip = dbb->dbb_page_manager.transPerTIP;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Read header page and allocate transaction number. Since
|
|
|
|
the transaction inventory page was initialized to zero, it
|
|
|
|
transaction is automatically marked active. */
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(HEADER_PAGE_NUMBER);
|
2004-02-20 07:43:27 +01:00
|
|
|
const header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header);
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG ceiling = header->hdr_next_transaction;
|
|
|
|
const SLONG active = header->hdr_oldest_active;
|
2001-05-23 15:26:42 +02:00
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
|
|
|
|
if (ceiling == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Zip thru transactions from the "oldest active" to the next looking for
|
|
|
|
active transactions. When one is found, declare it dead. */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG last = ceiling / trans_per_tip;
|
|
|
|
SLONG number = active % trans_per_tip;
|
|
|
|
SLONG limbo = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
for (SLONG sequence = active / trans_per_tip; sequence <= last;
|
2008-03-06 10:43:43 +01:00
|
|
|
sequence++, number = 0)
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
window.win_page = inventory_page(tdbb, sequence);
|
2004-02-20 07:43:27 +01:00
|
|
|
tx_inv_page* tip = (tx_inv_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_transactions);
|
2003-12-31 06:36:12 +01:00
|
|
|
SLONG max = ceiling - (sequence * trans_per_tip);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (max > trans_per_tip)
|
|
|
|
max = trans_per_tip - 1;
|
|
|
|
for (; number <= max; number++) {
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG trans_offset = TRANS_OFFSET(number);
|
|
|
|
UCHAR* byte = tip->tip_transactions + trans_offset;
|
|
|
|
const SSHORT shift = TRANS_SHIFT(number);
|
|
|
|
const SSHORT state = (*byte >> shift) & TRA_MASK;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (state == tra_limbo && limbo == 0)
|
|
|
|
limbo = sequence * trans_per_tip + number;
|
|
|
|
else if (state == tra_active) {
|
|
|
|
CCH_MARK(tdbb, &window);
|
|
|
|
*byte &= ~(TRA_MASK << shift);
|
2005-03-23 21:35:39 +01:00
|
|
|
|
|
|
|
// hvlad: mark system transaction as committed
|
|
|
|
if (sequence == 0 && number == 0) {
|
|
|
|
*byte |= tra_committed << shift;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*byte |= tra_dead << shift;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef SUPERSERVER_V2
|
|
|
|
if (sequence == last) {
|
|
|
|
CCH_MARK(tdbb, &window);
|
|
|
|
for (; number < trans_per_tip; number++) {
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG trans_offset = TRANS_OFFSET(number);
|
|
|
|
UCHAR* byte = tip->tip_transactions + trans_offset;
|
|
|
|
const SSHORT shift = TRANS_SHIFT(number);
|
2001-05-23 15:26:42 +02:00
|
|
|
*byte &= ~(TRA_MASK << shift);
|
|
|
|
if (tip->tip_next)
|
|
|
|
*byte |= tra_committed << shift;
|
|
|
|
else
|
2002-04-29 17:05:11 +02:00
|
|
|
*byte |= tra_active << shift;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SUPERSERVER_V2
|
|
|
|
window.win_page = inventory_page(tdbb, last);
|
2004-02-20 07:43:27 +01:00
|
|
|
tx_inv_page* tip =
|
|
|
|
(tx_inv_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_transactions);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
while (tip->tip_next) {
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
window.win_page = inventory_page(tdbb, ++last);
|
2004-02-20 07:43:27 +01:00
|
|
|
tip = (tx_inv_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_transactions);
|
2001-05-23 15:26:42 +02:00
|
|
|
CCH_MARK(tdbb, &window);
|
|
|
|
for (number = 0; number < trans_per_tip; number++) {
|
2004-03-18 06:56:06 +01:00
|
|
|
const SLONG trans_offset = TRANS_OFFSET(number);
|
2004-02-20 07:43:27 +01:00
|
|
|
UCHAR* byte = tip->tip_transactions + trans_offset;
|
|
|
|
const USHORT shift = TRANS_SHIFT(number);
|
2001-05-23 15:26:42 +02:00
|
|
|
*byte &= ~(TRA_MASK << shift);
|
|
|
|
if (tip->tip_next || !number)
|
|
|
|
*byte |= tra_committed << shift;
|
|
|
|
else
|
|
|
|
*byte |= tra_active << shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tip->tip_next)
|
|
|
|
dbb->dbb_next_transaction = last * trans_per_tip;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_commit(thread_db* tdbb, jrd_tra* transaction, const bool retaining_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ c o m m i t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Commit a transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
/* If this is a commit retaining, and no updates have been performed,
|
|
|
|
and no events have been posted (via stored procedures etc)
|
|
|
|
no-op the operation */
|
|
|
|
|
|
|
|
if (retaining_flag
|
|
|
|
&& !(transaction->tra_flags & TRA_write
|
2003-11-01 11:26:43 +01:00
|
|
|
|| transaction->tra_deferred_work))
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags &= ~TRA_prepared;
|
2005-01-04 14:37:11 +01:00
|
|
|
// Get rid of all user savepoints
|
2008-03-06 10:43:43 +01:00
|
|
|
while (transaction->tra_save_point &&
|
|
|
|
transaction->tra_save_point->sav_flags & SAV_user)
|
2005-01-04 14:37:11 +01:00
|
|
|
{
|
|
|
|
Savepoint* const next = transaction->tra_save_point->sav_next;
|
|
|
|
transaction->tra_save_point->sav_next = NULL;
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
2008-03-06 10:43:43 +01:00
|
|
|
transaction->tra_save_point = next;
|
2005-01-04 14:37:11 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_invalidated)
|
2003-11-08 17:40:17 +01:00
|
|
|
ERR_post(isc_trans_invalid, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-08-30 20:11:08 +02:00
|
|
|
Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Perform any meta data work deferred */
|
|
|
|
|
|
|
|
if (!(transaction->tra_flags & TRA_prepared))
|
2008-03-18 14:04:05 +01:00
|
|
|
DFW_perform_work(tdbb, transaction);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (transaction->tra_flags & (TRA_prepare2 | TRA_reconnected))
|
2003-12-22 11:00:59 +01:00
|
|
|
MET_update_transaction(tdbb, transaction, true);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Check in with external file system */
|
|
|
|
|
|
|
|
EXT_trans_commit(transaction);
|
|
|
|
|
|
|
|
#ifdef GARBAGE_THREAD
|
|
|
|
/* Flush pages if transaction logically modified data */
|
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_write)
|
|
|
|
#endif
|
2004-05-14 20:43:34 +02:00
|
|
|
CCH_flush(tdbb, FLUSH_TRAN, transaction->tra_number);
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef GARBAGE_THREAD
|
|
|
|
else if (transaction->tra_flags & (TRA_prepare2 | TRA_reconnected)) {
|
|
|
|
/* If the transaction only read data but is a member of a
|
|
|
|
multi-database transaction with a transaction description
|
|
|
|
message then flush RDB$TRANSACTIONS. */
|
|
|
|
|
2004-05-14 20:43:34 +02:00
|
|
|
CCH_flush(tdbb, FLUSH_SYSTEM, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (retaining_flag) {
|
2006-01-25 13:20:57 +01:00
|
|
|
retain_context(tdbb, transaction, true, tra_committed);
|
2001-05-23 15:26:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the state on the inventory page to be committed */
|
|
|
|
|
|
|
|
TRA_set_state(tdbb, transaction, transaction->tra_number, tra_committed);
|
|
|
|
|
|
|
|
/* Perform any post commit work */
|
|
|
|
|
|
|
|
DFW_perform_post_commit_work(transaction);
|
|
|
|
|
|
|
|
/* notify any waiting locks that this transaction is committing;
|
|
|
|
there could be no lock if this transaction is being reconnected */
|
|
|
|
|
|
|
|
++transaction->tra_use_count;
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock* lock = transaction->tra_lock;
|
2003-12-31 06:36:12 +01:00
|
|
|
if (lock && (lock->lck_logical < LCK_write))
|
2001-05-23 15:26:42 +02:00
|
|
|
LCK_convert(tdbb, lock, LCK_write, TRUE);
|
|
|
|
--transaction->tra_use_count;
|
|
|
|
|
|
|
|
TRA_release_transaction(tdbb, transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_extend_tip(thread_db* tdbb, ULONG sequence, WIN * precedence_window)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ e x t e n d _ t i p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Allocate and link in new TIP (transaction inventory page).
|
|
|
|
* This is called from TRA_start and from validate/repair.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* Start by fetching prior transaction page, if any */
|
2004-02-20 07:43:27 +01:00
|
|
|
tx_inv_page* prior_tip;
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN prior_window(DB_PAGE_SPACE, -1);
|
2003-12-11 11:33:30 +01:00
|
|
|
if (sequence) {
|
2001-05-23 15:26:42 +02:00
|
|
|
prior_tip =
|
|
|
|
fetch_inventory_page(tdbb, &prior_window, (SLONG) (sequence - 1),
|
|
|
|
LCK_write);
|
2003-12-11 11:33:30 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Allocate and format new page */
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2004-02-20 07:43:27 +01:00
|
|
|
tx_inv_page* tip = (tx_inv_page*) DPM_allocate(tdbb, &window);
|
2004-07-10 05:20:33 +02:00
|
|
|
tip->tip_header.pag_type = pag_transactions;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
CCH_must_write(&window);
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
|
|
|
|
/* Release prior page */
|
|
|
|
|
|
|
|
if (sequence) {
|
|
|
|
CCH_MARK_MUST_WRITE(tdbb, &prior_window);
|
2006-05-22 00:07:35 +02:00
|
|
|
prior_tip->tip_next = window.win_page.getPageNum();
|
2001-05-23 15:26:42 +02:00
|
|
|
CCH_RELEASE(tdbb, &prior_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Link into internal data structures */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
vcl* vector = dbb->dbb_t_pages =
|
2003-02-19 16:25:27 +01:00
|
|
|
vcl::newVector(*dbb->dbb_permanent, dbb->dbb_t_pages, sequence + 1);
|
2006-05-22 00:07:35 +02:00
|
|
|
(*vector)[sequence] = window.win_page.getPageNum();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Write into pages relation */
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
DPM_pages(tdbb, 0, pag_transactions, sequence, window.win_page.getPageNum());
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
int TRA_fetch_state(thread_db* tdbb, SLONG number)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ f e t c h _ s t a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2001-07-10 19:35:13 +02:00
|
|
|
* Physically fetch the state of a given
|
|
|
|
* transaction on the transaction inventory
|
|
|
|
* page.
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* locate and fetch the proper TIP page */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG tip_number = (ULONG) number;
|
2006-05-22 00:07:35 +02:00
|
|
|
const SLONG trans_per_tip = dbb->dbb_page_manager.transPerTIP;
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG tip_seq = tip_number / trans_per_tip;
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2004-03-18 06:56:06 +01:00
|
|
|
const tx_inv_page* tip = fetch_inventory_page(tdbb, &window, tip_seq, LCK_read);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* calculate the state of the desired transaction */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG byte = TRANS_OFFSET(tip_number % trans_per_tip);
|
|
|
|
const USHORT shift = TRANS_SHIFT(tip_number);
|
2004-03-18 06:56:06 +01:00
|
|
|
const USHORT state = (tip->tip_transactions[byte] >> shift) & TRA_MASK;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
void TRA_get_inventory(thread_db* tdbb, UCHAR* bit_vector, ULONG base, ULONG top)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ g e t _ i n v e n t o r y
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Get an inventory of the state of all transactions
|
|
|
|
* between the base and top transactions passed.
|
|
|
|
* To get a consistent view of the transaction
|
2001-07-10 19:35:13 +02:00
|
|
|
* inventory (in case we ever implement sub-transactions),
|
2001-05-23 15:26:42 +02:00
|
|
|
* do handoffs to read the pages in order.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
const ULONG trans_per_tip = dbb->dbb_page_manager.transPerTIP;
|
2003-12-31 06:36:12 +01:00
|
|
|
ULONG sequence = base / trans_per_tip;
|
|
|
|
const ULONG last = top / trans_per_tip;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* fetch the first inventory page */
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2004-03-18 06:56:06 +01:00
|
|
|
const tx_inv_page* tip =
|
|
|
|
fetch_inventory_page(tdbb, &window, (SLONG) sequence++, LCK_read);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* move the first page into the bit vector */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
UCHAR* p = bit_vector;
|
|
|
|
if (p) {
|
|
|
|
ULONG l = base % trans_per_tip;
|
|
|
|
const UCHAR* q = tip->tip_transactions + TRANS_OFFSET(l);
|
2001-05-23 15:26:42 +02:00
|
|
|
l = TRANS_OFFSET(MIN((top + TRA_MASK - base), trans_per_tip - l));
|
2008-02-03 11:41:44 +01:00
|
|
|
memcpy(p, q, l);
|
2001-05-23 15:26:42 +02:00
|
|
|
p += l;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* move successive pages into the bit vector */
|
|
|
|
|
|
|
|
while (sequence <= last) {
|
|
|
|
base = sequence * trans_per_tip;
|
|
|
|
|
|
|
|
/* release the read lock as we go, so that some one else can
|
|
|
|
* commit without having to signal all other transactions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
tip =
|
2004-02-20 07:43:27 +01:00
|
|
|
(tx_inv_page*) CCH_HANDOFF(tdbb, &window, inventory_page(tdbb, sequence++),
|
2001-05-23 15:26:42 +02:00
|
|
|
LCK_read, pag_transactions);
|
|
|
|
TPC_update_cache(tdbb, tip, sequence - 1);
|
|
|
|
if (p) {
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG l = TRANS_OFFSET(MIN((top + TRA_MASK - base), trans_per_tip));
|
2008-02-03 11:41:44 +01:00
|
|
|
memcpy(p, tip->tip_transactions, l);
|
2001-05-23 15:26:42 +02:00
|
|
|
p += l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
int TRA_get_state(thread_db* tdbb, SLONG number)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ g e t _ s t a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2001-07-10 19:35:13 +02:00
|
|
|
* Get the state of a given transaction on the
|
2001-05-23 15:26:42 +02:00
|
|
|
* transaction inventory page.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
if (dbb->dbb_tip_cache)
|
|
|
|
return TPC_snapshot_state(tdbb, number);
|
|
|
|
|
|
|
|
if (number && dbb->dbb_pc_transactions)
|
2008-02-24 04:23:40 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (TRA_precommited(tdbb, number, number))
|
|
|
|
return tra_precommitted;
|
2008-02-24 04:23:40 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return TRA_fetch_state(tdbb, number);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SUPERSERVER_V2
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_header_write(thread_db* tdbb, Database* dbb, SLONG number)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ h e a d e r _ w r i t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Force transaction ID on header to disk.
|
|
|
|
* Do post fetch check of the transaction
|
|
|
|
* ID header write as a concurrent thread
|
|
|
|
* might have written the header page
|
|
|
|
* while blocked on the latch.
|
|
|
|
*
|
|
|
|
* The idea is to amortize the cost of
|
|
|
|
* header page I/O across multiple transactions.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
/* If transaction number is already on disk just return. */
|
|
|
|
|
|
|
|
if (!number || dbb->dbb_last_header_write < number) {
|
2007-03-09 09:56:31 +01:00
|
|
|
WIN window(HEADER_PAGE_NUMBER);
|
2004-02-20 07:43:27 +01:00
|
|
|
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (header->hdr_next_transaction) {
|
|
|
|
if (header->hdr_oldest_active > header->hdr_next_transaction)
|
|
|
|
BUGCHECK(266); /*next transaction older than oldest active */
|
|
|
|
|
|
|
|
if (header->hdr_oldest_transaction > header->hdr_next_transaction)
|
|
|
|
BUGCHECK(267); /* next transaction older than oldest transaction */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The header page might have been written while waiting
|
|
|
|
for the latch; perform a post fetch check and optimize
|
|
|
|
this case by not writing the page again. */
|
|
|
|
|
|
|
|
if (!number || dbb->dbb_last_header_write < number) {
|
|
|
|
CCH_MARK_MUST_WRITE(tdbb, &window);
|
|
|
|
if (dbb->dbb_next_transaction > header->hdr_next_transaction)
|
|
|
|
header->hdr_next_transaction = dbb->dbb_next_transaction;
|
|
|
|
|
|
|
|
if (dbb->dbb_oldest_active > header->hdr_oldest_active)
|
|
|
|
header->hdr_oldest_active = dbb->dbb_oldest_active;
|
|
|
|
|
|
|
|
if (dbb->dbb_oldest_transaction > header->hdr_oldest_transaction)
|
|
|
|
header->hdr_oldest_transaction = dbb->dbb_oldest_transaction;
|
|
|
|
|
|
|
|
if (dbb->dbb_oldest_snapshot > header->hdr_oldest_snapshot)
|
|
|
|
header->hdr_oldest_snapshot = dbb->dbb_oldest_snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_init(thread_db* tdbb)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ i n i t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* "Start" the system transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
jrd_tra* trans = FB_NEW_RPT(*dbb->dbb_permanent, 0) jrd_tra(dbb->dbb_permanent);
|
2003-12-31 06:36:12 +01:00
|
|
|
dbb->dbb_sys_trans = trans;
|
2001-05-23 15:26:42 +02:00
|
|
|
trans->tra_flags |= TRA_system | TRA_ignore_limbo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-07 08:58:55 +01:00
|
|
|
void TRA_invalidate(Database* database, ULONG mask)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ i n v a l i d a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Invalidate any active transactions that may have
|
|
|
|
* modified a page that couldn't be written.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-18 06:56:06 +01:00
|
|
|
for (Attachment* attachment = database->dbb_attachments; attachment;
|
2003-12-31 06:36:12 +01:00
|
|
|
attachment = attachment->att_next)
|
|
|
|
{
|
2008-03-06 10:43:43 +01:00
|
|
|
for (jrd_tra* transaction = attachment->att_transactions; transaction;
|
|
|
|
transaction = transaction->tra_next)
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
|
|
|
const ULONG transaction_mask =
|
2001-05-23 15:26:42 +02:00
|
|
|
1L << (transaction->tra_number & (BITS_PER_LONG - 1));
|
|
|
|
if (transaction_mask & mask && transaction->tra_flags & TRA_write)
|
|
|
|
transaction->tra_flags |= TRA_invalidated;
|
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
void TRA_link_cursor(jrd_tra* transaction, dsql_req* cursor)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ l i n k _ c u r s o r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Add cursor to the list of open cursors belonging to this transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
|
|
|
fb_assert(!transaction->tra_open_cursors.exist(cursor));
|
|
|
|
transaction->tra_open_cursors.add(cursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TRA_unlink_cursor(jrd_tra* transaction, dsql_req* cursor)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ u n l i n k _ c u r s o r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Remove cursor from the list of open cursors.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
|
|
|
size_t pos;
|
|
|
|
if (transaction->tra_open_cursors.find(cursor, pos))
|
|
|
|
{
|
|
|
|
transaction->tra_open_cursors.remove(pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-18 04:50:38 +02:00
|
|
|
void TRA_post_resources(thread_db* tdbb, jrd_tra* transaction, ResourceList& resources)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ p o s t _ r e s o u r c e s
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2006-08-08 04:42:10 +02:00
|
|
|
* Post interest in relation/procedure/collation existence to transaction.
|
|
|
|
* This guarantees that the relation/procedure/collation won't be dropped
|
2001-05-23 15:26:42 +02:00
|
|
|
* out from under the transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2004-08-30 20:11:08 +02:00
|
|
|
Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-06 10:43:43 +01:00
|
|
|
for (Resource* rsc = resources.begin(); rsc < resources.end(); rsc++)
|
2004-04-18 04:50:38 +02:00
|
|
|
{
|
2004-02-20 07:43:27 +01:00
|
|
|
if (rsc->rsc_type == Resource::rsc_relation ||
|
2006-08-07 18:39:21 +02:00
|
|
|
rsc->rsc_type == Resource::rsc_procedure ||
|
2006-10-10 21:40:33 +02:00
|
|
|
rsc->rsc_type == Resource::rsc_collation)
|
2004-02-20 07:43:27 +01:00
|
|
|
{
|
2004-07-17 01:06:31 +02:00
|
|
|
size_t i;
|
2008-03-06 10:43:43 +01:00
|
|
|
if (!transaction->tra_resources.find(*rsc, i))
|
2003-09-13 14:03:11 +02:00
|
|
|
{
|
2004-04-18 04:50:38 +02:00
|
|
|
transaction->tra_resources.insert(i, *rsc);
|
2001-05-23 15:26:42 +02:00
|
|
|
switch (rsc->rsc_type) {
|
2004-02-20 07:43:27 +01:00
|
|
|
case Resource::rsc_relation:
|
2004-04-18 04:50:38 +02:00
|
|
|
MET_post_existence(tdbb, rsc->rsc_rel);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2004-02-20 07:43:27 +01:00
|
|
|
case Resource::rsc_procedure:
|
2004-04-18 04:50:38 +02:00
|
|
|
rsc->rsc_prc->prc_use_count++;
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef DEBUG_PROCS
|
|
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
sprintf(buffer,
|
|
|
|
"Called from TRA_post_resources():\n\t Incrementing use count of %s\n",
|
2004-09-13 23:06:41 +02:00
|
|
|
rsc->rsc_prc->prc_name->c_str());
|
2001-05-23 15:26:42 +02:00
|
|
|
JRD_print_procedure_info(tdbb, buffer);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
2006-10-10 21:40:33 +02:00
|
|
|
case Resource::rsc_collation:
|
|
|
|
rsc->rsc_coll->incUseCount(tdbb);
|
2006-08-07 18:39:21 +02:00
|
|
|
break;
|
2003-11-03 17:21:37 +01:00
|
|
|
default: // shut up compiler warning
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
bool TRA_precommited(thread_db* tdbb, SLONG old_number, SLONG new_number)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ p r e c o m m i t e d (s i c)
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2001-07-10 19:35:13 +02:00
|
|
|
* Maintain a vector of active precommitted
|
|
|
|
* transactions. If old_number <> new_number
|
2001-05-23 15:26:42 +02:00
|
|
|
* then swap old_number with new_number in
|
|
|
|
* the vector. If old_number equals new_number
|
|
|
|
* then test for that number's presence in
|
|
|
|
* the vector.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
vcl* vector = dbb->dbb_pc_transactions;
|
|
|
|
if (!vector) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (old_number == new_number)
|
2004-02-20 07:43:27 +01:00
|
|
|
return false;
|
2001-12-24 03:51:06 +01:00
|
|
|
vector = dbb->dbb_pc_transactions = vcl::newVector(*dbb->dbb_permanent, 1);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
SLONG* zp = 0;
|
2006-02-10 04:28:43 +01:00
|
|
|
for (vcl::iterator p = vector->begin(), end = vector->end(); p < end; ++p) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*p == old_number)
|
2004-02-20 07:43:27 +01:00
|
|
|
return (*p = new_number) ? true : false;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!zp && !*p)
|
2002-01-04 12:34:22 +01:00
|
|
|
zp = &*p;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (old_number == new_number || new_number == 0)
|
2004-02-20 07:43:27 +01:00
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (zp)
|
|
|
|
*zp = new_number;
|
|
|
|
else {
|
2001-12-24 03:51:06 +01:00
|
|
|
vector->resize(vector->count() + 1);
|
|
|
|
(*vector)[vector->count() - 1] = new_number;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_prepare(thread_db* tdbb, jrd_tra* transaction, USHORT length,
|
2003-10-29 11:53:47 +01:00
|
|
|
const UCHAR* msg)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ p r e p a r e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Put a transaction into limbo.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_prepared)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_invalidated)
|
2003-11-08 17:40:17 +01:00
|
|
|
ERR_post(isc_trans_invalid, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2001-07-10 19:35:13 +02:00
|
|
|
/* If there's a transaction description message, log it to RDB$TRANSACTION
|
2001-05-23 15:26:42 +02:00
|
|
|
We should only log a message to RDB$TRANSACTION if there is a message
|
|
|
|
to log (if the length = 0, we won't log the transaction in RDB$TRANSACTION)
|
|
|
|
These messages are used to recover transactions in limbo. The message indicates
|
|
|
|
the action that is to be performed (hence, if nothing is getting logged, don't
|
|
|
|
bother).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Make sure that if msg is NULL there is no length. The two
|
|
|
|
should go hand in hand
|
2001-07-10 19:35:13 +02:00
|
|
|
msg == NULL || *msg == NULL
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(!(!msg && length) || (msg && (!*msg && length)));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (msg && length) {
|
|
|
|
MET_prepare(tdbb, transaction, length, msg);
|
|
|
|
transaction->tra_flags |= TRA_prepare2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check in with external file system */
|
|
|
|
|
|
|
|
EXT_trans_prepare(transaction);
|
|
|
|
|
|
|
|
/* Perform any meta data work deferred */
|
|
|
|
|
2008-03-18 14:04:05 +01:00
|
|
|
DFW_perform_work(tdbb, transaction);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
#ifdef GARBAGE_THREAD
|
|
|
|
/* Flush pages if transaction logically modified data */
|
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_write)
|
|
|
|
#endif
|
2004-05-14 20:43:34 +02:00
|
|
|
CCH_flush(tdbb, FLUSH_TRAN, transaction->tra_number);
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef GARBAGE_THREAD
|
|
|
|
else if (transaction->tra_flags & TRA_prepare2) {
|
|
|
|
/* If the transaction only read data but is a member of a
|
|
|
|
multi-database transaction with a transaction description
|
|
|
|
message then flush RDB$TRANSACTIONS. */
|
|
|
|
|
2004-05-14 20:43:34 +02:00
|
|
|
CCH_flush(tdbb, FLUSH_SYSTEM, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Set the state on the inventory page to be limbo */
|
|
|
|
|
|
|
|
transaction->tra_flags |= TRA_prepared;
|
|
|
|
TRA_set_state(tdbb, transaction, transaction->tra_number, tra_limbo);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
jrd_tra* TRA_reconnect(thread_db* tdbb, const UCHAR* id, USHORT length)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ r e c o n n e c t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Reconnect to a transaction in limbo.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* Cannot work on limbo transactions for ReadOnly database */
|
|
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
|
|
ERR_post(isc_read_only_database, 0);
|
|
|
|
|
2008-01-29 11:11:52 +01:00
|
|
|
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
|
2008-01-16 13:22:11 +01:00
|
|
|
jrd_tra* trans = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
|
2003-11-16 02:44:51 +01:00
|
|
|
trans->tra_number = gds__vax_integer(id, length);
|
2001-05-23 15:26:42 +02:00
|
|
|
trans->tra_flags |= TRA_prepared | TRA_reconnected | TRA_write;
|
|
|
|
|
2003-11-01 11:26:43 +01:00
|
|
|
const UCHAR state = limbo_transaction(tdbb, trans->tra_number);
|
|
|
|
if (state != tra_limbo) {
|
|
|
|
USHORT message;
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
switch (state) {
|
|
|
|
case tra_active:
|
|
|
|
message = 262; /* ACTIVE */
|
|
|
|
break;
|
|
|
|
case tra_dead:
|
|
|
|
message = 264; /* ROLLED BACK */
|
|
|
|
break;
|
|
|
|
case tra_committed:
|
|
|
|
message = 263; /* COMMITTED */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
message = 265; /* ILL DEFINED */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-11-01 11:26:43 +01:00
|
|
|
const SLONG number = trans->tra_number;
|
2008-01-29 11:11:52 +01:00
|
|
|
MemoryPool* tra_pool = trans->tra_pool;
|
2008-01-23 15:20:40 +01:00
|
|
|
delete trans;
|
2008-01-29 11:11:52 +01:00
|
|
|
dbb->deletePool(tra_pool);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-01 11:26:43 +01:00
|
|
|
TEXT text[128];
|
2003-12-31 06:36:12 +01:00
|
|
|
USHORT flags = 0;
|
2008-03-12 08:33:12 +01:00
|
|
|
gds__msg_lookup(NULL, JRD_BUGCHK, message, sizeof(text), text, &flags);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
ERR_post(isc_no_recon,
|
|
|
|
isc_arg_gds, isc_tra_state,
|
|
|
|
isc_arg_number, number,
|
|
|
|
isc_arg_string, ERR_cstring(text), 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2006-10-07 12:53:01 +02:00
|
|
|
link_transaction(tdbb, trans);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return trans;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ r e l e a s e _ t r a n s a c t i o n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Cleanup a transaction. This is called by both COMMIT and
|
|
|
|
* ROLLBACK as well as code in JRD to get rid of remote
|
|
|
|
* transactions.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2008-01-29 11:11:52 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
Attachment* attachment = tdbb->getAttachment();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-06 10:43:43 +01:00
|
|
|
if (transaction->tra_blobs.getFirst())
|
|
|
|
while (true)
|
2006-04-06 10:18:53 +02:00
|
|
|
{
|
|
|
|
BlobIndex *current = &transaction->tra_blobs.current();
|
|
|
|
if (current->bli_materialized) {
|
|
|
|
if (!transaction->tra_blobs.getNext())
|
|
|
|
break;
|
2008-03-06 10:43:43 +01:00
|
|
|
}
|
2006-04-06 10:18:53 +02:00
|
|
|
else {
|
|
|
|
ULONG temp_id = current->bli_temp_id;
|
|
|
|
BLB_cancel(tdbb, current->bli_blob_object);
|
|
|
|
if (!transaction->tra_blobs.locate(Firebird::locGreat, temp_id))
|
|
|
|
break;
|
|
|
|
}
|
2004-06-22 22:13:10 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
while (transaction->tra_arrays)
|
|
|
|
BLB_release_array(transaction->tra_arrays);
|
|
|
|
|
2004-12-07 02:19:55 +01:00
|
|
|
if (transaction->tra_pool) {
|
|
|
|
// Iterate the doubly linked list of requests for transaction and null out the transaction references
|
|
|
|
while (transaction->tra_requests)
|
|
|
|
TRA_detach_request(transaction->tra_requests);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-07-18 04:45:35 +02:00
|
|
|
// Release interest in relation/procedure existence for transaction
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-05-09 07:48:33 +02:00
|
|
|
for (Resource* rsc = transaction->tra_resources.begin();
|
|
|
|
rsc < transaction->tra_resources.end(); rsc++)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
switch (rsc->rsc_type) {
|
2004-02-20 07:43:27 +01:00
|
|
|
case Resource::rsc_procedure:
|
2001-05-23 15:26:42 +02:00
|
|
|
CMP_decrement_prc_use_count(tdbb, rsc->rsc_prc);
|
|
|
|
break;
|
2006-10-10 21:40:33 +02:00
|
|
|
case Resource::rsc_collation:
|
|
|
|
rsc->rsc_coll->decUseCount(tdbb);
|
2006-08-07 18:39:21 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2008-01-26 14:17:19 +01:00
|
|
|
MET_release_existence(tdbb, rsc->rsc_rel);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-02-24 04:23:40 +01:00
|
|
|
{ // scope
|
2008-01-29 11:11:52 +01:00
|
|
|
vec<jrd_rel*>& rels = *dbb->dbb_relations;
|
2006-05-25 10:40:23 +02:00
|
|
|
for (size_t i = 0; i < rels.count(); i++)
|
2006-05-22 00:07:35 +02:00
|
|
|
{
|
2006-05-25 10:40:23 +02:00
|
|
|
jrd_rel* relation = rels[i];
|
2006-05-22 00:07:35 +02:00
|
|
|
if (relation && (relation->rel_flags & REL_temp_tran))
|
|
|
|
{
|
|
|
|
relation->delPages(tdbb, transaction->tra_number);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-24 04:23:40 +01:00
|
|
|
} // end scope
|
2006-05-22 00:07:35 +02:00
|
|
|
|
2006-07-18 04:45:35 +02:00
|
|
|
// Release the locks associated with the transaction
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-01-07 16:15:06 +01:00
|
|
|
if (transaction->tra_cancel_lock)
|
|
|
|
LCK_release(tdbb, transaction->tra_cancel_lock);
|
|
|
|
|
2005-12-02 08:35:34 +01:00
|
|
|
vec<Lock*>* vector = transaction->tra_relation_locks;
|
2003-12-31 06:36:12 +01:00
|
|
|
if (vector) {
|
2005-12-02 08:35:34 +01:00
|
|
|
vec<Lock*>::iterator lock = vector->begin();
|
|
|
|
for (ULONG i = 0; i < vector->count(); ++i, ++lock)
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*lock)
|
2005-12-02 08:35:34 +01:00
|
|
|
LCK_release(tdbb, *lock);
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
++transaction->tra_use_count;
|
|
|
|
if (transaction->tra_lock)
|
|
|
|
LCK_release(tdbb, transaction->tra_lock);
|
|
|
|
--transaction->tra_use_count;
|
|
|
|
|
2006-07-18 04:45:35 +02:00
|
|
|
// release the sparse bit map used for commit retain transaction
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-09-28 08:28:38 +02:00
|
|
|
delete transaction->tra_commit_sub_trans;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_precommitted)
|
2003-08-28 15:16:03 +02:00
|
|
|
TRA_precommited(tdbb, transaction->tra_number, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-07-18 04:45:35 +02:00
|
|
|
// Unlink the transaction from the database block
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-08 22:20:26 +01:00
|
|
|
for (jrd_tra** ptr = &attachment->att_transactions; *ptr; ptr = &(*ptr)->tra_next)
|
2008-03-06 10:43:43 +01:00
|
|
|
{
|
2003-04-25 16:51:04 +02:00
|
|
|
if (*ptr == transaction) {
|
2001-05-23 15:26:42 +02:00
|
|
|
*ptr = transaction->tra_next;
|
|
|
|
break;
|
|
|
|
}
|
2003-04-25 16:51:04 +02:00
|
|
|
}
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
// Release transaction's under-modification-rpb list
|
2003-04-25 16:51:04 +02:00
|
|
|
|
|
|
|
delete transaction->tra_rpblist;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-07-18 04:45:35 +02:00
|
|
|
// Release the database snapshot, if any
|
2006-07-17 19:44:18 +02:00
|
|
|
|
|
|
|
delete transaction->tra_db_snapshot;
|
|
|
|
|
2008-02-28 14:48:16 +01:00
|
|
|
// Close all open DSQL cursors
|
|
|
|
|
|
|
|
while (transaction->tra_open_cursors.getCount())
|
|
|
|
{
|
|
|
|
DSQL_free_statement(tdbb, transaction->tra_open_cursors.pop(), DSQL_close);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Release the transaction pool
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-29 11:11:52 +01:00
|
|
|
MemoryPool* tra_pool = transaction->tra_pool;
|
2008-01-23 15:20:40 +01:00
|
|
|
delete transaction;
|
2008-01-29 11:11:52 +01:00
|
|
|
dbb->deletePool(tra_pool);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-07-25 07:03:26 +02:00
|
|
|
void TRA_rollback(thread_db* tdbb, jrd_tra* transaction, const bool retaining_flag,
|
|
|
|
const bool force_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ r o l l b a c k
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Rollback a transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2004-08-30 20:11:08 +02:00
|
|
|
Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Check in with external file system */
|
|
|
|
|
|
|
|
EXT_trans_rollback(transaction);
|
|
|
|
|
|
|
|
if (transaction->tra_flags & (TRA_prepare2 | TRA_reconnected))
|
2003-12-22 11:00:59 +01:00
|
|
|
MET_update_transaction(tdbb, transaction, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-07-25 07:03:26 +02:00
|
|
|
/* If force flag is true, get rid of all savepoints to mark the transaction as dead */
|
|
|
|
if (force_flag) {
|
|
|
|
// Free all savepoint data
|
|
|
|
// We can do it in reverse order because nothing except simple deallocation
|
|
|
|
// of memory is really done in VIO_verb_cleanup when we pass NULL as sav_next
|
|
|
|
while (transaction->tra_save_point)
|
|
|
|
{
|
|
|
|
Savepoint* const next = transaction->tra_save_point->sav_next;
|
|
|
|
transaction->tra_save_point->sav_next = NULL;
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
2008-03-06 10:43:43 +01:00
|
|
|
transaction->tra_save_point = next;
|
2005-07-25 07:03:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-03 18:29:51 +01:00
|
|
|
/* Find out if there is a transaction savepoint we can use to rollback our transaction */
|
2003-12-31 06:36:12 +01:00
|
|
|
bool tran_sav = false;
|
2004-03-18 06:56:06 +01:00
|
|
|
for (const Savepoint* temp = transaction->tra_save_point; temp;
|
|
|
|
temp = temp->sav_next)
|
|
|
|
{
|
2002-11-03 18:29:51 +01:00
|
|
|
if (temp->sav_flags & SAV_trans_level) {
|
2003-12-31 06:36:12 +01:00
|
|
|
tran_sav = true;
|
2002-11-03 18:29:51 +01:00
|
|
|
break;
|
|
|
|
}
|
2004-03-18 06:56:06 +01:00
|
|
|
}
|
2005-07-25 07:03:26 +02:00
|
|
|
|
2002-11-03 18:29:51 +01:00
|
|
|
/* Measure transaction savepoint size if there is one. We'll use it for undo
|
|
|
|
only if it is small enough */
|
2004-09-28 23:50:10 +02:00
|
|
|
IPTR count = SAV_LARGE;
|
2002-11-03 18:29:51 +01:00
|
|
|
if (tran_sav) {
|
2004-03-18 06:56:06 +01:00
|
|
|
for (const Savepoint* temp = transaction->tra_save_point; temp;
|
|
|
|
temp = temp->sav_next)
|
|
|
|
{
|
2002-11-03 18:29:51 +01:00
|
|
|
count = VIO_savepoint_large(temp, count);
|
|
|
|
if (count < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-07-25 07:03:26 +02:00
|
|
|
|
2002-11-03 18:29:51 +01:00
|
|
|
// We are going to use savepoint to undo transaction
|
|
|
|
if (tran_sav && count > 0) {
|
|
|
|
// Undo all user savepoints work
|
2002-10-29 21:20:44 +01:00
|
|
|
while (transaction->tra_save_point->sav_flags & SAV_user) {
|
|
|
|
++transaction->tra_save_point->sav_verb_count; /* cause undo */
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
2008-03-06 10:43:43 +01:00
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
|
|
|
else {
|
2002-11-03 18:29:51 +01:00
|
|
|
// Free all savepoint data
|
|
|
|
// We can do it in reverse order because nothing except simple deallocation
|
|
|
|
// of memory is really done in VIO_verb_cleanup when we pass NULL as sav_next
|
2004-03-18 06:56:06 +01:00
|
|
|
while (transaction->tra_save_point &&
|
|
|
|
transaction->tra_save_point->sav_flags & SAV_user)
|
|
|
|
{
|
|
|
|
Savepoint* const next = transaction->tra_save_point->sav_next;
|
2002-11-03 18:29:51 +01:00
|
|
|
transaction->tra_save_point->sav_next = NULL;
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
2008-03-06 10:43:43 +01:00
|
|
|
transaction->tra_save_point = next;
|
2002-10-29 21:20:44 +01:00
|
|
|
}
|
2002-11-03 18:29:51 +01:00
|
|
|
if (transaction->tra_save_point) {
|
|
|
|
if (!(transaction->tra_save_point->sav_flags & SAV_trans_level))
|
|
|
|
BUGCHECK(287); /* Too many savepoints */
|
|
|
|
/* This transaction savepoint contains wrong data now. Clean it up */
|
|
|
|
VIO_verb_cleanup(tdbb, transaction); /* get rid of transaction savepoint */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-25 13:20:57 +01:00
|
|
|
SSHORT state = tra_dead;
|
|
|
|
|
2002-11-03 18:29:51 +01:00
|
|
|
// Only transaction savepoint could be there
|
|
|
|
if (transaction->tra_save_point)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!(transaction->tra_save_point->sav_flags & SAV_trans_level))
|
|
|
|
BUGCHECK(287); /* Too many savepoints */
|
|
|
|
|
|
|
|
/* Make sure that any error during savepoint undo is handled by marking
|
|
|
|
the transaction as dead. */
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
try {
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
/* In an attempt to avoid deadlocks, clear the precedence by writing
|
|
|
|
all dirty buffers for this transaction. */
|
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_write) {
|
2004-05-14 20:43:34 +02:00
|
|
|
CCH_flush(tdbb, FLUSH_TRAN, transaction->tra_number);
|
2001-05-23 15:26:42 +02:00
|
|
|
++transaction->tra_save_point->sav_verb_count; /* cause undo */
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
2004-05-14 20:43:34 +02:00
|
|
|
CCH_flush(tdbb, FLUSH_TRAN, transaction->tra_number);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
|
|
|
|
2006-01-25 13:20:57 +01:00
|
|
|
// All changes are undone, so we may mark the transaction
|
|
|
|
// as committed
|
|
|
|
state = tra_committed;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2006-05-19 17:17:02 +02:00
|
|
|
catch (const Firebird::Exception&) {
|
2001-12-24 03:51:06 +01:00
|
|
|
/* Prevent a bugcheck in TRA_set_state to cause a loop */
|
|
|
|
/* Clear the error because the rollback will succeed. */
|
2003-11-08 17:40:17 +01:00
|
|
|
tdbb->tdbb_status_vector[0] = isc_arg_gds;
|
2001-12-24 03:51:06 +01:00
|
|
|
tdbb->tdbb_status_vector[1] = 0;
|
2003-11-08 17:40:17 +01:00
|
|
|
tdbb->tdbb_status_vector[2] = isc_arg_end;
|
2001-12-24 03:51:06 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2006-01-25 13:20:57 +01:00
|
|
|
else if (!(transaction->tra_flags & TRA_write)) {
|
|
|
|
// There were no changes within the transaction, so we may mark it
|
|
|
|
// as committed
|
|
|
|
state = tra_committed;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2006-02-02 08:32:07 +01:00
|
|
|
// If this is a rollback retain abort this transaction and start a new one.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (retaining_flag) {
|
2006-01-25 13:20:57 +01:00
|
|
|
retain_context(tdbb, transaction, false, state);
|
2001-05-23 15:26:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-02-24 04:23:40 +01:00
|
|
|
TRA_set_state(tdbb, transaction, transaction->tra_number, state);
|
2001-05-23 15:26:42 +02:00
|
|
|
TRA_release_transaction(tdbb, transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
void TRA_set_state(thread_db* tdbb, jrd_tra* transaction, SLONG number, SSHORT state)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ s e t _ s t a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Set the state of a transaction in the inventory page.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* If we're terminating ourselves and we've
|
|
|
|
been precommitted then just return. */
|
|
|
|
|
|
|
|
if (transaction &&
|
|
|
|
transaction->tra_number == number &&
|
2003-12-31 06:36:12 +01:00
|
|
|
transaction->tra_flags & TRA_precommitted)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* If it is a ReadOnly DB, set the new state in the TIP cache and return */
|
|
|
|
if ((dbb->dbb_flags & DBB_read_only) && dbb->dbb_tip_cache) {
|
|
|
|
TPC_set_state(tdbb, number, state);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-05-25 10:40:23 +02:00
|
|
|
const ULONG trans_per_tip = dbb->dbb_page_manager.transPerTIP;
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG sequence = number / trans_per_tip;
|
2006-05-22 00:07:35 +02:00
|
|
|
//trans_per_tip = dbb->dbb_page_manager.transPerTIP;
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG byte = TRANS_OFFSET(number % trans_per_tip);
|
|
|
|
const SSHORT shift = TRANS_SHIFT(number);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2004-02-20 07:43:27 +01:00
|
|
|
tx_inv_page* tip = fetch_inventory_page(tdbb, &window, (SLONG) sequence, LCK_write);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
#ifdef SUPERSERVER_V2
|
|
|
|
CCH_MARK(tdbb, &window);
|
2004-03-18 06:56:06 +01:00
|
|
|
const ULONG generation = tip->pag_generation;
|
2001-05-23 15:26:42 +02:00
|
|
|
#else
|
|
|
|
CCH_MARK_MUST_WRITE(tdbb, &window);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* set the state on the TIP page */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
UCHAR* address = tip->tip_transactions + byte;
|
2001-05-23 15:26:42 +02:00
|
|
|
*address &= ~(TRA_MASK << shift);
|
|
|
|
*address |= state << shift;
|
|
|
|
|
|
|
|
/* set the new state in the TIP cache as well */
|
|
|
|
|
|
|
|
if (dbb->dbb_tip_cache)
|
|
|
|
TPC_set_state(tdbb, number, state);
|
|
|
|
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
|
|
|
|
#ifdef SUPERSERVER_V2
|
|
|
|
/* Let the TIP be lazily updated for read-only queries.
|
|
|
|
To amortize write of TIP page for update transactions,
|
|
|
|
exit engine to allow other transactions to update the TIP
|
|
|
|
and use page generation to determine if page was written. */
|
|
|
|
|
|
|
|
if (transaction && !(transaction->tra_flags & TRA_write))
|
|
|
|
return;
|
2008-02-24 04:23:40 +01:00
|
|
|
|
|
|
|
{ //scope
|
|
|
|
Database::Checkout dcoHolder(dbb);
|
|
|
|
THREAD_YIELD();
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-02-24 04:23:40 +01:00
|
|
|
tip = reinterpret_cast<tx_inv_page*>(CCH_FETCH(tdbb, &window, LCK_write, pag_transactions));
|
|
|
|
if (generation == tip->pag_generation)
|
|
|
|
CCH_MARK_MUST_WRITE(tdbb, &window);
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
void TRA_shutdown_attachment(thread_db* tdbb, Attachment* attachment)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ s h u t d o w n _ a t t a c h m e n t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Release locks associated with transactions for attachment.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
for (jrd_tra* transaction = attachment->att_transactions; transaction;
|
2008-03-06 10:43:43 +01:00
|
|
|
transaction = transaction->tra_next)
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/* Release the relation locks associated with the transaction */
|
|
|
|
|
2005-12-02 08:35:34 +01:00
|
|
|
vec<Lock*>* vector = transaction->tra_relation_locks;
|
2003-12-31 06:36:12 +01:00
|
|
|
if (vector) {
|
2005-12-02 08:35:34 +01:00
|
|
|
vec<Lock*>::iterator lock = vector->begin();
|
|
|
|
for (ULONG i = 0; i < vector->count(); ++i, ++lock) {
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*lock)
|
2005-12-02 08:35:34 +01:00
|
|
|
LCK_release(tdbb, *lock);
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Release transaction lock itself */
|
|
|
|
|
|
|
|
++transaction->tra_use_count;
|
|
|
|
if (transaction->tra_lock)
|
|
|
|
LCK_release(tdbb, transaction->tra_lock);
|
|
|
|
--transaction->tra_use_count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
int TRA_snapshot_state(thread_db* tdbb, const jrd_tra* trans, SLONG number)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ s n a p s h o t _ s t a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Get the state of a numbered transaction when a
|
|
|
|
* transaction started.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
if (number && TRA_precommited(tdbb, number, number))
|
|
|
|
return tra_precommitted;
|
|
|
|
|
|
|
|
if (number == trans->tra_number)
|
|
|
|
return tra_us;
|
|
|
|
|
2005-11-06 04:20:18 +01:00
|
|
|
// If the transaction is older than the oldest
|
|
|
|
// interesting transaction, it must be committed.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (number < trans->tra_oldest)
|
|
|
|
return tra_committed;
|
|
|
|
|
2005-11-06 04:20:18 +01:00
|
|
|
// If the transaction is the system transaction, it is considered committed.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-06 04:20:18 +01:00
|
|
|
if (number == TRA_system_transaction)
|
2001-05-23 15:26:42 +02:00
|
|
|
return tra_committed;
|
|
|
|
|
2005-11-06 04:20:18 +01:00
|
|
|
// Look in the transaction cache for read committed transactions
|
|
|
|
// fast, and the system transaction. The system transaction can read
|
|
|
|
// data from active transactions.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (trans->tra_flags & TRA_read_committed)
|
|
|
|
return TPC_snapshot_state(tdbb, number);
|
|
|
|
|
2005-11-06 04:20:18 +01:00
|
|
|
if (trans->tra_flags & TRA_system)
|
|
|
|
{
|
|
|
|
int state = TPC_snapshot_state(tdbb, number);
|
|
|
|
if (state == tra_active)
|
|
|
|
return tra_committed;
|
2008-02-24 04:23:40 +01:00
|
|
|
|
|
|
|
return state;
|
2005-11-06 04:20:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the transaction is a commited sub-transction - do the easy lookup.
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (trans->tra_commit_sub_trans &&
|
2004-09-28 08:28:38 +02:00
|
|
|
UInt32Bitmap::test(trans->tra_commit_sub_trans, number))
|
2004-03-18 06:56:06 +01:00
|
|
|
{
|
|
|
|
return tra_committed;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-06 04:20:18 +01:00
|
|
|
// If the transaction is younger than we are and we are not read committed
|
|
|
|
// or the system transaction, the transaction must be considered active.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (number > trans->tra_top)
|
|
|
|
return tra_active;
|
|
|
|
|
|
|
|
return TRA_state(trans->tra_transactions, trans->tra_oldest, number);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
jrd_tra* TRA_start(thread_db* tdbb, ULONG flags, SSHORT lock_timeout)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ s t a r t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Start a user transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
Attachment* attachment = tdbb->getAttachment();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
if (dbb->dbb_ast_flags & DBB_shut_tran)
|
|
|
|
{
|
2007-02-12 11:18:43 +01:00
|
|
|
ERR_post(isc_shutinprog, isc_arg_string,
|
2007-12-03 16:46:39 +01:00
|
|
|
ERR_string(tdbb->getAttachment()->att_filename),
|
2001-05-23 15:26:42 +02:00
|
|
|
0);
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
// To handle the problems of relation locks, allocate a temporary
|
|
|
|
// transaction block first, seize relation locks, then go ahead and
|
|
|
|
// make up the real transaction block.
|
2008-01-29 11:11:52 +01:00
|
|
|
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
|
2008-01-16 13:22:11 +01:00
|
|
|
jrd_tra* temp = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
temp->tra_flags = flags;
|
|
|
|
temp->tra_lock_timeout = lock_timeout;
|
2006-02-03 14:49:39 +01:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
return transaction_start(tdbb, temp);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ s t a r t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Start a user transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
Attachment* attachment = tdbb->getAttachment();
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
if (dbb->dbb_ast_flags & DBB_shut_tran)
|
|
|
|
{
|
|
|
|
ERR_post(isc_shutinprog, isc_arg_string,
|
|
|
|
ERR_string(tdbb->getAttachment()->att_filename),
|
|
|
|
0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
// To handle the problems of relation locks, allocate a temporary
|
|
|
|
// transaction block first, seize relation locks, then go ahead and
|
|
|
|
// make up the real transaction block.
|
2008-01-29 11:11:52 +01:00
|
|
|
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
|
2008-01-16 13:22:11 +01:00
|
|
|
jrd_tra* temp = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
transaction_options(tdbb, temp, tpb, tpb_length);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
return transaction_start(tdbb, temp);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
int TRA_state(const UCHAR* bit_vector, ULONG oldest, ULONG number)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ s t a t e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Get the state of a transaction from a cached
|
|
|
|
* bit vector.
|
|
|
|
* NOTE: This code is reproduced elsewhere in
|
|
|
|
* this module for speed. If changes are made
|
|
|
|
* to this code make them in the replicated code also.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
const ULONG base = oldest & ~TRA_MASK;
|
|
|
|
const ULONG byte = TRANS_OFFSET(number - base);
|
|
|
|
const USHORT shift = TRANS_SHIFT(number);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
return (bit_vector[byte] >> shift) & TRA_MASK;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
bool TRA_sweep(thread_db* tdbb, jrd_tra* trans)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* T R A _ s w e e p
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Make a garbage collection pass thru database.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
CHECK_DBB(dbb);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
/* No point trying to sweep a ReadOnly database */
|
|
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
|
|
return false;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
if (dbb->dbb_flags & DBB_sweep_in_progress)
|
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
/* fill out a lock block, zeroing it out first */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock temp_lock;
|
2001-05-23 15:26:42 +02:00
|
|
|
temp_lock.lck_dbb = dbb;
|
2004-02-20 07:43:27 +01:00
|
|
|
temp_lock.lck_object = trans;
|
2008-01-16 13:22:11 +01:00
|
|
|
temp_lock.lck_type = LCK_sweep;
|
2001-05-23 15:26:42 +02:00
|
|
|
temp_lock.lck_owner_handle =
|
|
|
|
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_length = sizeof(SLONG);
|
|
|
|
|
2008-01-26 14:17:19 +01:00
|
|
|
if (!LCK_lock(tdbb, &temp_lock, LCK_EX, (trans) ? LCK_NO_WAIT : LCK_WAIT))
|
2008-01-16 13:22:11 +01:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
dbb->dbb_flags |= DBB_sweep_in_progress;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
jrd_tra* const tdbb_old_trans = tdbb->getTransaction();
|
2008-02-13 17:46:11 +01:00
|
|
|
|
|
|
|
jrd_tra* transaction = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
/* Clean up the temporary locks we've gotten in case anything goes wrong */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
try {
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
/* Identify ourselves as a sweeper thread. This accomplishes
|
|
|
|
two goals: 1) Sweep transaction is started "precommitted"
|
|
|
|
and 2) Execution is throttled in JRD_reschedule() by
|
|
|
|
yielding the processor when our quantum expires. */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
tdbb->tdbb_flags |= TDBB_sweeper;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
/* Start a transaction, if necessary, to perform the sweep.
|
|
|
|
Save the transaction's oldest snapshot as it is refreshed
|
|
|
|
during the course of the database sweep. Since it is used
|
|
|
|
below to advance the OIT we must save it before it changes. */
|
2006-02-03 14:49:39 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (!(transaction = trans))
|
2006-10-10 08:41:36 +02:00
|
|
|
transaction = TRA_start(tdbb, sizeof(sweep_tpb), sweep_tpb);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
SLONG transaction_oldest_active = transaction->tra_oldest_active;
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->setTransaction(transaction);
|
2005-10-20 15:03:33 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
#ifdef GARBAGE_THREAD
|
|
|
|
/* The garbage collector runs asynchronously with respect to
|
|
|
|
our database sweep. This isn't good enough since we must
|
|
|
|
be absolutely certain that all dead transactions have been
|
|
|
|
swept from the database before advancing the OIT. Turn off
|
|
|
|
the "notify garbage collector" flag for the attachment and
|
|
|
|
synchronously perform the garbage collection ourselves. */
|
|
|
|
|
|
|
|
transaction->tra_attachment->att_flags &= ~ATT_notify_gc;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (VIO_sweep(tdbb, transaction)) {
|
2004-02-20 07:43:27 +01:00
|
|
|
const ULONG base = transaction->tra_oldest & ~TRA_MASK;
|
2006-02-23 06:08:26 +01:00
|
|
|
ULONG active = transaction->tra_oldest;
|
2008-03-06 10:43:43 +01:00
|
|
|
for (; active < (ULONG) transaction->tra_top; active++)
|
2006-02-23 06:08:26 +01:00
|
|
|
{
|
|
|
|
if (transaction->tra_flags & TRA_read_committed) {
|
|
|
|
if (TPC_cache_state(tdbb, active) == tra_limbo)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const ULONG byte = TRANS_OFFSET(active - base);
|
|
|
|
const USHORT shift = TRANS_SHIFT(active);
|
2008-03-08 22:20:26 +01:00
|
|
|
if (((transaction->tra_transactions[byte] >> shift) & TRA_MASK) == tra_limbo)
|
2006-02-23 06:08:26 +01:00
|
|
|
{
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Flush page buffers to insure that no dangling records from
|
|
|
|
dead transactions are left on-disk. This must be done before
|
|
|
|
the OIT is advanced and the header page is written to disk.
|
|
|
|
If the header page was written before flushing the page buffers
|
|
|
|
and there was a server crash, the dead records would appear
|
|
|
|
committed since their TID would now be less than the OIT recorded
|
|
|
|
in the database. */
|
|
|
|
|
2004-05-14 20:43:34 +02:00
|
|
|
CCH_flush(tdbb, FLUSH_SWEEP, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(HEADER_PAGE_NUMBER);
|
2004-02-20 07:43:27 +01:00
|
|
|
header_page* header =
|
|
|
|
(header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (header->hdr_oldest_transaction < --transaction_oldest_active) {
|
|
|
|
CCH_MARK_MUST_WRITE(tdbb, &window);
|
|
|
|
header->hdr_oldest_transaction =
|
2003-04-03 19:19:10 +02:00
|
|
|
MIN(active, (ULONG) transaction_oldest_active);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!trans)
|
2003-11-01 11:26:43 +01:00
|
|
|
TRA_commit(tdbb, transaction, false);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
LCK_release(tdbb, &temp_lock);
|
|
|
|
dbb->dbb_flags &= ~DBB_sweep_in_progress;
|
|
|
|
|
|
|
|
tdbb->tdbb_flags &= ~TDBB_sweeper;
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->setTransaction(tdbb_old_trans);
|
2001-12-24 03:51:06 +01:00
|
|
|
} // try
|
2006-05-19 17:17:02 +02:00
|
|
|
catch (const Firebird::Exception& ex) {
|
2004-03-01 04:35:23 +01:00
|
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
2001-12-24 03:51:06 +01:00
|
|
|
try {
|
|
|
|
if (!trans)
|
2003-11-01 11:26:43 +01:00
|
|
|
TRA_commit(tdbb, transaction, false);
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2004-06-07 18:10:42 +02:00
|
|
|
LCK_release(tdbb, &temp_lock);
|
|
|
|
dbb->dbb_flags &= ~DBB_sweep_in_progress;
|
|
|
|
tdbb->tdbb_flags &= ~TDBB_sweeper;
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->setTransaction(tdbb_old_trans);
|
2001-12-24 03:51:06 +01:00
|
|
|
}
|
2006-05-19 17:17:02 +02:00
|
|
|
catch (const Firebird::Exception& ex2) {
|
2004-05-12 21:39:17 +02:00
|
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex2);
|
2001-12-24 03:51:06 +01:00
|
|
|
LCK_release(tdbb, &temp_lock);
|
|
|
|
dbb->dbb_flags &= ~DBB_sweep_in_progress;
|
|
|
|
tdbb->tdbb_flags &= ~TDBB_sweeper;
|
2007-12-03 16:46:39 +01:00
|
|
|
tdbb->setTransaction(tdbb_old_trans);
|
2004-03-18 06:56:06 +01:00
|
|
|
return false;
|
2001-12-24 03:51:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-07 11:15:32 +02:00
|
|
|
int TRA_wait(thread_db* tdbb, jrd_tra* trans, SLONG number, jrd_tra::wait_t wait)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
2004-10-07 11:15:32 +02:00
|
|
|
* T R A _ w a i t
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Wait for a given transaction to drop into a stable state (i.e. non-active)
|
|
|
|
* state. To do this, we first wait on the transaction number. When we
|
|
|
|
* are able to get the lock, the transaction is not longer bona fide
|
|
|
|
* active. Next, we determine the state of the transaction from the
|
|
|
|
* transaction inventory page. If either committed, dead, or limbo,
|
|
|
|
* we return the state. If the transaction is still marked active,
|
|
|
|
* however, declare the transaction dead, and mark the transaction
|
|
|
|
* inventory page accordingly.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* Create, wait on, and release lock on target transaction. If
|
|
|
|
we can't get the lock due to deadlock */
|
|
|
|
|
2004-10-07 11:15:32 +02:00
|
|
|
if (wait != jrd_tra::tra_no_wait) {
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock temp_lock;
|
2001-05-23 15:26:42 +02:00
|
|
|
temp_lock.lck_dbb = dbb;
|
|
|
|
temp_lock.lck_type = LCK_tra;
|
|
|
|
temp_lock.lck_owner_handle =
|
|
|
|
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_length = sizeof(SLONG);
|
|
|
|
temp_lock.lck_key.lck_long = number;
|
|
|
|
|
2004-10-07 11:15:32 +02:00
|
|
|
const SSHORT timeout =
|
|
|
|
(wait == jrd_tra::tra_wait) ? trans->getLockWait() : 0;
|
|
|
|
|
2008-01-26 14:17:19 +01:00
|
|
|
if (!LCK_lock(tdbb, &temp_lock, LCK_read, timeout))
|
2001-05-23 15:26:42 +02:00
|
|
|
return tra_active;
|
|
|
|
|
|
|
|
LCK_release(tdbb, &temp_lock);
|
|
|
|
}
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
USHORT state = TRA_get_state(tdbb, number);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-10-07 11:15:32 +02:00
|
|
|
if (wait != jrd_tra::tra_no_wait && state == tra_committed)
|
2001-05-23 15:26:42 +02:00
|
|
|
return state;
|
|
|
|
|
|
|
|
if (state == tra_precommitted)
|
|
|
|
return state;
|
|
|
|
|
|
|
|
/* If the recorded state of the transaction is active, we know better. If
|
|
|
|
it were active, he'd be alive now. Mark him dead. */
|
|
|
|
|
|
|
|
if (state == tra_active) {
|
|
|
|
state = tra_dead;
|
|
|
|
TRA_set_state(tdbb, 0, number, tra_dead);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (number > trans->tra_top)
|
|
|
|
return state;
|
|
|
|
|
|
|
|
/* If the transaction disppeared into limbo, died, for constructively
|
|
|
|
died, tweak the transaction state snapshot to reflect the new state.
|
|
|
|
This is guarenteed safe. */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
const ULONG byte = TRANS_OFFSET(number - (trans->tra_oldest & ~TRA_MASK));
|
|
|
|
const USHORT shift = TRANS_SHIFT(number);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (trans->tra_flags & TRA_read_committed)
|
|
|
|
TPC_set_state(tdbb, number, state);
|
|
|
|
else {
|
|
|
|
trans->tra_transactions[byte] &= ~(TRA_MASK << shift);
|
|
|
|
trans->tra_transactions[byte] |= state << shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-07 16:15:06 +01:00
|
|
|
static int blocking_ast_transaction(void* ast_object)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* b l o c k i n g _ a s t _ t r a n s a c t i o n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Mark the transaction to cancel its active requests.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
jrd_tra* transaction = static_cast<jrd_tra*>(ast_object);
|
|
|
|
|
2008-03-12 17:53:57 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
Database* dbb = transaction->tra_cancel_lock->lck_dbb;
|
2008-01-26 14:17:19 +01:00
|
|
|
|
2008-03-12 17:53:57 +01:00
|
|
|
Database::SyncGuard dsGuard(dbb, true);
|
2007-01-07 16:15:06 +01:00
|
|
|
|
2008-03-12 17:53:57 +01:00
|
|
|
ThreadContextHolder tdbb;
|
|
|
|
tdbb->setDatabase(dbb);
|
|
|
|
tdbb->setAttachment(transaction->tra_cancel_lock->lck_attachment);
|
|
|
|
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, 0);
|
2007-01-07 16:15:06 +01:00
|
|
|
|
2008-03-12 17:53:57 +01:00
|
|
|
if (transaction->tra_cancel_lock)
|
|
|
|
LCK_release(tdbb, transaction->tra_cancel_lock);
|
|
|
|
|
|
|
|
transaction->tra_flags |= TRA_cancel_request;
|
|
|
|
}
|
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{} // no-op
|
2007-01-07 16:15:06 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef SUPERSERVER_V2
|
2004-03-11 06:04:26 +01:00
|
|
|
static SLONG bump_transaction_id(thread_db* tdbb, WIN * window)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* b u m p _ t r a n s a c t i o n _ i d
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Fetch header and bump next transaction id. If necessary,
|
|
|
|
* extend TIP.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
2008-03-06 10:43:43 +01:00
|
|
|
if (dbb->dbb_next_transaction >= MAX_TRA_NUMBER - 1)
|
2006-02-05 12:06:28 +01:00
|
|
|
{
|
|
|
|
CCH_RELEASE(tdbb, window);
|
|
|
|
ERR_post(isc_imp_exc, isc_arg_gds, isc_tra_num_exc, 0);
|
|
|
|
}
|
|
|
|
const SLONG number = ++dbb->dbb_next_transaction;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* No need to write TID onto the TIP page, for a RO DB */
|
|
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
|
|
return number;
|
|
|
|
|
|
|
|
/* If this is the first transaction on a TIP, allocate the TIP now. */
|
|
|
|
|
2008-03-08 22:20:26 +01:00
|
|
|
const bool new_tip = (number == 1 || (number % dbb->dbb_page_manager.transPerTIP) == 0);
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
if (new_tip) {
|
2001-05-23 15:26:42 +02:00
|
|
|
TRA_extend_tip(tdbb,
|
2006-05-22 00:07:35 +02:00
|
|
|
(ULONG) (number / dbb->dbb_page_manager.transPerTIP), window);
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
return number;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
static header_page* bump_transaction_id(thread_db* tdbb, WIN * window)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* b u m p _ t r a n s a c t i o n _ i d
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Fetch header and bump next transaction id. If necessary,
|
|
|
|
* extend TIP.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
window->win_page = HEADER_PAGE_NUMBER;
|
2004-02-20 07:43:27 +01:00
|
|
|
header_page* header = (header_page*) CCH_FETCH(tdbb, window, LCK_write, pag_header);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Before incrementing the next transaction Id, make sure the current one is valid */
|
|
|
|
if (header->hdr_next_transaction) {
|
|
|
|
if (header->hdr_oldest_active > header->hdr_next_transaction)
|
|
|
|
BUGCHECK(266); /*next transaction older than oldest active */
|
|
|
|
|
|
|
|
if (header->hdr_oldest_transaction > header->hdr_next_transaction)
|
|
|
|
BUGCHECK(267); /* next transaction older than oldest transaction */
|
|
|
|
}
|
|
|
|
|
2008-03-06 10:43:43 +01:00
|
|
|
if (header->hdr_next_transaction >= MAX_TRA_NUMBER - 1)
|
2006-02-03 18:44:19 +01:00
|
|
|
{
|
|
|
|
CCH_RELEASE(tdbb, window);
|
|
|
|
ERR_post(isc_imp_exc, isc_arg_gds, isc_tra_num_exc, 0);
|
|
|
|
}
|
|
|
|
const SLONG number = header->hdr_next_transaction + 1;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* If this is the first transaction on a TIP, allocate the TIP now. */
|
|
|
|
|
2008-03-08 22:20:26 +01:00
|
|
|
const bool new_tip = (number == 1 || (number % dbb->dbb_page_manager.transPerTIP) == 0);
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
if (new_tip) {
|
2001-05-23 15:26:42 +02:00
|
|
|
TRA_extend_tip(tdbb,
|
2006-05-22 00:07:35 +02:00
|
|
|
(ULONG) (number / dbb->dbb_page_manager.transPerTIP), window);
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Extend, if necessary, has apparently succeeded. Next, update header
|
|
|
|
page */
|
|
|
|
|
|
|
|
CCH_MARK_MUST_WRITE(tdbb, window);
|
|
|
|
header->hdr_next_transaction = number;
|
|
|
|
|
|
|
|
if (dbb->dbb_oldest_active > header->hdr_oldest_active)
|
|
|
|
header->hdr_oldest_active = dbb->dbb_oldest_active;
|
|
|
|
|
|
|
|
if (dbb->dbb_oldest_transaction > header->hdr_oldest_transaction)
|
|
|
|
header->hdr_oldest_transaction = dbb->dbb_oldest_transaction;
|
|
|
|
|
|
|
|
if (dbb->dbb_oldest_snapshot > header->hdr_oldest_snapshot)
|
|
|
|
header->hdr_oldest_snapshot = dbb->dbb_oldest_snapshot;
|
|
|
|
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-03-19 17:19:56 +01:00
|
|
|
static Lock* create_transaction_lock(thread_db* tdbb, void* object)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* c r e a t e _ t r a n s a c t i o n _ l o c k
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Allocate a transaction lock block.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
|
|
|
|
Lock* lock = FB_NEW_RPT(*tdbb->getDefaultPool(), sizeof(SLONG)) Lock();
|
|
|
|
lock->lck_type = LCK_tra;
|
|
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
|
|
lock->lck_length = sizeof(SLONG);
|
|
|
|
|
|
|
|
lock->lck_dbb = dbb;
|
|
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
|
|
lock->lck_object = object;
|
|
|
|
|
|
|
|
return lock;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef VMS
|
|
|
|
static void compute_oldest_retaining(
|
2004-03-11 06:04:26 +01:00
|
|
|
thread_db* tdbb,
|
2004-01-03 11:59:52 +01:00
|
|
|
jrd_tra* transaction, const bool write_flag)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* c o m p u t e _ o l d e s t _ r e t a i n i n g
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Read the oldest active for all transactions
|
|
|
|
* younger than us up to the youngest retaining
|
|
|
|
* transaction. If an "older" oldest active is
|
|
|
|
* found, by all means use it. Write flag is TRUE
|
|
|
|
* to write retaining lock and FALSE to read it.
|
|
|
|
* The retaining lock holds the youngest commit
|
|
|
|
* retaining transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* Get a commit retaining lock, if not present. */
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock* lock = dbb->dbb_retaining_lock;
|
2003-12-31 06:36:12 +01:00
|
|
|
if (!lock) {
|
2004-03-18 06:56:06 +01:00
|
|
|
lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock();
|
2001-05-23 15:26:42 +02:00
|
|
|
lock->lck_dbb = dbb;
|
|
|
|
lock->lck_type = LCK_retaining;
|
|
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
|
|
lock->lck_length = sizeof(SLONG);
|
2008-03-19 17:19:56 +01:00
|
|
|
lock->lck_object = dbb;
|
2008-01-26 14:17:19 +01:00
|
|
|
LCK_lock(tdbb, lock, LCK_SR, LCK_WAIT);
|
2001-05-23 15:26:42 +02:00
|
|
|
dbb->dbb_retaining_lock = lock;
|
|
|
|
}
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
SLONG number = transaction->tra_number;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Writers must synchronize their lock update so that
|
|
|
|
an older retaining is not written over a younger retaining.
|
|
|
|
In any case, lock types have been selected so that
|
|
|
|
readers and writers don't interfere. */
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
SLONG youngest_retaining;
|
2008-01-16 13:22:11 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (write_flag) {
|
|
|
|
LCK_convert(tdbb, lock, LCK_PW, TRUE);
|
|
|
|
youngest_retaining = LOCK_read_data(lock->lck_id);
|
|
|
|
if (number > youngest_retaining)
|
|
|
|
LCK_write_data(lock, number);
|
|
|
|
LCK_convert(tdbb, lock, LCK_SR, TRUE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
youngest_retaining = LOCK_read_data(lock->lck_id);
|
|
|
|
if (number > youngest_retaining)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* fill out a lock block, zeroing it out first */
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock temp_lock;
|
2001-05-23 15:26:42 +02:00
|
|
|
temp_lock.lck_dbb = dbb;
|
|
|
|
temp_lock.lck_type = LCK_tra;
|
|
|
|
temp_lock.lck_owner_handle =
|
|
|
|
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_length = sizeof(SLONG);
|
2004-02-20 07:43:27 +01:00
|
|
|
temp_lock.lck_object = transaction;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
while (number < youngest_retaining) {
|
|
|
|
temp_lock.lck_key.lck_long = ++number;
|
2004-02-20 07:43:27 +01:00
|
|
|
const SLONG data = LCK_read_data(&temp_lock);
|
|
|
|
if (data && data < transaction->tra_oldest_active)
|
|
|
|
transaction->tra_oldest_active = data;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
static void expand_view_lock(thread_db* tdbb, jrd_tra* transaction, jrd_rel* relation,
|
|
|
|
UCHAR lock_type, const char* option_name, RelationLockTypeMap& lockmap, const int level)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* e x p a n d _ v i e w _ l o c k
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* A view in a RESERVING will lead to all tables in the
|
|
|
|
* view being locked.
|
2008-03-12 08:33:12 +01:00
|
|
|
* Some checks only apply when the user reserved directly the table or view.
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
2008-03-12 08:33:12 +01:00
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
if (level == 30)
|
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_max_recursion, isc_arg_number, 30, 0);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
const char* const relation_name = relation->rel_name.c_str();
|
|
|
|
|
|
|
|
// LCK_none < LCK_SR < LCK_PR < LCK_SW < LCK_EX
|
|
|
|
UCHAR oldlock;
|
|
|
|
const bool found = lockmap.get(relation->rel_id, oldlock);
|
2008-03-13 03:43:32 +01:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
if (found && oldlock > lock_type)
|
|
|
|
{
|
|
|
|
const char* newname = get_lockname_v3(lock_type);
|
|
|
|
const char* oldname = get_lockname_v3(oldlock);
|
2008-03-13 03:43:32 +01:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
if (level)
|
|
|
|
{
|
|
|
|
lock_type = oldlock; // Preserve the old, more powerful lock.
|
|
|
|
ERR_post_warning(isc_tpb_reserv_stronger_wng,
|
|
|
|
isc_arg_string, relation_name, isc_arg_string, oldname,
|
|
|
|
isc_arg_string, newname, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_reserv_stronger,
|
|
|
|
isc_arg_string, relation_name, isc_arg_string, oldname,
|
|
|
|
isc_arg_string, newname, 0);
|
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
if (level == 0)
|
|
|
|
{
|
|
|
|
fb_assert(!relation->rel_view_rse && !relation->rel_view_contexts.getCount());
|
|
|
|
// Reject explicit attempts to take locks on virtual tables, but RDB$ADMIN role
|
|
|
|
// can do that for whatever is needed.
|
|
|
|
if (relation->isVirtual())
|
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_reserv_virtualtbl,
|
|
|
|
isc_arg_string, relation_name, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reject explicit attempts to take locks on system tables, but RDB$ADMIN role
|
|
|
|
// can do that for whatever is needed.
|
|
|
|
if (relation->isSystem() && !tdbb->getAttachment()->locksmith())
|
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_reserv_systbl,
|
|
|
|
isc_arg_string, relation_name, 0);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
if (relation->isTemporary() && (lock_type == LCK_PR || lock_type == LCK_EX))
|
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_reserv_temptbl,
|
|
|
|
isc_arg_string, get_lockname_v3(LCK_PR),
|
|
|
|
isc_arg_string, get_lockname_v3(LCK_EX),
|
|
|
|
isc_arg_string, relation_name, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fb_assert(relation->rel_view_rse && relation->rel_view_contexts.getCount());
|
|
|
|
// Ignore implicit attempts to take locks on special tables through views.
|
|
|
|
if (relation->isVirtual() || relation->isSystem())
|
|
|
|
return;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
// We can't propagate a view's LCK_PR or LCK_EX to a temporary table.
|
|
|
|
if (relation->isTemporary())
|
|
|
|
{
|
|
|
|
switch (lock_type)
|
|
|
|
{
|
|
|
|
case LCK_PR:
|
|
|
|
lock_type = LCK_SR;
|
|
|
|
break;
|
|
|
|
case LCK_EX:
|
|
|
|
lock_type = LCK_SW;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set up the lock on the relation/view
|
|
|
|
Lock* lock = RLCK_transaction_relation_lock(tdbb, transaction, relation);
|
2001-05-23 15:26:42 +02:00
|
|
|
lock->lck_logical = lock_type;
|
2008-03-12 08:33:12 +01:00
|
|
|
|
|
|
|
if (!found)
|
|
|
|
*lockmap.put(relation->rel_id) = lock_type;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
const ViewContexts& ctx = relation->rel_view_contexts;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-12 20:28:04 +02:00
|
|
|
for (size_t i = 0; i < ctx.getCount(); ++i)
|
2002-03-31 01:40:08 +01:00
|
|
|
{
|
2008-03-07 08:28:57 +01:00
|
|
|
jrd_rel* base_rel = MET_lookup_relation(tdbb, ctx[i].vcx_relation_name);
|
|
|
|
if (!base_rel)
|
2002-03-31 01:40:08 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content,
|
|
|
|
isc_arg_gds,
|
|
|
|
isc_tpb_reserv_baserelnotfound, /* should be a BUGCHECK */
|
2003-11-08 17:40:17 +01:00
|
|
|
isc_arg_string,
|
2005-05-12 20:28:04 +02:00
|
|
|
ERR_cstring(ctx[i].vcx_relation_name.c_str()),
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string,
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_cstring(relation_name),
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string,
|
|
|
|
option_name,
|
2002-03-31 01:40:08 +01:00
|
|
|
0);
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* force a scan to read view information */
|
2008-03-07 08:28:57 +01:00
|
|
|
MET_scan_relation(tdbb, base_rel);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
expand_view_lock(tdbb, transaction, base_rel, lock_type, option_name, lockmap, level + 1);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
static tx_inv_page* fetch_inventory_page(
|
2004-03-11 06:04:26 +01:00
|
|
|
thread_db* tdbb,
|
2001-05-23 15:26:42 +02:00
|
|
|
WIN * window,
|
|
|
|
SLONG sequence, USHORT lock_level)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* f e t c h _ i n v e n t o r y _ p a g e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Fetch a transaction inventory page.
|
|
|
|
* Use the opportunity to cache the info
|
|
|
|
* in the TIP cache.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2006-07-27 11:29:04 +02:00
|
|
|
window->win_page = inventory_page(tdbb, sequence);
|
2004-03-18 06:56:06 +01:00
|
|
|
tx_inv_page* tip =
|
|
|
|
(tx_inv_page*) CCH_FETCH(tdbb, window, lock_level, pag_transactions);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
TPC_update_cache(tdbb, tip, sequence);
|
|
|
|
|
|
|
|
return tip;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
static const char* get_lockname_v3(const UCHAR lock)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* g e t _ l o c k n a m e _ v 3
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Get the lock mnemonic, given its binary value.
|
|
|
|
* This is for TPB versions 1 & 3.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
const char* typestr = "unknown";
|
|
|
|
switch (lock)
|
|
|
|
{
|
|
|
|
case LCK_none:
|
|
|
|
case LCK_SR:
|
|
|
|
typestr = "isc_tpb_lock_read, isc_tpb_shared";
|
|
|
|
break;
|
|
|
|
case LCK_PR:
|
|
|
|
typestr = "isc_tpb_lock_read, isc_tpb_protected/isc_tpb_exclusive";
|
|
|
|
break;
|
|
|
|
case LCK_SW:
|
|
|
|
typestr = "isc_tpb_lock_write, isc_tpb_shared";
|
|
|
|
break;
|
|
|
|
case LCK_EX:
|
|
|
|
typestr = "isc_tpb_lock_write, isc_tpb_protected/isc_tpb_exclusive";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return typestr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
static SLONG inventory_page(thread_db* tdbb, SLONG sequence)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* i n v e n t o r y _ p a g e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Get the physical page number of the n-th transaction inventory
|
|
|
|
* page. If not found, try to reconstruct using sibling pointer
|
|
|
|
* from last known TIP page.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2003-12-31 06:36:12 +01:00
|
|
|
vcl* vector = dbb->dbb_t_pages;
|
|
|
|
while (!vector || sequence >= (SLONG) vector->count()) {
|
2001-05-23 15:26:42 +02:00
|
|
|
DPM_scan_pages(tdbb);
|
2003-04-03 19:19:10 +02:00
|
|
|
if ((vector = dbb->dbb_t_pages) && sequence < (SLONG) vector->count())
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
2001-12-24 03:51:06 +01:00
|
|
|
if (!vector)
|
2001-05-23 15:26:42 +02:00
|
|
|
BUGCHECK(165); /* msg 165 cannot find tip page */
|
2001-12-24 03:51:06 +01:00
|
|
|
window.win_page = (*vector)[vector->count() - 1];
|
2004-02-20 07:43:27 +01:00
|
|
|
tx_inv_page* tip =
|
|
|
|
(tx_inv_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_transactions);
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG next = tip->tip_next;
|
2001-05-23 15:26:42 +02:00
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
if (!(window.win_page = next))
|
|
|
|
BUGCHECK(165); /* msg 165 cannot find tip page */
|
2004-02-20 07:43:27 +01:00
|
|
|
// Type check it
|
|
|
|
tip = (tx_inv_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_transactions);
|
2001-05-23 15:26:42 +02:00
|
|
|
CCH_RELEASE(tdbb, &window);
|
2001-12-24 03:51:06 +01:00
|
|
|
DPM_pages(tdbb, 0, pag_transactions, vector->count(),
|
2006-05-22 00:07:35 +02:00
|
|
|
window.win_page.getPageNum());
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
return (*vector)[sequence];
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
static SSHORT limbo_transaction(thread_db* tdbb, SLONG id)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* l i m b o _ t r a n s a c t i o n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
*
|
|
|
|
* limbo_state is called when reconnecting
|
|
|
|
* to an existing transaction to assure that
|
|
|
|
* the transaction is actually in limbo.
|
|
|
|
* It returns the transaction state.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
const SLONG trans_per_tip = dbb->dbb_page_manager.transPerTIP;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG page = id / trans_per_tip;
|
|
|
|
const SLONG number = id % trans_per_tip;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2004-03-18 06:56:06 +01:00
|
|
|
const tx_inv_page* tip = fetch_inventory_page(tdbb, &window, page, LCK_write);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
const SLONG trans_offset = TRANS_OFFSET(number);
|
|
|
|
const UCHAR* byte = tip->tip_transactions + trans_offset;
|
|
|
|
const SSHORT shift = TRANS_SHIFT(number);
|
|
|
|
const SSHORT state = (*byte >> shift) & TRA_MASK;
|
2001-05-23 15:26:42 +02:00
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-07 12:53:01 +02:00
|
|
|
static void link_transaction(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* l i n k _ t r a n s a c t i o n
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Link transaction block into database attachment.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2008-01-26 14:17:19 +01:00
|
|
|
Attachment* attachment = tdbb->getAttachment();
|
2006-10-07 12:53:01 +02:00
|
|
|
transaction->tra_attachment = attachment;
|
|
|
|
transaction->tra_next = attachment->att_transactions;
|
|
|
|
attachment->att_transactions = transaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
static void restart_requests(thread_db* tdbb, jrd_tra* trans)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* r e s t a r t _ r e q u e s t s
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2001-07-10 19:35:13 +02:00
|
|
|
* Restart all requests in the current
|
|
|
|
* attachment to utilize the passed
|
2001-05-23 15:26:42 +02:00
|
|
|
* transaction.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2003-12-31 06:36:12 +01:00
|
|
|
for (jrd_req* request = trans->tra_attachment->att_requests; request;
|
2008-03-06 10:43:43 +01:00
|
|
|
request = request->req_request)
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (request->req_transaction) {
|
|
|
|
EXE_unwind(tdbb, request);
|
|
|
|
EXE_start(tdbb, request, trans);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now take care of any other request levels;
|
|
|
|
start at level 1 since level 0 was just handled */
|
|
|
|
|
2005-12-02 08:35:34 +01:00
|
|
|
vec<jrd_req*>* vector = request->req_sub_requests;
|
2003-12-31 06:36:12 +01:00
|
|
|
if (vector) {
|
|
|
|
for (USHORT level = 1; level < vector->count(); level++) {
|
2005-12-02 08:35:34 +01:00
|
|
|
jrd_req* clone = (*vector)[level];
|
2003-12-31 06:36:12 +01:00
|
|
|
if (clone && clone->req_transaction) {
|
2001-05-23 15:26:42 +02:00
|
|
|
EXE_unwind(tdbb, clone);
|
|
|
|
EXE_start(tdbb, clone, trans);
|
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-25 13:20:57 +01:00
|
|
|
static void retain_context(thread_db* tdbb, jrd_tra* transaction,
|
|
|
|
bool commit, SSHORT state)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* r e t a i n _ c o n t e x t
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* If 'commit' flag is true, commit the transaction,
|
|
|
|
* else rollback the transaction.
|
|
|
|
*
|
|
|
|
* Commit/rollback a transaction while preserving the
|
|
|
|
* context, in particular, its snapshot. The
|
|
|
|
* trick is to insure that the transaction's
|
|
|
|
* oldest active is seen by other transactions
|
|
|
|
* simultaneously starting up.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
2007-12-03 16:46:39 +01:00
|
|
|
Database* dbb = tdbb->getDatabase();
|
2001-05-23 15:26:42 +02:00
|
|
|
CHECK_DBB(dbb);
|
|
|
|
|
|
|
|
/* The new transaction needs to remember the 'commit-retained' transaction
|
|
|
|
because it must see the operations of the 'commit-retained' transaction and
|
|
|
|
its snapshot doesn't contain these operations. */
|
|
|
|
|
2003-12-31 06:36:12 +01:00
|
|
|
if (commit) {
|
2004-09-28 08:28:38 +02:00
|
|
|
SBM_SET(tdbb->getDefaultPool(), &transaction->tra_commit_sub_trans,
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_number);
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* Create a new transaction lock, inheriting oldest active
|
|
|
|
from transaction being committed. */
|
|
|
|
|
2006-05-22 00:07:35 +02:00
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2004-02-20 07:43:27 +01:00
|
|
|
SLONG new_number;
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef SUPERSERVER_V2
|
|
|
|
new_number = bump_transaction_id(tdbb, &window);
|
|
|
|
#else
|
|
|
|
if (dbb->dbb_flags & DBB_read_only)
|
2008-02-14 12:52:59 +01:00
|
|
|
new_number = dbb->dbb_next_transaction + fb_utils::genReadOnlyId();
|
2001-05-23 15:26:42 +02:00
|
|
|
else {
|
2004-02-20 07:43:27 +01:00
|
|
|
const header_page* header = bump_transaction_id(tdbb, &window);
|
2001-05-23 15:26:42 +02:00
|
|
|
new_number = header->hdr_next_transaction;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock* new_lock = 0;
|
|
|
|
Lock* old_lock = transaction->tra_lock;
|
2004-02-20 07:43:27 +01:00
|
|
|
if (old_lock) {
|
2008-03-19 17:19:56 +01:00
|
|
|
new_lock = create_transaction_lock(tdbb, transaction);
|
2001-05-23 15:26:42 +02:00
|
|
|
new_lock->lck_key.lck_long = new_number;
|
|
|
|
new_lock->lck_data = transaction->tra_lock->lck_data;
|
|
|
|
|
2008-01-26 14:17:19 +01:00
|
|
|
if (!LCK_lock(tdbb, new_lock, LCK_write, LCK_WAIT)) {
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifndef SUPERSERVER_V2
|
|
|
|
if (!(dbb->dbb_flags & DBB_read_only))
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
#endif
|
2003-11-08 17:40:17 +01:00
|
|
|
ERR_post(isc_lock_conflict, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef SUPERSERVER_V2
|
|
|
|
if (!(dbb->dbb_flags & DBB_read_only))
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Update database notion of the youngest commit retaining
|
|
|
|
transaction before committing the first transaction. This
|
|
|
|
secures the original snapshot by insuring the oldest active
|
|
|
|
is seen by other transactions. */
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
const SLONG old_number = transaction->tra_number;
|
2001-05-23 15:26:42 +02:00
|
|
|
#ifdef VMS
|
|
|
|
transaction->tra_number = new_number;
|
2003-11-01 11:26:43 +01:00
|
|
|
compute_oldest_retaining(tdbb, transaction, true);
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_number = old_number;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!(dbb->dbb_flags & DBB_read_only)) {
|
|
|
|
/* Set the state on the inventory page */
|
2006-01-25 13:20:57 +01:00
|
|
|
TRA_set_state(tdbb, transaction, old_number, state);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
transaction->tra_number = new_number;
|
|
|
|
|
|
|
|
/* Release transaction lock since it isn't needed
|
|
|
|
anymore and the new one is already in place. */
|
|
|
|
|
|
|
|
if (old_lock) {
|
|
|
|
++transaction->tra_use_count;
|
|
|
|
LCK_release(tdbb, old_lock);
|
|
|
|
transaction->tra_lock = new_lock;
|
|
|
|
--transaction->tra_use_count;
|
2001-12-24 03:51:06 +01:00
|
|
|
delete old_lock;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform any post commit work OR delete entries from deferred list */
|
|
|
|
|
|
|
|
if (commit)
|
|
|
|
DFW_perform_post_commit_work(transaction);
|
|
|
|
else
|
|
|
|
DFW_delete_deferred(transaction, -1);
|
|
|
|
|
|
|
|
transaction->tra_flags &= ~(TRA_write | TRA_prepared);
|
|
|
|
|
|
|
|
/* We have to mimic a TRA_commit and a TRA_start while reusing the
|
|
|
|
'transaction' control block: get rid of the transaction-level
|
|
|
|
savepoint and possibly start a new transaction-level savepoint. */
|
|
|
|
|
2002-11-03 18:29:51 +01:00
|
|
|
// Get rid of all user savepoints
|
|
|
|
// Why we can do this in reverse order described in commit method
|
2008-03-06 10:43:43 +01:00
|
|
|
while (transaction->tra_save_point &&
|
|
|
|
transaction->tra_save_point->sav_flags & SAV_user)
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2004-03-18 06:56:06 +01:00
|
|
|
Savepoint* const next = transaction->tra_save_point->sav_next;
|
2002-11-03 18:29:51 +01:00
|
|
|
transaction->tra_save_point->sav_next = NULL;
|
2002-10-29 21:20:44 +01:00
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
2008-03-06 10:43:43 +01:00
|
|
|
transaction->tra_save_point = next;
|
2002-11-03 18:29:51 +01:00
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (transaction->tra_save_point) {
|
|
|
|
if (!(transaction->tra_save_point->sav_flags & SAV_trans_level))
|
|
|
|
BUGCHECK(287); /* Too many savepoints */
|
2002-11-03 18:29:51 +01:00
|
|
|
VIO_verb_cleanup(tdbb, transaction); /* get rid of transaction savepoint */
|
|
|
|
VIO_start_save_point(tdbb, transaction); /* start new savepoint */
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_save_point->sav_flags |= SAV_trans_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transaction->tra_flags & TRA_precommitted) {
|
|
|
|
if (!(dbb->dbb_flags & DBB_read_only))
|
|
|
|
{
|
|
|
|
transaction->tra_flags &= ~TRA_precommitted;
|
|
|
|
TRA_set_state(tdbb, transaction, new_number, tra_committed);
|
|
|
|
transaction->tra_flags |= TRA_precommitted;
|
|
|
|
}
|
|
|
|
|
2003-08-28 15:16:03 +02:00
|
|
|
TRA_precommited(tdbb, old_number, new_number);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SWEEP_THREAD
|
2004-03-28 11:10:30 +02:00
|
|
|
static void start_sweeper(thread_db* tdbb, Database* dbb)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* s t a r t _ s w e e p e r
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Start a thread to sweep the database.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
|
2002-09-08 18:33:46 +02:00
|
|
|
if ((dbb->dbb_flags & DBB_sweep_in_progress)
|
|
|
|
|| (dbb->dbb_ast_flags & DBB_shutdown))
|
2001-05-24 16:54:26 +02:00
|
|
|
{
|
2004-03-28 11:10:30 +02:00
|
|
|
return; // false;
|
2001-05-24 16:54:26 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
// fill out the lock block
|
2004-03-18 06:56:06 +01:00
|
|
|
Lock temp_lock;
|
2001-12-24 03:51:06 +01:00
|
|
|
temp_lock.lck_dbb = dbb;
|
|
|
|
temp_lock.lck_type = LCK_sweep;
|
|
|
|
temp_lock.lck_owner_handle = LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_length = sizeof(SLONG);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-10-07 11:15:32 +02:00
|
|
|
if (!LCK_lock(tdbb, &temp_lock, LCK_EX, LCK_NO_WAIT))
|
2001-05-24 16:54:26 +02:00
|
|
|
{
|
2004-03-28 11:10:30 +02:00
|
|
|
return; // false;
|
2001-05-24 16:54:26 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
LCK_release(tdbb, &temp_lock);
|
|
|
|
|
2001-05-24 16:54:26 +02:00
|
|
|
/* allocate space for the string and a null at the end */
|
2007-12-03 16:46:39 +01:00
|
|
|
const char* pszFilename = tdbb->getAttachment()->att_filename.c_str();
|
2001-05-24 16:54:26 +02:00
|
|
|
|
|
|
|
char* database = (char*)gds__alloc(strlen(pszFilename) + 1);
|
|
|
|
|
|
|
|
if (!database)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
ERR_log(0, 0, "cannot start sweep thread, Out of Memory");
|
2004-03-28 11:10:30 +02:00
|
|
|
return; // false;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2001-05-24 16:54:26 +02:00
|
|
|
strcpy(database, pszFilename);
|
2004-06-08 15:41:08 +02:00
|
|
|
if (gds__thread_start(sweep_database, database,
|
|
|
|
THREAD_medium, 0, 0))
|
2001-05-24 16:54:26 +02:00
|
|
|
{
|
|
|
|
gds__free(database);
|
|
|
|
ERR_log(0, 0, "cannot start sweep thread");
|
|
|
|
}
|
|
|
|
|
2004-03-28 11:10:30 +02:00
|
|
|
return; // true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-08 15:41:08 +02:00
|
|
|
static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM database)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* s w e e p _ d a t a b a s e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Sweep database.
|
|
|
|
*
|
|
|
|
**************************************/
|
2005-11-27 21:53:09 +01:00
|
|
|
Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-09 20:19:47 +01:00
|
|
|
dpb.insertByte(isc_dpb_sweep, isc_dpb_records);
|
2007-09-04 10:22:48 +02:00
|
|
|
// sometimes security database is also to be swept
|
2007-02-12 16:52:57 +01:00
|
|
|
dpb.insertByte(isc_dpb_gsec_attach, 1);
|
2008-01-16 13:22:11 +01:00
|
|
|
// use trusted authentication to attach database
|
|
|
|
const char* szAuthenticator = "sweeper";
|
|
|
|
dpb.insertString(isc_dpb_trusted_auth, szAuthenticator, strlen(szAuthenticator));
|
2004-05-03 01:06:37 +02:00
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
ISC_STATUS_ARRAY status_vector = {0};
|
|
|
|
isc_db_handle db_handle = 0;
|
|
|
|
|
2004-06-08 15:41:08 +02:00
|
|
|
isc_attach_database(status_vector, 0, (char*)database,
|
2004-12-09 20:19:47 +01:00
|
|
|
&db_handle, dpb.getBufferLength(),
|
|
|
|
reinterpret_cast<const char*>(dpb.getBuffer()));
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (db_handle)
|
|
|
|
{
|
2003-11-08 17:40:17 +01:00
|
|
|
isc_detach_database(status_vector, &db_handle);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gds__free(database);
|
2004-06-08 15:41:08 +02:00
|
|
|
return 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-03-07 08:28:57 +01:00
|
|
|
static void transaction_options(thread_db* tdbb,
|
2004-01-03 11:59:52 +01:00
|
|
|
jrd_tra* transaction,
|
2003-11-01 11:26:43 +01:00
|
|
|
const UCHAR* tpb, USHORT tpb_length)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* t r a n s a c t i o n _ o p t i o n s
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Process transaction options.
|
|
|
|
*
|
|
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
if (!tpb_length)
|
|
|
|
return;
|
|
|
|
|
2003-11-01 11:26:43 +01:00
|
|
|
const UCHAR* const end = tpb + tpb_length;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
if (*tpb != isc_tpb_version3 && *tpb != isc_tpb_version1)
|
|
|
|
ERR_post(isc_bad_tpb_form, isc_arg_gds, isc_wrotpbver, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
RelationLockTypeMap lockmap;
|
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
TriState wait, lock_timeout;
|
|
|
|
TriState isolation, read_only, rec_version;
|
2008-03-12 08:33:12 +01:00
|
|
|
bool anylock_write = false;
|
2004-09-28 22:25:52 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
++tpb;
|
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
while (tpb < end)
|
|
|
|
{
|
2003-12-31 06:36:12 +01:00
|
|
|
const USHORT op = *tpb++;
|
2007-09-04 10:22:48 +02:00
|
|
|
switch (op)
|
|
|
|
{
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_consistency:
|
2007-09-04 10:22:48 +02:00
|
|
|
if (!isolation.assignOnce(true))
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_multiple_txn_isolation, 0);
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags |= TRA_degree3;
|
|
|
|
transaction->tra_flags &= ~TRA_read_committed;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_concurrency:
|
2007-09-04 10:22:48 +02:00
|
|
|
if (!isolation.assignOnce(true))
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_multiple_txn_isolation, 0);
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags &= ~TRA_degree3;
|
|
|
|
transaction->tra_flags &= ~TRA_read_committed;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_read_committed:
|
2007-09-04 10:22:48 +02:00
|
|
|
if (!isolation.assignOnce(true))
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_multiple_txn_isolation, 0);
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags &= ~TRA_degree3;
|
|
|
|
transaction->tra_flags |= TRA_read_committed;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_shared:
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_before_table, isc_arg_string, "isc_tpb_shared", 0);
|
2008-03-06 10:43:43 +01:00
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_protected:
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_before_table, isc_arg_string, "isc_tpb_protected", 0);
|
2008-03-06 10:43:43 +01:00
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_exclusive:
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_before_table, isc_arg_string, "isc_tpb_exclusive", 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_wait:
|
2007-09-04 10:22:48 +02:00
|
|
|
if (!wait.assignOnce(true))
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
|
|
|
if (!wait.asBool())
|
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_conflicting_options, isc_arg_string, "isc_tpb_wait",
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_nowait", 0);
|
|
|
|
}
|
|
|
|
else
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_multiple_spec, isc_arg_string, "isc_tpb_wait", 0);
|
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_rec_version:
|
2007-09-11 09:47:41 +02:00
|
|
|
if (isolation.isAssigned() && !(transaction->tra_flags & TRA_read_committed))
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_option_without_rc, isc_arg_string, "isc_tpb_rec_version", 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
if (!rec_version.assignOnce(true))
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_multiple_spec, isc_arg_string, "isc_tpb_rec_version", 0);
|
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags |= TRA_rec_version;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_no_rec_version:
|
2007-09-11 09:47:41 +02:00
|
|
|
if (isolation.isAssigned() && !(transaction->tra_flags & TRA_read_committed))
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_option_without_rc, isc_arg_string, "isc_tpb_no_rec_version", 0);
|
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
|
|
|
|
if (!rec_version.assignOnce(false))
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_multiple_spec, isc_arg_string, "isc_tpb_no_rec_version", 0);
|
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags &= ~TRA_rec_version;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_nowait:
|
2008-03-06 10:43:43 +01:00
|
|
|
if (lock_timeout.asBool())
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_conflicting_options, isc_arg_string, "isc_tpb_nowait",
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (!wait.assignOnce(false))
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
|
|
|
if (wait.asBool())
|
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_conflicting_options, isc_arg_string, "isc_tpb_nowait",
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_wait", 0);
|
|
|
|
}
|
|
|
|
else
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_multiple_spec, isc_arg_string, "isc_tpb_nowait", 0);
|
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2004-10-07 11:15:32 +02:00
|
|
|
transaction->tra_lock_timeout = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_read:
|
2007-09-04 10:22:48 +02:00
|
|
|
if (!read_only.assignOnce(true))
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
|
|
|
if (!read_only.asBool())
|
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_conflicting_options, isc_arg_string, "isc_tpb_read",
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_write", 0);
|
|
|
|
}
|
|
|
|
else
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_multiple_spec, isc_arg_string, "isc_tpb_read", 0);
|
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
}
|
2008-03-12 08:33:12 +01:00
|
|
|
|
|
|
|
// Cannot set the whole txn to R/O if we already saw a R/W table reservation.
|
|
|
|
if (anylock_write)
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_readtxn_after_writelock, 0);
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags |= TRA_readonly;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_write:
|
2007-09-04 10:22:48 +02:00
|
|
|
if (!read_only.assignOnce(false))
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
|
|
|
if (read_only.asBool())
|
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_conflicting_options, isc_arg_string, "isc_tpb_write",
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_read", 0);
|
|
|
|
}
|
|
|
|
else
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_multiple_spec, isc_arg_string, "isc_tpb_write", 0);
|
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags &= ~TRA_readonly;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_ignore_limbo:
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags |= TRA_ignore_limbo;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_no_auto_undo:
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags |= TRA_no_auto_undo;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_lock_write:
|
2008-03-12 08:33:12 +01:00
|
|
|
// Cannot set a R/W table reservation if the whole txn is R/O.
|
|
|
|
if (read_only.asBool())
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_writelock_after_readtxn, 0);
|
|
|
|
|
|
|
|
anylock_write = true;
|
|
|
|
// fall into
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_lock_read:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2008-03-06 10:43:43 +01:00
|
|
|
const char* option_name = (op == isc_tpb_lock_read) ?
|
|
|
|
"isc_tpb_lock_read" : "isc_tpb_lock_write";
|
2008-03-12 08:33:12 +01:00
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
// Do we have space for the identifier length?
|
|
|
|
if (tpb >= end)
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_missing_tlen, isc_arg_string, option_name, 0);
|
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
|
|
|
|
const USHORT len = *tpb++;
|
|
|
|
if (len > MAX_SQL_IDENTIFIER_LEN)
|
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_long_tlen, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, option_name, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
|
|
|
|
if (!len)
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_null_tlen, isc_arg_string, option_name, 0);
|
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
// Does the identifier length surpasses the remaining of the TPB?
|
2008-03-06 10:43:43 +01:00
|
|
|
if (tpb >= end)
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_missing_tname, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, option_name, 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (end - tpb < len)
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_corrup_tlen, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, option_name, 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2007-09-10 02:26:45 +02:00
|
|
|
const Firebird::MetaName name(reinterpret_cast<const char*>(tpb), len);
|
|
|
|
tpb += len;
|
2005-05-12 20:28:04 +02:00
|
|
|
jrd_rel* relation = MET_lookup_relation(tdbb, name);
|
2008-03-07 08:28:57 +01:00
|
|
|
if (!relation)
|
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_reserv_relnotfound, isc_arg_string, ERR_cstring(name),
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, option_name, 0);
|
2007-08-25 10:06:09 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2007-08-25 10:06:09 +02:00
|
|
|
/* force a scan to read view information */
|
|
|
|
MET_scan_relation(tdbb, relation);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-06 10:43:43 +01:00
|
|
|
UCHAR lock_type = (op == isc_tpb_lock_read) ? LCK_none : LCK_SW;
|
2007-09-04 10:22:48 +02:00
|
|
|
if (tpb < end)
|
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
switch (*tpb)
|
2007-08-25 10:06:09 +02:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
case isc_tpb_shared:
|
|
|
|
++tpb;
|
|
|
|
break;
|
|
|
|
case isc_tpb_protected:
|
|
|
|
case isc_tpb_exclusive:
|
|
|
|
++tpb;
|
2007-08-25 10:06:09 +02:00
|
|
|
lock_type = (lock_type == LCK_SW) ? LCK_EX : LCK_PR;
|
2008-03-12 08:33:12 +01:00
|
|
|
break;
|
|
|
|
// We'll assume table reservation doesn't make the concurrency type mandatory.
|
|
|
|
//default:
|
|
|
|
// ERR_post(0);
|
2007-08-25 10:06:09 +02:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
expand_view_lock(tdbb, transaction, relation, lock_type, option_name, lockmap, 0);
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2007-08-25 10:06:09 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_verb_time:
|
|
|
|
case isc_tpb_commit_time:
|
2003-12-31 06:36:12 +01:00
|
|
|
{
|
2008-03-07 08:28:57 +01:00
|
|
|
const char* option_name = (op == isc_tpb_verb_time) ?
|
|
|
|
"isc_tpb_verb_time" : "isc_tpb_commit_time";
|
2007-09-04 10:22:48 +02:00
|
|
|
// Harmless for now even if formally invalid.
|
2008-03-06 10:43:43 +01:00
|
|
|
if (tpb >= end)
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_missing_len, isc_arg_string, option_name, 0);
|
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2007-09-10 02:26:45 +02:00
|
|
|
const USHORT len = *tpb++;
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (tpb >= end && len > 0)
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_missing_value, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, option_name, 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (end - tpb < len)
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_corrupt_len, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, option_name, 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2007-09-10 02:26:45 +02:00
|
|
|
tpb += len;
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_autocommit:
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags |= TRA_autocommit;
|
|
|
|
break;
|
|
|
|
|
2003-11-08 17:40:17 +01:00
|
|
|
case isc_tpb_restart_requests:
|
2001-05-23 15:26:42 +02:00
|
|
|
transaction->tra_flags |= TRA_restart_requests;
|
|
|
|
break;
|
|
|
|
|
2004-09-28 22:25:52 +02:00
|
|
|
case isc_tpb_lock_timeout:
|
|
|
|
{
|
2008-03-06 10:43:43 +01:00
|
|
|
if (wait.isAssigned() && !wait.asBool())
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_conflicting_options, isc_arg_string, "isc_tpb_lock_timeout",
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_nowait", 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (!lock_timeout.assignOnce(true))
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_multiple_spec, isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
// Do we have space for the identifier length?
|
|
|
|
if (tpb >= end)
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_missing_len, isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2007-09-10 02:26:45 +02:00
|
|
|
const USHORT len = *tpb++;
|
2008-03-08 22:20:26 +01:00
|
|
|
|
2007-09-04 10:22:48 +02:00
|
|
|
// Does the encoded number's length surpasses the remaining of the TPB?
|
2008-03-06 10:43:43 +01:00
|
|
|
if (tpb >= end)
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_missing_value, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (end - tpb < len)
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_corrupt_len, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (len > sizeof(transaction->tra_lock_timeout))
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_overflow_len, isc_arg_number, len,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!len)
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_null_len, isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
|
2007-09-10 02:26:45 +02:00
|
|
|
transaction->tra_lock_timeout = gds__vax_integer(tpb, len);
|
2008-03-06 10:43:43 +01:00
|
|
|
|
|
|
|
if (transaction->tra_lock_timeout <= 0)
|
2008-03-07 08:28:57 +01:00
|
|
|
{
|
2008-03-12 08:33:12 +01:00
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds, isc_tpb_invalid_value,
|
2008-03-07 08:28:57 +01:00
|
|
|
isc_arg_number, transaction->tra_lock_timeout,
|
|
|
|
isc_arg_string, "isc_tpb_lock_timeout", 0);
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2007-09-10 02:26:45 +02:00
|
|
|
tpb += len;
|
2004-09-28 22:25:52 +02:00
|
|
|
}
|
2007-09-04 10:22:48 +02:00
|
|
|
break;
|
2004-09-28 22:25:52 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2003-11-08 17:40:17 +01:00
|
|
|
ERR_post(isc_bad_tpb_form, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
2008-03-06 10:43:43 +01:00
|
|
|
|
2008-03-12 08:33:12 +01:00
|
|
|
if (rec_version.isAssigned() && !(transaction->tra_flags & TRA_read_committed))
|
2007-09-11 09:47:41 +02:00
|
|
|
{
|
2008-03-07 08:28:57 +01:00
|
|
|
if (rec_version.asBool())
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_option_without_rc, isc_arg_string, "isc_tpb_rec_version", 0);
|
|
|
|
}
|
2008-03-07 08:28:57 +01:00
|
|
|
else
|
2008-03-12 08:33:12 +01:00
|
|
|
{
|
|
|
|
ERR_post(isc_bad_tpb_content, isc_arg_gds,
|
|
|
|
isc_tpb_option_without_rc, isc_arg_string, "isc_tpb_no_rec_version", 0);
|
|
|
|
}
|
2007-09-11 09:47:41 +02:00
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
/* If there aren't any relation locks to seize, we're done. */
|
|
|
|
|
2005-12-02 08:35:34 +01:00
|
|
|
vec<Lock*>* vector = transaction->tra_relation_locks;
|
2003-11-01 11:26:43 +01:00
|
|
|
if (!vector)
|
2001-05-23 15:26:42 +02:00
|
|
|
return;
|
|
|
|
|
2001-07-10 19:35:13 +02:00
|
|
|
/* Try to seize all relation locks.
|
2001-05-23 15:26:42 +02:00
|
|
|
If any can't be seized, release all and try again. */
|
|
|
|
|
2003-04-07 21:16:57 +02:00
|
|
|
for (ULONG id = 0; id < vector->count(); id++) {
|
2005-12-02 08:35:34 +01:00
|
|
|
Lock* lock = (*vector)[id];
|
2003-12-31 06:36:12 +01:00
|
|
|
if (!lock)
|
2001-05-23 15:26:42 +02:00
|
|
|
continue;
|
2003-12-31 06:36:12 +01:00
|
|
|
USHORT level = lock->lck_logical;
|
2008-01-26 14:17:19 +01:00
|
|
|
if ( (level == LCK_none) ||
|
|
|
|
LCK_lock(tdbb, lock, level, transaction->getLockWait()))
|
2003-11-01 11:26:43 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2006-10-10 08:41:36 +02:00
|
|
|
for (ULONG l = 0; l < id; l++) {
|
2005-12-02 08:35:34 +01:00
|
|
|
if ( (lock = (*vector)[l]) ) {
|
2001-05-23 15:26:42 +02:00
|
|
|
level = lock->lck_logical;
|
|
|
|
LCK_release(tdbb, lock);
|
|
|
|
lock->lck_logical = level;
|
|
|
|
}
|
2003-12-31 06:36:12 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
id = 0;
|
2004-10-07 11:15:32 +02:00
|
|
|
ERR_punt();
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
static jrd_tra* transaction_start(thread_db* tdbb, jrd_tra* temp)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
2008-01-16 13:22:11 +01:00
|
|
|
* t r a n s a c t i o n _ s t a r t
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
2008-01-16 13:22:11 +01:00
|
|
|
* Start a transaction.
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************/
|
2008-01-16 13:22:11 +01:00
|
|
|
SET_TDBB(tdbb);
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
WIN window(DB_PAGE_SPACE, -1);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-19 17:19:56 +01:00
|
|
|
Lock* lock = create_transaction_lock(tdbb, temp);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
/* Read header page and allocate transaction number. Since
|
|
|
|
the transaction inventory page was initialized to zero, it
|
|
|
|
transaction is automatically marked active. */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
ULONG oldest, number, active, oldest_active, oldest_snapshot;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
#ifdef SUPERSERVER_V2
|
|
|
|
number = bump_transaction_id(tdbb, &window);
|
|
|
|
oldest = dbb->dbb_oldest_transaction;
|
|
|
|
active = MAX(dbb->dbb_oldest_active, dbb->dbb_oldest_transaction);
|
|
|
|
oldest_active = dbb->dbb_oldest_active;
|
|
|
|
oldest_snapshot = dbb->dbb_oldest_snapshot;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
#else /* SUPERSERVER_V2 */
|
|
|
|
if (dbb->dbb_flags & DBB_read_only) {
|
2008-02-14 12:52:59 +01:00
|
|
|
number = dbb->dbb_next_transaction + fb_utils::genReadOnlyId();
|
2008-01-16 13:22:11 +01:00
|
|
|
oldest = dbb->dbb_oldest_transaction;
|
|
|
|
oldest_active = dbb->dbb_oldest_active;
|
|
|
|
oldest_snapshot = dbb->dbb_oldest_snapshot;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const header_page* header = bump_transaction_id(tdbb, &window);
|
|
|
|
number = header->hdr_next_transaction;
|
|
|
|
oldest = header->hdr_oldest_transaction;
|
|
|
|
oldest_active = header->hdr_oldest_active;
|
|
|
|
oldest_snapshot = header->hdr_oldest_snapshot;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-03-06 10:43:43 +01:00
|
|
|
// oldest (OIT) > oldest_active (OAT) if OIT was advanced by sweep
|
2008-01-16 13:22:11 +01:00
|
|
|
// and no transactions was started after the sweep starts
|
|
|
|
active = MAX(oldest_active, oldest);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
#endif /* SUPERSERVER_V2 */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
/* Allocate pool and transactions block. Since, by policy,
|
|
|
|
all transactions older than the oldest are either committed
|
|
|
|
or cleaned up, they can be all considered as committed. To
|
|
|
|
make everything simpler, round down the oldest to a multiple
|
|
|
|
of four, which puts the transaction on a byte boundary. */
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-01-16 13:22:11 +01:00
|
|
|
ULONG base = oldest & ~TRA_MASK;
|
|
|
|
|
|
|
|
jrd_tra* trans;
|
|
|
|
if (temp->tra_flags & TRA_read_committed)
|
|
|
|
trans = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
|
|
|
|
else {
|
|
|
|
trans = FB_NEW_RPT(*tdbb->getDefaultPool(), (number - base + TRA_MASK) / 4) jrd_tra(tdbb->getDefaultPool());
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(trans->tra_pool == temp->tra_pool);
|
|
|
|
trans->tra_relation_locks = temp->tra_relation_locks;
|
|
|
|
trans->tra_lock_timeout = temp->tra_lock_timeout;
|
|
|
|
trans->tra_flags = temp->tra_flags;
|
|
|
|
trans->tra_number = number;
|
|
|
|
trans->tra_top = number;
|
|
|
|
trans->tra_oldest = oldest;
|
|
|
|
trans->tra_oldest_active = active;
|
|
|
|
delete temp;
|
|
|
|
|
|
|
|
trans->tra_lock = lock;
|
|
|
|
lock->lck_key.lck_long = number;
|
|
|
|
|
|
|
|
// Put the TID of the oldest active transaction (from the header page)
|
2008-03-06 10:43:43 +01:00
|
|
|
// in the new transaction's lock.
|
|
|
|
// hvlad: it is important to put transaction number for read-committed
|
|
|
|
// transaction instead of oldest active to correctly calculate new oldest
|
|
|
|
// active value (look at call to LCK_query_data below which will take into
|
2008-01-16 13:22:11 +01:00
|
|
|
// account this new lock too)
|
|
|
|
|
|
|
|
lock->lck_data = (trans->tra_flags & TRA_read_committed) ? number : active;
|
|
|
|
lock->lck_object = trans;
|
|
|
|
|
2008-01-26 14:17:19 +01:00
|
|
|
if (!LCK_lock(tdbb, lock, LCK_write, LCK_WAIT)) {
|
2008-01-16 13:22:11 +01:00
|
|
|
#ifndef SUPERSERVER_V2
|
|
|
|
if (!(dbb->dbb_flags & DBB_read_only))
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
#endif
|
|
|
|
delete trans;
|
|
|
|
ERR_post(isc_lock_conflict, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Link the transaction to the attachment block before releasing
|
|
|
|
header page for handling signals. */
|
|
|
|
|
|
|
|
link_transaction(tdbb, trans);
|
|
|
|
|
|
|
|
#ifndef SUPERSERVER_V2
|
|
|
|
if (!(dbb->dbb_flags & DBB_read_only))
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (dbb->dbb_flags & DBB_read_only) {
|
|
|
|
/* Set transaction flags to TRA_precommitted, TRA_readonly */
|
|
|
|
trans->tra_flags |= (TRA_readonly | TRA_precommitted);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next, take a snapshot of all transactions between the oldest interesting
|
|
|
|
transaction and the current. Don't bother to get a snapshot for
|
|
|
|
read-committed transactions; they use the snapshot off the dbb block
|
|
|
|
since they need to know what is currently committed. */
|
|
|
|
|
|
|
|
if (trans->tra_flags & TRA_read_committed)
|
|
|
|
TPC_initialize_tpc(tdbb, number);
|
|
|
|
else
|
|
|
|
TRA_get_inventory(tdbb, trans->tra_transactions, base, number);
|
|
|
|
|
|
|
|
/* Next task is to find the oldest active transaction on the system. This
|
|
|
|
is needed for garbage collection. Things are made ever so slightly
|
|
|
|
more complicated by the fact that existing transaction may have oldest
|
|
|
|
actives older than they are. */
|
|
|
|
|
|
|
|
Lock temp_lock;
|
|
|
|
temp_lock.lck_dbb = dbb;
|
|
|
|
temp_lock.lck_object = trans;
|
|
|
|
temp_lock.lck_type = LCK_tra;
|
|
|
|
temp_lock.lck_owner_handle =
|
|
|
|
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
|
|
|
|
temp_lock.lck_parent = dbb->dbb_lock;
|
|
|
|
temp_lock.lck_length = sizeof(SLONG);
|
|
|
|
|
|
|
|
trans->tra_oldest_active = number;
|
|
|
|
base = oldest & ~TRA_MASK;
|
|
|
|
oldest_active = number;
|
|
|
|
bool cleanup = !(number % TRA_ACTIVE_CLEANUP);
|
|
|
|
USHORT oldest_state;
|
|
|
|
|
|
|
|
for (; active < number; active++) {
|
|
|
|
if (trans->tra_flags & TRA_read_committed)
|
|
|
|
oldest_state = TPC_cache_state(tdbb, active);
|
|
|
|
else {
|
|
|
|
const ULONG byte = TRANS_OFFSET(active - base);
|
|
|
|
const USHORT shift = TRANS_SHIFT(active);
|
|
|
|
oldest_state =
|
|
|
|
(trans->tra_transactions[byte] >> shift) & TRA_MASK;
|
|
|
|
}
|
|
|
|
if (oldest_state == tra_active) {
|
|
|
|
temp_lock.lck_key.lck_long = active;
|
2008-01-26 14:17:19 +01:00
|
|
|
SLONG data = LCK_read_data(tdbb, &temp_lock);
|
2008-01-16 13:22:11 +01:00
|
|
|
if (!data) {
|
|
|
|
if (cleanup) {
|
|
|
|
if (TRA_wait(tdbb, trans, active, jrd_tra::tra_no_wait) == tra_committed)
|
|
|
|
cleanup = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = active;
|
|
|
|
}
|
|
|
|
|
|
|
|
oldest_active = MIN(oldest_active, active);
|
|
|
|
|
|
|
|
/* Find the oldest record version that cannot be garbage collected yet
|
|
|
|
by taking the minimum of all all versions needed by all active
|
|
|
|
transactions. */
|
|
|
|
|
|
|
|
if (data < trans->tra_oldest_active)
|
|
|
|
trans->tra_oldest_active = data;
|
|
|
|
|
|
|
|
/* If the lock data for any active transaction matches a previously
|
|
|
|
computed value then there is no need to continue. There can't be
|
|
|
|
an older lock data in the remaining active transactions. */
|
|
|
|
|
|
|
|
if (trans->tra_oldest_active == (SLONG) oldest_snapshot)
|
|
|
|
break;
|
|
|
|
#ifndef VMS
|
|
|
|
/* Query the minimum lock data for all active transaction locks.
|
|
|
|
This will be the oldest active snapshot used for regulating
|
|
|
|
garbage collection. */
|
|
|
|
|
2008-01-26 14:17:19 +01:00
|
|
|
data = LCK_query_data(tdbb, dbb->dbb_lock, LCK_tra, LCK_MIN);
|
2008-01-16 13:22:11 +01:00
|
|
|
if (data && data < trans->tra_oldest_active)
|
|
|
|
trans->tra_oldest_active = data;
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
#endif
|
2008-01-16 13:22:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put the TID of the oldest active transaction (just calculated)
|
2008-03-06 10:43:43 +01:00
|
|
|
// in the new transaction's lock.
|
|
|
|
// hvlad: for read-committed transaction put tra_number to prevent
|
|
|
|
// unnecessary blocking of garbage collection by read-committed
|
|
|
|
// transactions
|
2008-01-16 13:22:11 +01:00
|
|
|
|
2008-03-06 10:43:43 +01:00
|
|
|
const ULONG lck_data =
|
2008-01-16 13:22:11 +01:00
|
|
|
(trans->tra_flags & TRA_read_committed) ? number : oldest_active;
|
|
|
|
|
|
|
|
if (lock->lck_data != (SLONG) lck_data)
|
2008-01-26 14:17:19 +01:00
|
|
|
LCK_write_data(tdbb, lock, lck_data);
|
2008-01-16 13:22:11 +01:00
|
|
|
|
|
|
|
/* Scan commit retaining transactions which have started after us but which
|
|
|
|
want to preserve an oldest active from an already committed transaction.
|
|
|
|
If a previously computed oldest snapshot was matched then there's no
|
|
|
|
need to worry about commit retaining transactions. */
|
|
|
|
|
|
|
|
#ifdef VMS
|
|
|
|
if (trans->tra_oldest_active != oldest_snapshot)
|
|
|
|
compute_oldest_retaining(tdbb, trans, false);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Finally, scan transactions looking for the oldest interesting transaction -- the oldest
|
|
|
|
non-commited transaction. This will not be updated immediately, but saved until the
|
|
|
|
next update access to the header page */
|
|
|
|
|
|
|
|
oldest_state = tra_committed;
|
|
|
|
|
|
|
|
for (oldest = trans->tra_oldest; oldest < number; oldest++) {
|
|
|
|
if (trans->tra_flags & TRA_read_committed)
|
|
|
|
oldest_state = TPC_cache_state(tdbb, oldest);
|
|
|
|
else {
|
|
|
|
const ULONG byte = TRANS_OFFSET(oldest - base);
|
|
|
|
const USHORT shift = TRANS_SHIFT(oldest);
|
|
|
|
oldest_state =
|
|
|
|
(trans->tra_transactions[byte] >> shift) & TRA_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldest_state != tra_committed && oldest_state != tra_precommitted)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--oldest > (ULONG) dbb->dbb_oldest_transaction)
|
|
|
|
dbb->dbb_oldest_transaction = oldest;
|
|
|
|
|
|
|
|
if (oldest_active > (ULONG) dbb->dbb_oldest_active)
|
|
|
|
dbb->dbb_oldest_active = oldest_active;
|
|
|
|
|
|
|
|
if (trans->tra_oldest_active > dbb->dbb_oldest_snapshot) {
|
|
|
|
dbb->dbb_oldest_snapshot = trans->tra_oldest_active;
|
|
|
|
|
|
|
|
#if defined(GARBAGE_THREAD)
|
|
|
|
if (!(dbb->dbb_flags & DBB_gc_active) &&
|
2008-03-08 22:20:26 +01:00
|
|
|
(dbb->dbb_flags & DBB_gc_background))
|
2008-01-16 13:22:11 +01:00
|
|
|
{
|
|
|
|
dbb->dbb_flags |= DBB_gc_pending;
|
|
|
|
ISC_event_post(dbb->dbb_gc_event);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the transaction block is getting out of hand, force a sweep */
|
|
|
|
|
|
|
|
if (dbb->dbb_sweep_interval &&
|
|
|
|
!(tdbb->getAttachment()->att_flags & ATT_no_cleanup) &&
|
|
|
|
(trans->tra_oldest_active - trans->tra_oldest >
|
|
|
|
dbb->dbb_sweep_interval) && oldest_state != tra_limbo)
|
|
|
|
{
|
|
|
|
#ifdef SWEEP_THREAD
|
|
|
|
// Why nobody checks the result? Changed the function to return nothing.
|
|
|
|
start_sweeper(tdbb, dbb);
|
|
|
|
#else
|
|
|
|
// force a sweep
|
|
|
|
TRA_sweep(tdbb, trans);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check in with external file system */
|
|
|
|
|
|
|
|
EXT_trans_start(trans);
|
|
|
|
|
|
|
|
/* Start a 'transaction-level' savepoint, unless this is the
|
|
|
|
system transaction, or unless the transactions doesn't want
|
|
|
|
a savepoint to be started. This savepoint will be used to
|
|
|
|
undo the transaction if it rolls back. */
|
|
|
|
|
|
|
|
if ((trans != dbb->dbb_sys_trans) &&
|
|
|
|
!(trans->tra_flags & TRA_no_auto_undo))
|
|
|
|
{
|
|
|
|
VIO_start_save_point(tdbb, trans);
|
|
|
|
trans->tra_save_point->sav_flags |= SAV_trans_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the cancellation lock */
|
|
|
|
|
|
|
|
lock = FB_NEW_RPT(*trans->tra_pool, sizeof(SLONG)) Lock();
|
|
|
|
trans->tra_cancel_lock = lock;
|
|
|
|
lock->lck_type = LCK_cancel;
|
|
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
|
|
lock->lck_length = sizeof(SLONG);
|
|
|
|
lock->lck_key.lck_long = trans->tra_number;
|
|
|
|
lock->lck_dbb = dbb;
|
|
|
|
lock->lck_ast = blocking_ast_transaction;
|
|
|
|
lock->lck_object = trans;
|
|
|
|
|
|
|
|
/* if the user asked us to restart all requests in this attachment,
|
|
|
|
do so now using the new transaction */
|
|
|
|
|
|
|
|
if (trans->tra_flags & TRA_restart_requests)
|
|
|
|
restart_requests(tdbb, trans);
|
|
|
|
|
|
|
|
/* If the transaction is read-only and read committed, it can be
|
|
|
|
precommitted because it can't modify any records and doesn't
|
|
|
|
need a snapshot preserved. This transaction type can run
|
|
|
|
forever without impacting garbage collection or causing
|
|
|
|
transaction bitmap growth. */
|
|
|
|
|
|
|
|
if (trans->tra_flags & TRA_readonly &&
|
|
|
|
trans->tra_flags & TRA_read_committed)
|
|
|
|
{
|
|
|
|
TRA_set_state(tdbb, trans, trans->tra_number, tra_committed);
|
|
|
|
LCK_release(tdbb, trans->tra_lock);
|
|
|
|
delete trans->tra_lock;
|
|
|
|
trans->tra_lock = NULL;
|
|
|
|
trans->tra_flags |= TRA_precommitted;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trans->tra_flags & TRA_precommitted)
|
|
|
|
TRA_precommited(tdbb, 0, trans->tra_number);
|
|
|
|
|
|
|
|
return trans;
|
|
|
|
}
|