8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 00:03:03 +01:00

Feature CORE-4707 : Implement ability to validate tables and indices online

This commit is contained in:
hvlad 2015-03-22 22:06:33 +00:00
parent fc4d8cff79
commit 8e58f69550
16 changed files with 1440 additions and 329 deletions

View File

@ -355,6 +355,19 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const
return IntSpb;
}
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");
break;

View File

@ -1028,4 +1028,65 @@ void logAndDie(const char* text)
#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

View File

@ -139,6 +139,9 @@ namespace fb_utils
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
#endif // INCLUDE_UTILS_PROTO_H

View File

@ -293,7 +293,8 @@
#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_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 *
@ -463,6 +464,16 @@
#define isc_spb_res_create 0x2000
#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 *
******************************************/

View File

@ -26,6 +26,7 @@
#include "../jrd/btr_proto.h"
#include "../jrd/dpm_proto.h"
#include "../jrd/idx_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/pag_proto.h"
#include "../jrd/vio_debug.h"
@ -382,6 +383,229 @@ bool jrd_rel::hasTriggers() const
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)
{
rel_next_free = nextFree;

View File

@ -23,6 +23,7 @@
#define JRD_RELATION_H
#include "../jrd/jrd.h"
#include "../jrd/lck.h"
#include "../jrd/pag.h"
#include "../jrd/val.h"
@ -202,6 +203,7 @@ public:
Lock* rel_existence_lock; // existence lock, if any
Lock* rel_partners_lock; // partners lock
Lock* rel_gc_lock; // garbage collection lock
IndexLock* rel_index_locks; // index existence locks
IndexBlock* rel_index_blocks; // index blocks for caching index info
trig_vec* rel_pre_erase; // Pre-operation erase trigger
@ -273,11 +275,50 @@ private:
RelationPages* getPagesInternal(thread_db* tdbb, SLONG tran, bool allocPages);
public:
explicit jrd_rel(MemoryPool& p)
: rel_name(p), rel_owner_name(p), rel_view_contexts(p), rel_security_name(p)
{ }
explicit jrd_rel(MemoryPool& p);
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
@ -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_virtual = 0x8000; // relation is virtual
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
{
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);
}
/// 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
class jrd_fld : public pool_alloc<type_fld>

View File

