mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 07:23:03 +01:00
Feature CORE-4707 : Implement ability to validate tables and indices online
This commit is contained in:
parent
fc4d8cff79
commit
8e58f69550
@ -355,6 +355,19 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const
|
|||||||
return IntSpb;
|
return IntSpb;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case isc_action_svc_validate:
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case isc_spb_val_tab_incl:
|
||||||
|
case isc_spb_val_tab_excl:
|
||||||
|
case isc_spb_val_idx_incl:
|
||||||
|
case isc_spb_val_idx_excl:
|
||||||
|
case isc_spb_dbname:
|
||||||
|
return StringSpb;
|
||||||
|
case isc_spb_val_lock_timeout:
|
||||||
|
return IntSpb;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
invalid_structure("wrong spb state");
|
invalid_structure("wrong spb state");
|
||||||
break;
|
break;
|
||||||
|
@ -1028,4 +1028,65 @@ void logAndDie(const char* text)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char switch_char = '-';
|
||||||
|
|
||||||
|
bool switchMatch(const Firebird::string& sw, const char* target)
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* s w i t c h M a t c h
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Functional description
|
||||||
|
* Returns true if switch matches target
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
size_t n = strlen(target);
|
||||||
|
if (n < sw.length())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
n = sw.length();
|
||||||
|
return memcmp(sw.c_str(), target, n) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
in_sw_tab_t* findSwitch(in_sw_tab_t* table, Firebird::string sw)
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* f i n d S w i t c h
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Functional description
|
||||||
|
* Returns pointer to in_sw_tab entry for current switch
|
||||||
|
* If not a switch, returns 0.
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
if (sw.isEmpty())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (sw[0] != switch_char)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sw.erase(0, 1);
|
||||||
|
sw.upper();
|
||||||
|
|
||||||
|
for (in_sw_tab_t* in_sw_tab = table; in_sw_tab->in_sw_name; in_sw_tab++)
|
||||||
|
{
|
||||||
|
if ((sw.length() >= in_sw_tab->in_sw_min_length) &&
|
||||||
|
switchMatch(sw, in_sw_tab->in_sw_name))
|
||||||
|
{
|
||||||
|
return in_sw_tab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fb_utils
|
} // namespace fb_utils
|
||||||
|
@ -139,6 +139,9 @@ namespace fb_utils
|
|||||||
|
|
||||||
void logAndDie(const char* text);
|
void logAndDie(const char* text);
|
||||||
|
|
||||||
|
// command-line helpers
|
||||||
|
bool switchMatch(const Firebird::string& sw, const char* target);
|
||||||
|
in_sw_tab_t* findSwitch(in_sw_tab_t* table, Firebird::string sw);
|
||||||
} // namespace fb_utils
|
} // namespace fb_utils
|
||||||
|
|
||||||
#endif // INCLUDE_UTILS_PROTO_H
|
#endif // INCLUDE_UTILS_PROTO_H
|
||||||
|
@ -293,7 +293,8 @@
|
|||||||
#define isc_action_svc_set_mapping 27 // Set auto admins mapping in security database
|
#define isc_action_svc_set_mapping 27 // Set auto admins mapping in security database
|
||||||
#define isc_action_svc_drop_mapping 28 // Drop auto admins mapping in security database
|
#define isc_action_svc_drop_mapping 28 // Drop auto admins mapping in security database
|
||||||
#define isc_action_svc_display_user_adm 29 // Displays user(s) from security database with admin info
|
#define isc_action_svc_display_user_adm 29 // Displays user(s) from security database with admin info
|
||||||
#define isc_action_svc_last 30 // keep it last !
|
#define isc_action_svc_validate 30 // Starts database online validation
|
||||||
|
#define isc_action_svc_last 31 // keep it last !
|
||||||
|
|
||||||
/*****************************
|
/*****************************
|
||||||
* Service information items *
|
* Service information items *
|
||||||
@ -463,6 +464,16 @@
|
|||||||
#define isc_spb_res_create 0x2000
|
#define isc_spb_res_create 0x2000
|
||||||
#define isc_spb_res_use_all_space 0x4000
|
#define isc_spb_res_use_all_space 0x4000
|
||||||
|
|
||||||
|
/*****************************************
|
||||||
|
* Parameters for isc_action_svc_validate *
|
||||||
|
*****************************************/
|
||||||
|
|
||||||
|
#define isc_spb_val_tab_incl 1 // include filter based on regular expression
|
||||||
|
#define isc_spb_val_tab_excl 2 // exclude filter based on regular expression
|
||||||
|
#define isc_spb_val_idx_incl 3 // regexp of indices to validate
|
||||||
|
#define isc_spb_val_idx_excl 4 // regexp of indices to NOT validate
|
||||||
|
#define isc_spb_val_lock_timeout 5 // how long to wait for table lock
|
||||||
|
|
||||||
/******************************************
|
/******************************************
|
||||||
* Parameters for isc_spb_res_access_mode *
|
* Parameters for isc_spb_res_access_mode *
|
||||||
******************************************/
|
******************************************/
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "../jrd/btr_proto.h"
|
#include "../jrd/btr_proto.h"
|
||||||
#include "../jrd/dpm_proto.h"
|
#include "../jrd/dpm_proto.h"
|
||||||
#include "../jrd/idx_proto.h"
|
#include "../jrd/idx_proto.h"
|
||||||
|
#include "../jrd/lck_proto.h"
|
||||||
#include "../jrd/met_proto.h"
|
#include "../jrd/met_proto.h"
|
||||||
#include "../jrd/pag_proto.h"
|
#include "../jrd/pag_proto.h"
|
||||||
#include "../jrd/vio_debug.h"
|
#include "../jrd/vio_debug.h"
|
||||||
@ -382,6 +383,229 @@ bool jrd_rel::hasTriggers() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Lock* jrd_rel::createLock(thread_db* tdbb, MemoryPool* pool, jrd_rel* relation, lck_t lckType, bool noAst)
|
||||||
|
{
|
||||||
|
Database *dbb = tdbb->getDatabase();
|
||||||
|
if (!pool)
|
||||||
|
pool = dbb->dbb_permanent;
|
||||||
|
|
||||||
|
const SSHORT relLockLen = relation->getRelLockKeyLength();
|
||||||
|
|
||||||
|
Lock* lock = FB_NEW_RPT(*pool, relLockLen) Lock();
|
||||||
|
lock->lck_dbb = dbb;
|
||||||
|
|
||||||
|
lock->lck_length = relLockLen;
|
||||||
|
relation->getRelLockKey(tdbb, &lock->lck_key.lck_string[0]);
|
||||||
|
|
||||||
|
lock->lck_type = lckType;
|
||||||
|
switch (lckType)
|
||||||
|
{
|
||||||
|
case LCK_relation:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LCK_rel_gc:
|
||||||
|
lock->lck_ast = noAst ? NULL : blocking_ast_gcLock;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fb_assert(false);
|
||||||
|
}
|
||||||
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
||||||
|
lock->lck_parent = dbb->dbb_lock;
|
||||||
|
lock->lck_object = relation;
|
||||||
|
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jrd_rel::acquireGCLock(thread_db* tdbb, int wait)
|
||||||
|
{
|
||||||
|
fb_assert(rel_flags & REL_gc_lockneed);
|
||||||
|
if (!(rel_flags & REL_gc_lockneed))
|
||||||
|
{
|
||||||
|
fb_assert(rel_gc_lock->lck_id);
|
||||||
|
fb_assert(rel_gc_lock->lck_physical == (rel_flags & REL_gc_disabled ? LCK_SR : LCK_SW));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rel_gc_lock)
|
||||||
|
rel_gc_lock = createLock(tdbb, NULL, this, LCK_rel_gc, false);
|
||||||
|
|
||||||
|
fb_assert(!rel_gc_lock->lck_id);
|
||||||
|
fb_assert(!(rel_flags & REL_gc_blocking));
|
||||||
|
|
||||||
|
ThreadStatusGuard temp_status(tdbb);
|
||||||
|
|
||||||
|
const USHORT level = (rel_flags & REL_gc_disabled) ? LCK_SR : LCK_SW;
|
||||||
|
bool ret = LCK_lock(tdbb, rel_gc_lock, level, wait);
|
||||||
|
if (!ret && (level == LCK_SW))
|
||||||
|
{
|
||||||
|
rel_flags |= REL_gc_disabled;
|
||||||
|
ret = LCK_lock(tdbb, rel_gc_lock, LCK_SR, wait);
|
||||||
|
if (!ret)
|
||||||
|
rel_flags &= ~REL_gc_disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
rel_flags &= ~REL_gc_lockneed;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jrd_rel::downgradeGCLock(thread_db* tdbb)
|
||||||
|
{
|
||||||
|
if (!rel_sweep_count && (rel_flags & REL_gc_blocking))
|
||||||
|
{
|
||||||
|
fb_assert(!(rel_flags & REL_gc_lockneed));
|
||||||
|
fb_assert(rel_gc_lock->lck_id);
|
||||||
|
fb_assert(rel_gc_lock->lck_physical == LCK_SW);
|
||||||
|
|
||||||
|
rel_flags &= ~REL_gc_blocking;
|
||||||
|
rel_flags |= REL_gc_disabled;
|
||||||
|
|
||||||
|
LCK_downgrade(tdbb, rel_gc_lock);
|
||||||
|
|
||||||
|
if (rel_gc_lock->lck_physical != LCK_SR)
|
||||||
|
{
|
||||||
|
rel_flags &= ~REL_gc_disabled;
|
||||||
|
if (rel_gc_lock->lck_physical < LCK_SR)
|
||||||
|
rel_flags |= REL_gc_lockneed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int jrd_rel::blocking_ast_gcLock(void* ast_object)
|
||||||
|
{
|
||||||
|
/****
|
||||||
|
SR - gc forbidden, awaiting moment to re-establish SW lock
|
||||||
|
SW - gc allowed, usual state
|
||||||
|
PW - gc allowed to the one connection only
|
||||||
|
****/
|
||||||
|
jrd_rel* relation = static_cast<jrd_rel*>(ast_object);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Lock* lock = relation->rel_gc_lock;
|
||||||
|
Database* dbb = lock->lck_dbb;
|
||||||
|
|
||||||
|
AstContextHolder tdbb(dbb, lock->lck_attachment);
|
||||||
|
|
||||||
|
fb_assert(!(relation->rel_flags & REL_gc_lockneed));
|
||||||
|
if (relation->rel_flags & REL_gc_lockneed) // work already done syncronously ?
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
relation->rel_flags |= REL_gc_blocking;
|
||||||
|
if (relation->rel_sweep_count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (relation->rel_flags & REL_gc_disabled)
|
||||||
|
{
|
||||||
|
// someone is acquired EX lock
|
||||||
|
|
||||||
|
fb_assert(lock->lck_id);
|
||||||
|
fb_assert(lock->lck_physical == LCK_SR);
|
||||||
|
|
||||||
|
LCK_release(tdbb, lock);
|
||||||
|
relation->rel_flags &= ~(REL_gc_disabled | REL_gc_blocking);
|
||||||
|
relation->rel_flags |= REL_gc_lockneed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// someone is acquired PW lock
|
||||||
|
|
||||||
|
fb_assert(lock->lck_id);
|
||||||
|
fb_assert(lock->lck_physical == LCK_SW);
|
||||||
|
|
||||||
|
relation->rel_flags |= REL_gc_disabled;
|
||||||
|
relation->downgradeGCLock(tdbb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const Firebird::Exception&)
|
||||||
|
{} // no-op
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// jrd_rel::GCExclusive
|
||||||
|
|
||||||
|
jrd_rel::GCExclusive::GCExclusive(thread_db* tdbb, jrd_rel* relation) :
|
||||||
|
m_tdbb(tdbb),
|
||||||
|
m_relation(relation),
|
||||||
|
m_lock(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
jrd_rel::GCExclusive::~GCExclusive()
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
delete m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jrd_rel::GCExclusive::acquire(int wait)
|
||||||
|
{
|
||||||
|
// if validation is already running - go out
|
||||||
|
if (m_relation->rel_flags & REL_gc_disabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ThreadStatusGuard temp_status(m_tdbb);
|
||||||
|
|
||||||
|
m_relation->rel_flags |= REL_gc_disabled;
|
||||||
|
|
||||||
|
int sleeps = -wait * 10;
|
||||||
|
while (m_relation->rel_sweep_count)
|
||||||
|
{
|
||||||
|
Database::Checkout cout(m_tdbb->getDatabase());
|
||||||
|
THD_sleep(100);
|
||||||
|
|
||||||
|
if (wait < 0 && --sleeps == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_relation->rel_sweep_count)
|
||||||
|
{
|
||||||
|
m_relation->rel_flags &= ~REL_gc_disabled;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(m_relation->rel_flags & REL_gc_lockneed))
|
||||||
|
{
|
||||||
|
m_relation->rel_flags |= REL_gc_lockneed;
|
||||||
|
LCK_release(m_tdbb, m_relation->rel_gc_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need no AST here
|
||||||
|
if (!m_lock)
|
||||||
|
m_lock = jrd_rel::createLock(m_tdbb, NULL, m_relation, LCK_rel_gc, true);
|
||||||
|
|
||||||
|
const bool ret = LCK_lock(m_tdbb, m_lock, LCK_PW, wait);
|
||||||
|
if (!ret)
|
||||||
|
m_relation->rel_flags &= ~REL_gc_disabled;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jrd_rel::GCExclusive::release()
|
||||||
|
{
|
||||||
|
if (!m_lock || !m_lock->lck_id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fb_assert(m_relation->rel_flags & REL_gc_disabled);
|
||||||
|
|
||||||
|
if (!(m_relation->rel_flags & REL_gc_lockneed))
|
||||||
|
{
|
||||||
|
m_relation->rel_flags |= REL_gc_lockneed;
|
||||||
|
LCK_release(m_tdbb, m_relation->rel_gc_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
LCK_convert(m_tdbb, m_lock, LCK_EX, LCK_WAIT);
|
||||||
|
m_relation->rel_flags &= ~REL_gc_disabled;
|
||||||
|
|
||||||
|
LCK_release(m_tdbb, m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// RelationPages
|
||||||
|
|
||||||
void RelationPages::free(RelationPages*& nextFree)
|
void RelationPages::free(RelationPages*& nextFree)
|
||||||
{
|
{
|
||||||
rel_next_free = nextFree;
|
rel_next_free = nextFree;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#define JRD_RELATION_H
|
#define JRD_RELATION_H
|
||||||
|
|
||||||
#include "../jrd/jrd.h"
|
#include "../jrd/jrd.h"
|
||||||
|
#include "../jrd/lck.h"
|
||||||
#include "../jrd/pag.h"
|
#include "../jrd/pag.h"
|
||||||
#include "../jrd/val.h"
|
#include "../jrd/val.h"
|
||||||
|
|
||||||
@ -202,6 +203,7 @@ public:
|
|||||||
|
|
||||||
Lock* rel_existence_lock; // existence lock, if any
|
Lock* rel_existence_lock; // existence lock, if any
|
||||||
Lock* rel_partners_lock; // partners lock
|
Lock* rel_partners_lock; // partners lock
|
||||||
|
Lock* rel_gc_lock; // garbage collection lock
|
||||||
IndexLock* rel_index_locks; // index existence locks
|
IndexLock* rel_index_locks; // index existence locks
|
||||||
IndexBlock* rel_index_blocks; // index blocks for caching index info
|
IndexBlock* rel_index_blocks; // index blocks for caching index info
|
||||||
trig_vec* rel_pre_erase; // Pre-operation erase trigger
|
trig_vec* rel_pre_erase; // Pre-operation erase trigger
|
||||||
@ -273,11 +275,50 @@ private:
|
|||||||
RelationPages* getPagesInternal(thread_db* tdbb, SLONG tran, bool allocPages);
|
RelationPages* getPagesInternal(thread_db* tdbb, SLONG tran, bool allocPages);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit jrd_rel(MemoryPool& p)
|
explicit jrd_rel(MemoryPool& p);
|
||||||
: rel_name(p), rel_owner_name(p), rel_view_contexts(p), rel_security_name(p)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
bool hasTriggers() const;
|
bool hasTriggers() const;
|
||||||
|
|
||||||
|
static Lock* createLock(thread_db* tdbb, MemoryPool* pool, jrd_rel* relation, lck_t, bool);
|
||||||
|
static int blocking_ast_gcLock(void*);
|
||||||
|
void downgradeGCLock(thread_db* tdbb);
|
||||||
|
bool acquireGCLock(thread_db* tdbb, int wait);
|
||||||
|
|
||||||
|
// This guard is used by regular code to prevent online validation while
|
||||||
|
// dead- or back- versions is removed from disk.
|
||||||
|
class GCShared
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GCShared(thread_db* tdbb, jrd_rel* relation);
|
||||||
|
~GCShared();
|
||||||
|
|
||||||
|
bool gcEnabled() const
|
||||||
|
{
|
||||||
|
return m_gcEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
thread_db* m_tdbb;
|
||||||
|
jrd_rel* m_relation;
|
||||||
|
bool m_gcEnabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This guard is used by online validation to prevent any modifications of
|
||||||
|
// table data while it is checked.
|
||||||
|
class GCExclusive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GCExclusive(thread_db* tdbb, jrd_rel* relation);
|
||||||
|
~GCExclusive();
|
||||||
|
|
||||||
|
bool acquire(int wait);
|
||||||
|
void release();
|
||||||
|
|
||||||
|
private:
|
||||||
|
thread_db* m_tdbb;
|
||||||
|
jrd_rel* m_relation;
|
||||||
|
Lock* m_lock;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// rel_flags
|
// rel_flags
|
||||||
@ -299,8 +340,19 @@ const ULONG REL_temp_tran = 0x2000; // relation is a GTT delete rows
|
|||||||
const ULONG REL_temp_conn = 0x4000; // relation is a GTT preserve rows
|
const ULONG REL_temp_conn = 0x4000; // relation is a GTT preserve rows
|
||||||
const ULONG REL_virtual = 0x8000; // relation is virtual
|
const ULONG REL_virtual = 0x8000; // relation is virtual
|
||||||
const ULONG REL_jrd_view = 0x10000; // relation is VIEW
|
const ULONG REL_jrd_view = 0x10000; // relation is VIEW
|
||||||
|
const ULONG REL_gc_blocking = 0x20000; // request to downgrade\release gc lock
|
||||||
|
const ULONG REL_gc_disabled = 0x40000; // gc is disabled temporary
|
||||||
|
const ULONG REL_gc_lockneed = 0x80000; // gc lock should be acquired
|
||||||
|
|
||||||
|
|
||||||
|
/// class jrd_rel
|
||||||
|
|
||||||
|
inline jrd_rel::jrd_rel(MemoryPool& p)
|
||||||
|
: rel_name(p), rel_owner_name(p), rel_view_contexts(p), rel_security_name(p),
|
||||||
|
rel_flags(REL_gc_lockneed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
inline bool jrd_rel::isSystem() const
|
inline bool jrd_rel::isSystem() const
|
||||||
{
|
{
|
||||||
return rel_flags & REL_system;
|
return rel_flags & REL_system;
|
||||||
@ -329,6 +381,39 @@ inline RelationPages* jrd_rel::getPages(thread_db* tdbb, SLONG tran, bool allocP
|
|||||||
return getPagesInternal(tdbb, tran, allocPages);
|
return getPagesInternal(tdbb, tran, allocPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// class jrd_rel::GCShared
|
||||||
|
|
||||||
|
inline jrd_rel::GCShared::GCShared(thread_db* tdbb, jrd_rel* relation) :
|
||||||
|
m_tdbb(tdbb),
|
||||||
|
m_relation(relation),
|
||||||
|
m_gcEnabled(false)
|
||||||
|
{
|
||||||
|
if (m_relation->rel_flags & (REL_gc_blocking | REL_gc_disabled))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_relation->rel_flags & REL_gc_lockneed)
|
||||||
|
m_relation->acquireGCLock(tdbb, LCK_NO_WAIT);
|
||||||
|
|
||||||
|
if (!(m_relation->rel_flags & (REL_gc_blocking | REL_gc_disabled | REL_gc_lockneed)))
|
||||||
|
{
|
||||||
|
++m_relation->rel_sweep_count;
|
||||||
|
m_gcEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_relation->rel_flags & REL_gc_blocking) && !m_relation->rel_sweep_count)
|
||||||
|
m_relation->downgradeGCLock(m_tdbb);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline jrd_rel::GCShared::~GCShared()
|
||||||
|
{
|
||||||
|
if (m_gcEnabled)
|
||||||
|
--m_relation->rel_sweep_count;
|
||||||
|
|
||||||
|
if ((m_relation->rel_flags & REL_gc_blocking) && !m_relation->rel_sweep_count)
|
||||||
|
m_relation->downgradeGCLock(m_tdbb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Field block, one for each field in a scanned relation
|
// Field block, one for each field in a scanned relation
|
||||||
|
|
||||||
class jrd_fld : public pool_alloc<type_fld>
|
class jrd_fld : public pool_alloc<type_fld>
|
||||||
|
@ -2558,6 +2558,11 @@ void CMP_shutdown_database(thread_db* tdbb)
|
|||||||
LCK_release(tdbb, relation->rel_partners_lock);
|
LCK_release(tdbb, relation->rel_partners_lock);
|
||||||
relation->rel_flags |= REL_check_partners;
|
relation->rel_flags |= REL_check_partners;
|
||||||
}
|
}
|
||||||
|
if (relation->rel_gc_lock)
|
||||||
|
{
|
||||||
|
LCK_release(tdbb, relation->rel_gc_lock);
|
||||||
|
relation->rel_flags |= REL_gc_lockneed;
|
||||||
|
}
|
||||||
for (IndexLock* index = relation->rel_index_locks; index; index = index->idl_next)
|
for (IndexLock* index = relation->rel_index_locks; index; index = index->idl_next)
|
||||||
{
|
{
|
||||||
if (index->idl_lock) {
|
if (index->idl_lock) {
|
||||||
|
@ -231,11 +231,8 @@ public:
|
|||||||
m_save_lock = att->att_wait_lock;
|
m_save_lock = att->att_wait_lock;
|
||||||
|
|
||||||
m_cancel_disabled = (m_tdbb->tdbb_flags & TDBB_wait_cancel_disable);
|
m_cancel_disabled = (m_tdbb->tdbb_flags & TDBB_wait_cancel_disable);
|
||||||
m_tdbb->tdbb_flags |= TDBB_wait_cancel_disable;
|
if (wait == LCK_WAIT)
|
||||||
|
{
|
||||||
if (!wait)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (lock->lck_type)
|
switch (lock->lck_type)
|
||||||
{
|
{
|
||||||
case LCK_tra:
|
case LCK_tra:
|
||||||
@ -245,7 +242,14 @@ public:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
;
|
m_tdbb->tdbb_flags |= TDBB_wait_cancel_disable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_tdbb->tdbb_flags &= ~TDBB_wait_cancel_disable;
|
||||||
|
if (att)
|
||||||
|
att->att_wait_lock = lock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,6 +502,7 @@ SLONG LCK_get_owner_handle(thread_db* tdbb, enum lck_t lock_type)
|
|||||||
case LCK_tt_exist:
|
case LCK_tt_exist:
|
||||||
case LCK_shared_counter:
|
case LCK_shared_counter:
|
||||||
case LCK_sweep:
|
case LCK_sweep:
|
||||||
|
case LCK_rel_gc:
|
||||||
handle = *LCK_OWNER_HANDLE_DBB(tdbb);
|
handle = *LCK_OWNER_HANDLE_DBB(tdbb);
|
||||||
break;
|
break;
|
||||||
case LCK_attachment:
|
case LCK_attachment:
|
||||||
|
@ -56,7 +56,8 @@ enum lck_t {
|
|||||||
LCK_cancel, // Cancellation lock
|
LCK_cancel, // Cancellation lock
|
||||||
LCK_btr_dont_gc, // Prevent removal of b-tree page from index
|
LCK_btr_dont_gc, // Prevent removal of b-tree page from index
|
||||||
LCK_shared_counter, // Database-wide shared counter
|
LCK_shared_counter, // Database-wide shared counter
|
||||||
LCK_tra_pc // Precommitted transaction lock
|
LCK_tra_pc, // Precommitted transaction lock
|
||||||
|
LCK_rel_gc // Allow garbage collection for relation
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lock owner types
|
// Lock owner types
|
||||||
|
@ -48,4 +48,53 @@ void LCK_release(Jrd::thread_db*, Jrd::Lock*);
|
|||||||
void LCK_re_post(Jrd::thread_db*, Jrd::Lock*);
|
void LCK_re_post(Jrd::thread_db*, Jrd::Lock*);
|
||||||
void LCK_write_data(Jrd::thread_db*, Jrd::Lock*, SLONG);
|
void LCK_write_data(Jrd::thread_db*, Jrd::Lock*, SLONG);
|
||||||
|
|
||||||
|
|
||||||
|
class AutoLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AutoLock(Jrd::thread_db* tdbb, Jrd::Lock* lck = NULL) :
|
||||||
|
m_tdbb(tdbb),
|
||||||
|
m_lock(lck)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~AutoLock()
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void release()
|
||||||
|
{
|
||||||
|
if (m_lock)
|
||||||
|
{
|
||||||
|
if (m_lock->lck_id)
|
||||||
|
LCK_release(m_tdbb, m_lock);
|
||||||
|
delete m_lock;
|
||||||
|
m_lock = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Jrd::Lock* operator-> ()
|
||||||
|
{
|
||||||
|
return m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Jrd::Lock* ()
|
||||||
|
{
|
||||||
|
return m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
Jrd::Lock* operator= (Jrd::Lock* lck)
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
m_lock = lck;
|
||||||
|
return m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Jrd::thread_db* m_tdbb;
|
||||||
|
Jrd::Lock* m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // JRD_LCK_PROTO_H
|
#endif // JRD_LCK_PROTO_H
|
||||||
|
@ -104,8 +104,15 @@ Lock* RLCK_reserve_relation(thread_db* tdbb,
|
|||||||
result = LCK_convert(tdbb, lock, level, transaction->getLockWait());
|
result = LCK_convert(tdbb, lock, level, transaction->getLockWait());
|
||||||
else
|
else
|
||||||
result = LCK_lock(tdbb, lock, level, transaction->getLockWait());
|
result = LCK_lock(tdbb, lock, level, transaction->getLockWait());
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
|
{
|
||||||
|
string err;
|
||||||
|
err.printf("Acquire lock for relation (%s) failed", relation->rel_name.c_str());
|
||||||
|
|
||||||
|
ERR_append_status(tdbb->tdbb_status_vector, Arg::Gds(isc_random) << Arg::Str(err));
|
||||||
ERR_punt();
|
ERR_punt();
|
||||||
|
}
|
||||||
|
|
||||||
return lock;
|
return lock;
|
||||||
}
|
}
|
||||||
@ -139,17 +146,8 @@ Lock* RLCK_transaction_relation_lock(thread_db* tdbb,
|
|||||||
vec<Lock*>::newVector(*transaction->tra_pool, transaction->tra_relation_locks,
|
vec<Lock*>::newVector(*transaction->tra_pool, transaction->tra_relation_locks,
|
||||||
relation->rel_id + 1);
|
relation->rel_id + 1);
|
||||||
|
|
||||||
const SSHORT relLockLen = relation->getRelLockKeyLength();
|
lock = jrd_rel::createLock(tdbb, transaction->tra_pool, relation, LCK_relation, true);
|
||||||
lock = FB_NEW_RPT(*transaction->tra_pool, relLockLen) Lock();
|
|
||||||
lock->lck_dbb = tdbb->getDatabase();
|
|
||||||
lock->lck_length = relLockLen;
|
|
||||||
relation->getRelLockKey(tdbb, &lock->lck_key.lck_string[0]);
|
|
||||||
lock->lck_type = LCK_relation;
|
|
||||||
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
||||||
lock->lck_parent = tdbb->getDatabase()->dbb_lock;
|
|
||||||
// the lck_object is used here to find the relation
|
|
||||||
// block from the lock block
|
|
||||||
lock->lck_object = relation;
|
|
||||||
// enter all relation locks into the intra-process lock manager and treat
|
// enter all relation locks into the intra-process lock manager and treat
|
||||||
// them as compatible within the attachment according to IPLM rules
|
// them as compatible within the attachment according to IPLM rules
|
||||||
lock->lck_compatible = tdbb->getAttachment();
|
lock->lck_compatible = tdbb->getAttachment();
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
#include "../jrd/trace/TraceManager.h"
|
#include "../jrd/trace/TraceManager.h"
|
||||||
#include "../jrd/trace/TraceObjects.h"
|
#include "../jrd/trace/TraceObjects.h"
|
||||||
#include "../jrd/trace/TraceService.h"
|
#include "../jrd/trace/TraceService.h"
|
||||||
|
#include "../jrd/val_proto.h"
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -657,12 +658,14 @@ THREAD_ENTRY_DECLARE main_gstat(THREAD_ENTRY_PARAM arg);
|
|||||||
#define MAIN_GSTAT main_gstat
|
#define MAIN_GSTAT main_gstat
|
||||||
#define MAIN_NBAK NBACKUP_main
|
#define MAIN_NBAK NBACKUP_main
|
||||||
#define MAIN_TRACE TRACE_main
|
#define MAIN_TRACE TRACE_main
|
||||||
|
#define MAIN_VALIDATE VAL_service
|
||||||
#else
|
#else
|
||||||
#define MAIN_GBAK NULL
|
#define MAIN_GBAK NULL
|
||||||
#define MAIN_GFIX NULL
|
#define MAIN_GFIX NULL
|
||||||
#define MAIN_GSTAT NULL
|
#define MAIN_GSTAT NULL
|
||||||
#define MAIN_NBAK NULL
|
#define MAIN_NBAK NULL
|
||||||
#define MAIN_TRACE NULL
|
#define MAIN_TRACE NULL
|
||||||
|
#define MAIN_VALIDATE NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(EMBEDDED) && !defined(BOOT_BUILD)
|
#if !defined(EMBEDDED) && !defined(BOOT_BUILD)
|
||||||
@ -726,6 +729,7 @@ static const serv_entry services[] =
|
|||||||
{ isc_action_svc_set_mapping, "Set Domain Admins Mapping to RDB$ADMIN", NULL, MAIN_GSEC },
|
{ isc_action_svc_set_mapping, "Set Domain Admins Mapping to RDB$ADMIN", NULL, MAIN_GSEC },
|
||||||
{ isc_action_svc_drop_mapping, "Drop Domain Admins Mapping to RDB$ADMIN", NULL, MAIN_GSEC },
|
{ isc_action_svc_drop_mapping, "Drop Domain Admins Mapping to RDB$ADMIN", NULL, MAIN_GSEC },
|
||||||
{ isc_action_svc_display_user_adm, "Display User with Admin Info", NULL, MAIN_GSEC },
|
{ isc_action_svc_display_user_adm, "Display User with Admin Info", NULL, MAIN_GSEC },
|
||||||
|
{ isc_action_svc_validate, "Validate Database", NULL, MAIN_VALIDATE },
|
||||||
/* actions with no names are undocumented */
|
/* actions with no names are undocumented */
|
||||||
{ isc_action_svc_set_config, NULL, NULL, TEST_THREAD },
|
{ isc_action_svc_set_config, NULL, NULL, TEST_THREAD },
|
||||||
{ isc_action_svc_default_config, NULL, NULL, TEST_THREAD },
|
{ isc_action_svc_default_config, NULL, NULL, TEST_THREAD },
|
||||||
@ -2166,7 +2170,8 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
|
|||||||
svc_id == isc_action_svc_trace_resume ||
|
svc_id == isc_action_svc_trace_resume ||
|
||||||
svc_id == isc_action_svc_trace_list ||
|
svc_id == isc_action_svc_trace_list ||
|
||||||
svc_id == isc_action_svc_set_mapping ||
|
svc_id == isc_action_svc_set_mapping ||
|
||||||
svc_id == isc_action_svc_drop_mapping)
|
svc_id == isc_action_svc_drop_mapping ||
|
||||||
|
svc_id == isc_action_svc_validate)
|
||||||
{
|
{
|
||||||
/* add the username and password to the end of svc_switches if needed */
|
/* add the username and password to the end of svc_switches if needed */
|
||||||
if (svc_switches.hasData())
|
if (svc_switches.hasData())
|
||||||
@ -2709,6 +2714,8 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
|
|||||||
string nbk_database, nbk_file;
|
string nbk_database, nbk_file;
|
||||||
int nbk_level = -1;
|
int nbk_level = -1;
|
||||||
|
|
||||||
|
bool val_database = false;
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
do
|
do
|
||||||
@ -3083,6 +3090,31 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case isc_action_svc_validate:
|
||||||
|
if (!get_action_svc_parameter(spb.getClumpTag(), val_option_in_sw_table, switches)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (spb.getClumpTag())
|
||||||
|
{
|
||||||
|
case isc_spb_dbname:
|
||||||
|
if (val_database) {
|
||||||
|
(Arg::Gds(isc_unexp_spb_form) << Arg::Str("only one isc_spb_dbname")).raise();
|
||||||
|
}
|
||||||
|
val_database = true;
|
||||||
|
// fall thru
|
||||||
|
case isc_spb_val_tab_incl:
|
||||||
|
case isc_spb_val_tab_excl:
|
||||||
|
case isc_spb_val_idx_incl:
|
||||||
|
case isc_spb_val_idx_excl:
|
||||||
|
get_action_svc_string(spb, switches);
|
||||||
|
break;
|
||||||
|
case isc_spb_val_lock_timeout:
|
||||||
|
get_action_svc_data(spb, switches);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3133,6 +3165,13 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
|
|||||||
switches += nbk_database;
|
switches += nbk_database;
|
||||||
switches += nbk_file;
|
switches += nbk_file;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case isc_action_svc_validate:
|
||||||
|
if (!val_database)
|
||||||
|
{
|
||||||
|
(Arg::Gds(isc_missing_required_spb) << Arg::Str("isc_spb_dbname")).raise();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switches.rtrim();
|
switches.rtrim();
|
||||||
@ -3179,7 +3218,7 @@ void Service::get_action_svc_data(const ClumpletReader& spb,
|
|||||||
string& switches)
|
string& switches)
|
||||||
{
|
{
|
||||||
string s;
|
string s;
|
||||||
s.printf("%"ULONGFORMAT" ", spb.getInt());
|
s.printf("%"SLONGFORMAT" ", spb.getInt());
|
||||||
switches += s;
|
switches += s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,29 @@
|
|||||||
#define JRD_VAL_PROTO_H
|
#define JRD_VAL_PROTO_H
|
||||||
|
|
||||||
bool VAL_validate(Jrd::thread_db*, USHORT);
|
bool VAL_validate(Jrd::thread_db*, USHORT);
|
||||||
|
THREAD_ENTRY_DECLARE VAL_service(THREAD_ENTRY_PARAM);
|
||||||
|
|
||||||
|
const int IN_SW_VAL_TAB_INCL = 1;
|
||||||
|
const int IN_SW_VAL_TAB_EXCL = 2;
|
||||||
|
const int IN_SW_VAL_IDX_INCL = 3;
|
||||||
|
const int IN_SW_VAL_IDX_EXCL = 4;
|
||||||
|
const int IN_SW_VAL_LOCK_TIMEOUT = 5;
|
||||||
|
const int IN_SW_VAL_DATABASE = 6;
|
||||||
|
const int IN_SW_VAL_TRUSTED_USER = 7;
|
||||||
|
|
||||||
|
static struct in_sw_tab_t val_option_in_sw_table [] =
|
||||||
|
{
|
||||||
|
{IN_SW_VAL_TAB_INCL, isc_spb_val_tab_incl, "TAB_INCLUDE", 0, 0, 0, false, 0, 5, NULL},
|
||||||
|
{IN_SW_VAL_TAB_EXCL, isc_spb_val_tab_excl, "TAB_EXCLUDE", 0, 0, 0, false, 0, 5, NULL},
|
||||||
|
{IN_SW_VAL_IDX_INCL, isc_spb_val_idx_incl, "IDX_INCLUDE", 0, 0, 0, false, 0, 5, NULL},
|
||||||
|
{IN_SW_VAL_IDX_EXCL, isc_spb_val_idx_excl, "IDX_EXCLUDE", 0, 0, 0, false, 0, 5, NULL},
|
||||||
|
{IN_SW_VAL_LOCK_TIMEOUT, isc_spb_val_lock_timeout, "WAIT", 0, 0, 0, false, 0, 1, NULL},
|
||||||
|
|
||||||
|
{IN_SW_VAL_DATABASE, isc_spb_dbname, "DATABASE", 0, 0, 0, false, 0, 1, NULL},
|
||||||
|
{IN_SW_VAL_TRUSTED_USER, 0, TRUSTED_USER_SWITCH, 0, 0, 0, false, 0, TRUSTED_USER_SWITCH_LEN, NULL},
|
||||||
|
|
||||||
|
{0, 0, NULL, 0, 0, 0, false, 0, 0, NULL} // End of List
|
||||||
|
};
|
||||||
|
|
||||||
#endif // JRD_VAL_PROTO_H
|
#endif // JRD_VAL_PROTO_H
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
110
src/jrd/vio.cpp
110
src/jrd/vio.cpp
@ -131,6 +131,45 @@ static void update_in_place(thread_db*, jrd_tra*, record_param*, record_param*);
|
|||||||
static void verb_post(thread_db*, jrd_tra*, record_param*, Record*, //record_param*,
|
static void verb_post(thread_db*, jrd_tra*, record_param*, Record*, //record_param*,
|
||||||
const bool, const bool);
|
const bool, const bool);
|
||||||
|
|
||||||
|
static bool assert_gc_enabled(const jrd_tra* transaction, const jrd_rel* relation)
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* a s s e r t _ g c _ e n a b l e d
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Functional description
|
||||||
|
* Ensure that calls of purge\expunge\VIO_backout is safe and don't broke
|
||||||
|
* results of online validation run.
|
||||||
|
*
|
||||||
|
* Notes
|
||||||
|
* System and temporary relations are not validated online.
|
||||||
|
* Non-zero rel_sweep_count is possible only under GCShared control when
|
||||||
|
* garbage collection is enabled.
|
||||||
|
*
|
||||||
|
* VIO_backout is more complex as it could run without GCShared control.
|
||||||
|
* Therefore we additionally check if we own relation lock in "write" mode -
|
||||||
|
* in this case online validation is not run against given relation.
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
if (relation->rel_sweep_count || relation->isSystem() || relation->isTemporary())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (relation->rel_flags & REL_gc_disabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vec<Lock*>* vector = transaction->tra_relation_locks;
|
||||||
|
if (!vector || vector->count() < relation->rel_id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Lock* lock = (*vector)[relation->rel_id];
|
||||||
|
if (!lock)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (lock->lck_physical == LCK_SW) || (lock->lck_physical == LCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
// Pick up relation ids
|
// Pick up relation ids
|
||||||
#include "../jrd/ini.h"
|
#include "../jrd/ini.h"
|
||||||
|
|
||||||
@ -250,6 +289,8 @@ void VIO_backout(thread_db* tdbb, record_param* rpb, const jrd_tra* transaction)
|
|||||||
Database* dbb = tdbb->getDatabase();
|
Database* dbb = tdbb->getDatabase();
|
||||||
CHECK_DBB(dbb);
|
CHECK_DBB(dbb);
|
||||||
|
|
||||||
|
fb_assert(assert_gc_enabled(transaction, rpb->rpb_relation));
|
||||||
|
|
||||||
#ifdef VIO_DEBUG
|
#ifdef VIO_DEBUG
|
||||||
if (debug_flag > DEBUG_WRITES)
|
if (debug_flag > DEBUG_WRITES)
|
||||||
{
|
{
|
||||||
@ -755,8 +796,10 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
case tra_precommitted:
|
case tra_precommitted:
|
||||||
|
{// scope
|
||||||
|
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
|
||||||
|
|
||||||
if (attachment->att_flags & ATT_NO_CLEANUP ||
|
if (attachment->att_flags & ATT_NO_CLEANUP || !gcGuard.gcEnabled() ||
|
||||||
rpb->rpb_flags & (rpb_chained | rpb_gc_active))
|
rpb->rpb_flags & (rpb_chained | rpb_gc_active))
|
||||||
{
|
{
|
||||||
if (rpb->rpb_b_page == 0) {
|
if (rpb->rpb_b_page == 0) {
|
||||||
@ -816,6 +859,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
|
|||||||
if (!DPM_get(tdbb, rpb, LCK_read)) {
|
if (!DPM_get(tdbb, rpb, LCK_read)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} // scope
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// If it's active, prepare to fetch the old version.
|
// If it's active, prepare to fetch the old version.
|
||||||
@ -982,6 +1026,12 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
CCH_RELEASE(tdbb, &rpb->getWindow(tdbb));
|
CCH_RELEASE(tdbb, &rpb->getWindow(tdbb));
|
||||||
|
|
||||||
|
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
|
||||||
|
|
||||||
|
if (!gcGuard.gcEnabled())
|
||||||
|
return false;
|
||||||
|
|
||||||
expunge(tdbb, rpb, transaction, (SLONG) 0);
|
expunge(tdbb, rpb, transaction, (SLONG) 0);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1024,7 +1074,14 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
{ // scope
|
||||||
|
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
|
||||||
|
|
||||||
|
if (!gcGuard.gcEnabled())
|
||||||
|
return true;
|
||||||
|
|
||||||
purge(tdbb, rpb);
|
purge(tdbb, rpb);
|
||||||
|
}
|
||||||
|
|
||||||
// Go back to be primary record version and chase versions all over again.
|
// Go back to be primary record version and chase versions all over again.
|
||||||
if (!DPM_get(tdbb, rpb, LCK_read)) {
|
if (!DPM_get(tdbb, rpb, LCK_read)) {
|
||||||
@ -1651,7 +1708,9 @@ bool VIO_garbage_collect(thread_db* tdbb, record_param* rpb, const jrd_tra* tran
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (transaction->tra_attachment->att_flags & ATT_no_cleanup) {
|
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
|
||||||
|
|
||||||
|
if (transaction->tra_attachment->att_flags & ATT_no_cleanup || !gcGuard.gcEnabled()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1956,7 +2015,14 @@ bool VIO_get_current(thread_db* tdbb,
|
|||||||
if (transaction->tra_attachment->att_flags & ATT_no_cleanup)
|
if (transaction->tra_attachment->att_flags & ATT_no_cleanup)
|
||||||
return !foreign_key;
|
return !foreign_key;
|
||||||
|
|
||||||
|
{
|
||||||
|
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
|
||||||
|
|
||||||
|
if (!gcGuard.gcEnabled())
|
||||||
|
return !foreign_key;
|
||||||
|
|
||||||
VIO_backout(tdbb, rpb, transaction);
|
VIO_backout(tdbb, rpb, transaction);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
case tra_precommitted:
|
case tra_precommitted:
|
||||||
Database::Checkout dcoHolder(dbb);
|
Database::Checkout dcoHolder(dbb);
|
||||||
@ -2050,7 +2116,14 @@ bool VIO_get_current(thread_db* tdbb,
|
|||||||
return !foreign_key;
|
return !foreign_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
|
||||||
|
|
||||||
|
if (!gcGuard.gcEnabled())
|
||||||
|
return !foreign_key;
|
||||||
|
|
||||||
VIO_backout(tdbb, rpb, transaction);
|
VIO_backout(tdbb, rpb, transaction);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case tra_limbo:
|
case tra_limbo:
|
||||||
@ -2939,6 +3012,7 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
|
|||||||
jrd_rel* relation = 0; // wasn't initialized: memory problem in catch() part.
|
jrd_rel* relation = 0; // wasn't initialized: memory problem in catch() part.
|
||||||
vec<jrd_rel*>* vector = 0;
|
vec<jrd_rel*>* vector = 0;
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
for (size_t i = 1; (vector = dbb->dbb_relations) && i < vector->count(); i++)
|
for (size_t i = 1; (vector = dbb->dbb_relations) && i < vector->count(); i++)
|
||||||
@ -2952,10 +3026,16 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
|
|||||||
!relation->isTemporary() &&
|
!relation->isTemporary() &&
|
||||||
relation->getPages(tdbb)->rel_pages)
|
relation->getPages(tdbb)->rel_pages)
|
||||||
{
|
{
|
||||||
|
jrd_rel::GCShared gcGuard(tdbb, relation);
|
||||||
|
if (!gcGuard.gcEnabled())
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
rpb.rpb_relation = relation;
|
rpb.rpb_relation = relation;
|
||||||
rpb.rpb_number.setValue(BOF_NUMBER);
|
rpb.rpb_number.setValue(BOF_NUMBER);
|
||||||
rpb.rpb_org_scans = relation->rel_scan_count++;
|
rpb.rpb_org_scans = relation->rel_scan_count++;
|
||||||
++relation->rel_sweep_count;
|
|
||||||
|
|
||||||
traceSweep->beginSweepRelation(relation);
|
traceSweep->beginSweepRelation(relation);
|
||||||
|
|
||||||
@ -2984,7 +3064,6 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
|
|||||||
|
|
||||||
traceSweep->endSweepRelation(relation);
|
traceSweep->endSweepRelation(relation);
|
||||||
|
|
||||||
--relation->rel_sweep_count;
|
|
||||||
--relation->rel_scan_count;
|
--relation->rel_scan_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2997,16 +3076,13 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
|
|||||||
delete rpb.rpb_record;
|
delete rpb.rpb_record;
|
||||||
if (relation)
|
if (relation)
|
||||||
{
|
{
|
||||||
if (relation->rel_sweep_count) {
|
|
||||||
--relation->rel_sweep_count;
|
|
||||||
}
|
|
||||||
if (relation->rel_scan_count) {
|
if (relation->rel_scan_count) {
|
||||||
--relation->rel_scan_count;
|
--relation->rel_scan_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ERR_punt();
|
ERR_punt();
|
||||||
}
|
}
|
||||||
return true;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3807,6 +3883,8 @@ static void expunge(thread_db* tdbb, record_param* rpb, const jrd_tra* transacti
|
|||||||
SET_TDBB(tdbb);
|
SET_TDBB(tdbb);
|
||||||
Attachment* attachment = transaction->tra_attachment;
|
Attachment* attachment = transaction->tra_attachment;
|
||||||
|
|
||||||
|
fb_assert(assert_gc_enabled(transaction, rpb->rpb_relation));
|
||||||
|
|
||||||
#ifdef VIO_DEBUG
|
#ifdef VIO_DEBUG
|
||||||
if (debug_flag > DEBUG_WRITES)
|
if (debug_flag > DEBUG_WRITES)
|
||||||
{
|
{
|
||||||
@ -4134,7 +4212,10 @@ static THREAD_ENTRY_DECLARE garbage_collector(THREAD_ENTRY_PARAM arg)
|
|||||||
relGarbage->getGarbage(dbb->dbb_oldest_snapshot, &relation->rel_gc_bitmap);
|
relGarbage->getGarbage(dbb->dbb_oldest_snapshot, &relation->rel_gc_bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
++relation->rel_sweep_count;
|
jrd_rel::GCShared gcGuard(tdbb, relation);
|
||||||
|
if (!gcGuard.gcEnabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
rpb.rpb_relation = relation;
|
rpb.rpb_relation = relation;
|
||||||
|
|
||||||
if (relation->rel_gc_bitmap)
|
if (relation->rel_gc_bitmap)
|
||||||
@ -4144,7 +4225,6 @@ static THREAD_ENTRY_DECLARE garbage_collector(THREAD_ENTRY_PARAM arg)
|
|||||||
const ULONG dp_sequence = relation->rel_gc_bitmap->current();
|
const ULONG dp_sequence = relation->rel_gc_bitmap->current();
|
||||||
|
|
||||||
if (!(dbb->dbb_flags & DBB_garbage_collector)) {
|
if (!(dbb->dbb_flags & DBB_garbage_collector)) {
|
||||||
--relation->rel_sweep_count;
|
|
||||||
goto gc_exit;
|
goto gc_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4185,12 +4265,14 @@ static THREAD_ENTRY_DECLARE garbage_collector(THREAD_ENTRY_PARAM arg)
|
|||||||
|
|
||||||
if (!(dbb->dbb_flags & DBB_garbage_collector))
|
if (!(dbb->dbb_flags & DBB_garbage_collector))
|
||||||
{
|
{
|
||||||
--relation->rel_sweep_count;
|
|
||||||
goto gc_exit;
|
goto gc_exit;
|
||||||
}
|
}
|
||||||
if (relation->rel_flags & REL_deleting) {
|
if (relation->rel_flags & REL_deleting) {
|
||||||
goto rel_exit;
|
goto rel_exit;
|
||||||
}
|
}
|
||||||
|
if (relation->rel_flags & REL_gc_disabled) {
|
||||||
|
goto rel_exit;
|
||||||
|
}
|
||||||
if (--tdbb->tdbb_quantum < 0) {
|
if (--tdbb->tdbb_quantum < 0) {
|
||||||
JRD_reschedule(tdbb, SWEEP_QUANTUM, true);
|
JRD_reschedule(tdbb, SWEEP_QUANTUM, true);
|
||||||
}
|
}
|
||||||
@ -4221,7 +4303,6 @@ rel_exit:
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
--relation->rel_sweep_count;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4283,9 +4364,6 @@ rel_exit:
|
|||||||
Firebird::stuff_exception(status_vector, ex);
|
Firebird::stuff_exception(status_vector, ex);
|
||||||
jrd_file* file = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE)->file;
|
jrd_file* file = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE)->file;
|
||||||
gds__log_status(file->fil_string, status_vector);
|
gds__log_status(file->fil_string, status_vector);
|
||||||
if (relation && relation->rel_sweep_count) {
|
|
||||||
--relation->rel_sweep_count;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gc_exit:
|
gc_exit:
|
||||||
@ -5003,6 +5081,8 @@ static void purge(thread_db* tdbb, record_param* rpb)
|
|||||||
Database* dbb = tdbb->getDatabase();
|
Database* dbb = tdbb->getDatabase();
|
||||||
CHECK_DBB(dbb);
|
CHECK_DBB(dbb);
|
||||||
|
|
||||||
|
fb_assert(assert_gc_enabled(tdbb->getTransaction(), rpb->rpb_relation));
|
||||||
|
|
||||||
#ifdef VIO_DEBUG
|
#ifdef VIO_DEBUG
|
||||||
if (debug_flag > DEBUG_TRACE_ALL) {
|
if (debug_flag > DEBUG_TRACE_ALL) {
|
||||||
printf("purge (record_param %"QUADFORMAT"d)\n", rpb->rpb_number.getValue());
|
printf("purge (record_param %"QUADFORMAT"d)\n", rpb->rpb_number.getValue());
|
||||||
|
@ -239,7 +239,14 @@ bool putNumericArgument(char**& av, ClumpletWriter& spb, unsigned int tag)
|
|||||||
if (! *av)
|
if (! *av)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int n = atoi(*av++);
|
char* err = NULL;
|
||||||
|
SLONG n = strtol(*av++, &err, 10);
|
||||||
|
|
||||||
|
if (err && *err)
|
||||||
|
{
|
||||||
|
(Arg::Gds(isc_fbsvcmgr_bad_arg) << av[-2]).raise();
|
||||||
|
}
|
||||||
|
|
||||||
spb.insertInt(tag, n);
|
spb.insertInt(tag, n);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -476,6 +483,17 @@ const SvcSwitches traceChgStateOptions[] =
|
|||||||
{0, 0, 0, 0, 0}
|
{0, 0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SvcSwitches validateOptions[] =
|
||||||
|
{
|
||||||
|
{"dbname", putStringArgument, 0, isc_spb_dbname, 0},
|
||||||
|
{"val_tab_incl", putStringArgument, 0, isc_spb_val_tab_incl, 0},
|
||||||
|
{"val_tab_excl", putStringArgument, 0, isc_spb_val_tab_excl, 0},
|
||||||
|
{"val_idx_incl", putStringArgument, 0, isc_spb_val_idx_incl, 0},
|
||||||
|
{"val_idx_excl", putStringArgument, 0, isc_spb_val_idx_excl, 0},
|
||||||
|
{"val_lock_timeout", putNumericArgument, 0, isc_spb_val_lock_timeout, 0},
|
||||||
|
{0, 0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
const SvcSwitches actionSwitch[] =
|
const SvcSwitches actionSwitch[] =
|
||||||
{
|
{
|
||||||
{"action_backup", putSingleTag, backupOptions, isc_action_svc_backup, isc_info_svc_to_eof},
|
{"action_backup", putSingleTag, backupOptions, isc_action_svc_backup, isc_info_svc_to_eof},
|
||||||
@ -498,6 +516,7 @@ const SvcSwitches actionSwitch[] =
|
|||||||
{"action_trace_list", putSingleTag, 0, isc_action_svc_trace_list, isc_info_svc_line},
|
{"action_trace_list", putSingleTag, 0, isc_action_svc_trace_list, isc_info_svc_line},
|
||||||
{"action_set_mapping", putSingleTag, mappingOptions, isc_action_svc_set_mapping, 0},
|
{"action_set_mapping", putSingleTag, mappingOptions, isc_action_svc_set_mapping, 0},
|
||||||
{"action_drop_mapping", putSingleTag, mappingOptions, isc_action_svc_drop_mapping, 0},
|
{"action_drop_mapping", putSingleTag, mappingOptions, isc_action_svc_drop_mapping, 0},
|
||||||
|
{"action_validate", putSingleTag, validateOptions, isc_action_svc_validate, isc_info_svc_line},
|
||||||
{0, 0, 0, 0, 0}
|
{0, 0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -846,6 +865,23 @@ static int shutdownCallback(const int reason, const int, void*)
|
|||||||
return FB_SUCCESS;
|
return FB_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShutdownHolder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
ShutdownHolder()
|
||||||
|
: active(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~ShutdownHolder()
|
||||||
|
{
|
||||||
|
if (active)
|
||||||
|
{
|
||||||
|
fb_shutdown(0, fb_shutrsn_exit_called);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// simple main function
|
// simple main function
|
||||||
|
|
||||||
@ -866,6 +902,8 @@ int main(int ac, char** av)
|
|||||||
prevCtrlCHandler = signal(SIGINT, ctrl_c_handler);
|
prevCtrlCHandler = signal(SIGINT, ctrl_c_handler);
|
||||||
fb_shutdown_callback(NULL, shutdownCallback, fb_shut_confirmation, NULL);
|
fb_shutdown_callback(NULL, shutdownCallback, fb_shut_confirmation, NULL);
|
||||||
|
|
||||||
|
ShutdownHolder shutdownHolder;
|
||||||
|
|
||||||
ISC_STATUS_ARRAY status;
|
ISC_STATUS_ARRAY status;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -931,6 +969,8 @@ int main(int ac, char** av)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shutdownHolder.active = true;
|
||||||
|
|
||||||
if (spbStart.getBufferLength() > 0)
|
if (spbStart.getBufferLength() > 0)
|
||||||
{
|
{
|
||||||
if (isc_service_start(status, &svc_handle, 0,
|
if (isc_service_start(status, &svc_handle, 0,
|
||||||
|
Loading…
Reference in New Issue
Block a user