@ -2558,6 +2558,11 @@ void CMP_shutdown_database(thread_db* tdbb)
LCK_release(tdbb, relation->rel_partners_lock);
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)
{
if (index->idl_lock) {

View File

@ -231,21 +231,25 @@ public:
m_save_lock = att->att_wait_lock;
m_cancel_disabled = (m_tdbb->tdbb_flags & TDBB_wait_cancel_disable);
m_tdbb->tdbb_flags |= TDBB_wait_cancel_disable;
if (!wait)
return;
switch (lock->lck_type)
if (wait == LCK_WAIT)
{
switch (lock->lck_type)
{
case LCK_tra:
m_tdbb->tdbb_flags &= ~TDBB_wait_cancel_disable;
if (att)
att->att_wait_lock = lock;
break;
default:
m_tdbb->tdbb_flags |= TDBB_wait_cancel_disable;
}
}
else
{
case LCK_tra:
m_tdbb->tdbb_flags &= ~TDBB_wait_cancel_disable;
if (att)
att->att_wait_lock = lock;
break;
default:
;
}
}
@ -498,6 +502,7 @@ SLONG LCK_get_owner_handle(thread_db* tdbb, enum lck_t lock_type)
case LCK_tt_exist:
case LCK_shared_counter:
case LCK_sweep:
case LCK_rel_gc:
handle = *LCK_OWNER_HANDLE_DBB(tdbb);
break;
case LCK_attachment:

View File

@ -56,7 +56,8 @@ enum lck_t {
LCK_cancel, // Cancellation lock
LCK_btr_dont_gc, // Prevent removal of b-tree page from index
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

View File

@ -48,4 +48,53 @@ void LCK_release(Jrd::thread_db*, Jrd::Lock*);
void LCK_re_post(Jrd::thread_db*, Jrd::Lock*);
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

View File

@ -104,8 +104,15 @@ Lock* RLCK_reserve_relation(thread_db* tdbb,
result = LCK_convert(tdbb, lock, level, transaction->getLockWait());
else
result = LCK_lock(tdbb, lock, level, transaction->getLockWait());
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();
}
return lock;
}
@ -139,17 +146,8 @@ Lock* RLCK_transaction_relation_lock(thread_db* tdbb,
vec<Lock*>::newVector(*transaction->tra_pool, transaction->tra_relation_locks,
relation->rel_id + 1);
const SSHORT relLockLen = relation->getRelLockKeyLength();
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;
lock = jrd_rel::createLock(tdbb, transaction->tra_pool, relation, LCK_relation, true);
// enter all relation locks into the intra-process lock manager and treat
// them as compatible within the attachment according to IPLM rules
lock->lck_compatible = tdbb->getAttachment();

View File

@ -71,6 +71,7 @@
#include "../jrd/trace/TraceManager.h"
#include "../jrd/trace/TraceObjects.h"
#include "../jrd/trace/TraceService.h"
#include "../jrd/val_proto.h"
#ifdef HAVE_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_NBAK NBACKUP_main
#define MAIN_TRACE TRACE_main
#define MAIN_VALIDATE VAL_service
#else
#define MAIN_GBAK NULL
#define MAIN_GFIX NULL
#define MAIN_GSTAT NULL
#define MAIN_NBAK NULL
#define MAIN_TRACE NULL
#define MAIN_VALIDATE NULL
#endif
#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_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_validate, "Validate Database", NULL, MAIN_VALIDATE },
/* actions with no names are undocumented */
{ isc_action_svc_set_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_list ||
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 */
if (svc_switches.hasData())
@ -2709,6 +2714,8 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
string nbk_database, nbk_file;
int nbk_level = -1;
bool val_database = false;
bool found = false;
do
@ -3083,6 +3090,31 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
}
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:
return false;
}
@ -3133,6 +3165,13 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
switches += nbk_database;
switches += nbk_file;
break;
case isc_action_svc_validate:
if (!val_database)
{
(Arg::Gds(isc_missing_required_spb) << Arg::Str("isc_spb_dbname")).raise();
}
break;
}
switches.rtrim();
@ -3179,7 +3218,7 @@ void Service::get_action_svc_data(const ClumpletReader& spb,
string& switches)
{
string s;
s.printf("%"ULONGFORMAT" ", spb.getInt());
s.printf("%"SLONGFORMAT" ", spb.getInt());
switches += s;
}

View File

@ -25,6 +25,29 @@
#define JRD_VAL_PROTO_H
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

File diff suppressed because it is too large Load Diff

View File

@ -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*,
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
#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();
CHECK_DBB(dbb);
fb_assert(assert_gc_enabled(transaction, rpb->rpb_relation));
#ifdef VIO_DEBUG
if (debug_flag > DEBUG_WRITES)
{
@ -755,8 +796,10 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
#endif
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))
{
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)) {
return false;
}
} // scope
break;
// 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
{
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);
}
return false;
@ -1024,7 +1074,14 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
return true;
}
#endif
purge(tdbb, rpb);
{ // scope
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
if (!gcGuard.gcEnabled())
return true;
purge(tdbb, rpb);
}
// Go back to be primary record version and chase versions all over again.
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
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;
}
@ -1956,7 +2015,14 @@ bool VIO_get_current(thread_db* tdbb,
if (transaction->tra_attachment->att_flags & ATT_no_cleanup)
return !foreign_key;
VIO_backout(tdbb, rpb, transaction);
{
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
if (!gcGuard.gcEnabled())
return !foreign_key;
VIO_backout(tdbb, rpb, transaction);
}
continue;
case tra_precommitted:
Database::Checkout dcoHolder(dbb);
@ -2050,7 +2116,14 @@ bool VIO_get_current(thread_db* tdbb,
return !foreign_key;
}
VIO_backout(tdbb, rpb, transaction);
{
jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation);
if (!gcGuard.gcEnabled())
return !foreign_key;
VIO_backout(tdbb, rpb, transaction);
}
break;
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.
vec<jrd_rel*>* vector = 0;
bool ret = true;
try {
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->getPages(tdbb)->rel_pages)
{
jrd_rel::GCShared gcGuard(tdbb, relation);
if (!gcGuard.gcEnabled())
{
ret = false;
break;
}
rpb.rpb_relation = relation;
rpb.rpb_number.setValue(BOF_NUMBER);
rpb.rpb_org_scans = relation->rel_scan_count++;
++relation->rel_sweep_count;
traceSweep->beginSweepRelation(relation);
@ -2984,7 +3064,6 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
traceSweep->endSweepRelation(relation);
--relation->rel_sweep_count;
--relation->rel_scan_count;
}
}
@ -2997,16 +3076,13 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
delete rpb.rpb_record;
if (relation)
{
if (relation->rel_sweep_count) {
--relation->rel_sweep_count;
}
if (relation->rel_scan_count) {
--relation->rel_scan_count;
}
}
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);
Attachment* attachment = transaction->tra_attachment;
fb_assert(assert_gc_enabled(transaction, rpb->rpb_relation));
#ifdef VIO_DEBUG
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);
}
++relation->rel_sweep_count;
jrd_rel::GCShared gcGuard(tdbb, relation);
if (!gcGuard.gcEnabled())
continue;
rpb.rpb_relation = relation;
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();
if (!(dbb->dbb_flags & DBB_garbage_collector)) {
--relation->rel_sweep_count;
goto gc_exit;
}
@ -4185,12 +4265,14 @@ static THREAD_ENTRY_DECLARE garbage_collector(THREAD_ENTRY_PARAM arg)
if (!(dbb->dbb_flags & DBB_garbage_collector))
{
--relation->rel_sweep_count;
goto gc_exit;
}
if (relation->rel_flags & REL_deleting) {
goto rel_exit;
}
if (relation->rel_flags & REL_gc_disabled) {
goto rel_exit;
}
if (--tdbb->tdbb_quantum < 0) {
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);
jrd_file* file = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE)->file;
gds__log_status(file->fil_string, status_vector);
if (relation && relation->rel_sweep_count) {
--relation->rel_sweep_count;
}
}
gc_exit:
@ -5003,6 +5081,8 @@ static void purge(thread_db* tdbb, record_param* rpb)
Database* dbb = tdbb->getDatabase();
CHECK_DBB(dbb);
fb_assert(assert_gc_enabled(tdbb->getTransaction(), rpb->rpb_relation));
#ifdef VIO_DEBUG
if (debug_flag > DEBUG_TRACE_ALL) {
printf("purge (record_param %"QUADFORMAT"d)\n", rpb->rpb_number.getValue());

View File

@ -239,7 +239,14 @@ bool putNumericArgument(char**& av, ClumpletWriter& spb, unsigned int tag)
if (! *av)
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);
return true;
@ -476,6 +483,17 @@ const SvcSwitches traceChgStateOptions[] =
{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[] =
{
{"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_set_mapping", putSingleTag, mappingOptions, isc_action_svc_set_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}
};
@ -846,6 +865,23 @@ static int shutdownCallback(const int reason, const int, void*)
return FB_SUCCESS;
}
class ShutdownHolder
{
public:
bool active;
ShutdownHolder()
: active(false)
{ }
~ShutdownHolder()
{
if (active)
{
fb_shutdown(0, fb_shutrsn_exit_called);
}
}
};
// simple main function
@ -866,6 +902,8 @@ int main(int ac, char** av)
prevCtrlCHandler = signal(SIGINT, ctrl_c_handler);
fb_shutdown_callback(NULL, shutdownCallback, fb_shut_confirmation, NULL);
ShutdownHolder shutdownHolder;
ISC_STATUS_ARRAY status;
try {
@ -931,6 +969,8 @@ int main(int ac, char** av)
return 1;
}
shutdownHolder.active = true;
if (spbStart.getBufferLength() > 0)
{
if (isc_service_start(status, &svc_handle, 0,