mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 10:00:38 +01:00
WIP - take into an account states of an object (needs load / normal / erased)
This commit is contained in:
parent
4215b9d220
commit
7736734e91
@ -72,7 +72,7 @@ protected:
|
||||
template <typename T, typename Storage = EmptyStorage<T> >
|
||||
class Array : protected Storage
|
||||
{
|
||||
static_assert(std::is_trivially_copyable<T>(), "Only simple (trivially copyable) types supported in array");
|
||||
// !!!!! temp commemnted out - FastLoadLevel failure static_assert(std::is_trivially_copyable<T>(), "Only simple (trivially copyable) types supported in array");
|
||||
|
||||
public:
|
||||
typedef FB_SIZE_T size_type;
|
||||
@ -421,7 +421,7 @@ public:
|
||||
data = this->getStorage();
|
||||
}
|
||||
|
||||
// This method only assigns "pos" if the element is found.
|
||||
// This methods only assigns "pos" if the element is found.
|
||||
// Maybe we should modify it to iterate directy with "pos".
|
||||
bool find(const T& item, size_type& pos) const
|
||||
{
|
||||
@ -436,6 +436,19 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find(std::function<int(const T& item)> compare, size_type& pos) const
|
||||
{
|
||||
for (size_type i = 0; i < count; i++)
|
||||
{
|
||||
if (compare(data[i]) == 0)
|
||||
{
|
||||
pos = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool findAndRemove(const T& item)
|
||||
{
|
||||
size_type pos;
|
||||
|
@ -30,13 +30,11 @@
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
class Resources;
|
||||
|
||||
class NodePrinter
|
||||
{
|
||||
public:
|
||||
NodePrinter(const Resources* aResources, unsigned aIndent = 0)
|
||||
: indent(aIndent), resources(aResources)
|
||||
NodePrinter(unsigned aIndent = 0)
|
||||
: indent(aIndent)
|
||||
{
|
||||
}
|
||||
|
||||
@ -339,9 +337,6 @@ private:
|
||||
private:
|
||||
unsigned indent;
|
||||
|
||||
public:
|
||||
const Resources* resources;
|
||||
|
||||
private:
|
||||
Firebird::ObjectsArray<Firebird::string> stack;
|
||||
Firebird::string text;
|
||||
|
@ -2696,7 +2696,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
|
||||
preModifyEraseTriggers(tdbb, &relation->rel_pre_erase, whichTrig, rpb, NULL, TRIGGER_DELETE);
|
||||
|
||||
if (relation->rel_file)
|
||||
EXT_erase(rpb, transaction);
|
||||
rel_file->erase(rpb, transaction);
|
||||
else if (relation->isVirtual())
|
||||
VirtualTable::erase(tdbb, rpb);
|
||||
else if (!relation->rel_view_rse)
|
||||
|
@ -923,7 +923,7 @@ Jrd::Request* Attachment::findSystemRequest(thread_db* tdbb, USHORT id, Internal
|
||||
// Msg363 "request depth exceeded. (Recursive definition?)"
|
||||
}
|
||||
|
||||
Request* clone = statement->getRequest(tdbb, n);
|
||||
Request* clone = statement->getRequest(tdbb, n, true);
|
||||
|
||||
if (!(clone->req_flags & (req_active | req_reserved)))
|
||||
{
|
||||
|
@ -25,6 +25,8 @@
|
||||
#ifndef JRD_ATTACHMENT_H
|
||||
#define JRD_ATTACHMENT_H
|
||||
|
||||
#define DEBUG_LCK_LIST
|
||||
|
||||
#include "firebird.h"
|
||||
// Definition of block types for data allocation in JRD
|
||||
#include "../include/fb_blk.h"
|
||||
@ -51,7 +53,6 @@
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#define DEBUG_LCK_LIST
|
||||
|
||||
namespace EDS {
|
||||
class Connection;
|
||||
@ -91,7 +92,7 @@ namespace Jrd
|
||||
class jrd_rel;
|
||||
class jrd_prc;
|
||||
class Trigger;
|
||||
class TrigVector;
|
||||
class Triggers;
|
||||
class Function;
|
||||
class Statement;
|
||||
class ProfilerManager;
|
||||
|
118
src/jrd/CharSetContainer.h
Normal file
118
src/jrd/CharSetContainer.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* PROGRAM: JRD Access Method
|
||||
* MODULE: CharSetContainer.h
|
||||
* DESCRIPTION: Container for character set and it's collations
|
||||
*
|
||||
* 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): ______________________________________.
|
||||
*
|
||||
* Alex Peshkoff <peshkoff@mail.ru>
|
||||
*/
|
||||
|
||||
#ifndef JRD_CHARSETCONTAINER_H
|
||||
#define JRD_CHARSETCONTAINER_H
|
||||
|
||||
#include "../jrd/HazardPtr.h"
|
||||
#include "../jrd/Collation.h"
|
||||
#include "../common/classes/alloc.h"
|
||||
|
||||
struct SubtypeInfo;
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
class CharSetContainer : public Firebird::PermanentStorage
|
||||
{
|
||||
public:
|
||||
CharSetContainer(MemoryPool& p, USHORT cs_id, const SubtypeInfo* info);
|
||||
|
||||
void destroy()
|
||||
{
|
||||
cs->destroy();
|
||||
}
|
||||
|
||||
static void destroy(CharSetContainer* container)
|
||||
{
|
||||
container->destroy();
|
||||
delete container;
|
||||
}
|
||||
|
||||
static CharSetContainer* create(thread_db* tdbb, MetaId id, CacheObject::Flag flags);
|
||||
static Lock* getLock(MemoryPool& p, thread_db* tdbb);
|
||||
|
||||
CharSet* getCharSet()
|
||||
{
|
||||
return cs;
|
||||
}
|
||||
|
||||
CsConvert lookupConverter(thread_db* tdbb, CHARSET_ID to_cs);
|
||||
|
||||
static CharSetContainer* lookupCharset(thread_db* tdbb, USHORT ttype);
|
||||
static Lock* createCollationLock(thread_db* tdbb, USHORT ttype, void* object = NULL);
|
||||
|
||||
bool hasData() const
|
||||
{
|
||||
return cs != nullptr;
|
||||
}
|
||||
|
||||
const char* c_name() const
|
||||
{
|
||||
return cs->getName();
|
||||
}
|
||||
|
||||
private:
|
||||
static bool lookupInternalCharSet(USHORT id, SubtypeInfo* info);
|
||||
|
||||
private:
|
||||
CharSet* cs;
|
||||
};
|
||||
|
||||
class CharSetVers final : public CacheObject
|
||||
{
|
||||
public:
|
||||
CharSetVers(CharSetContainer* parent)
|
||||
: perm(parent), charset_collations(perm->getPool())
|
||||
{ }
|
||||
|
||||
const char* c_name() const override
|
||||
{
|
||||
return perm->c_name();
|
||||
}
|
||||
|
||||
void release(thread_db* tdbb)
|
||||
{
|
||||
for (auto coll : charset_collations)
|
||||
{
|
||||
if (coll)
|
||||
coll->release(tdbb);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy(CharSetVers* csv);
|
||||
static CharSetVers* create(thread_db* tdbb, MemoryPool& p, MetaId id, CacheObject::Flag flags);
|
||||
|
||||
Collation* lookupCollation(thread_db* tdbb, MetaId id);
|
||||
//void unloadCollation(thread_db* tdbb, USHORT tt_id);
|
||||
|
||||
private:
|
||||
CharSetContainer* perm;
|
||||
Firebird::HalfStaticArray<Collation*, 16> charset_collations;
|
||||
};
|
||||
|
||||
} // namespace Jrd
|
||||
|
||||
#endif // JRD_CHARSETCONTAINER_H
|
||||
|
@ -39,13 +39,13 @@ ConfigTable::ConfigTable(MemoryPool& pool, const Config* conf) :
|
||||
RecordBuffer* ConfigTable::getRecords(thread_db* tdbb, jrd_rel* relation)
|
||||
{
|
||||
fb_assert(relation);
|
||||
fb_assert(relation->rel_id == rel_config);
|
||||
fb_assert(relation->getId() == rel_config);
|
||||
|
||||
RecordBuffer* recordBuffer = getData(relation);
|
||||
if (recordBuffer)
|
||||
return recordBuffer;
|
||||
|
||||
recordBuffer = allocBuffer(tdbb, *tdbb->getDefaultPool(), relation->rel_id);
|
||||
recordBuffer = allocBuffer(tdbb, *tdbb->getDefaultPool(), relation->getId());
|
||||
|
||||
// Check privileges to see RDB$CONFIG
|
||||
const Attachment* att = tdbb->getAttachment();
|
||||
|
@ -93,6 +93,7 @@ class GarbageCollector;
|
||||
class CryptoManager;
|
||||
class KeywordsMap;
|
||||
class MetadataCache;
|
||||
class ExtEngineManager;
|
||||
|
||||
// allocator for keywords table
|
||||
class KeywordsMapAllocator
|
||||
|
@ -278,7 +278,7 @@ RecordBuffer* DbCreatorsList::makeBuffer(thread_db* tdbb)
|
||||
RecordBuffer* DbCreatorsList::getList(thread_db* tdbb, jrd_rel* relation)
|
||||
{
|
||||
fb_assert(relation);
|
||||
fb_assert(relation->rel_id == rel_sec_db_creators);
|
||||
fb_assert(relation->getId() == rel_sec_db_creators);
|
||||
|
||||
RecordBuffer* buffer = getData(relation);
|
||||
if (buffer)
|
||||
|
@ -910,7 +910,7 @@ void ExtEngineManager::Trigger::execute(thread_db* tdbb, Request* request, unsig
|
||||
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
|
||||
const Nullable<bool>& ssDefiner = trg->ssDefiner.specified ? trg->ssDefiner :
|
||||
(trg->relation && trg->relation->rel_ss_definer.specified ? trg->relation->rel_ss_definer : Nullable<bool>() );
|
||||
const MetaString& userName = ssDefiner.specified && ssDefiner.value ? trg->relation->rel_owner_name.c_str() : "";
|
||||
const MetaString& userName = ssDefiner.specified && ssDefiner.value ? trg->relation->rel_perm->rel_owner_name.c_str() : "";
|
||||
ContextManager<IExternalTrigger> ctxManager(tdbb, attInfo, trigger,
|
||||
CallerName(obj_trigger, trg->name, userName));
|
||||
|
||||
@ -1598,7 +1598,7 @@ void ExtEngineManager::makeTrigger(thread_db* tdbb, CompilerScratch* csb, Jrd::T
|
||||
|
||||
if (relation)
|
||||
{
|
||||
metadata->triggerTable = relation->rel_name;
|
||||
metadata->triggerTable = relation->getName();
|
||||
|
||||
MsgMetadata* fieldsMsg = FB_NEW MsgMetadata;
|
||||
metadata->triggerFields = fieldsMsg;
|
||||
|
@ -68,7 +68,7 @@ Function* Function::lookup(thread_db* tdbb, USHORT id, bool return_deleted, bool
|
||||
Function* function = dbb->dbb_mdc->getFunction(tdbb, id, noscan);
|
||||
|
||||
if (function && function->getId() == id &&
|
||||
!(function->flags & Routine::FLAG_CLEARED) &&
|
||||
// !(function->flags & Routine::FLAG_CLEARED) &&
|
||||
!(function->flags & Routine::FLAG_BEING_SCANNED) &&
|
||||
((function->flags & Routine::FLAG_SCANNED) || noscan) &&
|
||||
!(function->flags & Routine::FLAG_BEING_ALTERED) &&
|
||||
@ -80,7 +80,6 @@ Function* Function::lookup(thread_db* tdbb, USHORT id, bool return_deleted, bool
|
||||
}
|
||||
|
||||
check_function = function;
|
||||
check_function->sharedCheckLock(tdbb);
|
||||
}
|
||||
|
||||
// We need to look up the function in RDB$FUNCTIONS
|
||||
@ -117,7 +116,7 @@ Function* Function::lookup(thread_db* tdbb, const QualifiedName& name, bool nosc
|
||||
// See if we already know the function by name
|
||||
|
||||
Function* function = dbb->dbb_mdc->lookupFunction(tdbb, name, noscan ? 0 : Routine::FLAG_SCANNED,
|
||||
Routine::FLAG_OBSOLETE | Routine::FLAG_CLEARED | Routine::FLAG_BEING_SCANNED | Routine::FLAG_BEING_ALTERED);
|
||||
Routine::FLAG_OBSOLETE | /*Routine::FLAG_CLEARED |*/ Routine::FLAG_BEING_SCANNED | Routine::FLAG_BEING_ALTERED);
|
||||
|
||||
if (function)
|
||||
{
|
||||
@ -166,20 +165,39 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Function* Function::create(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
Lock* Function::getLock(MemoryPool& p, thread_db* tdbb)
|
||||
{
|
||||
return FB_NEW_RPT(p, 0) Lock(tdbb, sizeof(SLONG), LCK_fun_exist, nullptr, blockingAst);
|
||||
}
|
||||
|
||||
int Function::blockingAst(void* ast_object)
|
||||
{
|
||||
RoutinePermanent* const routine = static_cast<RoutinePermanent*>(ast_object);
|
||||
|
||||
try
|
||||
{
|
||||
Database* const dbb = function->existenceLock->lck_dbb;
|
||||
|
||||
AsyncContextHolder tdbb(dbb, FB_FUNCTION, function->existenceLock);
|
||||
|
||||
LCK_release(tdbb, function->existenceLock);
|
||||
function->flags |= Routine::FLAG_OBSOLETE;
|
||||
}
|
||||
catch (const Firebird::Exception&)
|
||||
{} // no-op
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Function* Function::create(thread_db* tdbb, MemoryPool& pool, MetaId id, CacheObject::Flag flags)
|
||||
{
|
||||
Jrd::Attachment* attachment = tdbb->getAttachment();
|
||||
jrd_tra* sysTransaction = attachment->getSysTransaction();
|
||||
Database* const dbb = tdbb->getDatabase();
|
||||
|
||||
Function* function = FB_NEW_POOL(*dbb->dbb_permanent) Function(*dbb->dbb_permanent, id);
|
||||
/*
|
||||
if (!function->existenceLock)
|
||||
{
|
||||
function->existenceLock = FB_NEW_POOL(pool)
|
||||
ExistenceLock(pool, tdbb, LCK_fun_exist, function->getId(), function.getPointer());
|
||||
}
|
||||
*/
|
||||
RoutinePermanent* perm = MetadataCache::lookupFunction(tdbb, id, flags && CacheFlag::NOSCAN);
|
||||
fb_assert(perm);
|
||||
Function* function = FB_NEW_POOL(pool) Function(perm);
|
||||
|
||||
try
|
||||
{
|
||||
@ -191,15 +209,15 @@ Function* Function::create(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
X IN RDB$FUNCTIONS
|
||||
WITH X.RDB$FUNCTION_ID EQ id
|
||||
{
|
||||
function->setName(QualifiedName(X.RDB$FUNCTION_NAME,
|
||||
perm->setName(QualifiedName(X.RDB$FUNCTION_NAME,
|
||||
(X.RDB$PACKAGE_NAME.NULL ? nullptr : X.RDB$PACKAGE_NAME)));
|
||||
function->owner = X.RDB$OWNER_NAME;
|
||||
perm->owner = X.RDB$OWNER_NAME;
|
||||
|
||||
Nullable<bool> ssDefiner;
|
||||
|
||||
if (!X.RDB$SECURITY_CLASS.NULL)
|
||||
{
|
||||
function->setSecurityName(X.RDB$SECURITY_CLASS);
|
||||
perm->setSecurityName(X.RDB$SECURITY_CLASS);
|
||||
}
|
||||
else if (!X.RDB$PACKAGE_NAME.NULL)
|
||||
{
|
||||
@ -211,7 +229,7 @@ Function* Function::create(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
|
||||
if (!PKG.RDB$SECURITY_CLASS.NULL)
|
||||
{
|
||||
function->setSecurityName(PKG.RDB$SECURITY_CLASS);
|
||||
perm->setSecurityName(PKG.RDB$SECURITY_CLASS);
|
||||
}
|
||||
|
||||
// SQL SECURITY of function must be the same if it's defined in package
|
||||
@ -230,7 +248,7 @@ Function* Function::create(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
}
|
||||
|
||||
if (ssDefiner.orElse(false))
|
||||
function->invoker = attachment->getUserId(function->owner);
|
||||
function->invoker = attachment->getUserId(perm->owner);
|
||||
|
||||
size_t count = 0;
|
||||
ULONG length = 0;
|
||||
@ -249,7 +267,7 @@ Function* Function::create(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
Y.RDB$PACKAGE_NAME EQUIV NULLIF(function->getName().package.c_str(), '')
|
||||
SORTED BY Y.RDB$ARGUMENT_POSITION
|
||||
{
|
||||
Parameter* parameter = FB_NEW_POOL(function->getPool()) Parameter(function->getPool());
|
||||
Parameter* parameter = FB_NEW_POOL(pool) Parameter(pool);
|
||||
|
||||
if (Y.RDB$ARGUMENT_POSITION != X.RDB$RETURN_ARGUMENT)
|
||||
{
|
||||
@ -452,10 +470,10 @@ Function* Function::create(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
else
|
||||
{
|
||||
RefPtr<MsgMetadata> inputMetadata(REF_NO_INCR, createMetadata(function->getInputFields(), false));
|
||||
function->setInputFormat(createFormat(function->getPool(), inputMetadata, false));
|
||||
function->setInputFormat(createFormat(pool, inputMetadata, false));
|
||||
|
||||
RefPtr<MsgMetadata> outputMetadata(REF_NO_INCR, createMetadata(function->getOutputFields(), false));
|
||||
function->setOutputFormat(createFormat(function->getPool(), outputMetadata, true));
|
||||
function->setOutputFormat(createFormat(pool, outputMetadata, true));
|
||||
|
||||
function->setImplemented(false);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "../jrd/val.h"
|
||||
#include "../dsql/Nodes.h"
|
||||
#include "../jrd/HazardPtr.h"
|
||||
#include "../jrd/lck.h"
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
@ -38,21 +39,24 @@ namespace Jrd
|
||||
static const char* const EXCEPTION_MESSAGE;
|
||||
|
||||
public:
|
||||
static Lock* getLock(MemoryPool& p, thread_db* tdbb);
|
||||
static int blockingAst(void* ast_object);
|
||||
|
||||
static Function* lookup(thread_db* tdbb, MetaId id, bool return_deleted, bool noscan, USHORT flags);
|
||||
static Function* lookup(thread_db* tdbb, const QualifiedName& name, bool noscan);
|
||||
|
||||
explicit Function(MemoryPool& p)
|
||||
: Routine(p),
|
||||
explicit Function(RoutinePermanent* perm)
|
||||
: Routine(perm),
|
||||
fun_entrypoint(NULL),
|
||||
fun_inputs(0),
|
||||
fun_return_arg(0),
|
||||
fun_temp_length(0),
|
||||
fun_exception_message(p),
|
||||
fun_exception_message(perm->getPool()),
|
||||
fun_deterministic(false),
|
||||
fun_external(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
/* ????????????????
|
||||
private:
|
||||
Function(MemoryPool& p, MetaId id)
|
||||
: Routine(p, id),
|
||||
@ -65,10 +69,10 @@ private:
|
||||
fun_external(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
*/
|
||||
public:
|
||||
static Function* loadMetadata(thread_db* tdbb, MetaId id, bool noscan, USHORT flags);
|
||||
static Function* create(thread_db* tdbb, MetaId id, CacheObject::Flag flags);
|
||||
static Function* create(thread_db* tdbb, MemoryPool& pool, MetaId id, CacheObject::Flag flags);
|
||||
|
||||
public:
|
||||
virtual int getObjectType() const
|
||||
|
@ -30,6 +30,9 @@
|
||||
|
||||
#include "../jrd/HazardPtr.h"
|
||||
#include "../jrd/jrd.h"
|
||||
#include "../jrd/Database.h"
|
||||
#include "../jrd/tra.h"
|
||||
#include "../jrd/met.h"
|
||||
|
||||
using namespace Jrd;
|
||||
using namespace Firebird;
|
||||
@ -40,6 +43,33 @@ HazardObject::~HazardObject()
|
||||
CacheObject* TRAP = nullptr;
|
||||
|
||||
|
||||
// class TransactionNumber
|
||||
|
||||
TraNumber TransactionNumber::current(thread_db* tdbb)
|
||||
{
|
||||
jrd_tra* tra = tdbb->getTransaction();
|
||||
return tra ? tra->tra_number : 0;
|
||||
}
|
||||
|
||||
TraNumber TransactionNumber::oldestActive(thread_db* tdbb)
|
||||
{
|
||||
return tdbb->getDatabase()->dbb_oldest_active;
|
||||
}
|
||||
|
||||
TraNumber TransactionNumber::next(thread_db* tdbb)
|
||||
{
|
||||
return tdbb->getDatabase()->dbb_next_transaction;
|
||||
}
|
||||
|
||||
|
||||
// class VersionSupport
|
||||
|
||||
MdcVersion VersionSupport::next(thread_db* tdbb)
|
||||
{
|
||||
return tdbb->getDatabase()->dbb_mdc->nextVersion();
|
||||
}
|
||||
|
||||
|
||||
bool CacheObject::checkObject(thread_db*, Arg::StatusVector&)
|
||||
{
|
||||
return true;
|
||||
|
@ -41,6 +41,9 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../jrd/tdbb.h"
|
||||
#include "../jrd/Database.h"
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
class HazardObject
|
||||
@ -48,8 +51,6 @@ namespace Jrd {
|
||||
protected:
|
||||
void retire()
|
||||
{
|
||||
fb_assert(this);
|
||||
|
||||
struct Disposer
|
||||
{
|
||||
void operator()(HazardObject* ho)
|
||||
@ -377,6 +378,8 @@ namespace Jrd {
|
||||
Generation::destroy(currentData.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
void clear() { } // NO-op, rely on dtor
|
||||
|
||||
private:
|
||||
atomics::atomic<Generation*> currentData;
|
||||
};
|
||||
@ -392,6 +395,695 @@ namespace Jrd {
|
||||
virtual const char* c_name() const = 0;
|
||||
};
|
||||
|
||||
class ObjectBase : public HazardObject
|
||||
{
|
||||
public:
|
||||
enum ResetType {Recompile, Mark, Commit, Rollback};
|
||||
|
||||
typedef SLONG ReturnedId; // enable '-1' as not found
|
||||
|
||||
public:
|
||||
virtual void resetDependentObject(thread_db* tdbb, ResetType rt) = 0;
|
||||
virtual void eraseObject(thread_db* tdbb) = 0; // erase object
|
||||
|
||||
public:
|
||||
void resetDependentObjects(thread_db* tdbb, TraNumber olderThan);
|
||||
void addDependentObject(thread_db* tdbb, ObjectBase* dep);
|
||||
void removeDependentObject(thread_db* tdbb, ObjectBase* dep);
|
||||
[[noreturn]] void busyError(thread_db* tdbb, MetaId id, const char* name);
|
||||
};
|
||||
|
||||
namespace CacheFlag
|
||||
{
|
||||
static const CacheObject::Flag COMMITTED = 0x01;
|
||||
static const CacheObject::Flag ERASED = 0x02;
|
||||
static const CacheObject::Flag NOSCAN = 0x04;
|
||||
static const CacheObject::Flag AUTOCREATE = 0x08;
|
||||
static const CacheObject::Flag INIT = 0x10;
|
||||
|
||||
static const CacheObject::Flag IGNORE_MASK = COMMITTED | ERASED;
|
||||
}
|
||||
|
||||
|
||||
class VersionSupport
|
||||
{
|
||||
public:
|
||||
static MdcVersion next(thread_db* tdbb);
|
||||
};
|
||||
|
||||
class CachePool
|
||||
{
|
||||
public:
|
||||
static MemoryPool& get(thread_db* tdbb);
|
||||
/*
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
return dbb->dbb_mdc->getPool();
|
||||
*/
|
||||
};
|
||||
|
||||
template <class OBJ>
|
||||
class CacheList : public HazardObject
|
||||
{
|
||||
public:
|
||||
CacheList(OBJ* obj, TraNumber currentTrans, CacheObject::Flag fl)
|
||||
: object(obj), next(nullptr), traNumber(currentTrans), cacheFlags(fl)
|
||||
{ }
|
||||
|
||||
~CacheList()
|
||||
{
|
||||
OBJ::destroy(object);
|
||||
delete next;
|
||||
}
|
||||
|
||||
// find appropriate object in cache
|
||||
static OBJ* getObject(HazardPtr<CacheList>& listEntry, TraNumber currentTrans, CacheObject::Flag flags)
|
||||
{
|
||||
for (; listEntry; listEntry.set(listEntry->next))
|
||||
{
|
||||
CacheObject::Flag f(listEntry->cacheFlags.load() & ~(flags & CacheFlag::IGNORE_MASK));
|
||||
|
||||
if ((f & CacheFlag::COMMITTED) ||
|
||||
// committed (i.e. confirmed) objects are freely available
|
||||
(currentTrans && (listEntry->traNumber == currentTrans)))
|
||||
// transaction that created an object can always access it
|
||||
{
|
||||
if (f & CacheFlag::ERASED)
|
||||
{
|
||||
// object does not exist
|
||||
fb_assert(!listEntry->object);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// required entry found in the list
|
||||
return listEntry->object;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr; // object created (not by us) and not committed yet
|
||||
}
|
||||
|
||||
bool isBusy(TraNumber currentTrans) const
|
||||
{
|
||||
return traNumber != currentTrans && !(cacheFlags & CacheFlag::COMMITTED);
|
||||
}
|
||||
|
||||
// add new entry to the list
|
||||
static bool add(atomics::atomic<CacheList*>& list, CacheList* newVal)
|
||||
{
|
||||
HazardPtr<CacheList> oldVal(list);
|
||||
|
||||
do
|
||||
{
|
||||
if (oldVal && oldVal->isBusy(newVal->traNumber)) // modified in other transaction
|
||||
return false;
|
||||
newVal->next.store(oldVal.getPointer(), atomics::memory_order_acquire);
|
||||
} while (! oldVal.replace2(list, newVal));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// insert newVal in the beginning of a list provided there is still oldVal at the top of the list
|
||||
static bool replace(atomics::atomic<CacheList*>& list, CacheList* newVal, CacheList* oldVal)
|
||||
{
|
||||
if (oldVal && oldVal->isBusy(newVal->traNumber)) // modified in other transaction
|
||||
return false;
|
||||
|
||||
newVal->next.store(oldVal, atomics::memory_order_acquire);
|
||||
return list.compare_exchange_strong(oldVal, newVal, std::memory_order_release, std::memory_order_acquire);
|
||||
}
|
||||
|
||||
// remove too old objects - they are anyway can't be in use
|
||||
static TraNumber cleanup(atomics::atomic<CacheList*>& list, const TraNumber oldest)
|
||||
{
|
||||
TraNumber rc = 0;
|
||||
for (HazardPtr<CacheList> entry(list); entry; entry.set(entry->next))
|
||||
{
|
||||
if ((entry->cacheFlags & CacheFlag::COMMITTED) && entry->traNumber < oldest)
|
||||
{
|
||||
if (entry->cacheFlags.fetch_or(CacheFlag::ERASED) & CacheFlag::ERASED)
|
||||
break; // someone else also performs cleanup
|
||||
|
||||
// split remaining list off
|
||||
if (entry.replace2(list, nullptr))
|
||||
{
|
||||
while (entry && !(entry->cacheFlags.fetch_or(CacheFlag::ERASED) & CacheFlag::ERASED))
|
||||
{
|
||||
entry->retire();
|
||||
OBJ::destroy(entry->object);
|
||||
entry.set(entry->next);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// store traNumber of last not removed list element
|
||||
rc = entry->traNumber;
|
||||
}
|
||||
|
||||
return rc; // 0 is returned in a case when list becomes empty
|
||||
}
|
||||
|
||||
// created earlier object is OK and should become visible to the world
|
||||
void commit(thread_db* tdbb, TraNumber currentTrans, TraNumber nextTrans)
|
||||
{
|
||||
fb_assert(cacheFlags == 0);
|
||||
fb_assert(traNumber == currentTrans);
|
||||
traNumber = nextTrans;
|
||||
cacheFlags |= CacheFlag::COMMITTED;
|
||||
version = VersionSupport::next(tdbb);
|
||||
}
|
||||
|
||||
// created earlier object is bad and should be destroyed
|
||||
static void rollback(atomics::atomic<CacheList*>& list, const TraNumber currentTran)
|
||||
{
|
||||
// Take into an account that no other transaction except current (i.e. object creator)
|
||||
// can access uncommitted objects, only list entries may be accessed as hazard pointers.
|
||||
// Therefore rollback can retire such entries at once, a kind of pop() from stack.
|
||||
|
||||
HazardPtr<CacheList> entry(list);
|
||||
while (entry)
|
||||
{
|
||||
if (entry->cacheFlags & CacheFlag::COMMITTED)
|
||||
break;
|
||||
fb_assert(entry->traNumber == currentTran);
|
||||
|
||||
if (entry.replace2(list, entry->next))
|
||||
{
|
||||
entry->retire();
|
||||
OBJ::destroy(entry->object);
|
||||
entry = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertCommitted()
|
||||
{
|
||||
fb_assert(cacheFlags & CacheFlag::COMMITTED);
|
||||
}
|
||||
|
||||
private:
|
||||
// object (nill/not nill) & ERASED bit in cacheFlags together control state of cache element
|
||||
// | ERASED
|
||||
//----------------------------------|-----------------------------
|
||||
// object | true | false
|
||||
//----------------------------------|-----------------------------
|
||||
// nill | object dropped | cache to be loaded
|
||||
// not nill | prohibited | cache is actual
|
||||
|
||||
OBJ* object;
|
||||
atomics::atomic<CacheList*> next;
|
||||
TraNumber traNumber; // when COMMITTED not set - stores transaction that created this list element
|
||||
// when COMMITTED is set - stores transaction after which older elements are not needed
|
||||
// traNumber to be changed BEFORE setting COMMITTED
|
||||
MdcVersion version; // version of metadata cache when object was added
|
||||
atomics::atomic<CacheObject::Flag> cacheFlags;
|
||||
};
|
||||
|
||||
|
||||
class TransactionNumber
|
||||
{
|
||||
public:
|
||||
static TraNumber current(thread_db* tdbb);
|
||||
static TraNumber oldestActive(thread_db* tdbb);
|
||||
static TraNumber next(thread_db* tdbb);
|
||||
};
|
||||
|
||||
|
||||
class Lock;
|
||||
|
||||
template <class E>
|
||||
MemoryPool& getObjectPool(thread_db* tdbb, E* ext)
|
||||
{
|
||||
return ext->getPool();
|
||||
}
|
||||
|
||||
template <>
|
||||
MemoryPool& getObjectPool(thread_db* tdbb, NullClass* ext)
|
||||
{
|
||||
return CachePool::get(tdbb);
|
||||
}
|
||||
|
||||
template <class OBJ, class EXT>
|
||||
class CacheElement : public ObjectBase, public EXT
|
||||
{
|
||||
typedef CacheList<OBJ> CachedObj;
|
||||
|
||||
public:
|
||||
CacheElement(MemoryPool& p, MetaId id, Lock* lock) :
|
||||
EXT(p, id, lock), list(nullptr), resetAt(0), myId(id)
|
||||
{ }
|
||||
|
||||
CacheElement() :
|
||||
EXT(), list(nullptr), resetAt(0), myId(0)
|
||||
{ }
|
||||
|
||||
~CacheElement()
|
||||
{
|
||||
delete list.load();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
OBJ* getObject(thread_db* tdbb, CacheObject::Flag flags = 0)
|
||||
{
|
||||
TraNumber cur = TransactionNumber::current(tdbb);
|
||||
HazardPtr<CachedObj> listEntry(list);
|
||||
if (!listEntry)
|
||||
{
|
||||
OBJ* obj = OBJ::create(tdbb, getObjectPool<EXT>(tdbb, this), myId, 0);
|
||||
CachedObj* newVal = FB_NEW_POOL(CachePool::get(tdbb)) CachedObj(obj, cur, obj ? 0 : CacheFlag::ERASED);
|
||||
|
||||
if (CachedObj::replace(list, newVal, nullptr))
|
||||
return obj;
|
||||
|
||||
delete newVal;
|
||||
if (obj)
|
||||
OBJ::destroy(obj);
|
||||
|
||||
listEntry.set(list);
|
||||
fb_assert(listEntry);
|
||||
}
|
||||
return CachedObj::getObject(listEntry, cur, flags);
|
||||
}
|
||||
|
||||
bool storeObject(thread_db* tdbb, OBJ* obj, CacheObject::Flag fl = 0)
|
||||
{
|
||||
TraNumber oldest = TransactionNumber::oldestActive(tdbb);
|
||||
TraNumber oldResetAt = resetAt.load(atomics::memory_order_acquire);
|
||||
if (oldResetAt && oldResetAt < oldest)
|
||||
setNewResetAt(oldResetAt, CachedObj::cleanup(list, oldest));
|
||||
|
||||
TraNumber current = TransactionNumber::current(tdbb);
|
||||
CachedObj* value = FB_NEW CachedObj(obj, current, fl & CacheFlag::IGNORE_MASK);
|
||||
|
||||
bool stored = fl & CacheFlag::INIT ? CachedObj::replace(list, value, nullptr) : CachedObj::add(list, value);
|
||||
if (stored)
|
||||
setNewResetAt(oldResetAt, current);
|
||||
else
|
||||
delete value;
|
||||
|
||||
return stored;
|
||||
}
|
||||
|
||||
void storeObjectWithTimeout(thread_db* tdbb, OBJ* obj, std::function<void()> error);
|
||||
|
||||
void commit(thread_db* tdbb)
|
||||
{
|
||||
HazardPtr<CachedObj> current(list);
|
||||
if (current)
|
||||
current->commit(tdbb, TransactionNumber::current(tdbb), TransactionNumber::next(tdbb));
|
||||
}
|
||||
|
||||
void rollback(thread_db* tdbb)
|
||||
{
|
||||
CachedObj::rollback(list, TransactionNumber::current(tdbb));
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
list.load()->assertCommitted();
|
||||
CachedObj::cleanup(list, MAX_TRA_NUMBER);
|
||||
}
|
||||
|
||||
void resetDependentObject(thread_db* tdbb, ResetType rt) override
|
||||
{
|
||||
switch (rt)
|
||||
{
|
||||
case ObjectBase::ResetType::Recompile:
|
||||
{
|
||||
OBJ* newObj = OBJ::create(tdbb, CachePool::get(tdbb), myId, 0);
|
||||
if (!storeObject(tdbb, newObj))
|
||||
{
|
||||
OBJ::destroy(newObj);
|
||||
OBJ* oldObj = getObject(tdbb);
|
||||
busyError(tdbb, myId, oldObj ? oldObj->c_name() : nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectBase::ResetType::Mark:
|
||||
// used in AST, therefore ignore error when saving empty object
|
||||
if (storeObject(tdbb, nullptr))
|
||||
commit(tdbb);
|
||||
break;
|
||||
|
||||
case ObjectBase::ResetType::Commit:
|
||||
commit(tdbb);
|
||||
break;
|
||||
|
||||
case ObjectBase::ResetType::Rollback:
|
||||
rollback(tdbb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void eraseObject(thread_db* tdbb) override
|
||||
{
|
||||
HazardPtr<CachedObj> l(list);
|
||||
fb_assert(l);
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
if (!storeObject(tdbb, nullptr, CacheFlag::ERASED))
|
||||
{
|
||||
OBJ* oldObj = getObject(tdbb);
|
||||
busyError(tdbb, myId, oldObj ? oldObj->c_name() : nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking it does not protect from something to be added in this element at next cycle!!!
|
||||
bool hasData() const
|
||||
{
|
||||
return list.load(atomics::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
void setNewResetAt(TraNumber oldVal, TraNumber newVal)
|
||||
{
|
||||
resetAt.compare_exchange_strong(oldVal, newVal,
|
||||
atomics::memory_order_release, atomics::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
atomics::atomic<CachedObj*> list;
|
||||
atomics::atomic<TraNumber> resetAt;
|
||||
|
||||
public:
|
||||
atomics::atomic<ULONG> flags; // control non-versioned features (like foreign keys)
|
||||
const MetaId myId;
|
||||
};
|
||||
|
||||
|
||||
template <class E, class EXT = NullClass, unsigned SUBARRAY_SHIFT = 8>
|
||||
class CacheVector : public Firebird::PermanentStorage
|
||||
{
|
||||
public:
|
||||
static const unsigned SUBARRAY_SIZE = 1 << SUBARRAY_SHIFT;
|
||||
static const unsigned SUBARRAY_MASK = SUBARRAY_SIZE - 1;
|
||||
|
||||
typedef CacheElement<E, EXT> StoredObject;
|
||||
typedef atomics::atomic<StoredObject*> SubArrayData;
|
||||
typedef atomics::atomic<SubArrayData*> ArrayData;
|
||||
typedef SharedReadVector<ArrayData, 4> Storage;
|
||||
|
||||
explicit CacheVector(MemoryPool& pool)
|
||||
: Firebird::PermanentStorage(pool),
|
||||
m_objects(getPool())
|
||||
{}
|
||||
|
||||
private:
|
||||
static FB_SIZE_T getCount(const HazardPtr<typename Storage::Generation>& v)
|
||||
{
|
||||
return v->getCount() << SUBARRAY_SHIFT;
|
||||
}
|
||||
|
||||
SubArrayData* getDataPointer(MetaId id) const
|
||||
{
|
||||
auto up = m_objects.readAccessor();
|
||||
if (id >= getCount(up))
|
||||
return nullptr;
|
||||
|
||||
auto sub = up->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
fb_assert(sub);
|
||||
return &sub[id & SUBARRAY_MASK];
|
||||
}
|
||||
|
||||
void grow(FB_SIZE_T reqSize)
|
||||
{
|
||||
fb_assert(reqSize > 0);
|
||||
reqSize = ((reqSize - 1) >> SUBARRAY_SHIFT) + 1;
|
||||
|
||||
Firebird::MutexLockGuard g(objectsGrowMutex, FB_FUNCTION);
|
||||
|
||||
m_objects.grow(reqSize);
|
||||
auto wa = m_objects.writeAccessor();
|
||||
fb_assert(wa->getCapacity() >= reqSize);
|
||||
while (wa->getCount() < reqSize)
|
||||
{
|
||||
SubArrayData* sub = FB_NEW_POOL(getPool()) SubArrayData[SUBARRAY_SIZE];
|
||||
memset(sub, 0, sizeof(SubArrayData) * SUBARRAY_SIZE);
|
||||
wa->add()->store(sub, atomics::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
StoredObject* getData(MetaId id)
|
||||
{
|
||||
auto ptr = getDataPointer(id);
|
||||
return ptr ? *ptr : nullptr;
|
||||
}
|
||||
|
||||
E* getObject(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
{
|
||||
// In theory that should be endless cycle - object may arrive/disappear again and again.
|
||||
// But in order to faster find devel problems we run it very limited number of times.
|
||||
#ifdef DEV_BUILD
|
||||
for (int i = 0; i < 2; ++i)
|
||||
#else
|
||||
for (;;)
|
||||
#endif
|
||||
{
|
||||
auto ptr = getDataPointer(id);
|
||||
if (ptr)
|
||||
{
|
||||
HazardPtr<StoredObject> data(*ptr);
|
||||
if (data)
|
||||
{
|
||||
auto rc = data->getObject(tdbb, flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & CacheFlag::AUTOCREATE))
|
||||
return nullptr;
|
||||
|
||||
auto val = E::create(tdbb, getPool(), id, flags);
|
||||
if (!val)
|
||||
(Firebird::Arg::Gds(isc_random) << "Object create failed").raise();
|
||||
|
||||
if (storeObject(tdbb, id, val))
|
||||
return val;
|
||||
|
||||
E::destroy(val);
|
||||
}
|
||||
#ifdef DEV_BUILD
|
||||
(Firebird::Arg::Gds(isc_random) << "Object suddenly disappeared").raise();
|
||||
#endif
|
||||
}
|
||||
|
||||
StoredObject* storeObject(thread_db* tdbb, MetaId id, E* const val)
|
||||
{
|
||||
if (id >= getCount())
|
||||
grow(id + 1);
|
||||
|
||||
auto ptr = getDataPointer(id);
|
||||
fb_assert(ptr);
|
||||
|
||||
HazardPtr<StoredObject> data(*ptr);
|
||||
if (!data)
|
||||
{
|
||||
StoredObject* newData = FB_NEW_POOL(getPool()) StoredObject(getPool(), id, E::getLock(getPool(), tdbb));
|
||||
if (!data.replace2(*ptr, newData))
|
||||
delete newData;
|
||||
else
|
||||
data.set(*ptr);
|
||||
}
|
||||
|
||||
if (!data->storeObject(tdbb, val))
|
||||
data.clear();
|
||||
return data.getPointer();
|
||||
}
|
||||
|
||||
StoredObject* lookup(thread_db* tdbb, std::function<bool(E* val)> cmp, MetaId* foundId = nullptr) const
|
||||
{
|
||||
auto a = m_objects.readAccessor();
|
||||
for (FB_SIZE_T i = 0; i < a->getCount(); ++i)
|
||||
{
|
||||
SubArrayData* const sub = a->value(i).load(atomics::memory_order_relaxed);
|
||||
if (!sub)
|
||||
continue;
|
||||
|
||||
for (SubArrayData* end = &sub[SUBARRAY_SIZE]; sub < end--;)
|
||||
{
|
||||
StoredObject* ptr = end->load(atomics::memory_order_relaxed);
|
||||
if (!ptr)
|
||||
continue;
|
||||
|
||||
E* val = ptr->getObject(tdbb);
|
||||
if (val && cmp(val))
|
||||
{
|
||||
if (foundId)
|
||||
*foundId = (i << SUBARRAY_SHIFT) + (end - sub);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
~CacheVector()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
auto a = m_objects.writeAccessor();
|
||||
for (FB_SIZE_T i = 0; i < a->getCount(); ++i)
|
||||
{
|
||||
SubArrayData* const sub = a->value(i).load(atomics::memory_order_relaxed);
|
||||
if (!sub)
|
||||
continue;
|
||||
|
||||
for (SubArrayData* end = &sub[SUBARRAY_SIZE]; sub < end--;)
|
||||
delete *end; // no need using release here in CacheVector's dtor
|
||||
|
||||
delete[] sub;
|
||||
}
|
||||
|
||||
m_objects.clear();
|
||||
//delete a;
|
||||
}
|
||||
|
||||
FB_SIZE_T getCount() const
|
||||
{
|
||||
return m_objects.readAccessor()->getCount() << SUBARRAY_SHIFT;
|
||||
}
|
||||
|
||||
bool replace2(MetaId id, HazardPtr<E>& oldVal, E* const newVal)
|
||||
{
|
||||
if (id >= getCount())
|
||||
grow(id + 1);
|
||||
|
||||
auto a = m_objects.readAccessor();
|
||||
SubArrayData* sub = a->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
fb_assert(sub);
|
||||
sub = &sub[id & SUBARRAY_MASK];
|
||||
|
||||
return oldVal.replace2(sub, newVal);
|
||||
}
|
||||
|
||||
bool clear(MetaId id)
|
||||
{
|
||||
if (id >= getCount())
|
||||
return false;
|
||||
|
||||
auto a = m_objects.readAccessor();
|
||||
SubArrayData* sub = a->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
fb_assert(sub);
|
||||
sub = &sub[id & SUBARRAY_MASK];
|
||||
|
||||
sub->store(nullptr, atomics::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load(MetaId id, HazardPtr<E>& val) const
|
||||
{
|
||||
auto a = m_objects.readAccessor();
|
||||
if (id < getCount(a))
|
||||
{
|
||||
SubArrayData* sub = a->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
if (sub)
|
||||
{
|
||||
val.set(sub[id & SUBARRAY_MASK]);
|
||||
if (val && val->hasData())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HazardPtr<E> load(MetaId id) const
|
||||
{
|
||||
HazardPtr<E> val;
|
||||
if (!load(id, val))
|
||||
val.clear();
|
||||
return val;
|
||||
}
|
||||
|
||||
HazardPtr<typename Storage::Generation> readAccessor() const
|
||||
{
|
||||
return m_objects.readAccessor();
|
||||
}
|
||||
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
StoredObject* operator*()
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
/* StoredObject& operator->()
|
||||
{
|
||||
return get();
|
||||
}
|
||||
*/
|
||||
Iterator& operator++()
|
||||
{
|
||||
index = locateData(index + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator& itr) const
|
||||
{
|
||||
fb_assert(data == itr.data);
|
||||
return index == itr.index;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& itr) const
|
||||
{
|
||||
fb_assert(data == itr.data);
|
||||
return index != itr.index;
|
||||
}
|
||||
|
||||
private:
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
|
||||
public:
|
||||
enum class Location {Begin, End};
|
||||
Iterator(const CacheVector* v, Location loc)
|
||||
: data(v),
|
||||
index(loc == Location::Begin ? locateData(0) : data->getCount())
|
||||
{ }
|
||||
|
||||
StoredObject* get()
|
||||
{
|
||||
StoredObject* ptr = data->getData(index);
|
||||
fb_assert(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
FB_SIZE_T locateData(FB_SIZE_T i)
|
||||
{
|
||||
while (!data->getData(i))
|
||||
++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
private:
|
||||
const CacheVector* data;
|
||||
FB_SIZE_T index; // should be able to store MAX_METAID + 1 value
|
||||
};
|
||||
|
||||
Iterator begin() const
|
||||
{
|
||||
return Iterator(this, Iterator::Location::Begin);
|
||||
}
|
||||
|
||||
Iterator end() const
|
||||
{
|
||||
return Iterator(this, Iterator::Location::End);
|
||||
}
|
||||
|
||||
private:
|
||||
Storage m_objects;
|
||||
Firebird::Mutex objectsGrowMutex;
|
||||
};
|
||||
|
||||
} // namespace Jrd
|
||||
|
||||
#endif // JRD_HAZARDPTR_H
|
||||
|
@ -65,6 +65,48 @@ private:
|
||||
static Firebird::MemoryStats m_stats;
|
||||
};
|
||||
|
||||
class InitPool
|
||||
{
|
||||
public:
|
||||
explicit InitPool(MemoryPool&)
|
||||
{
|
||||
m_pool = InitCDS::createPool();
|
||||
m_pool->setStatsGroup(m_stats);
|
||||
}
|
||||
|
||||
~InitPool()
|
||||
{
|
||||
// m_pool will be deleted by InitCDS dtor after cds termination
|
||||
// some memory could still be not freed until that moment
|
||||
|
||||
#ifdef DEBUG_CDS_MEMORY
|
||||
char str[256];
|
||||
sprintf(str, "DHP pool stats:\n"
|
||||
" usage = %llu\n"
|
||||
" mapping = %llu\n"
|
||||
" max usage = %llu\n"
|
||||
" max mapping = %llu\n"
|
||||
"\n",
|
||||
m_stats.getCurrentUsage(),
|
||||
m_stats.getCurrentMapping(),
|
||||
m_stats.getMaximumUsage(),
|
||||
m_stats.getMaximumMapping()
|
||||
);
|
||||
gds__log(str);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* alloc(size_t size)
|
||||
{
|
||||
return m_pool->allocate(size ALLOC_ARGS);
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* m_pool;
|
||||
Firebird::MemoryStats m_stats;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Jrd
|
||||
|
||||
#endif // FB_INIT_CDSLIB_H
|
||||
|
@ -32,13 +32,13 @@ using namespace Firebird;
|
||||
RecordBuffer* KeywordsTable::getRecords(thread_db* tdbb, jrd_rel* relation)
|
||||
{
|
||||
fb_assert(relation);
|
||||
fb_assert(relation->rel_id == rel_keywords);
|
||||
fb_assert(relation->getId() == rel_keywords);
|
||||
|
||||
auto recordBuffer = getData(relation);
|
||||
if (recordBuffer)
|
||||
return recordBuffer;
|
||||
|
||||
recordBuffer = allocBuffer(tdbb, *tdbb->getDefaultPool(), relation->rel_id);
|
||||
recordBuffer = allocBuffer(tdbb, *tdbb->getDefaultPool(), relation->getId());
|
||||
|
||||
const auto record = recordBuffer->getTempRecord();
|
||||
|
||||
|
@ -1663,7 +1663,7 @@ RecordBuffer* MappingList::makeBuffer(thread_db* tdbb)
|
||||
RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation)
|
||||
{
|
||||
fb_assert(relation);
|
||||
fb_assert(relation->rel_id == rel_global_auth_mapping);
|
||||
fb_assert(relation->getId() == rel_global_auth_mapping);
|
||||
|
||||
RecordBuffer* buffer = getData(relation);
|
||||
if (buffer)
|
||||
|
@ -119,9 +119,9 @@ bool MonitoringTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation,
|
||||
if (!snapshot->getData(relation)->fetch(position, record))
|
||||
return false;
|
||||
|
||||
if (relation->rel_id == rel_mon_attachments || relation->rel_id == rel_mon_statements)
|
||||
if (relation->getId() == rel_mon_attachments || relation->getId() == rel_mon_statements)
|
||||
{
|
||||
const USHORT fieldId = (relation->rel_id == rel_mon_attachments) ?
|
||||
const USHORT fieldId = (relation->getId() == rel_mon_attachments) ?
|
||||
(USHORT) f_mon_att_idle_timer : (USHORT) f_mon_stmt_timer;
|
||||
|
||||
dsc desc;
|
||||
@ -133,7 +133,7 @@ bool MonitoringTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation,
|
||||
ISC_TIMESTAMP_TZ* ts = reinterpret_cast<ISC_TIMESTAMP_TZ*> (desc.dsc_address);
|
||||
ts->utc_timestamp = TimeZoneUtil::getCurrentGmtTimeStamp().utc_timestamp;
|
||||
|
||||
if (relation->rel_id == rel_mon_attachments)
|
||||
if (relation->getId() == rel_mon_attachments)
|
||||
{
|
||||
const SINT64 currClock = fb_utils::query_performance_counter() / fb_utils::query_performance_frequency();
|
||||
NoThrowTimeStamp::add10msec(&ts->utc_timestamp, clock - currClock, ISC_TIME_SECONDS_PRECISION);
|
||||
@ -630,7 +630,7 @@ RecordBuffer* SnapshotData::getData(const jrd_rel* relation) const
|
||||
{
|
||||
fb_assert(relation);
|
||||
|
||||
return getData(relation->rel_id);
|
||||
return getData(relation->getId());
|
||||
}
|
||||
|
||||
|
||||
@ -650,14 +650,13 @@ RecordBuffer* SnapshotData::allocBuffer(thread_db* tdbb, MemoryPool& pool, int r
|
||||
{
|
||||
jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, rel_id, false);
|
||||
fb_assert(relation);
|
||||
MET_scan_relation(tdbb, relation);
|
||||
fb_assert(relation->isVirtual());
|
||||
|
||||
const Format* const format = MET_current(tdbb, relation);
|
||||
fb_assert(format);
|
||||
|
||||
RecordBuffer* const buffer = FB_NEW_POOL(pool) RecordBuffer(pool, format);
|
||||
const RelationData data = {relation->rel_id, buffer};
|
||||
const RelationData data = {relation->getId(), buffer};
|
||||
m_snapshot.add(data);
|
||||
|
||||
return buffer;
|
||||
@ -708,11 +707,11 @@ void SnapshotData::putField(thread_db* tdbb, Record* record, const DumpField& fi
|
||||
SLONG rel_id;
|
||||
memcpy(&rel_id, field.data, field.length);
|
||||
|
||||
jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, rel_id, false);
|
||||
if (!relation || relation->rel_name.isEmpty())
|
||||
RelationPermanent* relation = MetadataCache::lookupRelation(tdbb, rel_id, false);
|
||||
if (!relation || relation->getName().isEmpty())
|
||||
return;
|
||||
|
||||
const MetaName& name = relation->rel_name;
|
||||
const MetaName& name = relation->getName();
|
||||
dsc from_desc;
|
||||
from_desc.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str());
|
||||
MOV_move(tdbb, &from_desc, &to_desc);
|
||||
|
@ -572,7 +572,7 @@ RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch*
|
||||
// Find relation either by id or by name
|
||||
AutoPtr<string> aliasString;
|
||||
MetaName name;
|
||||
CacheElement<jrd_rel>* rel = nullptr;
|
||||
CachedRelation* rel = nullptr;
|
||||
|
||||
switch (blrOp)
|
||||
{
|
||||
@ -656,7 +656,7 @@ string RelationSourceNode::internalPrint(NodePrinter& printer) const
|
||||
NODE_PRINT(printer, alias);
|
||||
NODE_PRINT(printer, context);
|
||||
if (relation.isSet())
|
||||
printer.print("rel_name", relation.get(JRD_get_thread_data(), printer.resources)->rel_name);
|
||||
printer.print("rel_name", relation(JRD_get_thread_data())->getName());
|
||||
|
||||
return "RelationSourceNode";
|
||||
}
|
||||
@ -734,13 +734,13 @@ RecordSourceNode* RelationSourceNode::pass1(thread_db* tdbb, CompilerScratch* cs
|
||||
if (relation.isSet() && !csb->csb_implicit_cursor)
|
||||
{
|
||||
const SLONG ssRelationId = tail->csb_view.isSet() ?
|
||||
tail->csb_view.get(tdbb, csb->csb_resources)->rel_id : view.isSet() ?
|
||||
view.get(tdbb, csb->csb_resources)->rel_id : csb->csb_view.isSet() ?
|
||||
csb->csb_view.get(tdbb, csb->csb_resources)->rel_id : 0;
|
||||
tail->csb_view()->getId() : view.isSet() ?
|
||||
view()->getId() : csb->csb_view.isSet() ?
|
||||
csb->csb_view()->getId() : 0;
|
||||
|
||||
const jrd_rel* r = relation.get(tdbb, csb->csb_resources);
|
||||
const RelationPermanent* r = relation();
|
||||
CMP_post_access(tdbb, csb, r->rel_security_name, ssRelationId,
|
||||
SCL_select, obj_relations, r->rel_name);
|
||||
SCL_select, obj_relations, r->getName());
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -757,11 +757,11 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN
|
||||
// prepare to check protection of relation when a field in the stream of the
|
||||
// relation is accessed.
|
||||
|
||||
CachedResource<jrd_rel> const parentView = csb->csb_view;
|
||||
Rsc::Rel const parentView = csb->csb_view;
|
||||
const StreamType viewStream = csb->csb_view_stream;
|
||||
|
||||
CachedResource<jrd_rel> relationView = relation;
|
||||
//csb->csb_resources.postResource(tdbb, Resource::rsc_relation, relationView, relationView->rel_id);
|
||||
Rsc::Rel relationView = relation;
|
||||
//csb->csb_resources.postResource(tdbb, Resource::rsc_relation, relationView, relationView->getId());
|
||||
view = parentView;
|
||||
|
||||
CompilerScratch::csb_repeat* const element = CMP_csb_element(csb, stream);
|
||||
@ -772,7 +772,7 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN
|
||||
|
||||
if (parentView.isSet())
|
||||
{
|
||||
const ViewContexts& ctx = parentView.get(tdbb, csb->csb_resources)->rel_view_contexts;
|
||||
const ViewContexts& ctx = parentView(tdbb)->rel_view_contexts;
|
||||
const USHORT key = context;
|
||||
FB_SIZE_T pos;
|
||||
|
||||
@ -785,7 +785,7 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN
|
||||
|
||||
// check for a view - if not, nothing more to do
|
||||
|
||||
RseNode* viewRse = relationView.get(tdbb, csb->csb_resources)->rel_view_rse;
|
||||
RseNode* viewRse = relationView(tdbb)->rel_view_rse;
|
||||
if (!viewRse)
|
||||
return;
|
||||
|
||||
@ -796,7 +796,7 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN
|
||||
|
||||
AutoSetRestore<USHORT> autoRemapVariable(&csb->csb_remap_variable,
|
||||
(csb->csb_variables ? csb->csb_variables->count() : 0) + 1);
|
||||
AutoSetRestore<CachedResource<jrd_rel>> autoView(&csb->csb_view, relationView);
|
||||
AutoSetRestore<Rsc::Rel> autoView(&csb->csb_view, relationView);
|
||||
AutoSetRestore<StreamType> autoViewStream(&csb->csb_view_stream, stream);
|
||||
|
||||
// We don't expand the view in two cases:
|
||||
@ -906,10 +906,10 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
const auto blrStartPos = csb->csb_blr_reader.getPos();
|
||||
jrd_prc* procedure = nullptr;
|
||||
AutoPtr<string> aliasString;
|
||||
QualifiedName name;
|
||||
CacheElement<jrd_prc>* proc = nullptr;
|
||||
CacheElement<jrd_prc, RoutinePermanent>* proc = nullptr;
|
||||
SubRoutine<jrd_prc> nodeProc;
|
||||
|
||||
switch (blrOp)
|
||||
{
|
||||
@ -953,43 +953,45 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
|
||||
{
|
||||
DeclareSubProcNode* declareNode;
|
||||
|
||||
for (auto curCsb = csb; curCsb && !procedure; curCsb = curCsb->mainCsb)
|
||||
for (auto curCsb = csb; curCsb; curCsb = curCsb->mainCsb)
|
||||
{
|
||||
if (curCsb->subProcedures.get(name.identifier, declareNode))
|
||||
procedure = declareNode->routine;
|
||||
{
|
||||
nodeProc = declareNode->routine;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HazardPtr<jrd_prc> proc = MetadataCache::lookup_procedure(tdbb, name, false);
|
||||
if (proc)
|
||||
procedure = csb->csb_resources.registerResource(tdbb, Resource::rsc_procedure, proc, proc->getId());
|
||||
}
|
||||
proc = MetadataCache::lookupProcedure(tdbb, name);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
if (proc)
|
||||
nodeProc = csb->csb_resources->procedures.registerResource(proc);
|
||||
|
||||
jrd_prc* procedure = nodeProc(tdbb);
|
||||
if (!procedure)
|
||||
PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()));
|
||||
else
|
||||
|
||||
if (procedure->isImplemented() && !procedure->isDefined())
|
||||
{
|
||||
if (procedure->isImplemented() && !procedure->isDefined())
|
||||
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
|
||||
{
|
||||
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
|
||||
{
|
||||
PAR_warning(
|
||||
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
}
|
||||
PAR_warning(
|
||||
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1006,9 +1008,8 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
|
||||
ProcedureSourceNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
||||
*tdbb->getDefaultPool());
|
||||
|
||||
node->procedure = csb->csb_resources.procedures->registerResource(tdbb, proc);
|
||||
node->isSubRoutine = procedure->isSubRoutine();
|
||||
node->procedureId = node->isSubRoutine ? 0 : procedure->getId();
|
||||
node->procedure = nodeProc;
|
||||
node->procedureId = nodeProc()->getId();
|
||||
|
||||
if (aliasString)
|
||||
node->alias = *aliasString;
|
||||
@ -1017,7 +1018,7 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
|
||||
{
|
||||
node->stream = PAR_context(csb, &node->context);
|
||||
|
||||
csb->csb_rpt[node->stream].csb_procedure = procedure;
|
||||
csb->csb_rpt[node->stream].csb_procedure = nodeProc;
|
||||
csb->csb_rpt[node->stream].csb_alias = aliasString.release();
|
||||
|
||||
PAR_procedure_parms(tdbb, csb, procedure, node->in_msg.getAddress(),
|
||||
@ -1171,22 +1172,24 @@ ProcedureSourceNode* ProcedureSourceNode::copy(thread_db* tdbb, NodeCopier& copi
|
||||
if (!copier.remap)
|
||||
BUGCHECK(221); // msg 221 (CMP) copy: cannot remap
|
||||
|
||||
ProcedureSourceNode* newSource = FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
||||
*tdbb->getDefaultPool());
|
||||
ProcedureSourceNode* newSource = FB_NEW_POOL(*tdbb->getDefaultPool())
|
||||
ProcedureSourceNode(*tdbb->getDefaultPool());
|
||||
|
||||
if (isSubRoutine)
|
||||
// is it really needed with new MDC ?????????????????
|
||||
|
||||
if (procedure.isSubRoutine())
|
||||
newSource->procedure = procedure;
|
||||
else
|
||||
{
|
||||
auto proc = MetadataCache::lookup_procedure_id(tdbb, procedureId, false, false, 0);
|
||||
newSource->procedure = copier.csb->csb_resources.registerResource(tdbb, Resource::rsc_procedure, proc, proc->getId());
|
||||
if (!newSource->procedure)
|
||||
auto proc = MetadataCache::lookupProcedure(tdbb, procedureId);
|
||||
if (!proc)
|
||||
{
|
||||
string name;
|
||||
name.printf("id %d", procedureId);
|
||||
delete newSource;
|
||||
ERR_post(Arg::Gds(isc_prcnotdef) << name);
|
||||
}
|
||||
newSource->procedure = copier.csb->csb_resources->procedures.registerResource(proc);
|
||||
}
|
||||
|
||||
// dimitr: See the appropriate code and comment in NodeCopier (in nod_argument).
|
||||
@ -1203,7 +1206,6 @@ ProcedureSourceNode* ProcedureSourceNode::copy(thread_db* tdbb, NodeCopier& copi
|
||||
newSource->stream = copier.csb->nextStream();
|
||||
copier.remap[stream] = newSource->stream;
|
||||
newSource->context = context;
|
||||
newSource->isSubRoutine = isSubRoutine;
|
||||
newSource->procedureId = procedureId;
|
||||
newSource->view = view;
|
||||
|
||||
@ -1236,13 +1238,10 @@ void ProcedureSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, Rse
|
||||
|
||||
pass1(tdbb, csb);
|
||||
|
||||
if (!isSubRoutine)
|
||||
{
|
||||
if (!procedure.isSubRoutine())
|
||||
CMP_post_procedure_access(tdbb, csb, procedure);
|
||||
csb->csb_resources.postResource(tdbb, Resource::rsc_procedure, procedure, procedureId);
|
||||
}
|
||||
|
||||
jrd_rel* const parentView = csb->csb_view;
|
||||
Rsc::Rel const parentView = csb->csb_view;
|
||||
const StreamType viewStream = csb->csb_view_stream;
|
||||
view = parentView;
|
||||
|
||||
@ -1254,7 +1253,7 @@ void ProcedureSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, Rse
|
||||
|
||||
if (parentView)
|
||||
{
|
||||
const ViewContexts& ctx = parentView->rel_view_contexts;
|
||||
const ViewContexts& ctx = parentView(tdbb)->rel_view_contexts;
|
||||
const USHORT key = context;
|
||||
FB_SIZE_T pos;
|
||||
|
||||
@ -1578,7 +1577,7 @@ void AggregateSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, Rse
|
||||
|
||||
pass1(tdbb, csb);
|
||||
|
||||
jrd_rel* const parentView = csb->csb_view;
|
||||
Rsc::Rel const parentView = csb->csb_view;
|
||||
const StreamType viewStream = csb->csb_view_stream;
|
||||
|
||||
CompilerScratch::csb_repeat* const element = CMP_csb_element(csb, stream);
|
||||
@ -1882,7 +1881,7 @@ void UnionSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode
|
||||
doPass1(tdbb, csb, ptr2->getAddress());
|
||||
}
|
||||
|
||||
jrd_rel* const parentView = csb->csb_view;
|
||||
Rsc::Rel const parentView = csb->csb_view;
|
||||
const StreamType viewStream = csb->csb_view_stream;
|
||||
|
||||
CompilerScratch::csb_repeat* const element = CMP_csb_element(csb, stream);
|
||||
@ -2282,7 +2281,7 @@ void WindowSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNod
|
||||
|
||||
pass1(tdbb, csb);
|
||||
|
||||
jrd_rel* const parentView = csb->csb_view;
|
||||
Rsc::Rel const parentView = csb->csb_view;
|
||||
const StreamType viewStream = csb->csb_view_stream;
|
||||
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
@ -2429,14 +2428,14 @@ string RseNode::internalPrint(NodePrinter& printer) const
|
||||
bool RseNode::dsqlAggregateFinder(AggregateFinder& visitor)
|
||||
{
|
||||
AutoSetRestore<USHORT> autoValidateExpr(&visitor.currentLevel, visitor.currentLevel + 1);
|
||||
return visitor.visit(dsqlStreams) | visitor.visit(dsqlWhere) | visitor.visit(dsqlSelectList);
|
||||
return visitor.visit(dsqlStreams) || visitor.visit(dsqlWhere) || visitor.visit(dsqlSelectList);
|
||||
}
|
||||
|
||||
bool RseNode::dsqlAggregate2Finder(Aggregate2Finder& visitor)
|
||||
{
|
||||
AutoSetRestore<bool> autoCurrentScopeLevelEqual(&visitor.currentScopeLevelEqual, false);
|
||||
// Pass dsqlWhere, dsqlSelectList and dsqlStreams.
|
||||
return visitor.visit(dsqlWhere) | visitor.visit(dsqlSelectList) | visitor.visit(dsqlStreams);
|
||||
return visitor.visit(dsqlWhere) || visitor.visit(dsqlSelectList) || visitor.visit(dsqlStreams);
|
||||
}
|
||||
|
||||
bool RseNode::dsqlInvalidReferenceFinder(InvalidReferenceFinder& visitor)
|
||||
@ -2453,7 +2452,7 @@ bool RseNode::dsqlSubSelectFinder(SubSelectFinder& visitor)
|
||||
bool RseNode::dsqlFieldFinder(FieldFinder& visitor)
|
||||
{
|
||||
// Pass dsqlWhere and dsqlSelectList and dsqlStreams.
|
||||
return visitor.visit(dsqlWhere) | visitor.visit(dsqlSelectList) | visitor.visit(dsqlStreams);
|
||||
return visitor.visit(dsqlWhere) || visitor.visit(dsqlSelectList) || visitor.visit(dsqlStreams);
|
||||
}
|
||||
|
||||
RseNode* RseNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
@ -3020,8 +3019,8 @@ void RseNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb)
|
||||
|
||||
if (rse_plan)
|
||||
{
|
||||
planSet(csb, rse_plan);
|
||||
planCheck(csb);
|
||||
planSet(tdbb, csb, rse_plan);
|
||||
planCheck(tdbb, csb);
|
||||
}
|
||||
|
||||
csb->csb_current_nodes.pop();
|
||||
@ -3093,7 +3092,7 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr
|
||||
|
||||
// Check that all streams in the RseNode have a plan specified for them.
|
||||
// If they are not, there are streams in the RseNode which were not mentioned in the plan.
|
||||
void RseNode::planCheck(const CompilerScratch* csb) const
|
||||
void RseNode::planCheck(thread_db* tdbb, const CompilerScratch* csb) const
|
||||
{
|
||||
// if any streams are not marked with a plan, give an error
|
||||
|
||||
@ -3109,26 +3108,27 @@ void RseNode::planCheck(const CompilerScratch* csb) const
|
||||
|
||||
if (!csb->csb_rpt[stream].csb_plan)
|
||||
{
|
||||
const auto name = relation ? relation->rel_name :
|
||||
procedure ? procedure->getName().toString() : "";
|
||||
const auto name = relation ? relation()->getName() :
|
||||
procedure ? procedure(tdbb)->getName().toString() : "";
|
||||
|
||||
ERR_post(Arg::Gds(isc_no_stream_plan) << Arg::Str(name));
|
||||
}
|
||||
}
|
||||
else if (const auto rse = nodeAs<RseNode>(node))
|
||||
rse->planCheck(csb);
|
||||
rse->planCheck(tdbb, csb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Go through the streams in the plan, find the corresponding streams in the RseNode and store the
|
||||
// plan for that stream. Do it once and only once to make sure there is a one-to-one correspondence
|
||||
// between streams in the query and streams in the plan.
|
||||
void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
|
||||
void RseNode::planSet(thread_db* tdbb, CompilerScratch* csb, PlanNode* plan)
|
||||
{
|
||||
if (plan->type == PlanNode::TYPE_JOIN)
|
||||
{
|
||||
for (auto planNode : plan->subNodes)
|
||||
planSet(csb, planNode);
|
||||
planSet(tdbb, csb, planNode);
|
||||
}
|
||||
|
||||
if (plan->type != PlanNode::TYPE_RETRIEVE)
|
||||
@ -3141,35 +3141,35 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
|
||||
|
||||
string planAlias;
|
||||
|
||||
jrd_rel* planRelation = nullptr;
|
||||
RelationPermanent* planRelation = nullptr;
|
||||
if (const auto relationNode = nodeAs<RelationSourceNode>(plan->recordSourceNode))
|
||||
{
|
||||
planRelation = relationNode->relation;
|
||||
planRelation = relationNode->relation();
|
||||
planAlias = relationNode->alias;
|
||||
}
|
||||
|
||||
jrd_prc* planProcedure = nullptr;
|
||||
RoutinePermanent* planProcedure = nullptr;
|
||||
if (const auto procedureNode = nodeAs<ProcedureSourceNode>(plan->recordSourceNode))
|
||||
{
|
||||
planProcedure = procedureNode->procedure;
|
||||
planProcedure = procedureNode->procedure();
|
||||
planAlias = procedureNode->alias;
|
||||
}
|
||||
|
||||
fb_assert(planRelation || planProcedure);
|
||||
|
||||
const auto name = planRelation ? planRelation->rel_name :
|
||||
const auto name = planRelation ? planRelation->getName() :
|
||||
planProcedure ? planProcedure->getName().toString() : "";
|
||||
|
||||
// If the plan references a view, find the real base relation
|
||||
// we are interested in by searching the view map
|
||||
StreamType* map = nullptr;
|
||||
jrd_rel* viewRelation = nullptr;
|
||||
jrd_prc* viewProcedure = nullptr;
|
||||
RelationPermanent* viewRelation = nullptr;
|
||||
RoutinePermanent* viewProcedure = nullptr;
|
||||
|
||||
if (tail->csb_map)
|
||||
{
|
||||
auto tailName = tail->csb_relation ? tail->csb_relation->rel_name :
|
||||
tail->csb_procedure ? tail->csb_procedure->getName().toString() : "";
|
||||
auto tailName = tail->csb_relation ? tail->csb_relation()->getName() :
|
||||
tail->csb_procedure ? tail->csb_procedure()->getName().toString() : "";
|
||||
|
||||
// If the user has specified an alias, skip past it to find the alias
|
||||
// for the base table (if multiple aliases are specified)
|
||||
@ -3194,15 +3194,15 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
|
||||
{
|
||||
map = mapBase;
|
||||
tail = &csb->csb_rpt[*map];
|
||||
viewRelation = tail->csb_relation;
|
||||
viewProcedure = tail->csb_procedure;
|
||||
viewRelation = tail->csb_relation();
|
||||
viewProcedure = tail->csb_procedure();
|
||||
|
||||
// If the plan references the view itself, make sure that
|
||||
// If the plan references the view itself, make sure that
|
||||
// the view is on a single table. If it is, fix up the plan
|
||||
// to point to the base relation.
|
||||
|
||||
if ((viewRelation && planRelation &&
|
||||
viewRelation->rel_id == planRelation->rel_id) ||
|
||||
viewRelation->getId() == planRelation->getId()) ||
|
||||
(viewProcedure && planProcedure &&
|
||||
viewProcedure->getId() == planProcedure->getId()))
|
||||
{
|
||||
@ -3237,11 +3237,11 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
|
||||
for (duplicateMap++; *duplicateMap; ++duplicateMap)
|
||||
{
|
||||
const auto duplicateTail = &csb->csb_rpt[*duplicateMap];
|
||||
const auto relation = duplicateTail->csb_relation;
|
||||
const auto procedure = duplicateTail->csb_procedure;
|
||||
const auto relation = duplicateTail->csb_relation();
|
||||
const auto procedure = duplicateTail->csb_procedure();
|
||||
|
||||
if ((relation && planRelation &&
|
||||
relation->rel_id == planRelation->rel_id) ||
|
||||
relation->getId() == planRelation->getId()) ||
|
||||
(procedure && planProcedure &&
|
||||
procedure->getId() == planProcedure->getId()))
|
||||
{
|
||||
@ -3253,7 +3253,7 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
|
||||
}
|
||||
else
|
||||
{
|
||||
duplicateName = relation ? relation->rel_name :
|
||||
duplicateName = relation ? relation->getName() :
|
||||
procedure ? procedure->getName().toString() : "";
|
||||
map = duplicateMap;
|
||||
tail = duplicateTail;
|
||||
@ -3271,8 +3271,8 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
|
||||
{
|
||||
tail = &csb->csb_rpt[*map];
|
||||
|
||||
tailName = tail->csb_relation ? tail->csb_relation->rel_name :
|
||||
tail->csb_procedure ? tail->csb_procedure->getName().toString() : "";
|
||||
tailName = tail->csb_relation ? tail->csb_relation()->getName() :
|
||||
tail->csb_procedure ? tail->csb_procedure()->getName().toString() : "";
|
||||
|
||||
// Match the user-supplied alias with the alias supplied
|
||||
// with the view definition. Failing that, try the base
|
||||
@ -3319,9 +3319,9 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
|
||||
}
|
||||
|
||||
if ((tail->csb_relation && planRelation &&
|
||||
tail->csb_relation->rel_id != planRelation->rel_id && !viewRelation) ||
|
||||
tail->csb_relation()->getId() != planRelation->getId() && !viewRelation) ||
|
||||
(tail->csb_procedure && planProcedure &&
|
||||
tail->csb_procedure->getId() != planProcedure->getId() && !viewProcedure))
|
||||
tail->csb_procedure()->getId() != planProcedure->getId() && !viewProcedure))
|
||||
{
|
||||
// table or procedure %s is referenced in the plan but not the from list
|
||||
ERR_post(Arg::Gds(isc_stream_not_found) << Arg::Str(name));
|
||||
|
@ -416,10 +416,10 @@ public:
|
||||
public:
|
||||
MetaName dsqlName;
|
||||
Firebird::string alias; // SQL alias for the relation
|
||||
CachedResource<jrd_rel> relation;
|
||||
Rsc::Rel relation;
|
||||
|
||||
private:
|
||||
CachedResource<jrd_rel> view; // parent view for posting access
|
||||
Rsc::Rel view; // parent view for posting access
|
||||
|
||||
public:
|
||||
SSHORT context; // user-specified context number for the relation reference
|
||||
@ -433,14 +433,11 @@ public:
|
||||
: TypedNode<RecordSourceNode, RecordSourceNode::TYPE_PROCEDURE>(pool),
|
||||
dsqlName(pool, aDsqlName),
|
||||
alias(pool),
|
||||
procedure(NULL),
|
||||
sourceList(NULL),
|
||||
targetList(NULL),
|
||||
in_msg(NULL),
|
||||
view(NULL),
|
||||
procedureId(0),
|
||||
context(0),
|
||||
isSubRoutine(false)
|
||||
context(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -508,17 +505,16 @@ public:
|
||||
cache management policies yet, so I leave it for the other day.
|
||||
***/
|
||||
|
||||
jrd_prc* procedure;
|
||||
SubRoutine<jrd_prc> procedure;
|
||||
NestConst<ValueListNode> sourceList;
|
||||
NestConst<ValueListNode> targetList;
|
||||
|
||||
private:
|
||||
NestConst<MessageNode> in_msg;
|
||||
|
||||
jrd_rel* view;
|
||||
Rsc::Rel view;
|
||||
USHORT procedureId;
|
||||
SSHORT context;
|
||||
bool isSubRoutine;
|
||||
};
|
||||
|
||||
class AggregateSourceNode final : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_AGGREGATE_SOURCE>
|
||||
@ -871,8 +867,8 @@ public:
|
||||
virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream);
|
||||
|
||||
private:
|
||||
void planCheck(const CompilerScratch* csb) const;
|
||||
static void planSet(CompilerScratch* csb, PlanNode* plan);
|
||||
void planCheck(thread_db* tdbb, const CompilerScratch* csb) const;
|
||||
static void planSet(thread_db* tdbb, CompilerScratch* csb, PlanNode* plan);
|
||||
|
||||
public:
|
||||
NestConst<ValueExprNode> dsqlFirst;
|
||||
|
@ -43,22 +43,41 @@ using namespace Firebird;
|
||||
|
||||
/// jrd_rel
|
||||
|
||||
jrd_rel::jrd_rel(MemoryPool& p, MetaId id)
|
||||
: rel_pool(&p), rel_id(id), rel_current_fmt(0),
|
||||
rel_flags(REL_gc_lockneed), rel_current_format(nullptr),
|
||||
rel_name(p), rel_owner_name(p), rel_security_name(p),
|
||||
rel_formats(nullptr), rel_fields(nullptr),
|
||||
rel_view_rse(nullptr), rel_view_contexts(p),
|
||||
rel_file(nullptr), rel_gc_records(p),
|
||||
rel_sweep_count(0), rel_scan_count(0),
|
||||
rel_partners_lock(nullptr), rel_rescan_lock(nullptr),
|
||||
rel_gc_lock(nullptr), rel_index_locks(p), rel_index_blocks(nullptr),
|
||||
rel_pre_erase(nullptr), rel_post_erase(nullptr),
|
||||
rel_pre_modify(nullptr), rel_post_modify(nullptr),
|
||||
rel_pre_store(nullptr), rel_post_store(nullptr),
|
||||
rel_ss_definer(false), rel_pages_inst(nullptr),
|
||||
rel_pages_base(p), rel_pages_free(nullptr)
|
||||
jrd_rel::jrd_rel(MemoryPool& p, RelationPermanent* r)
|
||||
: rel_pool(&p),
|
||||
rel_perm(r),
|
||||
rel_current_fmt(0),
|
||||
rel_flags(0),
|
||||
rel_current_format(nullptr),
|
||||
rel_fields(nullptr),
|
||||
rel_view_rse(nullptr),
|
||||
rel_view_contexts(p),
|
||||
rel_scan_count(0),
|
||||
rel_index_blocks(nullptr),
|
||||
rel_ss_definer(false)
|
||||
{ }
|
||||
|
||||
RelationPermanent::RelationPermanent(MemoryPool& p, MetaId id)
|
||||
: PermanentStorage(p),
|
||||
rel_existence_lock(nullptr),
|
||||
rel_partners_lock(nullptr),
|
||||
rel_rescan_lock(nullptr),
|
||||
rel_gc_lock(this),
|
||||
rel_gc_records(p),
|
||||
rel_formats(nullptr),
|
||||
rel_index_locks(getPool()),
|
||||
rel_name(p),
|
||||
rel_id(id),
|
||||
rel_flags(0u),
|
||||
rel_pages_inst(nullptr),
|
||||
rel_pages_base(p),
|
||||
rel_pages_free(nullptr),
|
||||
rel_file(nullptr)
|
||||
{ }
|
||||
|
||||
RelationPermanent::~RelationPermanent()
|
||||
{
|
||||
delete rel_file;
|
||||
}
|
||||
|
||||
bool jrd_rel::isReplicating(thread_db* tdbb)
|
||||
@ -71,12 +90,12 @@ bool jrd_rel::isReplicating(thread_db* tdbb)
|
||||
attachment->checkReplSetLock(tdbb);
|
||||
|
||||
if (rel_repl_state.isUnknown())
|
||||
rel_repl_state = MET_get_repl_state(tdbb, rel_name);
|
||||
rel_repl_state = MET_get_repl_state(tdbb, getName());
|
||||
|
||||
return rel_repl_state.value;
|
||||
}
|
||||
|
||||
RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool allocPages)
|
||||
RelationPages* RelationPermanent::getPagesInternal(thread_db* tdbb, TraNumber tran, bool allocPages)
|
||||
{
|
||||
if (tdbb->tdbb_flags & TDBB_use_db_page_space)
|
||||
return &rel_pages_base;
|
||||
@ -100,8 +119,10 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a
|
||||
else
|
||||
inst_id = PAG_attachment_id(tdbb);
|
||||
|
||||
MutexLockGuard relPerm(rel_pages_mutex, FB_FUNCTION);
|
||||
|
||||
if (!rel_pages_inst)
|
||||
rel_pages_inst = FB_NEW_POOL(*rel_pool) RelationPagesInstances(*rel_pool);
|
||||
rel_pages_inst = FB_NEW_POOL(getPool()) RelationPagesInstances(getPool());
|
||||
|
||||
FB_SIZE_T pos;
|
||||
if (!rel_pages_inst->find(inst_id, pos))
|
||||
@ -111,7 +132,7 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a
|
||||
|
||||
RelationPages* newPages = rel_pages_free;
|
||||
if (!newPages) {
|
||||
newPages = FB_NEW_POOL(*rel_pool) RelationPages(*rel_pool);
|
||||
newPages = FB_NEW_POOL(getPool()) RelationPages(getPool());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -132,7 +153,7 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"jrd_rel::getPages rel_id %u, inst %" UQUADFORMAT", ppp %" ULONGFORMAT", irp %" ULONGFORMAT", addr 0x%x\n",
|
||||
rel_id,
|
||||
getId(),
|
||||
newPages->rel_instance_id,
|
||||
newPages->rel_pages ? (*newPages->rel_pages)[0] : 0,
|
||||
newPages->rel_index_root,
|
||||
@ -167,7 +188,7 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"jrd_rel::getPages rel_id %u, inst %" UQUADFORMAT", irp %" ULONGFORMAT", idx %u, idx_root %" ULONGFORMAT", addr 0x%x\n",
|
||||
rel_id,
|
||||
getId(),
|
||||
newPages->rel_instance_id,
|
||||
newPages->rel_index_root,
|
||||
idx.idx_id,
|
||||
@ -187,7 +208,7 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a
|
||||
return pages;
|
||||
}
|
||||
|
||||
bool jrd_rel::delPages(thread_db* tdbb, TraNumber tran, RelationPages* aPages)
|
||||
bool RelationPermanent::delPages(thread_db* tdbb, TraNumber tran, RelationPages* aPages)
|
||||
{
|
||||
RelationPages* pages = aPages ? aPages : getPages(tdbb, tran, false);
|
||||
if (!pages || !pages->rel_instance_id)
|
||||
@ -203,7 +224,7 @@ bool jrd_rel::delPages(thread_db* tdbb, TraNumber tran, RelationPages* aPages)
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"jrd_rel::delPages rel_id %u, inst %" UQUADFORMAT", ppp %" ULONGFORMAT", irp %" ULONGFORMAT", addr 0x%x\n",
|
||||
rel_id,
|
||||
getId(),
|
||||
pages->rel_instance_id,
|
||||
pages->rel_pages ? (*pages->rel_pages)[0] : 0,
|
||||
pages->rel_index_root,
|
||||
@ -229,7 +250,7 @@ bool jrd_rel::delPages(thread_db* tdbb, TraNumber tran, RelationPages* aPages)
|
||||
return true;
|
||||
}
|
||||
|
||||
void jrd_rel::retainPages(thread_db* tdbb, TraNumber oldNumber, TraNumber newNumber)
|
||||
void RelationPermanent::retainPages(thread_db* tdbb, TraNumber oldNumber, TraNumber newNumber)
|
||||
{
|
||||
fb_assert(rel_flags & REL_temp_tran);
|
||||
fb_assert(oldNumber != 0);
|
||||
@ -252,9 +273,9 @@ void jrd_rel::retainPages(thread_db* tdbb, TraNumber oldNumber, TraNumber newNum
|
||||
rel_pages_inst->add(pages);
|
||||
}
|
||||
|
||||
void jrd_rel::getRelLockKey(thread_db* tdbb, UCHAR* key)
|
||||
void RelationPermanent::getRelLockKey(thread_db* tdbb, UCHAR* key)
|
||||
{
|
||||
const ULONG val = rel_id;
|
||||
const ULONG val = getId();
|
||||
memcpy(key, &val, sizeof(ULONG));
|
||||
key += sizeof(ULONG);
|
||||
|
||||
@ -262,19 +283,19 @@ void jrd_rel::getRelLockKey(thread_db* tdbb, UCHAR* key)
|
||||
memcpy(key, &inst_id, sizeof(inst_id));
|
||||
}
|
||||
|
||||
USHORT jrd_rel::getRelLockKeyLength() const
|
||||
USHORT constexpr RelationPermanent::getRelLockKeyLength()
|
||||
{
|
||||
return sizeof(ULONG) + sizeof(SINT64);
|
||||
}
|
||||
|
||||
void jrd_rel::cleanUp()
|
||||
void RelationPermanent::cleanUp()
|
||||
{
|
||||
delete rel_pages_inst;
|
||||
rel_pages_inst = NULL;
|
||||
}
|
||||
|
||||
|
||||
void jrd_rel::fillPagesSnapshot(RelPagesSnapshot& snapshot, const bool attachmentOnly)
|
||||
void RelationPermanent::fillPagesSnapshot(RelPagesSnapshot& snapshot, const bool attachmentOnly)
|
||||
{
|
||||
if (rel_pages_inst)
|
||||
{
|
||||
@ -311,7 +332,7 @@ void jrd_rel::fillPagesSnapshot(RelPagesSnapshot& snapshot, const bool attachmen
|
||||
snapshot.add(&rel_pages_base);
|
||||
}
|
||||
|
||||
void jrd_rel::RelPagesSnapshot::clear()
|
||||
void RelationPermanent::RelPagesSnapshot::clear()
|
||||
{
|
||||
#ifdef DEV_BUILD
|
||||
thread_db* tdbb = NULL;
|
||||
@ -355,52 +376,50 @@ bool jrd_rel::hasTriggers() const
|
||||
|
||||
void jrd_rel::releaseTriggers(thread_db* tdbb, bool destroy)
|
||||
{
|
||||
MET_release_triggers(tdbb, &rel_pre_store, destroy);
|
||||
MET_release_triggers(tdbb, &rel_post_store, destroy);
|
||||
MET_release_triggers(tdbb, &rel_pre_erase, destroy);
|
||||
MET_release_triggers(tdbb, &rel_post_erase, destroy);
|
||||
MET_release_triggers(tdbb, &rel_pre_modify, destroy);
|
||||
MET_release_triggers(tdbb, &rel_post_modify, destroy);
|
||||
for (int n = 0; n < TRIGGER_MAX; ++n)
|
||||
rel_triggers[n].release(tdbb, destroy);
|
||||
}
|
||||
|
||||
void jrd_rel::replaceTriggers(thread_db* tdbb, TrigVectorPtr* triggers)
|
||||
void Triggers::release(thread_db* tdbb, bool destroy)
|
||||
{
|
||||
TrigVectorPtr tmp_vector;
|
||||
/***********************************************
|
||||
*
|
||||
* M E T _ r e l e a s e _ t r i g g e r s
|
||||
*
|
||||
***********************************************
|
||||
*
|
||||
* Functional description
|
||||
* Release a possibly null vector of triggers.
|
||||
* If triggers are still active let someone
|
||||
* else do the work.
|
||||
*
|
||||
**************************************/
|
||||
/* TrigVector* vector = vector_ptr->load(); !!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
tmp_vector.store(rel_pre_store.load());
|
||||
rel_pre_store.store(triggers[TRIGGER_PRE_STORE].load());
|
||||
MET_release_triggers(tdbb, &tmp_vector, true);
|
||||
if (!vector)
|
||||
return;
|
||||
|
||||
tmp_vector.store(rel_post_store.load());
|
||||
rel_post_store.store(triggers[TRIGGER_POST_STORE].load());
|
||||
MET_release_triggers(tdbb, &tmp_vector, true);
|
||||
if (!destroy)
|
||||
{
|
||||
vector->decompile(tdbb);
|
||||
return;
|
||||
}
|
||||
|
||||
tmp_vector.store(rel_pre_erase.load());
|
||||
rel_pre_erase.store(triggers[TRIGGER_PRE_ERASE].load());
|
||||
MET_release_triggers(tdbb, &tmp_vector, true);
|
||||
*vector_ptr = NULL;
|
||||
|
||||
tmp_vector.store(rel_post_erase.load());
|
||||
rel_post_erase.store(triggers[TRIGGER_POST_ERASE].load());
|
||||
MET_release_triggers(tdbb, &tmp_vector, true);
|
||||
if (vector->hasActive())
|
||||
return;
|
||||
|
||||
tmp_vector.store(rel_pre_modify.load());
|
||||
rel_pre_modify.store(triggers[TRIGGER_PRE_MODIFY].load());
|
||||
MET_release_triggers(tdbb, &tmp_vector, true);
|
||||
|
||||
tmp_vector.store(rel_post_modify.load());
|
||||
rel_post_modify.store(triggers[TRIGGER_POST_MODIFY].load());
|
||||
MET_release_triggers(tdbb, &tmp_vector, true);
|
||||
vector->release(tdbb); */
|
||||
}
|
||||
|
||||
Lock* jrd_rel::createLock(thread_db* tdbb, MemoryPool* pool, jrd_rel* relation, lck_t lckType, bool noAst)
|
||||
Lock* RelationPermanent::createLock(thread_db* tdbb, lck_t lckType, bool noAst)
|
||||
{
|
||||
if (!pool)
|
||||
pool = relation->rel_pool;
|
||||
const USHORT relLockLen = getRelLockKeyLength();
|
||||
|
||||
const USHORT relLockLen = relation->getRelLockKeyLength();
|
||||
|
||||
Lock* lock = FB_NEW_RPT(*pool, relLockLen) Lock(tdbb, relLockLen, lckType, relation);
|
||||
relation->getRelLockKey(tdbb, lock->getKeyPtr());
|
||||
Lock* lock = FB_NEW_RPT(getPool(), relLockLen)
|
||||
Lock(tdbb, relLockLen, lckType, lckType == LCK_relation ? (void*)this : (void*)&rel_gc_lock);
|
||||
getRelLockKey(tdbb, lock->getKeyPtr());
|
||||
|
||||
lock->lck_type = lckType;
|
||||
switch (lckType)
|
||||
@ -409,7 +428,7 @@ Lock* jrd_rel::createLock(thread_db* tdbb, MemoryPool* pool, jrd_rel* relation,
|
||||
break;
|
||||
|
||||
case LCK_rel_gc:
|
||||
lock->lck_ast = noAst ? NULL : blocking_ast_gcLock;
|
||||
lock->lck_ast = noAst ? nullptr : GCLock::ast;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -419,210 +438,231 @@ Lock* jrd_rel::createLock(thread_db* tdbb, MemoryPool* pool, jrd_rel* relation,
|
||||
return lock;
|
||||
}
|
||||
|
||||
bool jrd_rel::acquireGCLock(thread_db* tdbb, int wait)
|
||||
|
||||
void GCLock::blockingAst()
|
||||
{
|
||||
fb_assert(rel_flags & REL_gc_lockneed);
|
||||
if (!(rel_flags & REL_gc_lockneed))
|
||||
/****
|
||||
SR - gc forbidden, awaiting moment to re-establish SW lock
|
||||
SW - gc allowed, usual state
|
||||
PW - gc allowed to the one connection only
|
||||
****/
|
||||
|
||||
Database* dbb = lck->lck_dbb;
|
||||
|
||||
AsyncContextHolder tdbb(dbb, FB_FUNCTION, lck);
|
||||
|
||||
unsigned oldFlags = flags.load(std::memory_order_acquire);
|
||||
do
|
||||
{
|
||||
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;
|
||||
fb_assert(oldFlags & GC_locked);
|
||||
if (!(oldFlags & GC_locked)) // work already done synchronously ?
|
||||
return;
|
||||
} while (!flags.compare_exchange_weak(oldFlags, oldFlags | GC_blocking,
|
||||
std::memory_order_release, std::memory_order_acquire));
|
||||
|
||||
if (oldFlags & GC_counterMask)
|
||||
return;
|
||||
|
||||
if (oldFlags & GC_disabled)
|
||||
{
|
||||
// someone acquired EX lock
|
||||
|
||||
fb_assert(lck->lck_id);
|
||||
fb_assert(lck->lck_physical == LCK_SR);
|
||||
|
||||
LCK_release(tdbb, lck);
|
||||
flags.fetch_and(~(GC_disabled | GC_blocking | GC_locked));
|
||||
}
|
||||
else
|
||||
{
|
||||
// someone acquired PW lock
|
||||
|
||||
fb_assert(lck->lck_id);
|
||||
fb_assert(lck->lck_physical == LCK_SW);
|
||||
|
||||
flags.fetch_or(GC_disabled);
|
||||
downgrade(tdbb);
|
||||
}
|
||||
}
|
||||
|
||||
bool GCLock::acquire(thread_db* tdbb, int wait)
|
||||
{
|
||||
unsigned oldFlags = flags.load(std::memory_order_acquire);
|
||||
for(;;)
|
||||
{
|
||||
if (oldFlags & (GC_blocking | GC_disabled)) // lock should not be obtained
|
||||
return false;
|
||||
|
||||
const unsigned newFlags = oldFlags + 1;
|
||||
if (newFlags & GC_guardBit)
|
||||
incrementError();
|
||||
|
||||
if (!flags.compare_exchange_weak(oldFlags, newFlags, std::memory_order_release, std::memory_order_acquire))
|
||||
continue;
|
||||
|
||||
if (oldFlags & GC_locked) // lock was already taken when we checked flags
|
||||
return true;
|
||||
|
||||
if (!(oldFlags & GC_counterMask)) // we must take lock
|
||||
break;
|
||||
|
||||
// unstable state - someone else it getting a lock right now
|
||||
// decrement counter, wait a bit and retry
|
||||
--flags;
|
||||
suspend();
|
||||
oldFlags = flags.fetch_sub(1, std::memory_order_acquire); // reload after wait
|
||||
}
|
||||
|
||||
if (!rel_gc_lock)
|
||||
rel_gc_lock = createLock(tdbb, NULL, this, LCK_rel_gc, false);
|
||||
// We incremented counter from 0 to 1 - take care about lck
|
||||
if (!lck)
|
||||
lck = relPerm->createLock(tdbb, LCK_rel_gc, false);
|
||||
|
||||
fb_assert(!rel_gc_lock->lck_id);
|
||||
fb_assert(!(rel_flags & REL_gc_blocking));
|
||||
fb_assert(!lck->lck_id);
|
||||
|
||||
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))
|
||||
bool ret;
|
||||
if (oldFlags & GC_disabled)
|
||||
ret = LCK_lock(tdbb, lck, LCK_SR, wait);
|
||||
else
|
||||
{
|
||||
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)
|
||||
ret = LCK_lock(tdbb, lck, LCK_SW, wait);
|
||||
if (ret)
|
||||
{
|
||||
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;
|
||||
|
||||
AsyncContextHolder tdbb(dbb, FB_FUNCTION, lock);
|
||||
|
||||
fb_assert(!(relation->rel_flags & REL_gc_lockneed));
|
||||
if (relation->rel_flags & REL_gc_lockneed) // work already done synchronously ?
|
||||
return 0;
|
||||
|
||||
relation->rel_flags |= REL_gc_blocking;
|
||||
if (relation->rel_sweep_count)
|
||||
return 0;
|
||||
|
||||
if (relation->rel_flags & REL_gc_disabled)
|
||||
{
|
||||
// someone 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;
|
||||
flags.fetch_or(GC_locked);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// someone acquired PW lock
|
||||
|
||||
fb_assert(lock->lck_id);
|
||||
fb_assert(lock->lck_physical == LCK_SW);
|
||||
|
||||
relation->rel_flags |= REL_gc_disabled;
|
||||
relation->downgradeGCLock(tdbb);
|
||||
flags.fetch_or(GC_disabled);
|
||||
ret = LCK_lock(tdbb, lck, LCK_SR, wait);
|
||||
}
|
||||
}
|
||||
catch (const Firebird::Exception&)
|
||||
{} // no-op
|
||||
|
||||
return 0;
|
||||
flags.fetch_sub(1, std::memory_order_release);
|
||||
if (!ret)
|
||||
flags.fetch_and(~GC_disabled, std::memory_order_release);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// jrd_rel::GCExclusive
|
||||
|
||||
jrd_rel::GCExclusive::GCExclusive(thread_db* tdbb, jrd_rel* relation) :
|
||||
m_tdbb(tdbb),
|
||||
m_relation(relation),
|
||||
m_lock(NULL)
|
||||
void GCLock::downgrade(thread_db* tdbb)
|
||||
{
|
||||
unsigned oldFlags = flags.load(std::memory_order_acquire);
|
||||
unsigned newFlags;
|
||||
do
|
||||
{
|
||||
newFlags = oldFlags - 1;
|
||||
if (newFlags & GC_guardBit)
|
||||
incrementError();
|
||||
|
||||
if ((newFlags & GC_counterMask == 0) && (newFlags & GC_blocking))
|
||||
{
|
||||
fb_assert(oldFlags & GC_locked);
|
||||
fb_assert(lck->lck_id);
|
||||
fb_assert(lck->lck_physical == LCK_SW);
|
||||
|
||||
LCK_downgrade(tdbb, lck);
|
||||
|
||||
if (lck->lck_physical != LCK_SR)
|
||||
{
|
||||
newFlags &= ~GC_disabled;
|
||||
if (lck->lck_physical < LCK_SR)
|
||||
newFlags &= GC_locked;
|
||||
}
|
||||
else
|
||||
newFlags |= GC_disabled;
|
||||
|
||||
newFlags &= ~GC_blocking;
|
||||
}
|
||||
} while (!flags.compare_exchange_weak(oldFlags, newFlags, std::memory_order_release, std::memory_order_acquire));
|
||||
}
|
||||
|
||||
jrd_rel::GCExclusive::~GCExclusive()
|
||||
bool GCLock::disable(thread_db* tdbb, int wait, Lock*& tempLock)
|
||||
{
|
||||
release();
|
||||
delete m_lock;
|
||||
}
|
||||
ThreadStatusGuard temp_status(tdbb);
|
||||
|
||||
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;
|
||||
unsigned oldFlags = flags.load(std::memory_order_acquire);
|
||||
do {
|
||||
if (oldFlags & GC_disabled)
|
||||
return false;
|
||||
} while (flags.compare_exchange_weak(oldFlags, oldFlags | GC_disabled,
|
||||
std::memory_order_release, std::memory_order_acquire));
|
||||
|
||||
int sleeps = -wait * 10;
|
||||
while (m_relation->rel_sweep_count)
|
||||
while (flags.load(std::memory_order_relaxed) & GC_counterMask)
|
||||
{
|
||||
EngineCheckout cout(m_tdbb, FB_FUNCTION);
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION);
|
||||
Thread::sleep(100);
|
||||
|
||||
if (wait < 0 && --sleeps == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_relation->rel_sweep_count)
|
||||
if (flags.load(std::memory_order_relaxed) & GC_counterMask)
|
||||
{
|
||||
m_relation->rel_flags &= ~REL_gc_disabled;
|
||||
flags.fetch_and(~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);
|
||||
}
|
||||
ensureReleased(tdbb);
|
||||
|
||||
// we need no AST here
|
||||
if (!m_lock)
|
||||
m_lock = jrd_rel::createLock(m_tdbb, NULL, m_relation, LCK_rel_gc, true);
|
||||
if (!tempLock)
|
||||
tempLock = relPerm->createLock(tdbb, LCK_rel_gc, true);
|
||||
|
||||
const bool ret = LCK_lock(m_tdbb, m_lock, LCK_PW, wait);
|
||||
const bool ret = LCK_lock(tdbb, tempLock, LCK_PW, wait);
|
||||
if (!ret)
|
||||
m_relation->rel_flags &= ~REL_gc_disabled;
|
||||
flags.fetch_and(~GC_disabled);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jrd_rel::GCExclusive::release()
|
||||
void GCLock::ensureReleased(thread_db* tdbb)
|
||||
{
|
||||
if (!m_lock || !m_lock->lck_id)
|
||||
unsigned oldFlags = flags.load(std::memory_order_acquire);
|
||||
for (;;)
|
||||
{
|
||||
if (oldFlags & GC_locked)
|
||||
{
|
||||
if (!flags.compare_exchange_strong(oldFlags, oldFlags & ~GC_locked,
|
||||
std::memory_order_release, std::memory_order_acquire))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// exactly one who cleared GC_locked bit releases a lock
|
||||
LCK_release(tdbb, lck);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GCLock::enable(thread_db* tdbb, Lock* tempLock)
|
||||
{
|
||||
if (!lck || !lck->lck_id)
|
||||
return;
|
||||
|
||||
fb_assert(m_relation->rel_flags & REL_gc_disabled);
|
||||
fb_assert(flags.load() & 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);
|
||||
}
|
||||
ensureReleased(tdbb);
|
||||
|
||||
LCK_convert(m_tdbb, m_lock, LCK_EX, LCK_WAIT);
|
||||
m_relation->rel_flags &= ~REL_gc_disabled;
|
||||
LCK_convert(tdbb, tempLock, LCK_EX, LCK_WAIT);
|
||||
flags.fetch_and(~GC_disabled);
|
||||
|
||||
LCK_release(m_tdbb, m_lock);
|
||||
LCK_release(tdbb, tempLock);
|
||||
}
|
||||
|
||||
|
||||
bool jrd_rel::checkObject(thread_db* tdbb, Arg::StatusVector& error)
|
||||
{
|
||||
bool rc = MetadataCache::checkRelation(tdbb, this);
|
||||
if (!rc)
|
||||
error << Arg::Gds(isc_relnotdef) << Arg::Str(rel_name);
|
||||
error << Arg::Gds(isc_relnotdef) << Arg::Str(getName());
|
||||
return rc;
|
||||
}
|
||||
|
||||
void jrd_rel::afterUnlock(thread_db* tdbb, unsigned flags)
|
||||
{
|
||||
// release trigger requests
|
||||
releaseTriggers(tdbb, false);
|
||||
|
||||
// close external file
|
||||
EXT_fini(this, true);
|
||||
}
|
||||
|
||||
/// RelationPages
|
||||
|
||||
void RelationPages::free(RelationPages*& nextFree)
|
||||
@ -643,15 +683,7 @@ void RelationPages::free(RelationPages*& nextFree)
|
||||
}
|
||||
|
||||
|
||||
/// TrigVector
|
||||
|
||||
HazardPtr<Trigger> TrigVector::add(thread_db* tdbb, Trigger* trig)
|
||||
{
|
||||
FB_SIZE_T id = addCount.fetch_add(1);
|
||||
return store(tdbb, id, trig);
|
||||
}
|
||||
|
||||
HazardPtr<IndexLock> jrd_rel::getIndexLock(thread_db* tdbb, USHORT id)
|
||||
IndexLock* RelationPermanent::getIndexLock(thread_db* tdbb, MetaId id)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -667,32 +699,31 @@ HazardPtr<IndexLock> jrd_rel::getIndexLock(thread_db* tdbb, USHORT id)
|
||||
SET_TDBB(tdbb);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
|
||||
HazardPtr<IndexLock> indexLock;
|
||||
if (rel_id < (USHORT) rel_MAX)
|
||||
return indexLock;
|
||||
if (getId() < (MetaId) rel_MAX)
|
||||
return nullptr;
|
||||
|
||||
if (rel_index_locks.load(tdbb, id, indexLock))
|
||||
return indexLock;
|
||||
|
||||
IndexLock* index = FB_NEW_POOL(*rel_pool) IndexLock(*rel_pool, tdbb, this, id);
|
||||
return rel_index_locks.getObject(tdbb, id, CacheFlag::AUTOCREATE);
|
||||
/*
|
||||
IndexLock* index = FB_NEW_POOL(getPool()) IndexLock(getPool(), tdbb, this, id);
|
||||
if (!rel_index_locks.replace(tdbb, id, indexLock, index))
|
||||
delete index;
|
||||
|
||||
return indexLock;
|
||||
*/
|
||||
}
|
||||
|
||||
IndexLock::IndexLock(MemoryPool& p, thread_db* tdbb, jrd_rel* rel, USHORT id)
|
||||
IndexLock::IndexLock(MemoryPool& p, thread_db* tdbb, RelationPermanent* rel, USHORT id)
|
||||
: idl_relation(rel),
|
||||
idl_id(id),
|
||||
idl_lock(p, tdbb, LCK_idx_exist, (rel->rel_id << 16) | id, rel)
|
||||
{ }
|
||||
idl_lock(FB_NEW_RPT(p, 0) Lock(tdbb, sizeof(SLONG), LCK_idx_exist))
|
||||
{
|
||||
idl_lock->setKey((idl_relation->rel_id << 16) | id);
|
||||
}
|
||||
|
||||
const char* IndexLock::c_name() const
|
||||
{
|
||||
return "* unk *";
|
||||
}
|
||||
|
||||
static void jrd_rel::destroy(jrd_rel* rel)
|
||||
void jrd_rel::destroy(jrd_rel* rel)
|
||||
{
|
||||
rel->rel_flags |= REL_deleted;
|
||||
/*
|
||||
@ -712,17 +743,18 @@ static void jrd_rel::destroy(jrd_rel* rel)
|
||||
delete rel;
|
||||
}
|
||||
|
||||
static jrd_rel* jrd_rel::create(thread_db* tdbb, MetaId id)
|
||||
jrd_rel* jrd_rel::create(thread_db* tdbb, MemoryPool& pool, MetaId id, CacheObject::Flag flags)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
CHECK_DBB(dbb);
|
||||
|
||||
MetadataCache* mdc = dbb->dbb_mdc;
|
||||
MemoryPool& pool = mdc->getPool();
|
||||
|
||||
jrd_rel* relation = FB_NEW_POOL(pool) jrd_rel(pool, id);
|
||||
relation->scan(tdbb, mdc);
|
||||
RelationPermanent* rlp = mdc->lookupRelation(tdbb, id, flags & CacheFlag::NOSCAN);
|
||||
jrd_rel* relation = FB_NEW_POOL(pool) jrd_rel(pool, rlp);
|
||||
if (!(flags & CacheFlag::NOSCAN))
|
||||
relation->scan(tdbb);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
#ifndef JRD_RELATION_H
|
||||
#define JRD_RELATION_H
|
||||
|
||||
#include "../common/classes/RefCounted.h"
|
||||
|
||||
#include "../jrd/vec.h"
|
||||
#include "../jrd/btr.h"
|
||||
#include "../jrd/lck.h"
|
||||
@ -30,6 +32,7 @@
|
||||
#include "../jrd/Attachment.h"
|
||||
#include "../jrd/HazardPtr.h"
|
||||
#include "../jrd/ExtEngineManager.h"
|
||||
#include "../jrd/met_proto.h"
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
@ -42,76 +45,69 @@ class jrd_fld;
|
||||
class ExternalFile;
|
||||
class IndexLock;
|
||||
class IndexBlock;
|
||||
class RelationPermanent;
|
||||
class jrd_rel;
|
||||
|
||||
// trigger types
|
||||
const int TRIGGER_PRE_STORE = 1;
|
||||
const int TRIGGER_POST_STORE = 2;
|
||||
const int TRIGGER_PRE_MODIFY = 3;
|
||||
const int TRIGGER_POST_MODIFY = 4;
|
||||
const int TRIGGER_PRE_ERASE = 5;
|
||||
const int TRIGGER_POST_ERASE = 6;
|
||||
const int TRIGGER_MAX = 7;
|
||||
|
||||
// trigger type prefixes
|
||||
const int TRIGGER_PRE = 0;
|
||||
const int TRIGGER_POST = 1;
|
||||
|
||||
// trigger type suffixes
|
||||
const int TRIGGER_STORE = 1;
|
||||
const int TRIGGER_MODIFY = 2;
|
||||
const int TRIGGER_ERASE = 3;
|
||||
|
||||
// that's how trigger action types are encoded
|
||||
/*
|
||||
bit 0 = TRIGGER_PRE/TRIGGER_POST flag,
|
||||
bits 1-2 = TRIGGER_STORE/TRIGGER_MODIFY/TRIGGER_ERASE (slot #1),
|
||||
bits 3-4 = TRIGGER_STORE/TRIGGER_MODIFY/TRIGGER_ERASE (slot #2),
|
||||
bits 5-6 = TRIGGER_STORE/TRIGGER_MODIFY/TRIGGER_ERASE (slot #3),
|
||||
and finally the above calculated value is decremented
|
||||
|
||||
example #1:
|
||||
TRIGGER_POST_ERASE =
|
||||
= ((TRIGGER_ERASE << 1) | TRIGGER_POST) - 1 =
|
||||
= ((3 << 1) | 1) - 1 =
|
||||
= 0x00000110 (6)
|
||||
|
||||
example #2:
|
||||
TRIGGER_PRE_STORE_MODIFY =
|
||||
= ((TRIGGER_MODIFY << 3) | (TRIGGER_STORE << 1) | TRIGGER_PRE) - 1 =
|
||||
= ((2 << 3) | (1 << 1) | 0) - 1 =
|
||||
= 0x00010001 (17)
|
||||
|
||||
example #3:
|
||||
TRIGGER_POST_MODIFY_ERASE_STORE =
|
||||
= ((TRIGGER_STORE << 5) | (TRIGGER_ERASE << 3) | (TRIGGER_MODIFY << 1) | TRIGGER_POST) - 1 =
|
||||
= ((1 << 5) | (3 << 3) | (2 << 1) | 1) - 1 =
|
||||
= 0x00111100 (60)
|
||||
*/
|
||||
|
||||
// that's how trigger types are decoded
|
||||
#define TRIGGER_ACTION(value, shift) \
|
||||
(((((value + 1) >> shift) & 3) << 1) | ((value + 1) & 1)) - 1
|
||||
|
||||
#define TRIGGER_ACTION_SLOT(value, slot) \
|
||||
TRIGGER_ACTION(value, (slot * 2 - 1) )
|
||||
|
||||
const int TRIGGER_COMBINED_MAX = 128;
|
||||
|
||||
|
||||
|
||||
// Relation trigger definition
|
||||
|
||||
class Trigger : public CacheObject
|
||||
class Trigger : public Firebird::RefCounted
|
||||
{
|
||||
/*
|
||||
public:
|
||||
Firebird::HalfStaticArray<UCHAR, 128> blr; // BLR code
|
||||
Firebird::HalfStaticArray<UCHAR, 128> debugInfo; // Debug info
|
||||
Statement* statement; // Compiled statement
|
||||
bool releaseInProgress;
|
||||
bool sysTrigger;
|
||||
FB_UINT64 type; // Trigger type
|
||||
USHORT flags; // Flags as they are in RDB$TRIGGERS table
|
||||
jrd_rel* relation; // Trigger parent relation
|
||||
MetaName name; // Trigger name
|
||||
MetaName engine; // External engine name
|
||||
Firebird::string entryPoint; // External trigger entrypoint
|
||||
Firebird::string extBody; // External trigger body
|
||||
ExtEngineManager::Trigger* extTrigger; // External trigger
|
||||
Nullable<bool> ssDefiner;
|
||||
MetaName owner; // Owner for SQL SECURITY
|
||||
|
||||
bool hasData() const
|
||||
{
|
||||
return name.hasData() || sysTrigger;
|
||||
}
|
||||
|
||||
Key getKey() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
void compile(thread_db*); // Ensure that trigger is compiled
|
||||
int release(thread_db*); // Try to free trigger request
|
||||
|
||||
explicit Trigger(MemoryPool& p)
|
||||
: blr(p),
|
||||
debugInfo(p),
|
||||
statement(nullptr),
|
||||
releaseInProgress(false),
|
||||
sysTrigger(false),
|
||||
type(0),
|
||||
flags(0),
|
||||
relation(nullptr),
|
||||
name(p),
|
||||
engine(p),
|
||||
entryPoint(p),
|
||||
extBody(p),
|
||||
extTrigger(NULL)
|
||||
{}
|
||||
|
||||
virtual ~Trigger()
|
||||
{
|
||||
delete extTrigger;
|
||||
}
|
||||
|
||||
void removeFromCache(thread_db* tdbb) override
|
||||
{
|
||||
delayedDelete(tdbb);
|
||||
}
|
||||
|
||||
const char* c_name() const override
|
||||
{
|
||||
return name.c_str();
|
||||
}
|
||||
*/
|
||||
|
||||
public:
|
||||
Firebird::HalfStaticArray<UCHAR, 128> blr; // BLR code
|
||||
Firebird::HalfStaticArray<UCHAR, 128> debugInfo; // Debug info
|
||||
@ -129,10 +125,12 @@ public:
|
||||
Nullable<bool> ssDefiner;
|
||||
MetaName owner; // Owner for SQL SECURITY
|
||||
|
||||
MemoryPool& getPool();
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
void compile(thread_db*); // Ensure that trigger is compiled
|
||||
void release(thread_db*); // Try to free trigger request
|
||||
void free(thread_db*); // Try to free trigger request
|
||||
|
||||
explicit Trigger(MemoryPool& p)
|
||||
: blr(p),
|
||||
@ -151,19 +149,52 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Array of triggers (suppose separate arrays for triggers of different types)
|
||||
class TrigVector
|
||||
// Set of triggers (suppose separate arrays for triggers of different types)
|
||||
class Triggers
|
||||
{
|
||||
public:
|
||||
Triggers()
|
||||
// : nullify everything needed
|
||||
{ }
|
||||
|
||||
bool hasActive() const;
|
||||
void decompile(thread_db* tdbb);
|
||||
|
||||
virtual ~TrigVector() { }
|
||||
void addTrigger(thread_db* tdbb, Trigger* trigger);
|
||||
|
||||
virtual void addTrigger(thread_db* tdbb, Trigger* trigger) = 0;
|
||||
Trigger** begin() const;
|
||||
Trigger** end() const;
|
||||
bool operator!() const;
|
||||
operator bool() const;
|
||||
//bool hasData() const;
|
||||
|
||||
void release(thread_db* tdbb, bool destroy);
|
||||
|
||||
static void destroy(Triggers* trigs);
|
||||
|
||||
private:
|
||||
// implementation ...
|
||||
};
|
||||
|
||||
typedef std::atomic<TrigVector*> TrigVectorPtr;
|
||||
typedef Triggers* TrigVectorPtr;
|
||||
|
||||
class DbTriggers final : public Triggers, public CacheObject
|
||||
{
|
||||
public:
|
||||
DbTriggers()
|
||||
: Triggers(), CacheObject()
|
||||
{ }
|
||||
|
||||
static DbTriggers* create(thread_db* tdbb, MemoryPool& pool, MetaId type, CacheObject::Flag);
|
||||
/*
|
||||
return FB_NEW_POOL(pool) DbTriggers();
|
||||
*/
|
||||
|
||||
const char* c_name() const override
|
||||
{
|
||||
return "Trigger's set";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// view context block to cache view aliases
|
||||
@ -321,7 +352,7 @@ private:
|
||||
Firebird::SortedArray<DPItem, Firebird::InlineStorage<DPItem, MAX_DPMAP_ITEMS>, ULONG, DPItem> dpMap;
|
||||
ULONG dpMapMark;
|
||||
|
||||
friend class jrd_rel;
|
||||
friend class RelationPermanent;
|
||||
};
|
||||
|
||||
|
||||
@ -356,73 +387,59 @@ struct frgn
|
||||
|
||||
// Index lock block
|
||||
|
||||
class IndexLock : public CacheObject
|
||||
class IndexLock final : public CacheObject
|
||||
{
|
||||
public:
|
||||
typedef USHORT Key;
|
||||
|
||||
IndexLock(MemoryPool& p, thread_db* tdbb, jrd_rel* rel, USHORT id);
|
||||
IndexLock(MemoryPool& p, thread_db* tdbb, RelationPermanent* rel, USHORT id);
|
||||
|
||||
~IndexLock()
|
||||
{
|
||||
fb_assert(idl_lock.getUseCount() == 0);
|
||||
}
|
||||
{ }
|
||||
|
||||
jrd_rel* idl_relation; // Parent relation
|
||||
private:
|
||||
RelationPermanent* idl_relation; // Parent relation
|
||||
USHORT idl_id; // Index id
|
||||
ExistenceLock idl_lock; // Lock block
|
||||
Lock* idl_lock; // Lock block
|
||||
|
||||
public:
|
||||
bool hasData() { return true; }
|
||||
const char* c_name() const override;
|
||||
const char* c_name() const;
|
||||
|
||||
static void destroy(IndexLock *idl);
|
||||
static IndexLock* create(thread_db* tdbb, MemoryPool& p, MetaId id, CacheObject::Flag flags);
|
||||
static Lock* getLock(MemoryPool& p, thread_db* tdbb);
|
||||
|
||||
void lockShared(thread_db* tdbb);
|
||||
void lockExclusive(thread_db* tdbb);
|
||||
void unlock(thread_db* tdbb);
|
||||
void unlockAll(thread_db* tdbb);
|
||||
};
|
||||
|
||||
|
||||
// Relation block; one is created for each relation referenced
|
||||
// in the database, though it is not really filled out until
|
||||
// the relation is scanned
|
||||
|
||||
typedef CacheElement<jrd_rel, RelationPermanent> CachedRelation;
|
||||
|
||||
class jrd_rel final : public CacheObject
|
||||
{
|
||||
typedef Firebird::HalfStaticArray<Record*, 4> GCRecordList;
|
||||
typedef Firebird::ObjectsArray<IndexLock> IndexLocks;
|
||||
|
||||
public:
|
||||
jrd_rel(MemoryPool& p, RelationPermanent* r);
|
||||
|
||||
MemoryPool* rel_pool;
|
||||
USHORT rel_id;
|
||||
RelationPermanent* rel_perm;
|
||||
USHORT rel_current_fmt; // Current format number
|
||||
ULONG rel_flags;
|
||||
Format* rel_current_format; // Current record format
|
||||
|
||||
MetaName rel_name; // ascii relation name
|
||||
MetaName rel_owner_name; // ascii owner
|
||||
MetaName rel_security_name; // security class name for relation
|
||||
|
||||
vec<Format*>* rel_formats; // Known record formats
|
||||
vec<jrd_fld*>* rel_fields; // vector of field blocks
|
||||
|
||||
RseNode* rel_view_rse; // view record select expression
|
||||
ViewContexts rel_view_contexts; // sorted array of view contexts
|
||||
|
||||
ExternalFile* rel_file; // external file name
|
||||
|
||||
GCRecordList rel_gc_records; // records for garbage collection
|
||||
|
||||
USHORT rel_sweep_count; // sweep and/or garbage collector threads active
|
||||
SSHORT rel_scan_count; // concurrent sequential scan count
|
||||
|
||||
Firebird::AutoPtr<ExistenceLock> rel_existence_lock; // existence lock, if any
|
||||
Lock* rel_partners_lock; // partners lock
|
||||
Lock* rel_rescan_lock; // lock forcing relation to be scanned
|
||||
Lock* rel_gc_lock; // garbage collection lock
|
||||
IndexLocks rel_index_locks; // index existence locks
|
||||
//Firebird::Mutex rel_mtx_il; // controls addition & removal of elements
|
||||
IndexBlock* rel_index_blocks; // index blocks for caching index info
|
||||
TrigVectorPtr rel_pre_erase; // Pre-operation erase trigger
|
||||
TrigVectorPtr rel_post_erase; // Post-operation erase trigger
|
||||
TrigVectorPtr rel_pre_modify; // Pre-operation modify trigger
|
||||
TrigVectorPtr rel_post_modify; // Post-operation modify trigger
|
||||
TrigVectorPtr rel_pre_store; // Pre-operation store trigger
|
||||
TrigVectorPtr rel_post_store; // Post-operation store trigger
|
||||
prim rel_primary_dpnds; // foreign dependencies on this relation's primary key
|
||||
frgn rel_foreign_refs; // foreign references to other relations' primary keys
|
||||
Nullable<bool> rel_ss_definer;
|
||||
@ -431,146 +448,33 @@ public:
|
||||
|
||||
Firebird::Mutex rel_drop_mutex, rel_trig_load_mutex;
|
||||
|
||||
bool isSystem() const;
|
||||
bool isTemporary() const;
|
||||
bool isVirtual() const;
|
||||
bool isView() const;
|
||||
bool hasData() const
|
||||
{
|
||||
return rel_name.hasData();
|
||||
}
|
||||
Triggers rel_triggers[TRIGGER_MAX];
|
||||
|
||||
bool isReplicating(thread_db* tdbb);
|
||||
|
||||
// global temporary relations attributes
|
||||
bool hasData() const;
|
||||
const char* c_name() const override;
|
||||
MetaId getId() const;
|
||||
RelationPages* getPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, bool allocPages = true);
|
||||
bool isTemporary() const;
|
||||
bool isView() const;
|
||||
bool isVirtual() const;
|
||||
bool isSystem() const;
|
||||
|
||||
RelationPages* getBasePages()
|
||||
{
|
||||
return &rel_pages_base;
|
||||
}
|
||||
|
||||
const char* c_name() const override
|
||||
{
|
||||
return rel_name.c_str();
|
||||
}
|
||||
|
||||
USHORT getId()
|
||||
{
|
||||
return rel_id;
|
||||
}
|
||||
|
||||
void scan(thread_db* tdbb);
|
||||
/*
|
||||
// Scan the relation if it hasn't already been scanned for meta data
|
||||
|
||||
- if (!(node->relation->rel_flags & REL_scanned) ||
|
||||
- (node->relation->rel_flags & REL_being_scanned))
|
||||
- {
|
||||
- MET_scan_relation(tdbb, this);
|
||||
- }
|
||||
*/
|
||||
bool delPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, RelationPages* aPages = NULL);
|
||||
void retainPages(thread_db* tdbb, TraNumber oldNumber, TraNumber newNumber);
|
||||
|
||||
void getRelLockKey(thread_db* tdbb, UCHAR* key);
|
||||
USHORT getRelLockKeyLength() const;
|
||||
|
||||
void cleanUp();
|
||||
|
||||
class RelPagesSnapshot : public Firebird::Array<RelationPages*>
|
||||
{
|
||||
public:
|
||||
typedef Firebird::Array<RelationPages*> inherited;
|
||||
|
||||
RelPagesSnapshot(thread_db* tdbb, jrd_rel* relation)
|
||||
{
|
||||
spt_tdbb = tdbb;
|
||||
spt_relation = relation;
|
||||
}
|
||||
|
||||
~RelPagesSnapshot() { clear(); }
|
||||
|
||||
void clear();
|
||||
private:
|
||||
thread_db* spt_tdbb;
|
||||
jrd_rel* spt_relation;
|
||||
|
||||
friend class jrd_rel;
|
||||
};
|
||||
|
||||
void fillPagesSnapshot(RelPagesSnapshot&, const bool AttachmentOnly = false);
|
||||
void scan(thread_db* tdbb); // Scan the newly loaded relation for meta data
|
||||
MetaName getName() const;
|
||||
MemoryPool& getPool() const;
|
||||
MetaName getSecurityName() const;
|
||||
ExternalFile* getExtFile();
|
||||
|
||||
bool checkObject(thread_db* tdbb, Firebird::Arg::StatusVector&) override;
|
||||
void afterUnlock(thread_db* tdbb, unsigned flags) override;
|
||||
|
||||
static void destroy(jrd_rel *rel);
|
||||
static jrd_rel* create(thread_db* tdbb, MetaId id, CacheObject::Flag flags);
|
||||
|
||||
private:
|
||||
typedef Firebird::SortedArray<
|
||||
RelationPages*,
|
||||
Firebird::EmptyStorage<RelationPages*>,
|
||||
RelationPages::InstanceId,
|
||||
RelationPages>
|
||||
RelationPagesInstances;
|
||||
|
||||
RelationPagesInstances* rel_pages_inst;
|
||||
RelationPages rel_pages_base;
|
||||
RelationPages* rel_pages_free;
|
||||
|
||||
RelationPages* getPagesInternal(thread_db* tdbb, TraNumber tran, bool allocPages);
|
||||
static jrd_rel* create(thread_db* tdbb, MemoryPool& p, MetaId id, CacheObject::Flag flags);
|
||||
|
||||
public:
|
||||
jrd_rel(MemoryPool& p, MetaId id);
|
||||
|
||||
// bool hasTriggers() const; unused ???????????????????
|
||||
void releaseTriggers(thread_db* tdbb, bool destroy);
|
||||
void replaceTriggers(thread_db* tdbb, TrigVectorPtr* triggers);
|
||||
|
||||
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);
|
||||
|
||||
HazardPtr<IndexLock> getIndexLock(thread_db* tdbb, USHORT id);
|
||||
|
||||
// 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
|
||||
@ -591,34 +495,303 @@ const ULONG REL_temp_tran = 0x1000; // relation is a GTT delete rows
|
||||
const ULONG REL_temp_conn = 0x2000; // relation is a GTT preserve rows
|
||||
const ULONG REL_virtual = 0x4000; // relation is virtual
|
||||
const ULONG REL_jrd_view = 0x8000; // relation is VIEW
|
||||
const ULONG REL_gc_blocking = 0x10000; // request to downgrade\release gc lock
|
||||
const ULONG REL_gc_disabled = 0x20000; // gc is disabled temporarily
|
||||
const ULONG REL_gc_lockneed = 0x40000; // gc lock should be acquired
|
||||
|
||||
const ULONG REL_perm_flags = REL_check_existence | REL_blocking | REL_check_partners |
|
||||
REL_temp_tran | REL_temp_conn | REL_virtual | REL_jrd_view |
|
||||
REL_system | REL_virtual | REL_jrd_view;
|
||||
const ULONG REL_version_flags = (~REL_perm_flags) & 0x7FFFF;
|
||||
|
||||
/// class jrd_rel
|
||||
|
||||
inline bool jrd_rel::isSystem() const
|
||||
class GCLock
|
||||
{
|
||||
return rel_flags & REL_system;
|
||||
public:
|
||||
GCLock(RelationPermanent* rl)
|
||||
: lck(nullptr),
|
||||
relPerm(rl),
|
||||
flags(0u)
|
||||
{ }
|
||||
|
||||
// This guard is used by regular code to prevent online validation while
|
||||
// dead- or back- versions is removed from disk.
|
||||
class Shared
|
||||
{
|
||||
public:
|
||||
Shared(thread_db* tdbb, RelationPermanent* rl);
|
||||
~Shared();
|
||||
|
||||
bool gcEnabled() const
|
||||
{
|
||||
return m_gcEnabled;
|
||||
}
|
||||
|
||||
private:
|
||||
thread_db* m_tdbb;
|
||||
RelationPermanent* m_rl;
|
||||
bool m_gcEnabled;
|
||||
};
|
||||
|
||||
// This guard is used by online validation to prevent any modifications of
|
||||
// table data while it is checked.
|
||||
class Exclusive
|
||||
{
|
||||
public:
|
||||
Exclusive(thread_db* tdbb, RelationPermanent* rl)
|
||||
: m_tdbb(tdbb), m_rl(rl), m_lock(nullptr)
|
||||
{ }
|
||||
|
||||
~Exclusive()
|
||||
{
|
||||
release();
|
||||
delete m_lock;
|
||||
}
|
||||
|
||||
bool acquire(int wait);
|
||||
void release();
|
||||
|
||||
private:
|
||||
thread_db* m_tdbb;
|
||||
RelationPermanent* m_rl;
|
||||
Lock* m_lock;
|
||||
};
|
||||
|
||||
public:
|
||||
bool acquire(thread_db* tdbb, int wait);
|
||||
void downgrade(thread_db* tdbb);
|
||||
void enable(thread_db* tdbb, Lock* tempLock);
|
||||
bool disable(thread_db* tdbb, int wait, Lock*& tempLock);
|
||||
|
||||
static int ast(void* self)
|
||||
{
|
||||
try
|
||||
{
|
||||
reinterpret_cast<GCLock*>(self)->blockingAst();
|
||||
}
|
||||
catch(const Firebird::Exception&) { }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void blockingAst();
|
||||
void ensureReleased(thread_db* tdbb);
|
||||
|
||||
void incrementError [[noreturn]] ();
|
||||
|
||||
private:
|
||||
Firebird::AutoPtr<Lock> lck;
|
||||
RelationPermanent* relPerm;
|
||||
std::atomic<unsigned> flags;
|
||||
|
||||
static const unsigned GC_counterMask = 0x0FFFFFFF;
|
||||
static const unsigned GC_guardBit = 0x10000000;
|
||||
static const unsigned GC_disabled = 0x20000000;
|
||||
static const unsigned GC_locked = 0x40000000;
|
||||
static const unsigned GC_blocking = 0x80000000;
|
||||
};
|
||||
|
||||
|
||||
// Non-versioned part of relation in cache
|
||||
|
||||
class RelationPermanent : public Firebird::PermanentStorage
|
||||
{
|
||||
// typedef Firebird::ObjectsArray<IndexLock> IndexLocks;
|
||||
typedef CacheVector<IndexLock> IndexLocks;
|
||||
typedef Firebird::HalfStaticArray<Record*, 4> GCRecordList;
|
||||
|
||||
public:
|
||||
RelationPermanent(MemoryPool& p, MetaId id);
|
||||
|
||||
~RelationPermanent();
|
||||
|
||||
void makeLocks(thread_db* tdbb, CachedRelation* relation);
|
||||
static constexpr USHORT getRelLockKeyLength();
|
||||
Lock* createLock(thread_db* tdbb, lck_t, bool);
|
||||
void extFile(thread_db* tdbb, const TEXT* file_name); // impl in ext.cpp
|
||||
|
||||
IndexLock* getIndexLock(thread_db* tdbb, USHORT id);
|
||||
|
||||
Lock* rel_existence_lock; // existence lock
|
||||
Lock* rel_partners_lock; // partners lock
|
||||
Lock* rel_rescan_lock; // lock forcing relation to be scanned
|
||||
GCLock rel_gc_lock; // garbage collection lock
|
||||
GCRecordList rel_gc_records; // records for garbage collection
|
||||
|
||||
class RelPagesSnapshot : public Firebird::Array<RelationPages*>
|
||||
{
|
||||
public:
|
||||
typedef Firebird::Array<RelationPages*> inherited;
|
||||
|
||||
RelPagesSnapshot(thread_db* tdbb, RelationPermanent* relation)
|
||||
{
|
||||
spt_tdbb = tdbb;
|
||||
spt_relation = relation;
|
||||
}
|
||||
|
||||
~RelPagesSnapshot() { clear(); }
|
||||
|
||||
void clear();
|
||||
private:
|
||||
thread_db* spt_tdbb;
|
||||
RelationPermanent* spt_relation;
|
||||
|
||||
friend class RelationPermanent;
|
||||
};
|
||||
|
||||
RelationPages* getPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, bool allocPages = true);
|
||||
bool delPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, RelationPages* aPages = NULL);
|
||||
void retainPages(thread_db* tdbb, TraNumber oldNumber, TraNumber newNumber);
|
||||
void cleanUp();
|
||||
void fillPagesSnapshot(RelPagesSnapshot&, const bool AttachmentOnly = false);
|
||||
|
||||
RelationPages* getBasePages()
|
||||
{
|
||||
return &rel_pages_base;
|
||||
}
|
||||
|
||||
|
||||
bool hasData() const
|
||||
{
|
||||
return rel_name.hasData();
|
||||
}
|
||||
|
||||
const char* c_name() const
|
||||
{
|
||||
return rel_name.c_str();
|
||||
}
|
||||
|
||||
MetaName getName() const
|
||||
{
|
||||
return rel_name;
|
||||
}
|
||||
|
||||
MetaId getId() const
|
||||
{
|
||||
return rel_id;
|
||||
}
|
||||
|
||||
ExternalFile* getExtFile()
|
||||
{
|
||||
return rel_file;
|
||||
}
|
||||
|
||||
|
||||
void getRelLockKey(thread_db* tdbb, UCHAR* key);
|
||||
|
||||
bool isSystem() const;
|
||||
bool isTemporary() const;
|
||||
bool isVirtual() const;
|
||||
bool isView() const;
|
||||
|
||||
vec<Format*>* rel_formats; // Known record formats
|
||||
IndexLocks rel_index_locks; // index existence locks
|
||||
MetaName rel_name; // ascii relation name
|
||||
MetaId rel_id;
|
||||
|
||||
MetaName rel_owner_name; // ascii owner
|
||||
MetaName rel_security_name; // security class name for relation
|
||||
ULONG rel_flags; // lock-related flags
|
||||
|
||||
private:
|
||||
Firebird::Mutex rel_pages_mutex;
|
||||
|
||||
typedef Firebird::SortedArray<
|
||||
RelationPages*,
|
||||
Firebird::EmptyStorage<RelationPages*>,
|
||||
RelationPages::InstanceId,
|
||||
RelationPages>
|
||||
RelationPagesInstances;
|
||||
|
||||
RelationPagesInstances* rel_pages_inst;
|
||||
RelationPages rel_pages_base;
|
||||
RelationPages* rel_pages_free;
|
||||
|
||||
RelationPages* getPagesInternal(thread_db* tdbb, TraNumber tran, bool allocPages);
|
||||
|
||||
ExternalFile* rel_file;
|
||||
};
|
||||
|
||||
|
||||
inline bool jrd_rel::hasData() const
|
||||
{
|
||||
return rel_perm->rel_name.hasData();
|
||||
}
|
||||
|
||||
inline const char* jrd_rel::c_name() const
|
||||
{
|
||||
return rel_perm->rel_name.c_str();
|
||||
}
|
||||
|
||||
inline MetaName jrd_rel::getName() const
|
||||
{
|
||||
return rel_perm->rel_name;
|
||||
}
|
||||
|
||||
inline MemoryPool& jrd_rel::getPool() const
|
||||
{
|
||||
return rel_perm->getPool();
|
||||
}
|
||||
|
||||
inline ExternalFile* jrd_rel::getExtFile()
|
||||
{
|
||||
return rel_perm->getExtFile();
|
||||
}
|
||||
|
||||
inline MetaName jrd_rel::getSecurityName() const
|
||||
{
|
||||
return rel_perm->rel_security_name;
|
||||
}
|
||||
|
||||
inline MetaId jrd_rel::getId() const
|
||||
{
|
||||
return rel_perm->rel_id;
|
||||
}
|
||||
|
||||
RelationPages* jrd_rel::getPages(thread_db* tdbb, TraNumber tran, bool allocPages)
|
||||
{
|
||||
return rel_perm->getPages(tdbb, tran, allocPages);
|
||||
}
|
||||
|
||||
inline bool jrd_rel::isTemporary() const
|
||||
{
|
||||
return (rel_flags & (REL_temp_tran | REL_temp_conn));
|
||||
}
|
||||
|
||||
inline bool jrd_rel::isVirtual() const
|
||||
{
|
||||
return (rel_flags & REL_virtual);
|
||||
return rel_perm->isTemporary();
|
||||
}
|
||||
|
||||
inline bool jrd_rel::isView() const
|
||||
{
|
||||
return rel_perm->isView();
|
||||
}
|
||||
|
||||
inline bool jrd_rel::isVirtual() const
|
||||
{
|
||||
return rel_perm->isVirtual();
|
||||
}
|
||||
|
||||
inline bool jrd_rel::isSystem() const
|
||||
{
|
||||
return rel_perm->isSystem();
|
||||
}
|
||||
|
||||
|
||||
inline bool RelationPermanent::isSystem() const
|
||||
{
|
||||
return rel_flags & REL_system;
|
||||
}
|
||||
|
||||
inline bool RelationPermanent::isTemporary() const
|
||||
{
|
||||
return (rel_flags & (REL_temp_tran | REL_temp_conn));
|
||||
}
|
||||
|
||||
inline bool RelationPermanent::isVirtual() const
|
||||
{
|
||||
return (rel_flags & REL_virtual);
|
||||
}
|
||||
|
||||
inline bool RelationPermanent::isView() const
|
||||
{
|
||||
return (rel_flags & REL_jrd_view);
|
||||
}
|
||||
|
||||
inline RelationPages* jrd_rel::getPages(thread_db* tdbb, TraNumber tran, bool allocPages)
|
||||
inline RelationPages* RelationPermanent::getPages(thread_db* tdbb, TraNumber tran, bool allocPages)
|
||||
{
|
||||
if (!isTemporary())
|
||||
return &rel_pages_base;
|
||||
@ -626,36 +799,28 @@ inline RelationPages* jrd_rel::getPages(thread_db* tdbb, TraNumber tran, bool al
|
||||
return getPagesInternal(tdbb, tran, allocPages);
|
||||
}
|
||||
|
||||
/// class jrd_rel::GCShared
|
||||
|
||||
inline jrd_rel::GCShared::GCShared(thread_db* tdbb, jrd_rel* relation)
|
||||
|
||||
/// class GCLock::Shared
|
||||
|
||||
inline GCLock::Shared::Shared(thread_db* tdbb, RelationPermanent* rl)
|
||||
: m_tdbb(tdbb),
|
||||
m_relation(relation),
|
||||
m_gcEnabled(false)
|
||||
{
|
||||
if (m_relation->rel_flags & (REL_gc_blocking | REL_gc_disabled))
|
||||
return;
|
||||
m_rl(rl),
|
||||
m_gcEnabled(m_rl->rel_gc_lock.acquire(m_tdbb, LCK_NO_WAIT))
|
||||
{ }
|
||||
|
||||
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()
|
||||
inline GCLock::Shared::~Shared()
|
||||
{
|
||||
if (m_gcEnabled)
|
||||
--m_relation->rel_sweep_count;
|
||||
m_rl->rel_gc_lock.downgrade(m_tdbb);
|
||||
}
|
||||
|
||||
if ((m_relation->rel_flags & REL_gc_blocking) && !m_relation->rel_sweep_count)
|
||||
m_relation->downgradeGCLock(m_tdbb);
|
||||
|
||||
/// class GCLock::Exclusive
|
||||
|
||||
inline bool GCLock::Exclusive::acquire(int wait)
|
||||
{
|
||||
return m_rl->rel_gc_lock.disable(m_tdbb, wait, m_lock);
|
||||
}
|
||||
|
||||
|
||||
@ -690,6 +855,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
#endif // JRD_RELATION_H
|
||||
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* PROGRAM: JRD Access Method
|
||||
* MODULE: Resource.cpp
|
||||
* DESCRIPTION: Resource used by request / transaction
|
||||
*
|
||||
* 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.28: Added rse_skip to class RecordSelExpr to support LIMIT.
|
||||
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
|
||||
* exception handling in SPs/triggers,
|
||||
* implemented ROWS_AFFECTED system variable
|
||||
* 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks
|
||||
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
|
||||
* Adriano dos Santos Fernandes
|
||||
*/
|
||||
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../jrd/Resource.h"
|
||||
#include "../jrd/Relation.h"
|
||||
|
||||
using namespace Jrd;
|
||||
|
||||
USHORT Resource::relId() const
|
||||
{
|
||||
return rsc_rel ? rsc_rel->rel_id : 0;
|
||||
}
|
||||
|
@ -32,11 +32,288 @@
|
||||
#ifndef JRD_RESOURCE_H
|
||||
#define JRD_RESOURCE_H
|
||||
|
||||
#include "../jrd/MetaName.h"
|
||||
#include "../common/classes/Bits.h"
|
||||
#include "fb_blk.h"
|
||||
#include "../jrd/HazardPtr.h"
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
class RelationPermanent;
|
||||
class RoutinePermanent;
|
||||
class CharSetContainer;
|
||||
class jrd_rel;
|
||||
class jrd_prc;
|
||||
class Function;
|
||||
class Trigger;
|
||||
class CharSetVers;
|
||||
|
||||
class Resources;
|
||||
|
||||
// Set of objects cached per particular MDC version
|
||||
|
||||
union VersionedPartPtr
|
||||
{
|
||||
jrd_rel* relation;
|
||||
jrd_prc* procedure;
|
||||
Function* function;
|
||||
};
|
||||
|
||||
class VersionedObjects : public pool_alloc_rpt<VersionedPartPtr>,
|
||||
public Firebird::RefCounted
|
||||
{
|
||||
|
||||
public:
|
||||
VersionedObjects(FB_SIZE_T cnt, MdcVersion ver) :
|
||||
version(ver),
|
||||
capacity(cnt)
|
||||
{ }
|
||||
|
||||
template <class C>
|
||||
void put(FB_SIZE_T n, C* obj)
|
||||
{
|
||||
fb_assert(n < capacity);
|
||||
fb_assert(!object<C>(n));
|
||||
object<C>(n) = obj;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
C* get(FB_SIZE_T n) const
|
||||
{
|
||||
fb_assert(n < capacity);
|
||||
return object<C>(n);
|
||||
}
|
||||
|
||||
FB_SIZE_T getCapacity()
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
const MdcVersion version; // version when created
|
||||
|
||||
private:
|
||||
FB_SIZE_T capacity;
|
||||
VersionedPartPtr data[1];
|
||||
|
||||
template <class C> C*& object(FB_SIZE_T n);
|
||||
template <class C> C* object(FB_SIZE_T n) const;
|
||||
};
|
||||
|
||||
// specialization
|
||||
template <> Function*& VersionedObjects::object<Function>(FB_SIZE_T n) { return data[n].function; }
|
||||
template <> jrd_prc*& VersionedObjects::object<jrd_prc>(FB_SIZE_T n) { return data[n].procedure; }
|
||||
template <> jrd_rel*& VersionedObjects::object<jrd_rel>(FB_SIZE_T n) { return data[n].relation; }
|
||||
|
||||
template <> jrd_rel* VersionedObjects::object<jrd_rel>(FB_SIZE_T n) const { return data[n].relation; }
|
||||
|
||||
//template <> *& object<*>(FB_SIZE_T n) { check(n); return data[n].; }
|
||||
|
||||
|
||||
template <class OBJ, class PERM>
|
||||
class CachedResource
|
||||
{
|
||||
public:
|
||||
CachedResource(CacheElement<OBJ, PERM>* elem, FB_SIZE_T version)
|
||||
: cacheElement(elem), versionOffset(version)
|
||||
{ }
|
||||
|
||||
CachedResource()
|
||||
: cacheElement(nullptr)
|
||||
{ }
|
||||
|
||||
OBJ* operator()(const VersionedObjects* runTime) const
|
||||
{
|
||||
return runTime->get<OBJ>(versionOffset);
|
||||
}
|
||||
|
||||
OBJ* operator()(thread_db* tdbb) const
|
||||
{
|
||||
return cacheElement->getObject(tdbb);
|
||||
}
|
||||
|
||||
CacheElement<OBJ, PERM>* operator()() const
|
||||
{
|
||||
return cacheElement;
|
||||
}
|
||||
|
||||
FB_SIZE_T getOffset() const
|
||||
{
|
||||
return versionOffset;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
cacheElement = nullptr;
|
||||
}
|
||||
|
||||
bool isSet() const
|
||||
{
|
||||
return cacheElement != nullptr;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return isSet();
|
||||
}
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
return !isSet();
|
||||
}
|
||||
|
||||
private:
|
||||
CacheElement<OBJ, PERM>* cacheElement;
|
||||
FB_SIZE_T versionOffset;
|
||||
};
|
||||
|
||||
|
||||
class Resources
|
||||
{
|
||||
public:
|
||||
template <class OBJ, class PERM>
|
||||
class RscArray : public Firebird::Array<CachedResource<OBJ, PERM>>
|
||||
{
|
||||
public:
|
||||
RscArray(MemoryPool& p, FB_SIZE_T& pos)
|
||||
: Firebird::Array<CachedResource<OBJ, PERM>>(p),
|
||||
versionCurrentPosition(pos)
|
||||
{ }
|
||||
|
||||
CachedResource<OBJ, PERM>& registerResource(CacheElement<OBJ, PERM>* res)
|
||||
{
|
||||
FB_SIZE_T pos;
|
||||
if (!this->find([res](const CachedResource<OBJ, PERM>& elem) {
|
||||
const void* p1 = elem();
|
||||
const void* p2 = res;
|
||||
return p1 < p2 ? -1 : p1 == p2 ? 0 : 1;
|
||||
}, pos))
|
||||
{
|
||||
CachedResource<OBJ, PERM> newPtr(res, versionCurrentPosition++);
|
||||
pos = this->add(newPtr);
|
||||
}
|
||||
|
||||
return this->getElement(pos);
|
||||
}
|
||||
|
||||
void transfer(thread_db* tdbb, VersionedObjects* to)
|
||||
{
|
||||
for (auto& resource : *this)
|
||||
to->put(resource.getOffset(), resource()->getObject(tdbb));
|
||||
}
|
||||
|
||||
private:
|
||||
FB_SIZE_T& versionCurrentPosition;
|
||||
};
|
||||
|
||||
void transfer(thread_db* tdbb, VersionedObjects* to); // Impl-ted in Statement.cpp
|
||||
|
||||
private:
|
||||
FB_SIZE_T versionCurrentPosition;
|
||||
|
||||
public:
|
||||
template <class OBJ, class PERM> const RscArray<OBJ, PERM>& objects() const;
|
||||
|
||||
Resources(MemoryPool& p)
|
||||
: versionCurrentPosition(0),
|
||||
charSets(p, versionCurrentPosition),
|
||||
relations(p, versionCurrentPosition),
|
||||
procedures(p, versionCurrentPosition),
|
||||
functions(p, versionCurrentPosition),
|
||||
triggers(p, versionCurrentPosition)
|
||||
{ }
|
||||
|
||||
RscArray<CharSetVers, CharSetContainer> charSets;
|
||||
RscArray<jrd_rel, RelationPermanent> relations;
|
||||
RscArray<jrd_prc, RoutinePermanent> procedures;
|
||||
RscArray<Function, RoutinePermanent> functions;
|
||||
RscArray<Trigger, NullClass> triggers;
|
||||
};
|
||||
|
||||
// specialization
|
||||
template <> const Resources::RscArray<jrd_rel, RelationPermanent>& Resources::objects() const { return relations; }
|
||||
template <> const Resources::RscArray<jrd_prc, RoutinePermanent>& Resources::objects() const { return procedures; }
|
||||
template <> const Resources::RscArray<Function, RoutinePermanent>& Resources::objects() const { return functions; }
|
||||
template <> const Resources::RscArray<CharSetVers, CharSetContainer>& Resources::objects() const { return charSets; }
|
||||
template <> const Resources::RscArray<Trigger, NullClass>& Resources::objects() const { return triggers; }
|
||||
|
||||
namespace Rsc
|
||||
{
|
||||
typedef CachedResource<jrd_rel, RelationPermanent> Rel;
|
||||
typedef CachedResource<jrd_prc, RoutinePermanent> Proc;
|
||||
typedef CachedResource<Function, RoutinePermanent> Fun;
|
||||
typedef CachedResource<CharSetVers, CharSetContainer> CSet;
|
||||
typedef CachedResource<Trigger, NullClass> Trig;
|
||||
}; //namespace Rsc
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
/*
|
||||
|
||||
class jrd_rel;
|
||||
class Routine;
|
||||
class Collation;
|
||||
@ -126,6 +403,8 @@ struct Resource
|
||||
USHORT relId() const;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
} // namespace Jrd
|
||||
|
||||
#endif // JRD_RESOURCE_H
|
||||
|
@ -35,6 +35,20 @@ using namespace Firebird;
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
RoutinePermanent::RoutinePermanent(MemoryPool& p, MetaId metaId, Lock* existence)
|
||||
: PermanentStorage(p),
|
||||
id(metaId),
|
||||
name(p),
|
||||
securityName(p),
|
||||
subRoutine(false),
|
||||
flags(0),
|
||||
alterCount(0),
|
||||
existenceLock(existence)
|
||||
{
|
||||
existenceLock->setKey(metaId);
|
||||
existenceLock->lck_object = this;
|
||||
}
|
||||
|
||||
|
||||
// Create a MsgMetadata from a parameters array.
|
||||
MsgMetadata* Routine::createMetadata(const Array<NestConst<Parameter> >& parameters, bool isExtern)
|
||||
@ -258,51 +272,6 @@ void Routine::parseMessages(thread_db* tdbb, CompilerScratch* csb, BlrReader blr
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement the routine's use count.
|
||||
void Routine::afterDecrement(thread_db* tdbb)
|
||||
{
|
||||
// intUseCount != 0 if and only if we are cleaning cache
|
||||
|
||||
if (intUseCount > 0)
|
||||
intUseCount--;
|
||||
}
|
||||
|
||||
|
||||
void Routine::afterUnlock(thread_db* tdbb, unsigned fl)
|
||||
{
|
||||
flags |= Routine::FLAG_OBSOLETE;
|
||||
|
||||
// Call recursively if and only if the use count is zero AND the routine
|
||||
// in the cache is different than this routine.
|
||||
// The routine will be different than in the cache only if it is a
|
||||
// floating copy, i.e. an old copy or a deleted routine.
|
||||
if (!(fl & ExistenceLock::inCache))
|
||||
{
|
||||
if (getStatement())
|
||||
releaseStatement(tdbb);
|
||||
|
||||
flags &= ~Routine::FLAG_BEING_ALTERED;
|
||||
//remove(tdbb);
|
||||
}
|
||||
}
|
||||
|
||||
int Routine::getUseCount() const
|
||||
{
|
||||
return existenceLock.hasData() ? existenceLock->getUseCount() : 1;
|
||||
}
|
||||
|
||||
void Routine::sharedCheckLock(thread_db* tdbb)
|
||||
{
|
||||
if (existenceLock->inc(tdbb) != Resource::State::Locked)
|
||||
existenceLock->enter245(tdbb);
|
||||
}
|
||||
|
||||
void Routine::sharedCheckUnlock(thread_db* tdbb)
|
||||
{
|
||||
existenceLock->dec(tdbb);
|
||||
existenceLock->releaseLock(tdbb, ExistenceLock::ReleaseMethod::DropObject);
|
||||
}
|
||||
|
||||
void Routine::releaseStatement(thread_db* tdbb)
|
||||
{
|
||||
if (getStatement())
|
||||
@ -362,51 +331,8 @@ bool jrd_prc::checkCache(thread_db* tdbb) const
|
||||
|
||||
void Routine::releaseLocks(thread_db* tdbb)
|
||||
{
|
||||
if (existenceLock)
|
||||
{
|
||||
existenceLock->releaseLock(tdbb, ExistenceLock::ReleaseMethod::CloseCache);
|
||||
flags |= Routine::FLAG_CHECK_EXISTENCE;
|
||||
}
|
||||
if (permanent->existenceLock)
|
||||
LCK_release(tdbb, permanent->existenceLock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Routine::adjust_dependencies()
|
||||
{
|
||||
if (intUseCount == -1)
|
||||
{
|
||||
// Already processed
|
||||
return;
|
||||
}
|
||||
|
||||
intUseCount = -1; // Mark as undeletable
|
||||
|
||||
if (getStatement())
|
||||
{
|
||||
// Loop over procedures from resource list of request
|
||||
for (auto resource : getStatement()->resources.getObjects(Resource::rsc_procedure))
|
||||
{
|
||||
auto routine = resource->rsc_routine;
|
||||
|
||||
if (routine->intUseCount == routine->getUseCount())
|
||||
{
|
||||
// Mark it and all dependent procedures as undeletable
|
||||
routine->adjust_dependencies();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto resource : getStatement()->resources.getObjects(Resource::rsc_function))
|
||||
{
|
||||
auto routine = resource->rsc_routine;
|
||||
|
||||
if (routine->intUseCount == routine->getUseCount())
|
||||
{
|
||||
// Mark it and all dependent functions as undeletable
|
||||
routine->adjust_dependencies();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Jrd
|
||||
|
@ -44,8 +44,6 @@ namespace Jrd
|
||||
class Format;
|
||||
class Parameter;
|
||||
class UserId;
|
||||
class ExistenceLock;
|
||||
|
||||
class StartupBarrier
|
||||
{
|
||||
public:
|
||||
@ -91,82 +89,21 @@ namespace Jrd
|
||||
bool flg;
|
||||
};
|
||||
|
||||
class Routine : public Firebird::PermanentStorage, public CacheObject
|
||||
class RoutinePermanent : public Firebird::PermanentStorage
|
||||
{
|
||||
protected:
|
||||
explicit Routine(MemoryPool& p)
|
||||
public:
|
||||
explicit RoutinePermanent(MemoryPool& p, MetaId metaId, Lock* existence);
|
||||
|
||||
explicit RoutinePermanent(MemoryPool& p)
|
||||
: PermanentStorage(p),
|
||||
id(~0),
|
||||
name(p),
|
||||
securityName(p),
|
||||
statement(NULL),
|
||||
subRoutine(true),
|
||||
implemented(true),
|
||||
defined(true),
|
||||
defaultCount(0),
|
||||
inputFormat(NULL),
|
||||
outputFormat(NULL),
|
||||
inputFields(p),
|
||||
outputFields(p),
|
||||
flags(0),
|
||||
intUseCount(0),
|
||||
alterCount(0),
|
||||
existenceLock(NULL),
|
||||
invoker(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Routine(MemoryPool& p, MetaId metaId)
|
||||
: PermanentStorage(p),
|
||||
id(metaId),
|
||||
name(p),
|
||||
securityName(p),
|
||||
statement(NULL),
|
||||
subRoutine(false),
|
||||
implemented(true),
|
||||
defined(true),
|
||||
defaultCount(0),
|
||||
inputFormat(NULL),
|
||||
outputFormat(NULL),
|
||||
inputFields(p),
|
||||
outputFields(p),
|
||||
flags(0),
|
||||
intUseCount(0),
|
||||
alterCount(0),
|
||||
existenceLock(NULL),
|
||||
invoker(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~Routine()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
static const USHORT FLAG_SCANNED = 1; // Field expressions scanned
|
||||
static const USHORT FLAG_OBSOLETE = 2; // Procedure known gonzo
|
||||
static const USHORT FLAG_BEING_SCANNED = 4; // New procedure needs dependencies during scan
|
||||
static const USHORT FLAG_BEING_ALTERED = 8; // Procedure is getting altered
|
||||
// This flag is used to make sure that MET_remove_routine
|
||||
// does not delete and remove procedure block from cache
|
||||
// so dfw.epp:modify_procedure() can flip procedure body without
|
||||
// invalidating procedure pointers from other parts of metadata cache
|
||||
static const USHORT FLAG_CHECK_EXISTENCE = 16; // Existence lock released
|
||||
static const USHORT FLAG_RELOAD = 32; // Recompile before execution
|
||||
static const USHORT FLAG_CLEARED = 64; // Routine cleared but not removed from cache
|
||||
|
||||
static const USHORT MAX_ALTER_COUNT = 64; // Number of times an in-cache routine can be altered
|
||||
|
||||
static Firebird::MsgMetadata* createMetadata(
|
||||
const Firebird::Array<NestConst<Parameter> >& parameters, bool isExtern);
|
||||
static Format* createFormat(MemoryPool& pool, Firebird::IMessageMetadata* params, bool addEof);
|
||||
|
||||
public:
|
||||
static void destroy(Routine* routine)
|
||||
{
|
||||
delete routine;
|
||||
}
|
||||
existenceLock(NULL)
|
||||
{ }
|
||||
|
||||
USHORT getId() const
|
||||
{
|
||||
@ -178,17 +115,74 @@ namespace Jrd
|
||||
|
||||
const QualifiedName& getName() const { return name; }
|
||||
void setName(const QualifiedName& value) { name = value; }
|
||||
const char* c_name() const override { return name.c_str(); }
|
||||
const char* c_name() const { return name.c_str(); }
|
||||
|
||||
const MetaName& getSecurityName() const { return securityName; }
|
||||
void setSecurityName(const MetaName& value) { securityName = value; }
|
||||
|
||||
/*const*/ Statement* getStatement() const { return statement; }
|
||||
void setStatement(Statement* value);
|
||||
bool hasData() const { return name.hasData(); }
|
||||
|
||||
bool isSubRoutine() const { return subRoutine; }
|
||||
void setSubRoutine(bool value) { subRoutine = value; }
|
||||
|
||||
int getObjectType() const;
|
||||
|
||||
private:
|
||||
USHORT id; // routine ID
|
||||
QualifiedName name; // routine name
|
||||
MetaName securityName; // security class name
|
||||
bool subRoutine; // Is this a subroutine?
|
||||
USHORT flags;
|
||||
USHORT alterCount; // No. of times the routine was altered
|
||||
|
||||
public:
|
||||
Lock* existenceLock; // existence lock, if any
|
||||
MetaName owner;
|
||||
};
|
||||
|
||||
class Routine : public CacheObject
|
||||
{
|
||||
protected:
|
||||
explicit Routine(RoutinePermanent* perm)
|
||||
: permanent(perm),
|
||||
statement(NULL),
|
||||
implemented(true),
|
||||
defined(true),
|
||||
defaultCount(0),
|
||||
inputFormat(NULL),
|
||||
outputFormat(NULL),
|
||||
inputFields(permanent->getPool()),
|
||||
outputFields(permanent->getPool()),
|
||||
flags(0),
|
||||
invoker(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~Routine()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
static const USHORT MAX_ALTER_COUNT = 64; // Number of times an in-cache routine can be altered ?????????
|
||||
|
||||
static Firebird::MsgMetadata* createMetadata(
|
||||
const Firebird::Array<NestConst<Parameter> >& parameters, bool isExtern);
|
||||
static Format* createFormat(MemoryPool& pool, Firebird::IMessageMetadata* params, bool addEof);
|
||||
|
||||
public:
|
||||
static void destroy(Routine* routine)
|
||||
{
|
||||
delete routine;
|
||||
}
|
||||
|
||||
const QualifiedName& getName() const { return permanent->getName(); }
|
||||
USHORT getId() const { return permanent->getId(); }
|
||||
const char* c_name() const override { return permanent->c_name(); }
|
||||
|
||||
/*const*/ Statement* getStatement() const { return statement; }
|
||||
void setStatement(Statement* value);
|
||||
|
||||
bool isImplemented() const { return implemented; }
|
||||
void setImplemented(bool value) { implemented = value; }
|
||||
|
||||
@ -212,18 +206,9 @@ namespace Jrd
|
||||
const Firebird::Array<NestConst<Parameter> >& getOutputFields() const { return outputFields; }
|
||||
Firebird::Array<NestConst<Parameter> >& getOutputFields() { return outputFields; }
|
||||
|
||||
bool hasData() const { return name.hasData(); }
|
||||
|
||||
void parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id, bid* blobDbg);
|
||||
void parseMessages(thread_db* tdbb, CompilerScratch* csb, Firebird::BlrReader blrReader);
|
||||
|
||||
bool isUsed() const
|
||||
{
|
||||
return getUseCount() != 0;
|
||||
}
|
||||
|
||||
int getUseCount() const;
|
||||
|
||||
virtual void releaseFormat()
|
||||
{
|
||||
}
|
||||
@ -236,9 +221,6 @@ namespace Jrd
|
||||
{
|
||||
}
|
||||
|
||||
void adjust_dependencies();
|
||||
|
||||
void sharedCheckLock(thread_db* tdbb);
|
||||
void sharedCheckUnlock(thread_db* tdbb);
|
||||
void releaseLocks(thread_db* tdbb);
|
||||
|
||||
@ -247,12 +229,11 @@ namespace Jrd
|
||||
virtual SLONG getSclType() const = 0;
|
||||
virtual bool checkCache(thread_db* tdbb) const = 0;
|
||||
|
||||
public:
|
||||
RoutinePermanent* permanent; // Permanent part of data
|
||||
|
||||
private:
|
||||
USHORT id; // routine ID
|
||||
QualifiedName name; // routine name
|
||||
MetaName securityName; // security class name
|
||||
Statement* statement; // compiled routine statement
|
||||
bool subRoutine; // Is this a subroutine?
|
||||
bool implemented; // Is the packaged routine missing the body/entrypoint?
|
||||
bool defined; // UDF has its implementation module available
|
||||
USHORT defaultCount; // default input arguments
|
||||
@ -262,7 +243,6 @@ namespace Jrd
|
||||
Firebird::Array<NestConst<Parameter> > outputFields; // array of field blocks
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool reload(thread_db* tdbb) = 0;
|
||||
|
||||
public:
|
||||
@ -270,15 +250,6 @@ namespace Jrd
|
||||
StartupBarrier startup;
|
||||
|
||||
public:
|
||||
SSHORT intUseCount; // number of routines compiled with routine, set and
|
||||
// used internally in the clear_cache() routine
|
||||
// no code should rely on value of this field
|
||||
// (it will usually be 0)
|
||||
USHORT alterCount; // No. of times the routine was altered
|
||||
|
||||
Firebird::AutoPtr<ExistenceLock> existenceLock; // existence lock, if any
|
||||
|
||||
MetaName owner;
|
||||
Jrd::UserId* invoker; // Invoker ID
|
||||
};
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ PerformanceInfo* RuntimeStatistics::computeDifference(Attachment* att,
|
||||
|
||||
// Calculate relation-level statistics
|
||||
temp.clear();
|
||||
MetadataCache* mdc = att->att_database->dbb_mdc;
|
||||
|
||||
// This loop assumes that base array is smaller than new one
|
||||
RelCounters::iterator base_cnts = rel_counts.begin();
|
||||
@ -113,8 +112,8 @@ PerformanceInfo* RuntimeStatistics::computeDifference(Attachment* att,
|
||||
TraceCounts traceCounts;
|
||||
traceCounts.trc_relation_id = rel_id;
|
||||
traceCounts.trc_counters = base_cnts->getCounterVector();
|
||||
jrd_rel* relation = mdc->getRelation(att, rel_id);
|
||||
traceCounts.trc_relation_name = relation ? relation->rel_name.c_str() : NULL;
|
||||
auto relation = att->att_database->dbb_mdc->lookupRelation(rel_id);
|
||||
traceCounts.trc_relation_name = relation ? relation->c_name() : NULL;
|
||||
temp.add(traceCounts);
|
||||
}
|
||||
|
||||
@ -127,8 +126,8 @@ PerformanceInfo* RuntimeStatistics::computeDifference(Attachment* att,
|
||||
TraceCounts traceCounts;
|
||||
traceCounts.trc_relation_id = rel_id;
|
||||
traceCounts.trc_counters = new_cnts->getCounterVector();
|
||||
jrd_rel* relation = mdc->getRelation(att, rel_id);
|
||||
traceCounts.trc_relation_name = relation ? relation->rel_name.c_str() : NULL;
|
||||
auto relation = att->att_database->dbb_mdc->lookupRelation(rel_id);
|
||||
traceCounts.trc_relation_name = relation ? relation->c_name() : NULL;
|
||||
temp.add(traceCounts);
|
||||
}
|
||||
};
|
||||
@ -140,7 +139,7 @@ PerformanceInfo* RuntimeStatistics::computeDifference(Attachment* att,
|
||||
}
|
||||
|
||||
RuntimeStatistics::Accumulator::Accumulator(thread_db* tdbb, const jrd_rel* relation, StatType type)
|
||||
: m_tdbb(tdbb), m_type(type), m_id(relation->rel_id), m_counter(0)
|
||||
: m_tdbb(tdbb), m_type(type), m_id(relation->getId()), m_counter(0)
|
||||
{}
|
||||
|
||||
RuntimeStatistics::Accumulator::~Accumulator()
|
||||
|
@ -79,7 +79,7 @@ namespace Jrd
|
||||
{
|
||||
public:
|
||||
VerbAction()
|
||||
: vct_next(NULL), vct_relation(NULL), vct_records(NULL), vct_undo(NULL)
|
||||
: vct_next(NULL), vct_records(NULL), vct_undo(NULL)
|
||||
{}
|
||||
|
||||
~VerbAction()
|
||||
|
@ -67,7 +67,6 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
requests(*p),
|
||||
externalList(*p),
|
||||
accessList(*p),
|
||||
resources(*p, false),
|
||||
triggerName(*p),
|
||||
triggerInvoker(NULL),
|
||||
parentStatement(NULL),
|
||||
@ -76,7 +75,8 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
localTables(*p),
|
||||
invariants(*p),
|
||||
blr(*p),
|
||||
mapFieldInfo(*p)
|
||||
mapFieldInfo(*p),
|
||||
resources(csb->csb_resources)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -94,8 +94,8 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
|
||||
mapFieldInfo.takeOwnership(csb->csb_map_field_info);
|
||||
|
||||
// Take out existence locks on resources used in statement.
|
||||
resources.transferResources(tdbb, csb->csb_resources);
|
||||
// versioned metadata support
|
||||
loadResources(tdbb);
|
||||
|
||||
impureSize = csb->csb_impure;
|
||||
|
||||
@ -120,6 +120,8 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
auto tail = csb->csb_rpt.begin();
|
||||
const auto* const streams_end = tail + csb->csb_n_stream;
|
||||
|
||||
// Add more strems info (format, relation, procedure) to rpbsSetup
|
||||
// in order to check format match when mdc version grows !!!!!!!!!!!!!!!!!!!!!!!!
|
||||
for (auto rpb = rpbsSetup.begin(); tail < streams_end; ++rpb, ++tail)
|
||||
{
|
||||
// fetch input stream for update if all booleans matched against indices
|
||||
@ -155,6 +157,7 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
csb->outerMessagesMap.clear();
|
||||
csb->outerVarsMap.clear();
|
||||
csb->csb_rpt.free();
|
||||
csb->csb_resources = nullptr;
|
||||
}
|
||||
catch (Exception&)
|
||||
{
|
||||
@ -169,6 +172,34 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
|
||||
}
|
||||
}
|
||||
|
||||
void Statement::loadResources(thread_db* tdbb)
|
||||
{
|
||||
const MdcVersion currentMdcVersion = tdbb->getDatabase()->dbb_mdc->getVersion();
|
||||
if ((!latestVersion) || (latestVersion->version != currentMdcVersion))
|
||||
{
|
||||
// Also check for changed streams from known sources
|
||||
if (!streamsFormatCompare(tdbb))
|
||||
ERR_post(Arg::Gds(isc_random) << "Statement format outdated, need to be reprepared");
|
||||
|
||||
// OK, format of data sources remained the same, we can update version of cached objects in current request
|
||||
const FB_SIZE_T resourceCount = latestVersion ? latestVersion->getCapacity() :
|
||||
resources->charSets.getCount() + resources->relations.getCount() + resources->procedures.getCount() +
|
||||
resources->functions.getCount() + resources->triggers.getCount();
|
||||
|
||||
latestVersion = FB_NEW_RPT(*pool, resourceCount) VersionedObjects(resourceCount, currentMdcVersion);
|
||||
resources->transfer(tdbb, latestVersion);
|
||||
}
|
||||
}
|
||||
|
||||
void Resources::transfer(thread_db* tdbb, VersionedObjects* to)
|
||||
{
|
||||
charSets.transfer(tdbb, to);
|
||||
relations.transfer(tdbb, to);
|
||||
procedures.transfer(tdbb, to);
|
||||
functions.transfer(tdbb, to);
|
||||
triggers.transfer(tdbb, to);
|
||||
}
|
||||
|
||||
// Turn a parsed scratch into a statement.
|
||||
Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag,
|
||||
std::function<void ()> beforeCsbRelease)
|
||||
@ -188,8 +219,6 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool
|
||||
fb_assert(!"wrong pool in makeStatement");
|
||||
found:
|
||||
|
||||
Request* const old_request = tdbb->getRequest();
|
||||
tdbb->setRequest(NULL);
|
||||
const auto attachment = tdbb->getAttachment();
|
||||
|
||||
const auto old_request = tdbb->getRequest();
|
||||
@ -401,7 +430,7 @@ Request* Statement::findRequest(thread_db* tdbb, bool unique)
|
||||
return clone;
|
||||
}
|
||||
|
||||
Request* Statement::getRequest(thread_db* tdbb, USHORT level)
|
||||
Request* Statement::getRequest(thread_db* tdbb, USHORT level, bool systemRequest)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
@ -409,21 +438,31 @@ Request* Statement::getRequest(thread_db* tdbb, USHORT level)
|
||||
Database* const dbb = tdbb->getDatabase();
|
||||
fb_assert(dbb);
|
||||
|
||||
if (level < requests.getCount() && requests[level])
|
||||
return requests[level];
|
||||
if (level >= requests.getCount() || !requests[level])
|
||||
{
|
||||
// Create the request.
|
||||
AutoMemoryPool reqPool(MemoryPool::createPool(pool));
|
||||
auto request = FB_NEW_POOL(*reqPool) Request(reqPool, attachment, this);
|
||||
|
||||
// MemoryStats* const parentStats = (flags & FLAG_INTERNAL) ?
|
||||
// &dbb->dbb_memory_stats : &attachment->att_memory_stats;
|
||||
{ // guard scope
|
||||
MutexLockGuard guard(requestsGrow, FB_FUNCTION);
|
||||
|
||||
// Create the request.
|
||||
const auto request = FB_NEW_POOL(*pool) Request(attachment, this, &dbb->dbb_memory_stats);
|
||||
if (level >= requests.getCount() || !requests[level])
|
||||
{
|
||||
requests.grow(level + 1);
|
||||
requests[level] = request;
|
||||
request = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (level == 0)
|
||||
pool->setStatsGroup(request->req_memory_stats);
|
||||
|
||||
requests.grow(level + 1);
|
||||
requests[level] = request;
|
||||
if (request)
|
||||
delete request;
|
||||
}
|
||||
|
||||
const auto request = requests[level];
|
||||
if (!(systemRequest && request->resources))
|
||||
loadResources(tdbb);
|
||||
request->resources = latestVersion;
|
||||
return request;
|
||||
}
|
||||
|
||||
@ -442,7 +481,7 @@ void Statement::verifyAccess(thread_db* tdbb)
|
||||
|
||||
for (ExternalAccess* item = external.begin(); item != external.end(); ++item)
|
||||
{
|
||||
HazardPtr<Routine> routine(tdbb);
|
||||
Routine* routine = nullptr;
|
||||
int aclType;
|
||||
|
||||
if (item->exa_action == ExternalAccess::exa_procedure)
|
||||
@ -479,24 +518,24 @@ void Statement::verifyAccess(thread_db* tdbb)
|
||||
MetaName userName = item->user;
|
||||
if (item->exa_view_id)
|
||||
{
|
||||
jrd_rel* view = MetadataCache::lookup_relation_id(tdbb, item->exa_view_id, false);
|
||||
if (view && (view->rel_flags & REL_sql_relation))
|
||||
auto view = MetadataCache::lookupRelation(tdbb, item->exa_view_id);
|
||||
if (view && (view->getId() >= USER_DEF_REL_INIT_ID))
|
||||
userName = view->rel_owner_name;
|
||||
}
|
||||
|
||||
switch (item->exa_action)
|
||||
{
|
||||
case ExternalAccess::exa_insert:
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_pre_store, userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_post_store, userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_triggers[TRIGGER_PRE_STORE], userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_triggers[TRIGGER_POST_STORE], userName);
|
||||
break;
|
||||
case ExternalAccess::exa_update:
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_pre_modify, userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_post_modify, userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_triggers[TRIGGER_PRE_MODIFY], userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_triggers[TRIGGER_POST_MODIFY], userName);
|
||||
break;
|
||||
case ExternalAccess::exa_delete:
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_pre_erase, userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_post_erase, userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_triggers[TRIGGER_PRE_ERASE], userName);
|
||||
verifyTriggerAccess(tdbb, relation, relation->rel_triggers[TRIGGER_POST_ERASE], userName);
|
||||
break;
|
||||
default:
|
||||
fb_assert(false);
|
||||
@ -515,8 +554,8 @@ void Statement::verifyAccess(thread_db* tdbb)
|
||||
|
||||
if (access.acc_ss_rel_id)
|
||||
{
|
||||
jrd_rel* view = MetadataCache::lookup_relation_id(tdbb, access->acc_ss_rel_id, false);
|
||||
if (view && (view->rel_flags & REL_sql_relation))
|
||||
auto view = MetadataCache::lookupRelation(tdbb, access.acc_ss_rel_id);
|
||||
if (view && (view->getId() >= USER_DEF_REL_INIT_ID))
|
||||
userName = view->rel_owner_name;
|
||||
}
|
||||
|
||||
@ -583,8 +622,8 @@ void Statement::verifyAccess(thread_db* tdbb)
|
||||
|
||||
if (access->acc_ss_rel_id)
|
||||
{
|
||||
jrd_rel* view = MetadataCache::lookup_relation_id(tdbb, access->acc_ss_rel_id, false);
|
||||
if (view && (view->rel_flags & REL_sql_relation))
|
||||
auto view = MetadataCache::lookupRelation(tdbb, access->acc_ss_rel_id);
|
||||
if (view && (view->getId() >= USER_DEF_REL_INIT_ID))
|
||||
userName = view->rel_owner_name;
|
||||
}
|
||||
|
||||
@ -614,7 +653,7 @@ void Statement::release(thread_db* tdbb)
|
||||
|
||||
// Release existence locks on references.
|
||||
|
||||
resources.releaseResources(tdbb);
|
||||
// resources.releaseResources(tdbb); !!!!!!!!!!!!!!! place to release
|
||||
|
||||
for (Request** instance = requests.begin(); instance != requests.end(); ++instance)
|
||||
{
|
||||
@ -630,8 +669,6 @@ void Statement::release(thread_db* tdbb)
|
||||
|
||||
if (attachment)
|
||||
{
|
||||
// !!!!!!!!!!!!!!!! need to walk all attachments in database
|
||||
// or change att_statements to dbb_statements
|
||||
if (!attachment->att_statements.findAndRemove(this))
|
||||
fb_assert(false);
|
||||
}
|
||||
@ -659,19 +696,15 @@ string Statement::getPlan(thread_db* tdbb, bool detailed) const
|
||||
|
||||
// Check that we have enough rights to access all resources this list of triggers touches.
|
||||
void Statement::verifyTriggerAccess(thread_db* tdbb, const jrd_rel* ownerRelation,
|
||||
TrigVector* triggers, MetaName userName)
|
||||
const Triggers& triggers, MetaName userName)
|
||||
{
|
||||
if (!triggers)
|
||||
return;
|
||||
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
for (FB_SIZE_T i = 0; i < triggers->getCount(tdbb); i++)
|
||||
for (auto t : triggers)
|
||||
{
|
||||
HazardPtr<Trigger> t(tdbb);
|
||||
if (!triggers->load(tdbb, i, t))
|
||||
continue;
|
||||
|
||||
t->compile(tdbb);
|
||||
if (!t->statement)
|
||||
continue;
|
||||
@ -691,12 +724,12 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, const jrd_rel* ownerRelatio
|
||||
if (!(ownerRelation->rel_flags & REL_system))
|
||||
{
|
||||
if (access->acc_type == obj_relations &&
|
||||
(ownerRelation->rel_name == access->acc_name))
|
||||
(ownerRelation->getName() == access->acc_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (access->acc_type == obj_column &&
|
||||
(ownerRelation->rel_name == access->acc_r_name))
|
||||
(ownerRelation->getName() == access->acc_r_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -705,8 +738,8 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, const jrd_rel* ownerRelatio
|
||||
// a direct access to an object from this trigger
|
||||
if (access->acc_ss_rel_id)
|
||||
{
|
||||
jrd_rel* view = MetadataCache::lookup_relation_id(tdbb, access->acc_ss_rel_id, false);
|
||||
if (view && (view->rel_flags & REL_sql_relation))
|
||||
auto view = MetadataCache::lookupRelation(tdbb, access->acc_ss_rel_id);
|
||||
if (view && (view->getId() >= USER_DEF_REL_INIT_ID))
|
||||
userName = view->rel_owner_name;
|
||||
}
|
||||
else if (t->ssDefiner.specified && t->ssDefiner.value)
|
||||
@ -726,17 +759,13 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, const jrd_rel* ownerRelatio
|
||||
|
||||
// Invoke buildExternalAccess for triggers in vector
|
||||
inline void Statement::triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list,
|
||||
TrigVector* tvec, const MetaName& user)
|
||||
const Triggers& tvec, const MetaName& user)
|
||||
{
|
||||
if (!tvec)
|
||||
return;
|
||||
|
||||
for (FB_SIZE_T i = 0; i < tvec->getCount(tdbb); i++)
|
||||
for (auto t : tvec)
|
||||
{
|
||||
HazardPtr<Trigger> t(tdbb);
|
||||
if (!tvec->load(tdbb, i, t))
|
||||
continue;
|
||||
|
||||
t->compile(tdbb);
|
||||
if (t->statement)
|
||||
{
|
||||
@ -757,7 +786,7 @@ void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, c
|
||||
// Add externals recursively
|
||||
if (item->exa_action == ExternalAccess::exa_procedure)
|
||||
{
|
||||
HazardPtr<jrd_prc> procedure = MetadataCache::lookup_procedure_id(tdbb, item->exa_prc_id, false, false, 0);
|
||||
auto procedure = MetadataCache::lookup_procedure_id(tdbb, item->exa_prc_id, false, false, 0);
|
||||
if (procedure && procedure->getStatement())
|
||||
{
|
||||
item->user = procedure->invoker ? MetaName(procedure->invoker->getUserName()) : user;
|
||||
@ -769,7 +798,7 @@ void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, c
|
||||
}
|
||||
else if (item->exa_action == ExternalAccess::exa_function)
|
||||
{
|
||||
Function* function = Function::lookup(tdbb, item->exa_fun_id, false, false, 0);
|
||||
auto function = Function::lookup(tdbb, item->exa_fun_id, false, false, 0);
|
||||
if (function && function->getStatement())
|
||||
{
|
||||
item->user = function->invoker ? MetaName(function->invoker->getUserName()) : user;
|
||||
@ -782,36 +811,35 @@ void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, c
|
||||
else
|
||||
{
|
||||
jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, item->exa_rel_id, false);
|
||||
|
||||
if (!relation)
|
||||
continue;
|
||||
|
||||
RefPtr<TrigVector> vec1, vec2;
|
||||
|
||||
Triggers *vec1, *vec2;
|
||||
switch (item->exa_action)
|
||||
{
|
||||
case ExternalAccess::exa_insert:
|
||||
vec1 = relation->rel_pre_store;
|
||||
vec2 = relation->rel_post_store;
|
||||
vec1 = &relation->rel_triggers[TRIGGER_PRE_STORE];
|
||||
vec2 = &relation->rel_triggers[TRIGGER_POST_STORE];
|
||||
break;
|
||||
case ExternalAccess::exa_update:
|
||||
vec1 = relation->rel_pre_modify;
|
||||
vec2 = relation->rel_post_modify;
|
||||
vec1 = &relation->rel_triggers[TRIGGER_PRE_MODIFY];
|
||||
vec2 = &relation->rel_triggers[TRIGGER_POST_MODIFY];
|
||||
break;
|
||||
case ExternalAccess::exa_delete:
|
||||
vec1 = relation->rel_pre_erase;
|
||||
vec2 = relation->rel_post_erase;
|
||||
vec1 = &relation->rel_triggers[TRIGGER_PRE_ERASE];
|
||||
vec2 = &relation->rel_triggers[TRIGGER_POST_ERASE];
|
||||
break;
|
||||
default:
|
||||
fb_assert(false);
|
||||
continue; // should never happen, silence the compiler
|
||||
}
|
||||
|
||||
item->user = relation->rel_ss_definer.orElse(false) ? relation->rel_owner_name : user;
|
||||
item->user = relation->rel_ss_definer.orElse(false) ? relation->rel_perm->rel_owner_name : user;
|
||||
if (list.find(*item, i))
|
||||
continue;
|
||||
list.insert(i, *item);
|
||||
triggersExternalAccess(tdbb, list, vec1, item->user);
|
||||
triggersExternalAccess(tdbb, list, vec2, item->user);
|
||||
triggersExternalAccess(tdbb, list, *vec1, item->user);
|
||||
triggersExternalAccess(tdbb, list, *vec2, item->user);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -859,32 +887,6 @@ template <typename T> static void makeSubRoutines(thread_db* tdbb, Statement* st
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Request::Request(Attachment* attachment, /*const*/ Statement* aStatement,
|
||||
Firebird::MemoryStats* parent_stats)
|
||||
: statement(aStatement),
|
||||
req_pool(statement->pool),
|
||||
req_memory_stats(parent_stats),
|
||||
req_blobs(req_pool),
|
||||
req_stats(*req_pool),
|
||||
req_base_stats(*req_pool),
|
||||
req_ext_stmt(NULL),
|
||||
req_cursors(*req_pool),
|
||||
req_ext_resultset(NULL),
|
||||
req_timeout(0),
|
||||
req_domain_validation(NULL),
|
||||
req_sorts(*req_pool),
|
||||
req_rpb(*req_pool),
|
||||
impureArea(*req_pool),
|
||||
req_auto_trans(*req_pool)
|
||||
{
|
||||
fb_assert(statement);
|
||||
setAttachment(attachment);
|
||||
req_rpb = statement->rpbsSetup;
|
||||
impureArea.grow(statement->impureSize);
|
||||
}
|
||||
|
||||
|
||||
bool Request::hasInternalStatement() const
|
||||
{
|
||||
return statement->flags & Statement::FLAG_INTERNAL;
|
||||
@ -912,33 +914,6 @@ StmtNumber Request::getRequestId() const
|
||||
return req_id;
|
||||
}
|
||||
|
||||
Request::Request(Firebird::AutoMemoryPool& pool, Attachment* attachment, /*const*/ Statement* aStatement)
|
||||
: statement(aStatement),
|
||||
req_pool(pool),
|
||||
req_memory_stats(&aStatement->pool->getStatsGroup()),
|
||||
req_blobs(req_pool),
|
||||
req_stats(*req_pool),
|
||||
req_base_stats(*req_pool),
|
||||
req_ext_stmt(NULL),
|
||||
req_cursors(*req_pool),
|
||||
req_ext_resultset(NULL),
|
||||
req_timeout(0),
|
||||
req_domain_validation(NULL),
|
||||
req_auto_trans(*req_pool),
|
||||
req_sorts(*req_pool),
|
||||
req_rpb(*req_pool),
|
||||
impureArea(*req_pool)
|
||||
{
|
||||
fb_assert(statement);
|
||||
setAttachment(attachment);
|
||||
req_rpb = statement->rpbsSetup;
|
||||
impureArea.grow(statement->impureSize);
|
||||
|
||||
pool->setStatsGroup(req_memory_stats);
|
||||
pool.release();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
|
||||
// Function is designed to be called from debugger to print subtree of current execution node
|
||||
|
@ -74,19 +74,25 @@ public:
|
||||
bool isActive() const;
|
||||
|
||||
Request* findRequest(thread_db* tdbb, bool unique = false);
|
||||
Request* getRequest(thread_db* tdbb, USHORT level);
|
||||
Request* getRequest(thread_db* tdbb, USHORT level, bool systemRequest = false);
|
||||
void verifyAccess(thread_db* tdbb);
|
||||
void release(thread_db* tdbb);
|
||||
|
||||
Firebird::string getPlan(thread_db* tdbb, bool detailed) const;
|
||||
const Resources* getResources()
|
||||
{
|
||||
return resources;
|
||||
}
|
||||
|
||||
private:
|
||||
static void verifyTriggerAccess(thread_db* tdbb, const jrd_rel* ownerRelation, TrigVector* triggers,
|
||||
static void verifyTriggerAccess(thread_db* tdbb, const jrd_rel* ownerRelation, const Triggers& triggers,
|
||||
MetaName userName);
|
||||
static void triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, TrigVector* tvec, const MetaName &user);
|
||||
|
||||
static void triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, const Triggers& tvec, const MetaName &user);
|
||||
void buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, const MetaName& user);
|
||||
|
||||
void loadResources(thread_db* tdbb);
|
||||
bool streamsFormatCompare(thread_db* tdbb);
|
||||
|
||||
public:
|
||||
MemoryPool* pool;
|
||||
unsigned flags; // statement flags
|
||||
@ -94,11 +100,11 @@ public:
|
||||
ULONG impureSize; // Size of impure area
|
||||
mutable StmtNumber id; // statement identifier
|
||||
USHORT charSetId; // client character set (CS_METADATA for internal statements)
|
||||
Firebird::Array<record_param> rpbsSetup;
|
||||
Firebird::Array<RecordParameter> rpbsSetup;
|
||||
Firebird::Array<Request*> requests; // vector of requests
|
||||
Firebird::Mutex requestsGrow; // vector of requests protection when added new element
|
||||
ExternalAccessList externalList; // Access to procedures/triggers to be checked
|
||||
AccessItemList accessList; // Access items to be checked
|
||||
//ResourceList resources; // Resources (relations and indices)
|
||||
const jrd_prc* procedure; // procedure, if any
|
||||
const Function* function; // function, if any
|
||||
MetaName triggerName; // name of request (trigger), if any
|
||||
@ -114,8 +120,8 @@ public:
|
||||
MapFieldInfo mapFieldInfo; // Map field name to field info
|
||||
|
||||
private:
|
||||
Resources resources;
|
||||
Firebird::RefPtr<VersionedObjects> latestVersion;
|
||||
Resources* resources; // Resources (relations, routines, etc.)
|
||||
Firebird::RefPtr<VersionedObjects> latestVersion; // want std::atomic<std::shared_ptr> or mutex is needed
|
||||
};
|
||||
|
||||
|
||||
|
@ -5315,7 +5315,7 @@ dsc* evlMakeDbkey(Jrd::thread_db* tdbb, const SysFunction* function, const NestV
|
||||
if (!relation)
|
||||
(Arg::Gds(isc_relnotdef) << Arg::Str(relName)).raise();
|
||||
|
||||
relId = relation->rel_id;
|
||||
relId = relation->getId();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -5892,7 +5892,7 @@ dsc* evlPosition(thread_db* tdbb, const SysFunction* function, const NestValueAr
|
||||
|
||||
// we'll use the collation from the second string
|
||||
const USHORT ttype = value2->getTextType();
|
||||
HazardPtr<TextType> tt = INTL_texttype_lookup(tdbb, ttype);
|
||||
TextType* tt = INTL_texttype_lookup(tdbb, ttype);
|
||||
CharSet* cs = tt->getCharSet();
|
||||
const UCHAR canonicalWidth = tt->getCanonicalWidth();
|
||||
|
||||
@ -6076,7 +6076,7 @@ dsc* evlReplace(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
|
||||
}
|
||||
|
||||
const USHORT ttype = values[0]->getTextType();
|
||||
HazardPtr<TextType> tt = INTL_texttype_lookup(tdbb, ttype);
|
||||
TextType* tt = INTL_texttype_lookup(tdbb, ttype);
|
||||
CharSet* cs = tt->getCharSet();
|
||||
const UCHAR canonicalWidth = tt->getCanonicalWidth();
|
||||
|
||||
|
@ -580,7 +580,7 @@ void UserManagement::list(IUser* u, unsigned cachePosition)
|
||||
RecordBuffer* UserManagement::getList(thread_db* tdbb, jrd_rel* relation)
|
||||
{
|
||||
fb_assert(relation);
|
||||
fb_assert(relation->rel_id == rel_sec_user_attributes || relation->rel_id == rel_sec_users);
|
||||
fb_assert(relation->getId() == rel_sec_user_attributes || relation->getId() == rel_sec_users);
|
||||
|
||||
RecordBuffer* recordBuffer = getData(relation);
|
||||
if (recordBuffer)
|
||||
|
@ -59,7 +59,7 @@ void VirtualTable::erase(thread_db* tdbb, record_param* rpb)
|
||||
dsc desc;
|
||||
lck_t lock_type;
|
||||
|
||||
if (relation->rel_id == rel_mon_attachments)
|
||||
if (relation->getId() == rel_mon_attachments)
|
||||
{
|
||||
// Get attachment id
|
||||
if (!EVL_field(relation, rpb->rpb_record, f_mon_att_id, &desc))
|
||||
@ -75,7 +75,7 @@ void VirtualTable::erase(thread_db* tdbb, record_param* rpb)
|
||||
|
||||
lock_type = LCK_attachment;
|
||||
}
|
||||
else if (relation->rel_id == rel_mon_statements)
|
||||
else if (relation->getId() == rel_mon_statements)
|
||||
{
|
||||
// Get attachment id
|
||||
if (!EVL_field(relation, rpb->rpb_record, f_mon_stmt_att_id, &desc))
|
||||
|
@ -122,8 +122,6 @@ void WorkerStableAttachment::fini()
|
||||
Monitoring::cleanupAttachment(tdbb);
|
||||
attachment->releaseLocks(tdbb);
|
||||
LCK_fini(tdbb, LCK_OWNER_attachment);
|
||||
|
||||
attachment->releaseRelations(tdbb);
|
||||
}
|
||||
|
||||
destroy(attachment);
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "../common/dsc_proto.h"
|
||||
#include "../common/classes/array.h"
|
||||
#include "../common/classes/VaryStr.h"
|
||||
#include "../jrd/Statement.h"
|
||||
|
||||
using namespace Jrd;
|
||||
using namespace Firebird;
|
||||
@ -80,15 +81,9 @@ using namespace Firebird;
|
||||
typedef Ods::blob_page blob_page;
|
||||
|
||||
static ArrayField* alloc_array(jrd_tra*, Ods::InternalArrayDesc*);
|
||||
//static blb* allocate_blob(thread_db*, jrd_tra*);
|
||||
static ISC_STATUS blob_filter(USHORT, BlobControl*);
|
||||
//static blb* copy_blob(thread_db*, const bid*, bid*, USHORT, const UCHAR*, USHORT);
|
||||
//static void delete_blob(thread_db*, blb*, ULONG);
|
||||
//static void delete_blob_id(thread_db*, const bid*, ULONG, jrd_rel*);
|
||||
static ArrayField* find_array(jrd_tra*, const bid*);
|
||||
static BlobFilter* find_filter(thread_db*, SSHORT, SSHORT);
|
||||
//static blob_page* get_next_page(thread_db*, blb*, WIN *);
|
||||
//static void insert_page(thread_db*, blb*);
|
||||
static void move_from_string(Jrd::thread_db*, const dsc*, dsc*, jrd_rel*, Record*, USHORT);
|
||||
static void move_to_string(Jrd::thread_db*, dsc*, dsc*);
|
||||
static void slice_callback(array_slice*, ULONG, dsc*);
|
||||
@ -469,7 +464,7 @@ void BLB_garbage_collect(thread_db* tdbb,
|
||||
const bid* blob = (bid*) desc.dsc_address;
|
||||
if (!blob->isEmpty())
|
||||
{
|
||||
if (blob->bid_internal.bid_relation_id == relation->rel_id)
|
||||
if (blob->bid_internal.bid_relation_id == relation->getId())
|
||||
{
|
||||
const RecordNumber number = blob->get_permanent_number();
|
||||
bmGoing.set(number.getValue());
|
||||
@ -482,7 +477,7 @@ void BLB_garbage_collect(thread_db* tdbb,
|
||||
// ignore it. To be reconsider latter based on real user reports.
|
||||
// The same about staying blob few lines below
|
||||
gds__log("going blob (%ld:%ld) is not owned by relation (id = %d), ignored",
|
||||
blob->bid_quad.bid_quad_high, blob->bid_quad.bid_quad_low, relation->rel_id);
|
||||
blob->bid_quad.bid_quad_high, blob->bid_quad.bid_quad_low, relation->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -508,7 +503,7 @@ void BLB_garbage_collect(thread_db* tdbb,
|
||||
const bid* blob = (bid*) desc.dsc_address;
|
||||
if (!blob->isEmpty())
|
||||
{
|
||||
if (blob->bid_internal.bid_relation_id == relation->rel_id)
|
||||
if (blob->bid_internal.bid_relation_id == relation->getId())
|
||||
{
|
||||
const RecordNumber number = blob->get_permanent_number();
|
||||
if (bmGoing.test(number.getValue()))
|
||||
@ -521,7 +516,7 @@ void BLB_garbage_collect(thread_db* tdbb,
|
||||
else
|
||||
{
|
||||
gds__log("staying blob (%ld:%ld) is not owned by relation (id = %d), ignored",
|
||||
blob->bid_quad.bid_quad_high, blob->bid_quad.bid_quad_low, relation->rel_id);
|
||||
blob->bid_quad.bid_quad_high, blob->bid_quad.bid_quad_low, relation->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -535,7 +530,7 @@ void BLB_garbage_collect(thread_db* tdbb,
|
||||
const FB_UINT64 id = bmGoing.current();
|
||||
|
||||
bid blob;
|
||||
blob.set_permanent(relation->rel_id, RecordNumber(id));
|
||||
blob.set_permanent(relation->getId(), RecordNumber(id));
|
||||
|
||||
blb::delete_blob_id(tdbb, &blob, prior_page, relation);
|
||||
} while (bmGoing.getNext());
|
||||
@ -1045,7 +1040,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc,
|
||||
|
||||
// We should not materialize the blob if the destination field
|
||||
// stream (nod_union, for example) doesn't have a relation.
|
||||
const bool simpleMove = (relation == NULL);
|
||||
const bool simpleMove = !relation;
|
||||
|
||||
// Use local copy of source blob id to not change contents of from_desc in
|
||||
// a case when it points to materialized temporary blob (see below for
|
||||
@ -1102,11 +1097,11 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc,
|
||||
|
||||
Request* request = tdbb->getRequest();
|
||||
|
||||
if (relation->isVirtual()) {
|
||||
if (relation->rel_perm->isVirtual()) {
|
||||
ERR_post(Arg::Gds(isc_read_only));
|
||||
}
|
||||
|
||||
RelationPages* relPages = relation->getPages(tdbb);
|
||||
RelationPages* relPages = relation->rel_perm->getPages(tdbb);
|
||||
|
||||
// If either the source value is null or the blob id itself is null
|
||||
// (all zeros), then the blob is null.
|
||||
@ -1125,7 +1120,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc,
|
||||
// If the target is a view, this must be from a view update trigger.
|
||||
// Just pass the blob id thru.
|
||||
|
||||
if (relation->rel_view_rse)
|
||||
if (relation->isView())
|
||||
{
|
||||
// But if the sub_type or charset is different, create a new blob.
|
||||
if (DTYPE_IS_BLOB_OR_QUAD(from_desc->dsc_dtype) &&
|
||||
@ -1259,7 +1254,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc,
|
||||
if (bulk)
|
||||
blob->blb_flags |= BLB_bulk;
|
||||
|
||||
destination->set_permanent(relation->rel_id, DPM_store_blob(tdbb, blob, record));
|
||||
destination->set_permanent(relation->getId(), DPM_store_blob(tdbb, blob, relation, record));
|
||||
// This is the only place in the engine where blobs are materialized
|
||||
// If new places appear code below should transform to common sub-routine
|
||||
if (materialized_blob)
|
||||
@ -1450,7 +1445,7 @@ blb* blb::open2(thread_db* tdbb,
|
||||
ERR_post(Arg::Gds(isc_bad_segstr_id));
|
||||
|
||||
blob->blb_pg_space_id = relation->getPages(tdbb)->rel_pg_space_id;
|
||||
DPM_get_blob(tdbb, blob, relation.getPointer(), blobId.get_permanent_number(), false, 0);
|
||||
DPM_get_blob(tdbb, blob, relation, blobId.get_permanent_number(), false, 0);
|
||||
|
||||
#ifdef CHECK_BLOB_FIELD_ACCESS_FOR_SELECT
|
||||
if (!relation->isSystem() && blob->blb_fld_id < relation->rel_fields->count())
|
||||
@ -1753,15 +1748,12 @@ void blb::put_slice(thread_db* tdbb,
|
||||
|
||||
SSHORT n;
|
||||
if (info.sdl_info_field.length()) {
|
||||
n = MET_lookup_field(tdbb, relation.getPointer(), info.sdl_info_field);
|
||||
n = MET_lookup_field(tdbb, relation, info.sdl_info_field);
|
||||
}
|
||||
else {
|
||||
n = info.sdl_info_fid;
|
||||
}
|
||||
|
||||
// Make sure relation is scanned
|
||||
MET_scan_relation(tdbb, relation);
|
||||
|
||||
jrd_fld* field;
|
||||
if (n < 0 || !(field = MET_get_field(relation, n))) {
|
||||
IBERROR(197); // msg 197 field for array not known
|
||||
@ -2273,7 +2265,7 @@ void blb::delete_blob_id(thread_db* tdbb, const bid* blob_id, ULONG prior_page,
|
||||
if (blob_id->isEmpty())
|
||||
return;
|
||||
|
||||
if (blob_id->bid_internal.bid_relation_id != relation->rel_id)
|
||||
if (blob_id->bid_internal.bid_relation_id != relation->getId())
|
||||
CORRUPT(200); // msg 200 invalid blob id
|
||||
|
||||
// Fetch blob
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "firebird/Interface.h"
|
||||
#include "../common/classes/ImplementHelper.h"
|
||||
#include "../common/dsc.h"
|
||||
#include "../jrd/Resource.h"
|
||||
|
||||
namespace Ods
|
||||
{
|
||||
@ -51,6 +52,7 @@ namespace Jrd
|
||||
class Attachment;
|
||||
class BlobControl;
|
||||
class jrd_rel;
|
||||
class RelationPermanent;
|
||||
class Request;
|
||||
class jrd_tra;
|
||||
class vcl;
|
||||
@ -102,22 +104,22 @@ public:
|
||||
bool BLB_close(thread_db*);
|
||||
static blb* create(thread_db*, jrd_tra*, bid*);
|
||||
static blb* create2(thread_db*, jrd_tra*, bid*, USHORT, const UCHAR*, bool = false);
|
||||
static Jrd::blb* get_array(Jrd::thread_db*, Jrd::jrd_tra*, const Jrd::bid*, Ods::InternalArrayDesc*);
|
||||
static blb* get_array(thread_db*, jrd_tra*, const bid*, Ods::InternalArrayDesc*);
|
||||
ULONG BLB_get_data(thread_db*, UCHAR*, SLONG, bool = true);
|
||||
USHORT BLB_get_segment(thread_db*, void*, USHORT);
|
||||
static SLONG get_slice(Jrd::thread_db*, Jrd::jrd_tra*, const Jrd::bid*, const UCHAR*, USHORT,
|
||||
static SLONG get_slice(thread_db*, jrd_tra*, const bid*, const UCHAR*, USHORT,
|
||||
const UCHAR*, SLONG, UCHAR*);
|
||||
SLONG BLB_lseek(USHORT, SLONG);
|
||||
static void move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, jrd_rel* relation = nullptr, Record* record = nullptr, USHORT fieldId = 0, bool bulk = false);
|
||||
static void move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, jrd_rel* = nullptr, Record* record = nullptr, USHORT fieldId = 0, bool bulk = false);
|
||||
static blb* open(thread_db*, jrd_tra*, const bid*);
|
||||
static blb* open2(thread_db*, jrd_tra*, const bid*, USHORT, const UCHAR*, bool = false);
|
||||
void BLB_put_data(thread_db*, const UCHAR*, SLONG);
|
||||
void BLB_put_segment(thread_db*, const void*, USHORT);
|
||||
static void put_slice(thread_db*, jrd_tra*, bid*, const UCHAR*, USHORT, const UCHAR*, SLONG, UCHAR*);
|
||||
static void release_array(Jrd::ArrayField*);
|
||||
static void scalar(Jrd::thread_db*, Jrd::jrd_tra*, const Jrd::bid*, USHORT, const SLONG*, Jrd::impure_value*);
|
||||
static void release_array(ArrayField*);
|
||||
static void scalar(thread_db*, jrd_tra*, const bid*, USHORT, const SLONG*, impure_value*);
|
||||
|
||||
static void delete_blob_id(thread_db*, const bid*, ULONG, jrd_rel*);
|
||||
static void delete_blob_id(thread_db*, const bid*, ULONG, Jrd::jrd_rel*);
|
||||
void fromPageHeader(const Ods::blh* header);
|
||||
void toPageHeader(Ods::blh* header) const;
|
||||
void getFromPage(USHORT length, const UCHAR* data);
|
||||
|
@ -282,7 +282,7 @@ void IndexErrorContext::raise(thread_db* tdbb, idx_e result, Record* record)
|
||||
if (result == idx_e_conversion || result == idx_e_interrupt)
|
||||
ERR_punt();
|
||||
|
||||
const MetaName& relationName = isLocationDefined ? m_location.relation->rel_name : m_relation->rel_name;
|
||||
const MetaName& relationName = isLocationDefined ? m_location.relation->getName() : m_relation->getName();
|
||||
const USHORT indexId = isLocationDefined ? m_location.indexId : m_index->idx_id;
|
||||
|
||||
MetaName indexName(m_indexName), constraintName;
|
||||
@ -1417,14 +1417,14 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
error.value()[1] == isc_expression_eval_index))
|
||||
{
|
||||
MetaName indexName;
|
||||
MetadataCache::lookup_index(tdbb, indexName, relation->rel_name, idx->idx_id + 1);
|
||||
MetadataCache::lookup_index(tdbb, indexName, relation->getName(), idx->idx_id + 1);
|
||||
|
||||
if (indexName.isEmpty())
|
||||
indexName = "***unknown***";
|
||||
|
||||
error.prepend(Arg::Gds(isc_expression_eval_index) <<
|
||||
Arg::Str(indexName) <<
|
||||
Arg::Str(relation->rel_name));
|
||||
Arg::Str(relation->getName()));
|
||||
}
|
||||
|
||||
error.copyTo(tdbb->tdbb_status_vector);
|
||||
@ -3581,7 +3581,7 @@ static ULONG fast_load(thread_db* tdbb,
|
||||
// only for debug) so the id is actually redundant.
|
||||
btree_page* bucket = (btree_page*) DPM_allocate(tdbb, &leafLevel->window);
|
||||
bucket->btr_header.pag_type = pag_index;
|
||||
bucket->btr_relation = relation->rel_id;
|
||||
bucket->btr_relation = relation->getId();
|
||||
bucket->btr_id = (UCHAR)(idx->idx_id % 256);
|
||||
bucket->btr_level = 0;
|
||||
bucket->btr_length = BTR_SIZE;
|
||||
@ -3936,7 +3936,7 @@ static ULONG fast_load(thread_db* tdbb,
|
||||
|
||||
currLevel->bucket = bucket = (btree_page*) DPM_allocate(tdbb, window);
|
||||
bucket->btr_header.pag_type = pag_index;
|
||||
bucket->btr_relation = relation->rel_id;
|
||||
bucket->btr_relation = relation->getId();
|
||||
bucket->btr_id = (UCHAR)(idx->idx_id % 256);
|
||||
fb_assert(level <= MAX_UCHAR);
|
||||
bucket->btr_level = (UCHAR) level;
|
||||
@ -4265,7 +4265,7 @@ static ULONG fast_load(thread_db* tdbb,
|
||||
|
||||
if (window)
|
||||
{
|
||||
delete_tree(tdbb, relation->rel_id, idx->idx_id,
|
||||
delete_tree(tdbb, relation->getId(), idx->idx_id,
|
||||
window->win_page, PageNumber(window->win_page.getPageSpaceID(), 0));
|
||||
}
|
||||
|
||||
@ -4295,7 +4295,7 @@ static index_root_page* fetch_root(thread_db* tdbb, WIN* window, const jrd_rel*
|
||||
|
||||
if ((window->win_page = relPages->rel_index_root) == 0)
|
||||
{
|
||||
if (relation->rel_id == 0)
|
||||
if (relation->getId() == 0)
|
||||
return NULL;
|
||||
|
||||
DPM_scan_pages(tdbb);
|
||||
@ -6201,13 +6201,6 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re
|
||||
string key;
|
||||
|
||||
fb_assert(relation && idx && record);
|
||||
jrd_rel* wrk = MET_scan_relation(tdbb, relation->rel_id);
|
||||
if (!wrk)
|
||||
{
|
||||
key.printf("(target relation %s deleted)", relation->c_name());
|
||||
return key;
|
||||
}
|
||||
relation = wrk;
|
||||
|
||||
const FB_SIZE_T MAX_KEY_STRING_LEN = 250;
|
||||
string value;
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "../jrd/req.h"
|
||||
#include "../jrd/exe.h"
|
||||
|
||||
void BTR_all(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::IndexDescList&, Jrd::RelationPages*);
|
||||
void BTR_all(Jrd::thread_db*, Jrd::RelationPermanent*, Jrd::IndexDescList&, Jrd::RelationPages*);
|
||||
void BTR_complement_key(Jrd::temporary_key*);
|
||||
void BTR_create(Jrd::thread_db*, Jrd::IndexCreation&, Jrd::SelectivityList&);
|
||||
bool BTR_delete_index(Jrd::thread_db*, Jrd::win*, USHORT);
|
||||
|
@ -5447,49 +5447,6 @@ void BCBHashTable::remove(BufferDesc* bdb)
|
||||
|
||||
#ifdef HASH_USE_CDS_LIST
|
||||
|
||||
/// class ListNodeAllocator<T>
|
||||
|
||||
class InitPool
|
||||
{
|
||||
public:
|
||||
explicit InitPool(MemoryPool&)
|
||||
{
|
||||
m_pool = InitCDS::createPool();
|
||||
m_pool->setStatsGroup(m_stats);
|
||||
}
|
||||
|
||||
~InitPool()
|
||||
{
|
||||
// m_pool will be deleted by InitCDS dtor after cds termination
|
||||
// some memory could still be not freed until that moment
|
||||
|
||||
#ifdef DEBUG_CDS_MEMORY
|
||||
char str[256];
|
||||
sprintf(str, "CCH list's common pool stats:\n"
|
||||
" usage = %llu\n"
|
||||
" mapping = %llu\n"
|
||||
" max usage = %llu\n"
|
||||
" max mapping = %llu\n"
|
||||
"\n",
|
||||
m_stats.getCurrentUsage(),
|
||||
m_stats.getCurrentMapping(),
|
||||
m_stats.getMaximumUsage(),
|
||||
m_stats.getMaximumMapping()
|
||||
);
|
||||
gds__log(str);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* alloc(size_t size)
|
||||
{
|
||||
return m_pool->allocate(size ALLOC_ARGS);
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* m_pool;
|
||||
MemoryStats m_stats;
|
||||
};
|
||||
|
||||
static InitInstance<InitPool> initPool;
|
||||
|
||||
|
||||
@ -5506,4 +5463,9 @@ void ListNodeAllocator<T>::deallocate(T* p, std::size_t /* n */)
|
||||
MemoryPool::globalFree(p);
|
||||
}
|
||||
|
||||
void suspend()
|
||||
{
|
||||
cds::backoff::pause();
|
||||
}
|
||||
|
||||
#endif // HASH_USE_CDS_LIST
|
||||
|
@ -174,9 +174,9 @@ Statement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool
|
||||
"\t%2d - view_stream: %2d; alias: %s; relation: %s; procedure: %s; view: %s\n",
|
||||
i, s.csb_view_stream,
|
||||
(s.csb_alias ? s.csb_alias->c_str() : ""),
|
||||
(s.csb_relation ? s.csb_relation->rel_name.c_str() : ""),
|
||||
(s.csb_procedure ? s.csb_procedure->getName().toString().c_str() : ""),
|
||||
(s.csb_view ? s.csb_view->rel_name.c_str() : ""));
|
||||
(s.csb_relation ? s.csb_relation()->getName().c_str() : ""),
|
||||
(s.csb_procedure ? s.csb_procedure()->getName().toString().c_str() : ""),
|
||||
(s.csb_view ? s.csb_view->getName().c_str() : ""));
|
||||
}
|
||||
|
||||
cmp_trace("\n%s\n", csb->csb_dump.c_str());
|
||||
@ -264,9 +264,9 @@ const Format* CMP_format(thread_db* tdbb, CompilerScratch* csb, StreamType strea
|
||||
if (!tail->csb_format)
|
||||
{
|
||||
if (tail->csb_relation)
|
||||
tail->csb_format = MET_current(tdbb, tail->csb_relation);
|
||||
tail->csb_format = MET_current(tdbb, tail->csb_relation(tdbb));
|
||||
else if (tail->csb_procedure)
|
||||
tail->csb_format = tail->csb_procedure->prc_record_format;
|
||||
tail->csb_format = tail->csb_procedure(tdbb)->prc_record_format;
|
||||
//// TODO: LocalTableSourceNode
|
||||
else
|
||||
IBERROR(222); // msg 222 bad blr - invalid stream
|
||||
@ -375,7 +375,7 @@ ItemInfo* CMP_pass2_validation(thread_db* tdbb, CompilerScratch* csb, const Item
|
||||
}
|
||||
|
||||
|
||||
void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedure)
|
||||
void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, Rsc::Proc proc)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -399,21 +399,21 @@ void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* p
|
||||
return;
|
||||
|
||||
// this request must have EXECUTE permission on the stored procedure
|
||||
if (procedure->getName().package.isEmpty())
|
||||
if (proc()->getName().package.isEmpty())
|
||||
{
|
||||
CMP_post_access(tdbb, csb, procedure->getSecurityName(),
|
||||
(csb->csb_view ? csb->csb_view->rel_id : 0),
|
||||
SCL_execute, obj_procedures, procedure->getName().identifier);
|
||||
CMP_post_access(tdbb, csb, proc()->getSecurityName(),
|
||||
(csb->csb_view ? csb->csb_view()->getId() : 0),
|
||||
SCL_execute, obj_procedures, proc()->getName().identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
CMP_post_access(tdbb, csb, procedure->getSecurityName(),
|
||||
(csb->csb_view ? csb->csb_view->rel_id : 0),
|
||||
SCL_execute, obj_packages, procedure->getName().package);
|
||||
CMP_post_access(tdbb, csb, proc()->getSecurityName(),
|
||||
(csb->csb_view ? csb->csb_view()->getId() : 0),
|
||||
SCL_execute, obj_packages, proc()->getName().package);
|
||||
}
|
||||
|
||||
// Add the procedure to list of external objects accessed
|
||||
ExternalAccess temp(ExternalAccess::exa_procedure, procedure->getId());
|
||||
ExternalAccess temp(ExternalAccess::exa_procedure, proc()->getId());
|
||||
FB_SIZE_T idx;
|
||||
if (!csb->csb_external.find(temp, idx))
|
||||
csb->csb_external.insert(idx, temp);
|
||||
|
@ -25,14 +25,9 @@
|
||||
#define JRD_CMP_PROTO_H
|
||||
|
||||
#include "../jrd/req.h"
|
||||
// req.h includes exe.h => Jrd::CompilerScratch and Jrd::CompilerScratch::csb_repeat.
|
||||
// req.h includes exe.h => Jrd::CompilerScratch and Jrd::CompilerScratch::csb_repeat, Jrd::Subroutine
|
||||
#include "../jrd/scl.h"
|
||||
|
||||
//namespace Jrd
|
||||
//{
|
||||
// class RelationSourceNode;
|
||||
// ????????????????????????? }
|
||||
|
||||
StreamType* CMP_alloc_map(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType stream);
|
||||
Jrd::ValueExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*);
|
||||
Jrd::BoolExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::BoolExprNode*);
|
||||
@ -49,7 +44,7 @@ void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::MetaName
|
||||
Jrd::SecurityClass::flags_t, ObjectType obj_type, const Jrd::MetaName&,
|
||||
const Jrd::MetaName& = "");
|
||||
|
||||
void CMP_post_procedure_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*);
|
||||
void CMP_post_procedure_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::SubRoutine<Jrd::jrd_prc>);
|
||||
Jrd::RecordSource* CMP_post_rse(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::RseNode*);
|
||||
void CMP_release(Jrd::thread_db*, Jrd::Request*);
|
||||
|
||||
|
@ -724,7 +724,7 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2, DecimalStatus decSt)
|
||||
else
|
||||
ttype1 = ttype_binary;
|
||||
|
||||
HazardPtr<TextType> obj1 = INTL_texttype_lookup(tdbb, ttype1);
|
||||
TextType* obj1 = INTL_texttype_lookup(tdbb, ttype1);
|
||||
ttype1 = obj1->getType();
|
||||
|
||||
// Is arg2 a blob?
|
||||
@ -747,7 +747,7 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2, DecimalStatus decSt)
|
||||
else
|
||||
ttype2 = ttype_binary;
|
||||
|
||||
HazardPtr<TextType> obj2 = INTL_texttype_lookup(tdbb, ttype2);
|
||||
TextType* obj2 = INTL_texttype_lookup(tdbb, ttype2);
|
||||
ttype2 = obj2->getType();
|
||||
|
||||
if (ttype1 == ttype_binary || ttype2 == ttype_binary)
|
||||
|
6503
src/jrd/dfw.epp
6503
src/jrd/dfw.epp
File diff suppressed because it is too large
Load Diff
100
src/jrd/dpm.epp
100
src/jrd/dpm.epp
@ -153,7 +153,7 @@ void DPM_backout( thread_db* tdbb, record_param* rpb)
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"DPM_backout (rel_id %u, record_param %" QUADFORMAT"d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue());
|
||||
relation->getId(), rpb->rpb_number.getValue());
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" record %" ULONGFORMAT":%d transaction %" ULONGFORMAT" back %"
|
||||
@ -345,7 +345,7 @@ bool DPM_chain( thread_db* tdbb, record_param* org_rpb, record_param* new_rpb)
|
||||
jrd_rel* relation = org_rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"DPM_chain (rel_id %u, org_rpb %" QUADFORMAT"d, new_rpb %" QUADFORMAT"d)\n",
|
||||
relation->rel_id, org_rpb->rpb_number.getValue(),
|
||||
relation->getId(), org_rpb->rpb_number.getValue(),
|
||||
new_rpb ? new_rpb->rpb_number.getValue() : 0);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
@ -560,21 +560,21 @@ void DPM_create_relation( thread_db* tdbb, jrd_rel* relation)
|
||||
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"DPM_create_relation (relation %d)\n", relation->rel_id);
|
||||
"DPM_create_relation (relation %d)\n", relation->getId());
|
||||
#endif
|
||||
|
||||
RelationPages* relPages = relation->getBasePages();
|
||||
DPM_create_relation_pages(tdbb, relation, relPages);
|
||||
RelationPages* relPages = relation->rel_perm->getBasePages();
|
||||
DPM_create_relation_pages(tdbb, relation->rel_perm, relPages);
|
||||
|
||||
// Store page numbers in RDB$PAGES
|
||||
DPM_pages(tdbb, relation->rel_id, pag_pointer, (ULONG) 0,
|
||||
DPM_pages(tdbb, relation->getId(), pag_pointer, (ULONG) 0,
|
||||
(*relPages->rel_pages)[0] /*window.win_page*/);
|
||||
DPM_pages(tdbb, relation->rel_id, pag_root, (ULONG) 0,
|
||||
DPM_pages(tdbb, relation->getId(), pag_root, (ULONG) 0,
|
||||
relPages->rel_index_root /*root_window.win_page*/);
|
||||
}
|
||||
|
||||
|
||||
void DPM_create_relation_pages(thread_db* tdbb, jrd_rel* relation, RelationPages* relPages)
|
||||
void DPM_create_relation_pages(thread_db* tdbb, RelationPermanent* relation, RelationPages* relPages)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
@ -584,13 +584,13 @@ void DPM_create_relation_pages(thread_db* tdbb, jrd_rel* relation, RelationPages
|
||||
WIN window(relPages->rel_pg_space_id, -1);
|
||||
pointer_page* page = (pointer_page*) DPM_allocate(tdbb, &window);
|
||||
page->ppg_header.pag_type = pag_pointer;
|
||||
page->ppg_relation = relation->rel_id;
|
||||
page->ppg_relation = relation->getId();
|
||||
page->ppg_header.pag_flags = ppg_eof;
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
|
||||
// If this is relation 0 (RDB$PAGES), update the header
|
||||
|
||||
if (relation->rel_id == 0)
|
||||
if (relation->getId() == 0)
|
||||
{
|
||||
WIN root_window(HEADER_PAGE_NUMBER);
|
||||
header_page* header = (header_page*) CCH_FETCH(tdbb, &root_window, LCK_write, pag_header);
|
||||
@ -604,7 +604,7 @@ void DPM_create_relation_pages(thread_db* tdbb, jrd_rel* relation, RelationPages
|
||||
|
||||
if (!relPages->rel_pages)
|
||||
{
|
||||
vcl* vector = vcl::newVector(*relation->rel_pool, 1);
|
||||
vcl* vector = vcl::newVector(relation->getPool(), 1);
|
||||
relPages->rel_pages = vector;
|
||||
}
|
||||
(*relPages->rel_pages)[0] = window.win_page.getPageNum();
|
||||
@ -615,7 +615,7 @@ void DPM_create_relation_pages(thread_db* tdbb, jrd_rel* relation, RelationPages
|
||||
WIN root_window(relPages->rel_pg_space_id, -1);
|
||||
index_root_page* root = (index_root_page*) DPM_allocate(tdbb, &root_window);
|
||||
root->irt_header.pag_type = pag_root;
|
||||
root->irt_relation = relation->rel_id;
|
||||
root->irt_relation = relation->getId();
|
||||
//root->irt_count = 0;
|
||||
CCH_RELEASE(tdbb, &root_window);
|
||||
relPages->rel_index_root = root_window.win_page.getPageNum();
|
||||
@ -640,7 +640,7 @@ ULONG DPM_data_pages(thread_db* tdbb, jrd_rel* relation)
|
||||
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"DPM_data_pages (relation %d)\n", relation->rel_id);
|
||||
"DPM_data_pages (relation %d)\n", relation->getId());
|
||||
#endif
|
||||
|
||||
RelationPages* relPages = relation->getPages(tdbb);
|
||||
@ -945,7 +945,7 @@ void DPM_delete( thread_db* tdbb, record_param* rpb, ULONG prior_page)
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
"\tDPM_delete: page %" ULONGFORMAT
|
||||
" is empty and about to be released from relation %d\n",
|
||||
window->win_page.getPageNum(), rpb->rpb_relation->rel_id);
|
||||
window->win_page.getPageNum(), rpb->rpb_relation->getId());
|
||||
#endif
|
||||
|
||||
// Make sure that the pointer page is written after the data page.
|
||||
@ -1002,7 +1002,7 @@ void DPM_delete( thread_db* tdbb, record_param* rpb, ULONG prior_page)
|
||||
}
|
||||
|
||||
|
||||
void DPM_delete_relation( thread_db* tdbb, jrd_rel* relation)
|
||||
void DPM_delete_relation( thread_db* tdbb, RelationPermanent* relation)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -1025,7 +1025,7 @@ void DPM_delete_relation( thread_db* tdbb, jrd_rel* relation)
|
||||
AutoRequest handle;
|
||||
|
||||
FOR(REQUEST_HANDLE handle) X IN RDB$PAGES WITH
|
||||
X.RDB$RELATION_ID EQ relation->rel_id
|
||||
X.RDB$RELATION_ID EQ relation->getId()
|
||||
{
|
||||
ERASE X;
|
||||
}
|
||||
@ -1047,12 +1047,12 @@ void DPM_delete_relation_pages(Jrd::thread_db* tdbb, Jrd::jrd_rel* relation,
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"DPM_delete_relation_pages (relation %d, instance %" SQUADFORMAT")\n",
|
||||
relation->rel_id, relPages->rel_instance_id);
|
||||
relation->getId(), relPages->rel_instance_id);
|
||||
#endif
|
||||
|
||||
// Delete all data and pointer pages
|
||||
|
||||
SortedArray<ULONG, InlineStorage<ULONG, 256> > pages(*relation->rel_pool);
|
||||
SortedArray<ULONG, InlineStorage<ULONG, 256> > pages(relation->getPool());
|
||||
|
||||
for (ULONG sequence = 0; true; sequence++)
|
||||
{
|
||||
@ -1142,7 +1142,7 @@ bool DPM_fetch(thread_db* tdbb, record_param* rpb, USHORT lock)
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_READS,
|
||||
"DPM_fetch (rel_id %u, record_param %" QUADFORMAT"d, lock %d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), lock);
|
||||
relation->getId(), rpb->rpb_number.getValue(), lock);
|
||||
|
||||
VIO_trace(DEBUG_READS_INFO,
|
||||
" record %" ULONGFORMAT":%d\n", rpb->rpb_page, rpb->rpb_line);
|
||||
@ -1205,7 +1205,7 @@ bool DPM_fetch_back(thread_db* tdbb, record_param* rpb, USHORT lock, SSHORT latc
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_READS,
|
||||
"DPM_fetch_back (rel_id %u, record_param %" QUADFORMAT"d, lock %d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), lock);
|
||||
relation->getId(), rpb->rpb_number.getValue(), lock);
|
||||
|
||||
VIO_trace(DEBUG_READS_INFO,
|
||||
" record %" ULONGFORMAT":%d transaction %" ULONGFORMAT
|
||||
@ -1270,7 +1270,7 @@ void DPM_fetch_fragment( thread_db* tdbb, record_param* rpb, USHORT lock)
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_READS,
|
||||
"DPM_fetch_fragment (rel_id %u, record_param %" QUADFORMAT"d, lock %d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), lock);
|
||||
relation->getId(), rpb->rpb_number.getValue(), lock);
|
||||
|
||||
VIO_trace(DEBUG_READS_INFO,
|
||||
" record %" ULONGFORMAT":%d transaction %" ULONGFORMAT
|
||||
@ -1454,7 +1454,7 @@ bool DPM_get(thread_db* tdbb, record_param* rpb, SSHORT lock_type)
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_READS,
|
||||
"DPM_get (rel_id %u, record_param %" QUADFORMAT"d, lock type %d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), lock_type);
|
||||
relation->getId(), rpb->rpb_number.getValue(), lock_type);
|
||||
#endif
|
||||
|
||||
WIN* window = &rpb->getWindow(tdbb);
|
||||
@ -1483,7 +1483,7 @@ bool DPM_get(thread_db* tdbb, record_param* rpb, SSHORT lock_type)
|
||||
const bool pageOk =
|
||||
dpage->dpg_header.pag_type == pag_data &&
|
||||
!(dpage->dpg_header.pag_flags & (dpg_secondary | dpg_large | dpg_orphan)) &&
|
||||
dpage->dpg_relation == rpb->rpb_relation->rel_id &&
|
||||
dpage->dpg_relation == rpb->rpb_relation->getId() &&
|
||||
dpage->dpg_sequence == dpSequence &&
|
||||
(dpage->dpg_count > 0);
|
||||
|
||||
@ -1562,7 +1562,7 @@ ULONG DPM_get_blob(thread_db* tdbb,
|
||||
VIO_trace(DEBUG_READS,
|
||||
"DPM_get_blob (rel_id %u, blob, record_number %" QUADFORMAT
|
||||
"d, delete_flag %d, prior_page %" ULONGFORMAT")\n",
|
||||
relation->rel_id, record_number.getValue(), (int)delete_flag, prior_page);
|
||||
relation->getId(), record_number.getValue(), (int)delete_flag, prior_page);
|
||||
#endif
|
||||
|
||||
// Find starting point
|
||||
@ -1686,7 +1686,7 @@ bool DPM_next(thread_db* tdbb, record_param* rpb, USHORT lock_type, FindNextReco
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_READS,
|
||||
"DPM_next (rel_id %u, record_param %" QUADFORMAT"d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue());
|
||||
relation->getId(), rpb->rpb_number.getValue());
|
||||
#endif
|
||||
|
||||
WIN* window = &rpb->getWindow(tdbb);
|
||||
@ -1747,7 +1747,7 @@ bool DPM_next(thread_db* tdbb, record_param* rpb, USHORT lock_type, FindNextReco
|
||||
const bool pageOk =
|
||||
dpage->dpg_header.pag_type == pag_data &&
|
||||
!(dpage->dpg_header.pag_flags & (dpg_secondary | dpg_large | dpg_orphan)) &&
|
||||
dpage->dpg_relation == rpb->rpb_relation->rel_id &&
|
||||
dpage->dpg_relation == rpb->rpb_relation->getId() &&
|
||||
dpage->dpg_sequence == dpSequence &&
|
||||
(dpage->dpg_count > 0);
|
||||
|
||||
@ -2019,7 +2019,7 @@ void DPM_scan_pages( thread_db* tdbb)
|
||||
// infinite recursion from this internal request when RDB$PAGES
|
||||
// has been extended with another pointer page.
|
||||
|
||||
jrd_rel* relation = MetadataCache::findRelation(tdbb, 0);
|
||||
RelationPermanent* relation = MetadataCache::lookupRelation(tdbb, 0u);
|
||||
RelationPages* relPages = relation->getBasePages();
|
||||
|
||||
vcl** address = &relPages->rel_pages;
|
||||
@ -2042,7 +2042,7 @@ void DPM_scan_pages( thread_db* tdbb)
|
||||
|
||||
FOR(REQUEST_HANDLE request) X IN RDB$PAGES
|
||||
{
|
||||
relation = MetadataCache::findRelation(tdbb, X.RDB$RELATION_ID);
|
||||
relation = MetadataCache::lookupRelation(tdbb, X.RDB$RELATION_ID);
|
||||
relPages = relation->getBasePages();
|
||||
sequence = X.RDB$PAGE_SEQUENCE;
|
||||
MemoryPool* pool = dbb->dbb_permanent;
|
||||
@ -2054,7 +2054,7 @@ void DPM_scan_pages( thread_db* tdbb)
|
||||
|
||||
case pag_pointer:
|
||||
address = &relPages->rel_pages;
|
||||
pool = relation->rel_pool;
|
||||
pool = &relation->getPool();
|
||||
break;
|
||||
|
||||
case pag_transactions:
|
||||
@ -2096,7 +2096,7 @@ void DPM_store( thread_db* tdbb, record_param* rpb, PageStack& stack, const Jrd:
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"DPM_store (rel_id %u, record_param %" QUADFORMAT"d, stack, type %d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), type);
|
||||
relation->getId(), rpb->rpb_number.getValue(), type);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" record to store %" ULONGFORMAT":%d transaction %" ULONGFORMAT
|
||||
@ -2187,7 +2187,7 @@ RecordNumber DPM_store_blob(thread_db* tdbb, blb* blob, jrd_rel* relation, Recor
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"DPM_store_blob (rel_id %u, blob, record)\n",
|
||||
relation->rel_id);
|
||||
relation()->getId());
|
||||
#endif
|
||||
|
||||
// Figure out length of blob on page. Remember that blob can either
|
||||
@ -2261,7 +2261,7 @@ void DPM_rewrite_header( thread_db* tdbb, record_param* rpb)
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"DPM_rewrite_header (rel_id %u, record_param %" QUADFORMAT"d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue());
|
||||
relation->getId(), rpb->rpb_number.getValue());
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" record %" ULONGFORMAT":%d\n", rpb->rpb_page, rpb->rpb_line);
|
||||
@ -2316,7 +2316,7 @@ void DPM_update( thread_db* tdbb, record_param* rpb, PageStack* stack, const jrd
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"DPM_update (rel_id %u, record_param %" QUADFORMAT"d, stack, transaction %" ULONGFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" record %" ULONGFORMAT":%d transaction %" ULONGFORMAT" back %"
|
||||
@ -2694,7 +2694,7 @@ static void fragment(thread_db* tdbb,
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"fragment (rel_id %u, record_param %" QUADFORMAT
|
||||
"d, available_space %d, dcc, length %d, transaction %" ULONGFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), available_space, length,
|
||||
relation->getId(), rpb->rpb_number.getValue(), available_space, length,
|
||||
transaction ? transaction->tra_number : 0);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
@ -2869,7 +2869,7 @@ static void extend_relation(thread_db* tdbb, jrd_rel* relation, WIN* window, con
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" extend_relation (relation %d, instance %" SQUADFORMAT", window)\n",
|
||||
relation->rel_id, relPages->rel_instance_id);
|
||||
relation->getId(), relPages->rel_instance_id);
|
||||
#endif
|
||||
|
||||
// Search pointer pages for an empty slot.
|
||||
@ -2915,20 +2915,20 @@ static void extend_relation(thread_db* tdbb, jrd_rel* relation, WIN* window, con
|
||||
pointer_page* new_ppage = (pointer_page*) DPM_allocate(tdbb, &new_pp_window);
|
||||
new_ppage->ppg_header.pag_type = pag_pointer;
|
||||
new_ppage->ppg_header.pag_flags |= ppg_eof;
|
||||
new_ppage->ppg_relation = relation->rel_id;
|
||||
new_ppage->ppg_relation = relation->getId();
|
||||
new_ppage->ppg_sequence = ++pp_sequence;
|
||||
slot = 0;
|
||||
CCH_must_write(tdbb, &new_pp_window);
|
||||
CCH_RELEASE(tdbb, &new_pp_window);
|
||||
|
||||
vcl* vector = relPages->rel_pages =
|
||||
vcl::newVector(*relation->rel_pool, relPages->rel_pages, pp_sequence + 1);
|
||||
vcl::newVector(relation->getPool(), relPages->rel_pages, pp_sequence + 1);
|
||||
(*vector)[pp_sequence] = new_pp_window.win_page.getPageNum();
|
||||
|
||||
// hvlad: temporary tables don't save their pointer pages in RDB$PAGES
|
||||
if (relation->rel_id && (relPages->rel_instance_id == 0))
|
||||
if (relation->getId() && (relPages->rel_instance_id == 0))
|
||||
{
|
||||
DPM_pages(tdbb, relation->rel_id, pag_pointer,
|
||||
DPM_pages(tdbb, relation->getId(), pag_pointer,
|
||||
pp_sequence, new_pp_window.win_page.getPageNum());
|
||||
}
|
||||
relPages->rel_slot_space = pp_sequence;
|
||||
@ -2965,7 +2965,7 @@ static void extend_relation(thread_db* tdbb, jrd_rel* relation, WIN* window, con
|
||||
const PageNumber firstPage = window->win_page;
|
||||
|
||||
dpage->dpg_sequence = pp_sequence * dbb->dbb_dp_per_pp + slot;
|
||||
dpage->dpg_relation = relation->rel_id;
|
||||
dpage->dpg_relation = relation->getId();
|
||||
dpage->dpg_header.pag_type = pag_data;
|
||||
if (type != DPM_primary) {
|
||||
dpage->dpg_header.pag_flags |= dpg_secondary;
|
||||
@ -2980,7 +2980,7 @@ static void extend_relation(thread_db* tdbb, jrd_rel* relation, WIN* window, con
|
||||
|
||||
dpage = (data_page*) CCH_fake(tdbb, window, 1);
|
||||
dpage->dpg_sequence = pp_sequence * dbb->dbb_dp_per_pp + slot + i;
|
||||
dpage->dpg_relation = relation->rel_id;
|
||||
dpage->dpg_relation = relation->getId();
|
||||
dpage->dpg_header.pag_type = pag_data;
|
||||
CCH_RELEASE_TAIL(tdbb, window);
|
||||
|
||||
@ -3026,7 +3026,7 @@ static void extend_relation(thread_db* tdbb, jrd_rel* relation, WIN* window, con
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" extended_relation (relation %d, window_page %" ULONGFORMAT")\n",
|
||||
relation->rel_id, window->win_page.getPageNum());
|
||||
relation->getId(), window->win_page.getPageNum());
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -3197,7 +3197,7 @@ static bool get_header(WIN* window, USHORT line, record_param* rpb)
|
||||
rpb->rpb_transaction_nr = Ods::getTraNum(header);
|
||||
rpb->rpb_format_number = header->rhdf_format;
|
||||
|
||||
if (rpb->rpb_relation->rel_id == 0 /*i.e.RDB$PAGES*/ && rpb->rpb_transaction_nr != 0)
|
||||
if (rpb->rpb_relation->getId() == 0 /*i.e.RDB$PAGES*/ && rpb->rpb_transaction_nr != 0)
|
||||
{
|
||||
// RDB$PAGES relation should be modified only by system transaction
|
||||
ERR_post(Arg::Gds(isc_wrong_page));
|
||||
@ -3261,14 +3261,14 @@ static pointer_page* get_pointer_page(thread_db* tdbb,
|
||||
|
||||
// hvlad: temporary tables don't save their pointer pages in RDB$PAGES
|
||||
if (relPages->rel_instance_id == 0)
|
||||
DPM_pages(tdbb, relation->rel_id, pag_pointer, vector->count(), next_ppg);
|
||||
DPM_pages(tdbb, relation->getId(), pag_pointer, vector->count(), next_ppg);
|
||||
}
|
||||
}
|
||||
|
||||
window->win_page = (*vector)[sequence];
|
||||
pointer_page* page = (pointer_page*) CCH_FETCH(tdbb, window, lock, pag_pointer);
|
||||
|
||||
if (page->ppg_relation != relation->rel_id || page->ppg_sequence != sequence)
|
||||
if (page->ppg_relation != relation->getId() || page->ppg_sequence != sequence)
|
||||
CORRUPT(259); // msg 259 bad pointer page
|
||||
|
||||
return page;
|
||||
@ -3344,7 +3344,7 @@ static rhd* locate_space(thread_db* tdbb,
|
||||
const bool pageOk =
|
||||
dpage->dpg_header.pag_type == pag_data &&
|
||||
!(dpage->dpg_header.pag_flags & wrongFlags) &&
|
||||
dpage->dpg_relation == rpb->rpb_relation->rel_id &&
|
||||
dpage->dpg_relation == rpb->rpb_relation->getId() &&
|
||||
//dpage->dpg_sequence == dpSequence &&
|
||||
(dpage->dpg_count > 0);
|
||||
|
||||
@ -3527,7 +3527,7 @@ static rhd* locate_space(thread_db* tdbb,
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" extended relation %d with page %" ULONGFORMAT" to get %d bytes\n",
|
||||
relation->rel_id, window->win_page.getPageNum(), size);
|
||||
relation->getId(), window->win_page.getPageNum(), size);
|
||||
#endif
|
||||
|
||||
return (rhd*) space;
|
||||
@ -3554,7 +3554,7 @@ static void mark_full(thread_db* tdbb, record_param* rpb)
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"mark_full (rel_id %u)\n", relation->rel_id);
|
||||
"mark_full (rel_id %u)\n", relation->getId());
|
||||
#endif
|
||||
|
||||
// We need to access the pointer page for write. To avoid deadlocks,
|
||||
@ -3713,7 +3713,7 @@ static void store_big_record(thread_db* tdbb,
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"store_big_record (rel_id %u, record %" SQUADFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue());
|
||||
relation->getId(), rpb->rpb_number.getValue());
|
||||
#endif
|
||||
|
||||
// Start compression from the end.
|
||||
@ -3739,7 +3739,7 @@ static void store_big_record(thread_db* tdbb,
|
||||
data_page* page = (data_page*) DPM_allocate(tdbb, &rpb->getWindow(tdbb));
|
||||
page->dpg_header.pag_type = pag_data;
|
||||
page->dpg_header.pag_flags = dpg_orphan | dpg_full;
|
||||
page->dpg_relation = rpb->rpb_relation->rel_id;
|
||||
page->dpg_relation = rpb->rpb_relation->getId();
|
||||
page->dpg_count = 1;
|
||||
|
||||
const auto inLength = dcc.truncateTail(max_data);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "../jrd/RecordNumber.h"
|
||||
#include "../jrd/sbm.h"
|
||||
#include "../jrd/vio_proto.h"
|
||||
#include "../jrd/Resource.h"
|
||||
|
||||
// fwd. decl.
|
||||
namespace Jrd
|
||||
@ -62,13 +63,13 @@ bool DPM_chain(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*);
|
||||
void DPM_create_relation(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
ULONG DPM_data_pages(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
void DPM_delete(Jrd::thread_db*, Jrd::record_param*, ULONG);
|
||||
void DPM_delete_relation(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
void DPM_delete_relation(Jrd::thread_db*, Jrd::RelationPermanent*);
|
||||
bool DPM_fetch(Jrd::thread_db*, Jrd::record_param*, USHORT);
|
||||
bool DPM_fetch_back(Jrd::thread_db*, Jrd::record_param*, USHORT, SSHORT);
|
||||
void DPM_fetch_fragment(Jrd::thread_db*, Jrd::record_param*, USHORT);
|
||||
SINT64 DPM_gen_id(Jrd::thread_db*, SLONG, bool, SINT64);
|
||||
bool DPM_get(Jrd::thread_db*, Jrd::record_param*, SSHORT);
|
||||
ULONG DPM_get_blob(Jrd::thread_db*, Jrd::blb*, RecordNumber, bool, ULONG);
|
||||
ULONG DPM_get_blob(Jrd::thread_db*, Jrd::blb*, Jrd::jrd_rel*, RecordNumber, bool, ULONG);
|
||||
bool DPM_next(Jrd::thread_db*, Jrd::record_param*, USHORT, Jrd::FindNextRecordScope);
|
||||
void DPM_pages(Jrd::thread_db*, SSHORT, int, ULONG, ULONG);
|
||||
#ifdef SUPERSERVER_V2
|
||||
@ -80,7 +81,7 @@ RecordNumber DPM_store_blob(Jrd::thread_db*, Jrd::blb*, Jrd::jrd_rel*, Jrd::Reco
|
||||
void DPM_rewrite_header(Jrd::thread_db*, Jrd::record_param*);
|
||||
void DPM_update(Jrd::thread_db*, Jrd::record_param*, Jrd::PageStack*, const Jrd::jrd_tra*);
|
||||
|
||||
void DPM_create_relation_pages(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::RelationPages*);
|
||||
void DPM_delete_relation_pages(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::RelationPages*);
|
||||
void DPM_create_relation_pages(Jrd::thread_db*, Jrd::RelationPermanent*, Jrd::RelationPages*);
|
||||
void DPM_delete_relation_pages(Jrd::thread_db*, Jrd::RelationPermanent*, Jrd::RelationPages*);
|
||||
|
||||
#endif // JRD_DPM_PROTO_H
|
||||
|
@ -324,7 +324,7 @@ void EVL_dbkey_bounds(thread_db* tdbb, const Array<DbKeyRangeNode*>& ranges,
|
||||
Aligner<RecordNumber::Packed> alignedNumber(ptr, length);
|
||||
const auto dbkey = (const RecordNumber::Packed*) alignedNumber;
|
||||
|
||||
if (dbkey->bid_relation_id == relation->rel_id)
|
||||
if (dbkey->bid_relation_id == relation->getId())
|
||||
{
|
||||
RecordNumber recno;
|
||||
recno.bid_decode(dbkey);
|
||||
@ -357,7 +357,7 @@ void EVL_dbkey_bounds(thread_db* tdbb, const Array<DbKeyRangeNode*>& ranges,
|
||||
Aligner<RecordNumber::Packed> alignedNumber(ptr, length);
|
||||
const auto dbkey = (const RecordNumber::Packed*) alignedNumber;
|
||||
|
||||
if (dbkey->bid_relation_id == relation->rel_id)
|
||||
if (dbkey->bid_relation_id == relation->getId())
|
||||
{
|
||||
RecordNumber recno;
|
||||
recno.bid_decode(dbkey);
|
||||
@ -429,7 +429,7 @@ bool EVL_field(jrd_rel* relation, Record* record, USHORT id, dsc* desc)
|
||||
break;
|
||||
}
|
||||
|
||||
format = MET_format(tdbb, relation, format->fmt_version + 1);
|
||||
format = MET_format(tdbb, relation->rel_perm, format->fmt_version + 1);
|
||||
fb_assert(format);
|
||||
}
|
||||
|
||||
|
425
src/jrd/exe.cpp
425
src/jrd/exe.cpp
@ -530,7 +530,7 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, TriggerActio
|
||||
return;
|
||||
}
|
||||
|
||||
TrigVectorPtr* triggers = attachment->att_database->dbb_mdc->getTriggers(type | TRIGGER_TYPE_DB);
|
||||
const Triggers* triggers = attachment->att_database->dbb_mdc->getTriggers(type | TRIGGER_TYPE_DB);
|
||||
if (triggers && *triggers)
|
||||
{
|
||||
jrd_tra* old_transaction = tdbb->getTransaction();
|
||||
@ -538,7 +538,7 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, TriggerActio
|
||||
|
||||
try
|
||||
{
|
||||
EXE_execute_triggers(tdbb, triggers, NULL, NULL, trigger_action, StmtNode::ALL_TRIGS);
|
||||
EXE_execute_triggers(tdbb, *triggers, NULL, NULL, trigger_action, StmtNode::ALL_TRIGS);
|
||||
tdbb->setTransaction(old_transaction);
|
||||
}
|
||||
catch (const Exception&)
|
||||
@ -556,30 +556,28 @@ void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTri
|
||||
Jrd::Database* const dbb = tdbb->getDatabase();
|
||||
|
||||
// Our caller verifies (ATT_no_db_triggers) if DDL triggers should not run.
|
||||
TrigVectorPtr* cachedTriggers = dbb->dbb_mdc->getTriggers(TRIGGER_TYPE_DDL);
|
||||
const Triggers* cachedTriggers = dbb->dbb_mdc->getTriggers(TRIGGER_TYPE_DDL);
|
||||
|
||||
if (cachedTriggers && *cachedTriggers)
|
||||
{
|
||||
TrigVector triggers;
|
||||
TrigVector* triggersPtr = &triggers;
|
||||
unsigned n = 0;
|
||||
Triggers triggers;
|
||||
|
||||
for (auto t : cachedTriggers->load()->snapshot())
|
||||
for (auto t : *cachedTriggers)
|
||||
{
|
||||
const bool preTrigger = ((t->type & 0x1) == 0);
|
||||
|
||||
if ((t->type & (1LL << action)) && (preTriggers == preTrigger))
|
||||
triggers.store(tdbb, n++, t);
|
||||
triggers.addTrigger(tdbb, t);
|
||||
}
|
||||
|
||||
if (triggers.hasData())
|
||||
if (triggers)
|
||||
{
|
||||
jrd_tra* const oldTransaction = tdbb->getTransaction();
|
||||
tdbb->setTransaction(transaction);
|
||||
|
||||
try
|
||||
{
|
||||
EXE_execute_triggers(tdbb, &triggersPtr, NULL, NULL, TRIGGER_DDL,
|
||||
EXE_execute_triggers(tdbb, triggers, NULL, NULL, TRIGGER_DDL,
|
||||
preTriggers ? StmtNode::PRE_TRIG : StmtNode::POST_TRIG);
|
||||
|
||||
tdbb->setTransaction(oldTransaction);
|
||||
@ -887,7 +885,7 @@ void EXE_start(thread_db* tdbb, Request* request, jrd_tra* transaction)
|
||||
provide transaction stability by preventing a relation from being
|
||||
dropped after it has been referenced from an active transaction. */
|
||||
|
||||
TRA_post_resources(tdbb, transaction, statement->resources);
|
||||
transaction->postResources(tdbb, statement->getResources());
|
||||
|
||||
TRA_attach_request(transaction, request);
|
||||
request->req_flags &= req_restart_ready;
|
||||
@ -1117,7 +1115,7 @@ static void execute_looper(thread_db* tdbb,
|
||||
|
||||
|
||||
void EXE_execute_triggers(thread_db* tdbb,
|
||||
TrigVectorPtr* triggers,
|
||||
const Triggers& triggers,
|
||||
record_param* old_rpb,
|
||||
record_param* new_rpb,
|
||||
TriggerAction trigger_action, StmtNode::WhichTrigger which_trig)
|
||||
@ -1133,15 +1131,11 @@ void EXE_execute_triggers(thread_db* tdbb,
|
||||
* if any blow up.
|
||||
*
|
||||
**************************************/
|
||||
if (!(triggers && *triggers))
|
||||
return;
|
||||
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
Request* const request = tdbb->getRequest();
|
||||
jrd_tra* const transaction = request ? request->req_transaction : tdbb->getTransaction();
|
||||
|
||||
TrigVectorPtr vector(triggers->load());
|
||||
Record* const old_rec = old_rpb ? old_rpb->rpb_record : NULL;
|
||||
Record* const new_rec = new_rpb ? new_rpb->rpb_record : NULL;
|
||||
|
||||
@ -1171,10 +1165,8 @@ void EXE_execute_triggers(thread_db* tdbb,
|
||||
|
||||
try
|
||||
{
|
||||
for (auto ptr : vector.load()->snapshot())
|
||||
for (auto* ptr : triggers)
|
||||
{
|
||||
ptr->compile(tdbb);
|
||||
|
||||
trigger = ptr->statement->findRequest(tdbb);
|
||||
|
||||
if (!is_db_trigger)
|
||||
@ -1252,15 +1244,9 @@ void EXE_execute_triggers(thread_db* tdbb,
|
||||
|
||||
trigger = NULL;
|
||||
}
|
||||
|
||||
if (vector != *triggers)
|
||||
MET_release_triggers(tdbb, &vector, true);
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
if (vector != *triggers)
|
||||
MET_release_triggers(tdbb, &vector, true);
|
||||
|
||||
if (trigger)
|
||||
{
|
||||
EXE_unwind(tdbb, trigger);
|
||||
@ -1712,392 +1698,3 @@ void AutoRequest::release()
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceList::setResetPointersHazard(thread_db* tdbb, bool set)
|
||||
{
|
||||
if (hazardFlag != set)
|
||||
{
|
||||
// (un-)register hazard pointers
|
||||
for (auto r : list)
|
||||
{
|
||||
void* hazardPointer = nullptr;
|
||||
|
||||
switch (r.rsc_type)
|
||||
{
|
||||
case Resource::rsc_relation:
|
||||
hazardPointer = r.rsc_rel;
|
||||
break;
|
||||
|
||||
case Resource::rsc_index:
|
||||
break;
|
||||
|
||||
case Resource::rsc_procedure:
|
||||
case Resource::rsc_function:
|
||||
hazardPointer = r.rsc_routine;
|
||||
break;
|
||||
|
||||
case Resource::rsc_collation:
|
||||
hazardPointer = r.rsc_coll;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hazardPointer)
|
||||
{
|
||||
if (set)
|
||||
hazardDelayed->add(hazardPointer, FB_FUNCTION);
|
||||
else
|
||||
hazardDelayed->remove(hazardPointer, FB_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
hazardFlag = set;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceList::transferList(thread_db* tdbb, const InternalResourceList& from,
|
||||
Resource::State resetState, ResourceTypes rt, NewResources* nr, ResourceList* hazardList)
|
||||
{
|
||||
// Copy needed resources
|
||||
FB_SIZE_T pos = 0;
|
||||
for (auto src : from)
|
||||
{
|
||||
if (src.rsc_state == Resource::State::Registered) // registered but never posted
|
||||
continue;
|
||||
|
||||
if (!rt.test(src.rsc_type)) // skip some types of resources
|
||||
continue;
|
||||
|
||||
while (pos < list.getCount() && src > list[pos])
|
||||
++pos;
|
||||
list.insert(pos, src);
|
||||
nr->push(pos);
|
||||
|
||||
if (resetState != Resource::State::Locked) // The strongest state
|
||||
list[pos].rsc_state = resetState;
|
||||
|
||||
++pos; // minor performance optimization
|
||||
}
|
||||
|
||||
// Increase use counters
|
||||
NewResources toLock;
|
||||
{ // scope
|
||||
//MutexEnsureUnlock g(tdbb->getDatabase()->dbb_mdc->mdc_use_mutex, FB_FUNCTION);
|
||||
MutexLockGuard g(tdbb->getDatabase()->dbb_mdc->mdc_use_mutex, FB_FUNCTION);
|
||||
//bool useMutexLocked = false;
|
||||
|
||||
for (auto n : *nr)
|
||||
{
|
||||
Resource& r = list[n];
|
||||
if (r.rsc_state != Resource::State::Posted) // use count was already increased
|
||||
continue;
|
||||
/*
|
||||
// First take care about locking
|
||||
switch (r.rsc_type)
|
||||
{
|
||||
case Resource::rsc_procedure:
|
||||
case Resource::rsc_function:
|
||||
if (!useMutexLocked)
|
||||
{
|
||||
g.enter();
|
||||
useMutexLocked = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (useMutexLocked)
|
||||
{
|
||||
g.leave();
|
||||
useMutexLocked = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Next increment counter
|
||||
*/ switch (r.rsc_type)
|
||||
{
|
||||
case Resource::rsc_relation:
|
||||
{
|
||||
ExistenceLock* lock = r.rsc_rel->rel_existence_lock;
|
||||
r.rsc_state = lock ? lock->inc(tdbb) : Resource::State::Locked;
|
||||
break;
|
||||
}
|
||||
|
||||
case Resource::rsc_index:
|
||||
// Relation locks MUST be taken before index locks - skip it here.
|
||||
break;
|
||||
|
||||
case Resource::rsc_procedure:
|
||||
case Resource::rsc_function:
|
||||
{
|
||||
ExistenceLock* lock = r.rsc_routine->existenceLock;
|
||||
r.rsc_state = lock ? lock->inc(tdbb) : Resource::State::Locked;
|
||||
|
||||
#ifdef DEBUG_PROCS
|
||||
string buffer;
|
||||
buffer.printf(
|
||||
"Called from Statement::makeRequest:\n\t Incrementing use count of %s\n",
|
||||
routine->getName()->toString().c_str());
|
||||
JRD_print_procedure_info(tdbb, buffer.c_str());
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Resource::rsc_collation:
|
||||
{
|
||||
Collation* coll = r.rsc_coll;
|
||||
coll->incUseCount(tdbb);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BUGCHECK(219); // msg 219 request of unknown resource
|
||||
}
|
||||
|
||||
if (r.rsc_state != Resource::State::Locked)
|
||||
toLock.push(n);
|
||||
}
|
||||
}
|
||||
|
||||
if (hazardList)
|
||||
hazardList->setResetPointersHazard(tdbb, false);
|
||||
|
||||
// Now lock not yet locked objects
|
||||
for (auto n : toLock)
|
||||
{
|
||||
Resource& r = list[n];
|
||||
|
||||
if (r.rsc_state != Resource::State::Counted) // use count was already increased
|
||||
continue;
|
||||
|
||||
ExistenceLock* lock = nullptr;
|
||||
|
||||
switch (r.rsc_type)
|
||||
{
|
||||
case Resource::rsc_relation:
|
||||
lock = r.rsc_rel->rel_existence_lock;
|
||||
break;
|
||||
|
||||
case Resource::rsc_index:
|
||||
{
|
||||
HazardPtr<IndexLock> index = r.rsc_rel->getIndexLock(tdbb, r.rsc_id);
|
||||
r.rsc_state = index ? index->idl_lock.inc(tdbb) : Resource::State::Locked;
|
||||
if (index && r.rsc_state != Resource::State::Locked)
|
||||
lock = &index->idl_lock;
|
||||
}
|
||||
break;
|
||||
|
||||
case Resource::rsc_procedure:
|
||||
case Resource::rsc_function:
|
||||
lock = r.rsc_routine->existenceLock;
|
||||
break;
|
||||
|
||||
case Resource::rsc_collation:
|
||||
{
|
||||
Collation* coll = r.rsc_coll;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lock)
|
||||
lock->enter245(tdbb);
|
||||
r.rsc_state = Resource::State::Locked;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceList::raiseNotRegistered(const Resource& r, Resource::rsc_s type, const char* name)
|
||||
{
|
||||
if (r.rsc_rel && r.rsc_rel->isSystem())
|
||||
return;
|
||||
|
||||
// Resource type (r.type) and actual type may differ when working with index
|
||||
const char* typeName = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case Resource::rsc_relation:
|
||||
typeName = "Relation";
|
||||
break;
|
||||
|
||||
case Resource::rsc_index:
|
||||
typeName = "Index";
|
||||
break;
|
||||
|
||||
case Resource::rsc_procedure:
|
||||
typeName = "Procedure";
|
||||
break;
|
||||
|
||||
case Resource::rsc_function:
|
||||
typeName = "Function";
|
||||
break;
|
||||
|
||||
case Resource::rsc_collation:
|
||||
typeName = "Collation";
|
||||
break;
|
||||
}
|
||||
|
||||
fb_assert(typeName);
|
||||
string msg;
|
||||
msg.printf("%s %s was not registered for use by request or transaction", typeName, name);
|
||||
ERR_post(Arg::Gds(isc_random) << msg);
|
||||
}
|
||||
|
||||
void ResourceList::transferResources(thread_db* tdbb, ResourceList& from,
|
||||
ResourceTypes rt, NewResources& nr)
|
||||
{
|
||||
transferList(tdbb, from.list, Resource::State::Posted, rt, &nr, nullptr);
|
||||
}
|
||||
|
||||
void ResourceList::transferResources(thread_db* tdbb, ResourceList& from)
|
||||
{
|
||||
NewResources work;
|
||||
transferList(tdbb, from.list, Resource::State::Locked, ResourceTypes().setAll(), &work, &from);
|
||||
}
|
||||
|
||||
void ResourceList::releaseResources(thread_db* tdbb, jrd_tra* transaction)
|
||||
{
|
||||
// 0. Get ready to run drom dtor()
|
||||
if (!list.hasData())
|
||||
return;
|
||||
if (!tdbb)
|
||||
tdbb = JRD_get_thread_data();
|
||||
|
||||
// 1. Need to take hazard locks on all involved objects
|
||||
setResetPointersHazard(tdbb, true);
|
||||
|
||||
// 2. First of all release indices - they do not need refcnt mutex locked
|
||||
for (auto r : getObjects(Resource::rsc_index))
|
||||
{
|
||||
HazardPtr<IndexLock> index = r->rsc_rel->getIndexLock(tdbb, r->rsc_id);
|
||||
if (index)
|
||||
{
|
||||
if (r->rsc_state == Resource::State::Locked)
|
||||
r->rsc_state = index->idl_lock.dec(tdbb);
|
||||
else if (r->rsc_state == Resource::State::Counted)
|
||||
{
|
||||
r->rsc_state = Resource::State::Posted;
|
||||
index->idl_lock.dec(tdbb);
|
||||
}
|
||||
|
||||
if (r->rsc_state == Resource::State::Unlocking)
|
||||
{
|
||||
index->idl_lock.leave245(tdbb);
|
||||
r->rsc_state = Resource::State::Posted;
|
||||
}
|
||||
}
|
||||
else
|
||||
r->rsc_state = Resource::State::Posted;
|
||||
}
|
||||
|
||||
// 3. Decrement lock count of all objects - refcnt mutex to be locked
|
||||
HalfStaticArray<Resource*, 128> toUnlock;
|
||||
{ // scope
|
||||
MutexLockGuard g(tdbb->getDatabase()->dbb_mdc->mdc_use_mutex, FB_FUNCTION);
|
||||
|
||||
for (auto r : list)
|
||||
{
|
||||
if (r.rsc_state == Resource::State::Extra)
|
||||
{
|
||||
fb_assert(r.rsc_type == Resource::rsc_relation);
|
||||
|
||||
if (r.rsc_rel && r.rsc_rel->rel_file)
|
||||
{
|
||||
if (!transaction)
|
||||
transaction = tdbb->getTransaction();
|
||||
|
||||
fb_assert(transaction);
|
||||
if (transaction)
|
||||
EXT_tra_detach(r.rsc_rel->rel_file, transaction);
|
||||
}
|
||||
r.rsc_state = Resource::State::Locked;
|
||||
}
|
||||
|
||||
if (r.rsc_state == Resource::State::Posted ||
|
||||
r.rsc_state == Resource::State::Registered ||
|
||||
r.rsc_state == Resource::State::Unlocking) // use count is not increased
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (r.rsc_type)
|
||||
{
|
||||
case Resource::rsc_relation:
|
||||
{
|
||||
ExistenceLock* lock = r.rsc_rel->rel_existence_lock;
|
||||
r.rsc_state = lock ? lock->dec(tdbb) : Resource::State::Posted;
|
||||
break;
|
||||
}
|
||||
|
||||
case Resource::rsc_index:
|
||||
break;
|
||||
|
||||
case Resource::rsc_procedure:
|
||||
case Resource::rsc_function:
|
||||
{
|
||||
ExistenceLock* lock = r.rsc_routine->existenceLock;
|
||||
r.rsc_state = lock ? lock->dec(tdbb) : Resource::State::Posted;
|
||||
break;
|
||||
}
|
||||
|
||||
case Resource::rsc_collation:
|
||||
{
|
||||
Collation* coll = r.rsc_coll;
|
||||
coll->decUseCount(tdbb);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BUGCHECK(219); // msg 219 request of unknown resource
|
||||
}
|
||||
|
||||
if (r.rsc_state == Resource::State::Unlocking)
|
||||
toUnlock.push(&r);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Release not needed any more locks
|
||||
for (auto r : toUnlock)
|
||||
{
|
||||
fb_assert(r->rsc_state == Resource::State::Unlocking);
|
||||
|
||||
ExistenceLock* lock = nullptr;
|
||||
|
||||
switch (r->rsc_type)
|
||||
{
|
||||
case Resource::rsc_relation:
|
||||
lock = r->rsc_rel->rel_existence_lock;
|
||||
break;
|
||||
|
||||
case Resource::rsc_index:
|
||||
fb_assert(false);
|
||||
break;
|
||||
|
||||
case Resource::rsc_procedure:
|
||||
case Resource::rsc_function:
|
||||
lock = r->rsc_routine->existenceLock;
|
||||
break;
|
||||
|
||||
case Resource::rsc_collation:
|
||||
{
|
||||
Collation* coll = r->rsc_coll;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lock)
|
||||
lock->leave245(tdbb);
|
||||
r->rsc_state = Resource::State::Posted;
|
||||
}
|
||||
|
||||
// 5. Finally time to release hazard locks on objects and cleanup
|
||||
setResetPointersHazard(tdbb, false);
|
||||
list.clear();
|
||||
}
|
||||
|
||||
void ResourceList::postIndex(thread_db* tdbb, jrd_rel* relation, USHORT idxId)
|
||||
{
|
||||
abort();
|
||||
// resources.checkResource(Resource::rsc_relation, relation);
|
||||
// resources.postResource(Resource::rsc_index, relation, idx->idx_id);
|
||||
}
|
||||
|
||||
|
208
src/jrd/exe.h
208
src/jrd/exe.h
@ -46,6 +46,7 @@
|
||||
#include "../common/dsc.h"
|
||||
|
||||
#include "../jrd/err_proto.h"
|
||||
#include "../jrd/met_proto.h"
|
||||
#include "../jrd/scl.h"
|
||||
#include "../jrd/sbm.h"
|
||||
#include "../jrd/sort.h"
|
||||
@ -168,96 +169,6 @@ struct impure_agg_sort
|
||||
};
|
||||
|
||||
|
||||
template <class OBJ> class CacheElement;
|
||||
|
||||
class Resources
|
||||
{
|
||||
public:
|
||||
template <class OBJ>
|
||||
class VersionedPtr
|
||||
{
|
||||
public:
|
||||
CacheElement<OBJ>* ptr;
|
||||
FB_SIZE_T versionedObject;
|
||||
};
|
||||
|
||||
template <class OBJ>
|
||||
class RscArray : public Firebird::Array<VersionedPtr<OBJ>>
|
||||
{
|
||||
public:
|
||||
RscArray(MemoryPool& p)
|
||||
: Firebird::Array<VersionedPtr<OBJ>>(p)
|
||||
{ }
|
||||
|
||||
FB_SIZE_T registerResource(CacheElement<OBJ>* res)
|
||||
{
|
||||
FB_SIZE_T pos;
|
||||
if (this->find([res](const VersionedPtr<OBJ>& elem) {
|
||||
const void* p1 = elem.ptr;
|
||||
const void* p2 = res;
|
||||
return p1 < p2 ? -1 : p1 == p2 ? 0 : 1;
|
||||
}, pos))
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
VersionedPtr<OBJ> newPtr(res);
|
||||
return this->append(newPtr);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template <class OBJ> const RscArray<OBJ>& objects() const;
|
||||
|
||||
Resources(MemoryPool& p)
|
||||
: charSets(p), relations(p), procedures(p), functions(p), triggers(p)
|
||||
{ }
|
||||
|
||||
RscArray<CharSetContainer> charSets;
|
||||
RscArray<jrd_rel> relations;
|
||||
RscArray<jrd_prc> procedures;
|
||||
RscArray<Function> functions;
|
||||
RscArray<Trigger> triggers;
|
||||
};
|
||||
|
||||
// specialization
|
||||
template <> const Resources::RscArray<jrd_rel>& Resources::objects() const { return relations; }
|
||||
|
||||
|
||||
template <class OBJ>
|
||||
class CachedResource
|
||||
{
|
||||
public:
|
||||
CachedResource(FB_SIZE_T offset)
|
||||
: compileOffset(offset)
|
||||
{ }
|
||||
|
||||
CachedResource();
|
||||
|
||||
OBJ* get(thread_db* tdbb, const Resources* compileTime) const
|
||||
{
|
||||
auto array = compileTime->objects<OBJ>();
|
||||
return array[compileOffset]->ptr->getObject(tdbb);
|
||||
}
|
||||
|
||||
bool isSet() const;
|
||||
/*
|
||||
operator OBJ*() const
|
||||
{
|
||||
return getPtr();
|
||||
}
|
||||
|
||||
OBJ* operator->() const
|
||||
{
|
||||
return getPtr();
|
||||
}
|
||||
*/
|
||||
|
||||
private:
|
||||
FB_SIZE_T compileOffset;
|
||||
};
|
||||
|
||||
|
||||
// Access items
|
||||
// In case we start to use MetaName with required pool parameter,
|
||||
// access item to be reworked!
|
||||
@ -481,34 +392,90 @@ typedef Firebird::GenericMap<Firebird::Pair<Firebird::Left<MetaNamePair, FieldIn
|
||||
MapFieldInfo;
|
||||
typedef Firebird::GenericMap<Firebird::Pair<Firebird::Right<Item, ItemInfo> > > MapItemInfo;
|
||||
|
||||
template <class R>
|
||||
class SubRoutine
|
||||
{
|
||||
public:
|
||||
SubRoutine()
|
||||
: routine(), subroutine(nullptr)
|
||||
{ }
|
||||
|
||||
SubRoutine(const CachedResource<R, RoutinePermanent>& r)
|
||||
: routine(r), subroutine(nullptr)
|
||||
{ }
|
||||
|
||||
SubRoutine(R* r)
|
||||
: routine(), subroutine(r)
|
||||
{ }
|
||||
|
||||
SubRoutine& operator=(const CachedResource<R, RoutinePermanent>& r)
|
||||
{
|
||||
routine = r;
|
||||
subroutine = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SubRoutine& operator=(R* r)
|
||||
{
|
||||
routine.clear();
|
||||
subroutine = r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
R* operator()(thread_db* tdbb) const
|
||||
{
|
||||
return isSubRoutine() ? subroutine : routine(tdbb);
|
||||
}
|
||||
|
||||
RoutinePermanent* operator()() const
|
||||
{
|
||||
return isSubRoutine() ? subroutine->permanent : routine();
|
||||
}
|
||||
|
||||
bool isSubRoutine() const
|
||||
{
|
||||
return subroutine != nullptr;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
fb_assert((routine.isSet() ? 1 : 0) + (subroutine ? 1 : 0) < 2);
|
||||
return routine.isSet() || subroutine;
|
||||
}
|
||||
|
||||
private:
|
||||
CachedResource<R, RoutinePermanent> routine;
|
||||
R* subroutine;
|
||||
};
|
||||
|
||||
// Compile scratch block
|
||||
|
||||
struct Dependency
|
||||
{
|
||||
explicit Dependency(int aObjType)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
objType = aObjType;
|
||||
}
|
||||
|
||||
int objType;
|
||||
|
||||
union
|
||||
{
|
||||
jrd_rel* relation;
|
||||
const Function* function;
|
||||
const jrd_prc* procedure;
|
||||
const MetaName* name;
|
||||
SLONG number;
|
||||
};
|
||||
|
||||
const MetaName* subName;
|
||||
SLONG subNumber;
|
||||
};
|
||||
|
||||
class CompilerScratch : public pool_alloc<type_csb>
|
||||
{
|
||||
public:
|
||||
struct Dependency
|
||||
{
|
||||
explicit Dependency(int aObjType)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
objType = aObjType;
|
||||
}
|
||||
|
||||
int objType;
|
||||
|
||||
union
|
||||
{
|
||||
jrd_rel* relation;
|
||||
const Function* function;
|
||||
const jrd_prc* procedure;
|
||||
const MetaName* name;
|
||||
SLONG number;
|
||||
};
|
||||
|
||||
const MetaName* subName;
|
||||
SLONG subNumber;
|
||||
};
|
||||
|
||||
explicit CompilerScratch(MemoryPool& p, CompilerScratch* aMainCsb = NULL)
|
||||
: /*csb_node(0),
|
||||
csb_variables(0),
|
||||
@ -630,9 +597,9 @@ public:
|
||||
MetaName csb_domain_validation; // Parsing domain constraint in PSQL
|
||||
|
||||
// used in cmp.cpp/pass1
|
||||
CachedResource<jrd_rel> csb_view;
|
||||
Rsc::Rel csb_view;
|
||||
StreamType csb_view_stream;
|
||||
CachedResource<jrd_rel> csb_parent_relation;
|
||||
Rsc::Rel csb_parent_relation;
|
||||
unsigned blrVersion;
|
||||
USHORT csb_remap_variable;
|
||||
bool csb_validate_expr;
|
||||
@ -666,10 +633,10 @@ public:
|
||||
StreamType csb_view_stream; // stream number for view relation, below
|
||||
USHORT csb_flags;
|
||||
|
||||
CachedResource<jrd_rel> csb_relation;
|
||||
Rsc::Rel csb_relation;
|
||||
Firebird::string* csb_alias; // SQL alias name for this instance of relation
|
||||
CachedResource<jrd_prc> csb_procedure;
|
||||
CachedResource<jrd_rel> csb_view; // parent view
|
||||
SubRoutine<jrd_prc> csb_procedure;
|
||||
Rsc::Rel csb_view; // parent view
|
||||
|
||||
IndexDescList* csb_idx; // Packed description of indices
|
||||
MessageNode* csb_message; // Msg for send/receive
|
||||
@ -692,10 +659,9 @@ inline CompilerScratch::csb_repeat::csb_repeat()
|
||||
: csb_stream(0),
|
||||
csb_view_stream(0),
|
||||
csb_flags(0),
|
||||
csb_relation(0),
|
||||
csb_relation(),
|
||||
csb_alias(0),
|
||||
csb_procedure(0),
|
||||
csb_view(0),
|
||||
csb_view(),
|
||||
csb_idx(0),
|
||||
csb_message(0),
|
||||
csb_format(0),
|
||||
|
@ -52,7 +52,7 @@ bool EXE_get_stack_trace(const Jrd::Request* request, Firebird::string& sTrace);
|
||||
const Jrd::StmtNode* EXE_looper(Jrd::thread_db* tdbb, Jrd::Request* request,
|
||||
const Jrd::StmtNode* in_node);
|
||||
|
||||
void EXE_execute_triggers(Jrd::thread_db*, Jrd::TrigVectorPtr*, Jrd::record_param*, Jrd::record_param*,
|
||||
void EXE_execute_triggers(Jrd::thread_db*, const Jrd::Triggers&, Jrd::record_param*, Jrd::record_param*,
|
||||
enum TriggerAction, Jrd::StmtNode::WhichTrigger);
|
||||
|
||||
void EXE_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false);
|
||||
|
310
src/jrd/ext.cpp
310
src/jrd/ext.cpp
@ -38,17 +38,18 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../jrd/ext_proto.h"
|
||||
|
||||
#include "../jrd/jrd.h"
|
||||
#include "../jrd/req.h"
|
||||
#include "../jrd/val.h"
|
||||
#include "../jrd/exe.h"
|
||||
#include "../jrd/ext.h"
|
||||
#include "../jrd/tra.h"
|
||||
#include "../dsql/ExprNodes.h"
|
||||
#include "iberror.h"
|
||||
#include "../jrd/cmp_proto.h"
|
||||
#include "../jrd/err_proto.h"
|
||||
#include "../jrd/ext_proto.h"
|
||||
#include "../yvalve/gds_proto.h"
|
||||
#include "../jrd/met_proto.h"
|
||||
#include "../jrd/mov_proto.h"
|
||||
@ -118,47 +119,47 @@ namespace {
|
||||
|
||||
#ifdef WIN_NT
|
||||
static const char* const FOPEN_TYPE = "a+b";
|
||||
static const char* const FOPEN_READ_ONLY = "rb";
|
||||
#else
|
||||
static const char* const FOPEN_TYPE = "a+";
|
||||
static const char* const FOPEN_READ_ONLY = "r";
|
||||
#endif
|
||||
static const char* const FOPEN_READ_ONLY = "rb";
|
||||
|
||||
FILE* ext_fopen(Database* dbb, ExternalFile* ext_file)
|
||||
{
|
||||
const char* file_name = ext_file->ext_filename;
|
||||
|
||||
ExternalFileDirectoryList::create(dbb);
|
||||
if (!dbb->dbb_external_file_directory_list->isPathInList(file_name))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("external file") <<
|
||||
Arg::Str(file_name));
|
||||
}
|
||||
|
||||
// If the database is updateable, then try opening the external files in
|
||||
// RW mode. If the DB is ReadOnly, then open the external files only in
|
||||
// ReadOnly mode, thus being consistent.
|
||||
if (!dbb->readOnly())
|
||||
ext_file->ext_ifi = os_utils::fopen(file_name, FOPEN_TYPE);
|
||||
|
||||
if (!ext_file->ext_ifi)
|
||||
{
|
||||
// could not open the file as read write attempt as read only
|
||||
if (!(ext_file->ext_ifi = os_utils::fopen(file_name, FOPEN_READ_ONLY)))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fopen") << Arg::Str(file_name) <<
|
||||
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
|
||||
}
|
||||
else {
|
||||
ext_file->ext_flags |= EXT_readonly;
|
||||
}
|
||||
}
|
||||
|
||||
return ext_file->ext_ifi;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ExternalFile::open(Database* dbb)
|
||||
{
|
||||
fb_assert(ext_sync.locked());
|
||||
|
||||
double EXT_cardinality(thread_db* tdbb, jrd_rel* relation)
|
||||
ExternalFileDirectoryList::create(dbb);
|
||||
|
||||
if (!dbb->dbb_external_file_directory_list->isPathInList(ext_filename))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("external file") <<
|
||||
Arg::Str(ext_filename));
|
||||
}
|
||||
|
||||
// If the database is updateable then try opening the external files in RW mode.
|
||||
ext_flags = 0;
|
||||
if (!dbb->readOnly())
|
||||
ext_ifi = os_utils::fopen(ext_filename, FOPEN_TYPE);
|
||||
|
||||
// If the DB is ReadOnly or RW access failed then open the external files only in ReadOnly mode.
|
||||
if (!ext_ifi)
|
||||
{
|
||||
if (!(ext_ifi = os_utils::fopen(ext_filename, FOPEN_READ_ONLY)))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fopen") << Arg::Str(ext_filename) <<
|
||||
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
|
||||
}
|
||||
else {
|
||||
ext_flags |= EXT_readonly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double ExternalFile::getCardinality(thread_db* tdbb, jrd_rel* relation) noexcept
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -170,35 +171,24 @@ double EXT_cardinality(thread_db* tdbb, jrd_rel* relation)
|
||||
* Return cardinality for the external file.
|
||||
*
|
||||
**************************************/
|
||||
ExternalFile* const file = relation->rel_file;
|
||||
fb_assert(file);
|
||||
|
||||
try
|
||||
{
|
||||
bool must_close = false;
|
||||
if (!file->ext_ifi)
|
||||
{
|
||||
ext_fopen(tdbb->getDatabase(), file);
|
||||
must_close = true;
|
||||
}
|
||||
|
||||
FB_UINT64 file_size = 0;
|
||||
|
||||
// no need locking mutex here
|
||||
traAttach(tdbb);
|
||||
{ // scope
|
||||
Cleanup clean([this]() { traDetach(); });
|
||||
#ifdef WIN_NT
|
||||
struct __stat64 statistics;
|
||||
if (!_fstat64(_fileno(file->ext_ifi), &statistics))
|
||||
struct __stat64 statistics;
|
||||
if (!_fstat64(_fileno(ext_ifi), &statistics))
|
||||
#else
|
||||
struct STAT statistics;
|
||||
if (!os_utils::fstat(fileno(file->ext_ifi), &statistics))
|
||||
struct STAT statistics;
|
||||
if (!os_utils::fstat(fileno(ext_ifi), &statistics))
|
||||
#endif
|
||||
{
|
||||
file_size = statistics.st_size;
|
||||
}
|
||||
|
||||
if (must_close)
|
||||
{
|
||||
fclose(file->ext_ifi);
|
||||
file->ext_ifi = NULL;
|
||||
{
|
||||
file_size = statistics.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
const Format* const format = MET_current(tdbb, relation);
|
||||
@ -217,7 +207,7 @@ double EXT_cardinality(thread_db* tdbb, jrd_rel* relation)
|
||||
}
|
||||
|
||||
|
||||
void EXT_erase(record_param*, jrd_tra*)
|
||||
void ExternalFile::erase(record_param*, jrd_tra*)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -234,8 +224,7 @@ void EXT_erase(record_param*, jrd_tra*)
|
||||
}
|
||||
|
||||
|
||||
// Third param is unused.
|
||||
ExternalFile* EXT_file(jrd_rel* relation, const TEXT* file_name) //, bid* description)
|
||||
void RelationPermanent::extFile(thread_db* tdbb, const TEXT* file_name)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -250,11 +239,7 @@ ExternalFile* EXT_file(jrd_rel* relation, const TEXT* file_name) //, bid* descri
|
||||
Database* dbb = GET_DBB();
|
||||
CHECK_DBB(dbb);
|
||||
|
||||
// if we already have a external file associated with this relation just
|
||||
// return the file structure
|
||||
if (relation->rel_file) {
|
||||
EXT_fini(relation.getPointer(), false);
|
||||
}
|
||||
fb_assert(!rel_file);
|
||||
|
||||
#ifdef WIN_NT
|
||||
// Default number of file handles stdio.h on Windows is 512, use this
|
||||
@ -304,48 +289,11 @@ ExternalFile* EXT_file(jrd_rel* relation, const TEXT* file_name) //, bid* descri
|
||||
|
||||
paths.clear();
|
||||
|
||||
ExternalFile* file = FB_NEW_RPT(*relation->rel_pool, (strlen(file_name) + 1)) ExternalFile();
|
||||
relation->rel_file = file;
|
||||
strcpy(file->ext_filename, file_name);
|
||||
file->ext_flags = 0;
|
||||
file->ext_ifi = NULL;
|
||||
|
||||
return file;
|
||||
rel_file = ExternalFile::create(getPool(), file_name);
|
||||
}
|
||||
|
||||
|
||||
void EXT_fini(jrd_rel* relation, bool close_only)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* E X T _ f i n i
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Close the file associated with a relation.
|
||||
*
|
||||
**************************************/
|
||||
if (relation->rel_file)
|
||||
{
|
||||
ExternalFile* file = relation->rel_file;
|
||||
if (file->ext_ifi)
|
||||
{
|
||||
fclose(file->ext_ifi);
|
||||
file->ext_ifi = NULL;
|
||||
}
|
||||
|
||||
// before zeroing out the rel_file we need to deallocate the memory
|
||||
if (!close_only)
|
||||
{
|
||||
delete file;
|
||||
relation->rel_file = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool EXT_get(thread_db* tdbb, record_param* rpb, FB_UINT64& position)
|
||||
bool ExternalFile::get(thread_db* tdbb, record_param* rpb, FB_UINT64& position)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -358,8 +306,7 @@ bool EXT_get(thread_db* tdbb, record_param* rpb, FB_UINT64& position)
|
||||
*
|
||||
**************************************/
|
||||
jrd_rel* const relation = rpb->rpb_relation;
|
||||
ExternalFile* const file = relation->rel_file;
|
||||
fb_assert(file->ext_ifi);
|
||||
//fb_assert(relation->rel_perm->rel_file == this);
|
||||
|
||||
Record* const record = rpb->rpb_record;
|
||||
const Format* const format = record->getFormat();
|
||||
@ -368,52 +315,57 @@ bool EXT_get(thread_db* tdbb, record_param* rpb, FB_UINT64& position)
|
||||
UCHAR* p = record->getData() + offset;
|
||||
const ULONG l = record->getLength() - offset;
|
||||
|
||||
if (file->ext_ifi == NULL)
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << "fseek" << Arg::Str(file->ext_filename) <<
|
||||
Arg::Gds(isc_io_open_err) << Arg::Unix(EBADF) <<
|
||||
Arg::Gds(isc_random) << "File not opened");
|
||||
}
|
||||
{ //scope
|
||||
MutexLockGuard g(ext_sync, FB_FUNCTION);
|
||||
|
||||
// hvlad: fseek will flush file buffer and degrade performance, so don't
|
||||
// call it if it is not necessary. Note that we must flush file buffer if we
|
||||
// do read after write
|
||||
|
||||
bool doSeek = false;
|
||||
if (!(file->ext_flags & EXT_last_read))
|
||||
{
|
||||
doSeek = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SINT64 offset = FTELL64(file->ext_ifi);
|
||||
if (offset < 0)
|
||||
if (ext_ifi == NULL)
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << STRINGIZE(FTELL64) << Arg::Str(file->ext_filename) <<
|
||||
Arg::Gds(isc_io_read_err) << SYS_ERR(errno));
|
||||
ERR_post(Arg::Gds(isc_io_error) << "fseek" << Arg::Str(ext_filename) <<
|
||||
Arg::Gds(isc_io_open_err) << Arg::Unix(EBADF) <<
|
||||
Arg::Gds(isc_random) << "File not opened");
|
||||
}
|
||||
doSeek = (static_cast<FB_UINT64>(offset) != position);
|
||||
}
|
||||
|
||||
// reset both flags cause we are going to move the file pointer
|
||||
file->ext_flags &= ~(EXT_last_write | EXT_last_read);
|
||||
// hvlad: fseek will flush file buffer and degrade performance, so don't
|
||||
// call it if it is not necessary. Note that we must flush file buffer if we
|
||||
// do read after write
|
||||
|
||||
if (doSeek)
|
||||
{
|
||||
if (FSEEK64(file->ext_ifi, position, SEEK_SET) != 0)
|
||||
bool doSeek = false;
|
||||
if (!(ext_flags & EXT_last_read))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << STRINGIZE(FSEEK64) << Arg::Str(file->ext_filename) <<
|
||||
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
|
||||
doSeek = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SINT64 offset = FTELL64(ext_ifi);
|
||||
if (offset < 0)
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << STRINGIZE(FTELL64) << Arg::Str(ext_filename) <<
|
||||
Arg::Gds(isc_io_read_err) << SYS_ERR(errno));
|
||||
}
|
||||
doSeek = (static_cast<FB_UINT64>(offset) != position);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fread(p, l, 1, file->ext_ifi))
|
||||
{
|
||||
return false;
|
||||
// reset both flags cause we are going to move the file pointer
|
||||
ext_flags &= ~(EXT_last_write | EXT_last_read);
|
||||
|
||||
if (doSeek)
|
||||
{
|
||||
if (FSEEK64(ext_ifi, position, SEEK_SET) != 0)
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << STRINGIZE(FSEEK64) << Arg::Str(ext_filename) <<
|
||||
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (!fread(p, l, 1, ext_ifi))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ext_flags |= EXT_last_read;
|
||||
}
|
||||
|
||||
position += l;
|
||||
file->ext_flags |= EXT_last_read;
|
||||
|
||||
// Loop thru fields setting missing fields to either blanks/zeros or the missing value
|
||||
|
||||
@ -449,7 +401,7 @@ bool EXT_get(thread_db* tdbb, record_param* rpb, FB_UINT64& position)
|
||||
}
|
||||
|
||||
|
||||
void EXT_modify(record_param* /*old_rpb*/, record_param* /*new_rpb*/, jrd_tra* /*transaction*/)
|
||||
void ExternalFile::modify(record_param* /*old_rpb*/, record_param* /*new_rpb*/, jrd_tra* /*transaction*/)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -466,25 +418,7 @@ void EXT_modify(record_param* /*old_rpb*/, record_param* /*new_rpb*/, jrd_tra* /
|
||||
}
|
||||
|
||||
|
||||
void EXT_open(Database* dbb, ExternalFile* file)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* E X T _ o p e n
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Open a record stream for an external file.
|
||||
*
|
||||
**************************************/
|
||||
if (!file->ext_ifi) {
|
||||
ext_fopen(dbb, file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EXT_store(thread_db* tdbb, record_param* rpb)
|
||||
void ExternalFile::store(thread_db* tdbb, record_param* rpb)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -497,18 +431,15 @@ void EXT_store(thread_db* tdbb, record_param* rpb)
|
||||
*
|
||||
**************************************/
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
ExternalFile* file = relation->rel_file;
|
||||
Record* record = rpb->rpb_record;
|
||||
const Format* const format = record->getFormat();
|
||||
|
||||
if (!file->ext_ifi) {
|
||||
ext_fopen(tdbb->getDatabase(), file);
|
||||
}
|
||||
fb_assert(ext_ifi);
|
||||
|
||||
// Loop thru fields setting missing fields to either blanks/zeros or the missing value
|
||||
|
||||
// check if file is read only if read only then post error we cannot write to this file
|
||||
if (file->ext_flags & EXT_readonly)
|
||||
if (ext_flags & EXT_readonly)
|
||||
{
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
CHECK_DBB(dbb);
|
||||
@ -517,7 +448,7 @@ void EXT_store(thread_db* tdbb, record_param* rpb)
|
||||
ERR_post(Arg::Gds(isc_read_only_database));
|
||||
else
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("insert") << Arg::Str(file->ext_filename) <<
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("insert") << Arg::Str(ext_filename) <<
|
||||
Arg::Gds(isc_io_write_err) <<
|
||||
Arg::Gds(isc_ext_readonly_err));
|
||||
}
|
||||
@ -553,31 +484,32 @@ void EXT_store(thread_db* tdbb, record_param* rpb)
|
||||
const UCHAR* p = record->getData() + offset;
|
||||
const ULONG l = record->getLength() - offset;
|
||||
|
||||
MutexLockGuard g(ext_sync, FB_FUNCTION);
|
||||
|
||||
// hvlad: fseek will flush file buffer and degrade performance, so don't
|
||||
// call it if it is not necessary. Note that we must flush file buffer if we
|
||||
// do write after read
|
||||
file->ext_flags &= ~EXT_last_read;
|
||||
if (file->ext_ifi == NULL ||
|
||||
(!(file->ext_flags & EXT_last_write) && FSEEK64(file->ext_ifi, (SINT64) 0, SEEK_END) != 0) )
|
||||
ext_flags &= ~EXT_last_read;
|
||||
if (ext_ifi == NULL ||
|
||||
(!(ext_flags & EXT_last_write) && FSEEK64(ext_ifi, (SINT64) 0, SEEK_END) != 0) )
|
||||
{
|
||||
file->ext_flags &= ~EXT_last_write;
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fseek") << Arg::Str(file->ext_filename) <<
|
||||
ext_flags &= ~EXT_last_write;
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fseek") << Arg::Str(ext_filename) <<
|
||||
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
|
||||
}
|
||||
|
||||
if (!fwrite(p, l, 1, file->ext_ifi))
|
||||
if (!fwrite(p, l, 1, ext_ifi))
|
||||
{
|
||||
file->ext_flags &= ~EXT_last_write;
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fwrite") << Arg::Str(file->ext_filename) <<
|
||||
ext_flags &= ~EXT_last_write;
|
||||
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fwrite") << Arg::Str(ext_filename) <<
|
||||
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
|
||||
}
|
||||
|
||||
// fflush(file->ext_ifi);
|
||||
file->ext_flags |= EXT_last_write;
|
||||
ext_flags |= EXT_last_write;
|
||||
}
|
||||
|
||||
|
||||
void EXT_tra_attach(ExternalFile* file, jrd_tra*) noexcept
|
||||
void ExternalFile::traAttach(thread_db* tdbb)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -590,11 +522,18 @@ void EXT_tra_attach(ExternalFile* file, jrd_tra*) noexcept
|
||||
* Increment transactions use count.
|
||||
*
|
||||
**************************************/
|
||||
MutexLockGuard g(ext_sync, FB_FUNCTION);
|
||||
|
||||
file->ext_tra_cnt++;
|
||||
if (ext_tra_cnt++ == 0)
|
||||
{
|
||||
fb_assert(!ext_ifi);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
open(dbb);
|
||||
fb_assert(ext_ifi);
|
||||
}
|
||||
}
|
||||
|
||||
void EXT_tra_detach(ExternalFile* file, jrd_tra*) noexcept
|
||||
void ExternalFile::traDetach() noexcept
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -608,11 +547,12 @@ void EXT_tra_detach(ExternalFile* file, jrd_tra*) noexcept
|
||||
* external file if count is zero.
|
||||
*
|
||||
**************************************/
|
||||
MutexLockGuard g(ext_sync, FB_FUNCTION);
|
||||
|
||||
file->ext_tra_cnt--;
|
||||
if (!file->ext_tra_cnt && file->ext_ifi)
|
||||
if (--ext_tra_cnt == 0)
|
||||
{
|
||||
fclose(file->ext_ifi);
|
||||
file->ext_ifi = NULL;
|
||||
fb_assert(ext_ifi);
|
||||
fclose(ext_ifi);
|
||||
ext_ifi = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* PROGRAM: JRD Access Method
|
||||
* MODULE: ext.h
|
||||
* DESCRIPTION: External file access definitions
|
||||
*
|
||||
* 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): ______________________________________.
|
||||
*/
|
||||
|
||||
#ifndef JRD_EXT_H
|
||||
#define JRD_EXT_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
// External file access block
|
||||
|
||||
class ExternalFile : public pool_alloc_rpt<SCHAR, type_ext>
|
||||
{
|
||||
public:
|
||||
USHORT ext_flags; // Misc and cruddy flags
|
||||
USHORT ext_tra_cnt; // How many transactions used the file
|
||||
FILE* ext_ifi; // Internal file identifier
|
||||
char ext_filename[1];
|
||||
};
|
||||
|
||||
const int EXT_readonly = 1; // File could only be opened for read
|
||||
const int EXT_last_read = 2; // last operation was read
|
||||
const int EXT_last_write = 4; // last operation was write
|
||||
|
||||
} //namespace Jrd
|
||||
|
||||
#endif // JRD_EXT_H
|
@ -21,29 +21,76 @@
|
||||
* Contributor(s): ______________________________________.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fb_blk.h"
|
||||
#include "../common/classes/alloc.h"
|
||||
#include "../common/classes/locks.h"
|
||||
|
||||
#ifndef JRD_EXT_PROTO_H
|
||||
#define JRD_EXT_PROTO_H
|
||||
|
||||
namespace Jrd {
|
||||
class ExternalFile;
|
||||
class jrd_tra;
|
||||
class RecordSource;
|
||||
class jrd_rel;
|
||||
struct record_param;
|
||||
struct bid;
|
||||
}
|
||||
|
||||
double EXT_cardinality(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
void EXT_erase(Jrd::record_param*, Jrd::jrd_tra*);
|
||||
Jrd::ExternalFile* EXT_file(Jrd::jrd_rel*, const TEXT*); //, Jrd::bid*);
|
||||
void EXT_fini(Jrd::jrd_rel*, bool);
|
||||
bool EXT_get(Jrd::thread_db*, Jrd::record_param*, FB_UINT64&);
|
||||
void EXT_modify(Jrd::record_param*, Jrd::record_param*, Jrd::jrd_tra*);
|
||||
class jrd_tra;
|
||||
class RecordSource;
|
||||
class jrd_rel;
|
||||
struct record_param;
|
||||
struct bid;
|
||||
class Database;
|
||||
class thread_db;
|
||||
|
||||
void EXT_open(Jrd::Database*, Jrd::ExternalFile*);
|
||||
void EXT_store(Jrd::thread_db*, Jrd::record_param*);
|
||||
// External file access block
|
||||
|
||||
void EXT_tra_attach(Jrd::ExternalFile*, Jrd::jrd_tra*) noexcept;
|
||||
void EXT_tra_detach(Jrd::ExternalFile*, Jrd::jrd_tra*) noexcept;
|
||||
class ExternalFile : public pool_alloc_rpt<SCHAR, type_ext>
|
||||
{
|
||||
private:
|
||||
ExternalFile()
|
||||
: ext_flags(0), ext_tra_cnt(0), ext_ifi(nullptr)
|
||||
{ }
|
||||
|
||||
void open(Database* dbb);
|
||||
|
||||
public:
|
||||
static ExternalFile* create(MemoryPool& pool, const char* name)
|
||||
{
|
||||
ExternalFile* file = FB_NEW_RPT(pool, (strlen(name) + 1)) ExternalFile();
|
||||
strcpy(file->ext_filename, name);
|
||||
return file;
|
||||
}
|
||||
|
||||
~ExternalFile()
|
||||
{
|
||||
fb_assert(!ext_ifi);
|
||||
}
|
||||
|
||||
FILE* getFile()
|
||||
{
|
||||
return ext_ifi;
|
||||
}
|
||||
|
||||
void traAttach(thread_db* tdbb);
|
||||
void traDetach() noexcept;
|
||||
double getCardinality(thread_db* tdbb, jrd_rel* relation) noexcept;
|
||||
void erase(record_param*, jrd_tra*);
|
||||
bool get(thread_db* tdbb, record_param* rpb, FB_UINT64& position);
|
||||
void modify(record_param*, record_param*, jrd_tra*);
|
||||
void store(thread_db*, record_param*);
|
||||
|
||||
private:
|
||||
Firebird::Mutex ext_sync;
|
||||
USHORT ext_flags; // Misc and cruddy flags
|
||||
USHORT ext_tra_cnt; // How many transactions used the file
|
||||
FILE* ext_ifi; // Internal file identifier
|
||||
char ext_filename[1];
|
||||
};
|
||||
|
||||
// ext_flags
|
||||
const USHORT EXT_readonly = 1; // File could only be opened for read
|
||||
const USHORT EXT_last_read = 2; // last operation was read
|
||||
const USHORT EXT_last_write = 4; // last operation was write
|
||||
|
||||
} //namespace Jrd
|
||||
|
||||
#endif // JRD_EXT_PROTO_H
|
||||
|
@ -143,10 +143,7 @@ void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_
|
||||
if (!MET_lookup_partner(tdbb, relation, &idx, 0))
|
||||
continue;
|
||||
|
||||
auto referenced = MetadataCache::findRelation(tdbb, idx.idx_primary_relation);
|
||||
auto referenced_relation = csb->csb_resources.registerResource(tdbb, Resource::rsc_relation,
|
||||
referenced, idx.idx_primary_relation);
|
||||
MET_scan_relation(tdbb, referenced);
|
||||
auto referenced_relation = MetadataCache::findRelation(tdbb, idx.idx_primary_relation);
|
||||
const USHORT index_id = idx.idx_primary_index;
|
||||
|
||||
// get the description of the primary key index
|
||||
@ -171,14 +168,14 @@ void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_
|
||||
const jrd_fld* referenced_field =
|
||||
MET_get_field(referenced_relation, idx_desc->idx_field);
|
||||
CMP_post_access(tdbb, csb,
|
||||
referenced_relation->rel_security_name,
|
||||
(view ? view->rel_id : 0),
|
||||
referenced_relation->getSecurityName(),
|
||||
(view ? view->getId() : 0),
|
||||
SCL_references, obj_relations,
|
||||
referenced_relation->rel_name);
|
||||
referenced_relation->getName());
|
||||
CMP_post_access(tdbb, csb,
|
||||
referenced_field->fld_security_name, 0,
|
||||
SCL_references, obj_column,
|
||||
referenced_field->fld_name, referenced_relation->rel_name);
|
||||
referenced_field->fld_name, referenced_relation->getName());
|
||||
}
|
||||
|
||||
CCH_RELEASE(tdbb, &referenced_window);
|
||||
@ -486,9 +483,7 @@ bool IndexCreateTask::handler(WorkItem& _item)
|
||||
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
Attachment* attachment = tdbb->getAttachment();
|
||||
jrd_rel* relation = MET_relation(tdbb, m_creation->relation->rel_id);
|
||||
if (!(relation->rel_flags & REL_scanned))
|
||||
MET_scan_relation(tdbb, relation);
|
||||
jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, m_creation->relation->getId(), false);
|
||||
|
||||
index_desc* idx = &item->m_idx;
|
||||
jrd_tra* transaction = item->m_tra ? item->m_tra : m_creation->transaction;
|
||||
@ -549,7 +544,7 @@ bool IndexCreateTask::handler(WorkItem& _item)
|
||||
// if (!MET_lookup_partner(tdbb, relation, idx, m_creation->index_name)) {
|
||||
// BUGCHECK(173); // msg 173 referenced index description not found
|
||||
// }
|
||||
partner_relation = MET_relation(tdbb, idx->idx_primary_relation);
|
||||
partner_relation = MetadataCache::lookup_relation_id(tdbb, idx->idx_primary_relation, false);
|
||||
partner_index_id = idx->idx_primary_index;
|
||||
}
|
||||
|
||||
@ -558,7 +553,7 @@ bool IndexCreateTask::handler(WorkItem& _item)
|
||||
fb_assert(!m_exprBlob.isEmpty());
|
||||
|
||||
CompilerScratch* csb = NULL;
|
||||
Jrd::ContextPoolHolder context(tdbb, attachment->createPool());
|
||||
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
|
||||
|
||||
idx->idx_expression = static_cast<ValueExprNode*> (MET_parse_blob(tdbb, relation, &m_exprBlob,
|
||||
&csb, &idx->idx_expression_statement, false, false));
|
||||
@ -850,10 +845,10 @@ void IDX_create_index(thread_db* tdbb,
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
Jrd::Attachment* attachment = tdbb->getAttachment();
|
||||
|
||||
if (relation->rel_file)
|
||||
if (relation->getExtFile())
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_no_meta_update) <<
|
||||
Arg::Gds(isc_extfile_uns_op) << Arg::Str(relation->rel_name));
|
||||
Arg::Gds(isc_extfile_uns_op) << Arg::Str(relation->getName()));
|
||||
}
|
||||
else if (relation->isVirtual())
|
||||
{
|
||||
@ -974,13 +969,9 @@ void IDX_create_index(thread_db* tdbb,
|
||||
|
||||
if ((relation->rel_flags & REL_temp_conn) && (relation->getPages(tdbb)->rel_instance_id != 0))
|
||||
{
|
||||
IndexLock* idx_lock = CMP_get_index_lock(tdbb, relation, idx->idx_id);
|
||||
IndexLock* idx_lock = relation->rel_perm->getIndexLock(tdbb, idx->idx_id);
|
||||
if (idx_lock)
|
||||
{
|
||||
++idx_lock->idl_count;
|
||||
if (idx_lock->idl_count == 1)
|
||||
LCK_lock(tdbb, idx_lock->idl_lock, LCK_SR, LCK_WAIT);
|
||||
}
|
||||
idx_lock->lockShared(tdbb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1017,7 +1008,7 @@ IndexBlock* IDX_create_index_block(thread_db* tdbb, jrd_rel* relation, USHORT id
|
||||
Lock* lock = FB_NEW_RPT(*relation->rel_pool, 0)
|
||||
Lock(tdbb, sizeof(SLONG), LCK_expression, index_block, index_block_flush);
|
||||
index_block->idb_lock = lock;
|
||||
lock->setKey((relation->rel_id << 16) | index_block->idb_id);
|
||||
lock->setKey((relation->getId() << 16) | index_block->idb_id);
|
||||
|
||||
return index_block;
|
||||
}
|
||||
@ -1047,9 +1038,9 @@ void IDX_delete_index(thread_db* tdbb, jrd_rel* relation, USHORT id)
|
||||
if ((relation->rel_flags & REL_temp_conn) && (relation->getPages(tdbb)->rel_instance_id != 0) &&
|
||||
tree_exists)
|
||||
{
|
||||
HazardPtr<IndexLock> idx_lock = relation->getIndexLock(tdbb, id);
|
||||
IndexLock* idx_lock = relation->rel_perm->getIndexLock(tdbb, id);
|
||||
if (idx_lock)
|
||||
idx_lock->idl_lock.leave245(tdbb);
|
||||
idx_lock->unlockAll(tdbb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1083,9 +1074,9 @@ void IDX_delete_indices(thread_db* tdbb, jrd_rel* relation, RelationPages* relPa
|
||||
|
||||
if (is_temp && tree_exists)
|
||||
{
|
||||
HazardPtr<IndexLock> idx_lock = relation->getIndexLock(tdbb, i);
|
||||
IndexLock* idx_lock = relation->rel_perm->getIndexLock(tdbb, i);
|
||||
if (idx_lock)
|
||||
idx_lock->idl_lock.releaseLock(tdbb, ExistenceLock::ReleaseMethod::Normal);
|
||||
idx_lock->unlockAll(tdbb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1738,7 +1729,7 @@ static idx_e check_foreign_key(thread_db* tdbb,
|
||||
if (!MET_lookup_partner(tdbb, relation, idx, 0))
|
||||
return result;
|
||||
|
||||
jrd_rel* partner_relation(tdbb);
|
||||
jrd_rel* partner_relation = nullptr;
|
||||
USHORT index_id = 0;
|
||||
|
||||
if (idx->idx_flags & idx_foreign)
|
||||
@ -1746,7 +1737,7 @@ static idx_e check_foreign_key(thread_db* tdbb,
|
||||
partner_relation = MetadataCache::findRelation(tdbb, idx->idx_primary_relation);
|
||||
index_id = idx->idx_primary_index;
|
||||
result = check_partner_index(tdbb, relation, record, transaction, idx,
|
||||
partner_relation.getPointer(), index_id);
|
||||
partner_relation, index_id);
|
||||
}
|
||||
else if (idx->idx_flags & (idx_primary | idx_unique))
|
||||
{
|
||||
@ -1762,15 +1753,15 @@ static idx_e check_foreign_key(thread_db* tdbb,
|
||||
|
||||
if ((relation->rel_flags & REL_temp_conn) && (partner_relation->rel_flags & REL_temp_tran))
|
||||
{
|
||||
jrd_rel::RelPagesSnapshot pagesSnapshot(tdbb, partner_relation.getPointer());
|
||||
partner_relation->fillPagesSnapshot(pagesSnapshot, true);
|
||||
RelationPermanent::RelPagesSnapshot pagesSnapshot(tdbb, partner_relation->rel_perm);
|
||||
partner_relation->rel_perm->fillPagesSnapshot(pagesSnapshot, true);
|
||||
|
||||
for (FB_SIZE_T i = 0; i < pagesSnapshot.getCount(); i++)
|
||||
{
|
||||
RelationPages* partnerPages = pagesSnapshot[i];
|
||||
tdbb->tdbb_temp_traid = partnerPages->rel_instance_id;
|
||||
if ( (result = check_partner_index(tdbb, relation, record,
|
||||
transaction, idx, partner_relation.getPointer(), index_id)) )
|
||||
transaction, idx, partner_relation, index_id)) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -1783,7 +1774,7 @@ static idx_e check_foreign_key(thread_db* tdbb,
|
||||
else
|
||||
{
|
||||
if ( (result = check_partner_index(tdbb, relation, record,
|
||||
transaction, idx, partner_relation.getPointer(), index_id)) )
|
||||
transaction, idx, partner_relation, index_id)) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -1796,7 +1787,7 @@ static idx_e check_foreign_key(thread_db* tdbb,
|
||||
if (idx->idx_flags & idx_foreign)
|
||||
context.setErrorLocation(relation, idx->idx_id);
|
||||
else
|
||||
context.setErrorLocation(partner_relation.getPointer(), index_id);
|
||||
context.setErrorLocation(partner_relation, index_id);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1854,7 +1845,7 @@ static idx_e check_partner_index(thread_db* tdbb,
|
||||
{
|
||||
if (idx_desc->idx_itype >= idx_first_intl_string)
|
||||
{
|
||||
HazardPtr<TextType> textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(idx_desc->idx_itype));
|
||||
TextType* textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(idx_desc->idx_itype));
|
||||
|
||||
if (textType->getFlags() & TEXTTYPE_SEPARATE_UNIQUE)
|
||||
{
|
||||
|
@ -41,11 +41,11 @@ namespace Jrd
|
||||
|
||||
void IDX_check_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_rel*, Jrd::jrd_rel*);
|
||||
bool IDX_check_master_types (Jrd::thread_db*, Jrd::index_desc&, Jrd::jrd_rel*, int&);
|
||||
void IDX_create_index(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*, const TEXT*,
|
||||
void IDX_create_index(Jrd::thread_db*, const Jrd::RelationPermanent*, Jrd::index_desc*, const TEXT*,
|
||||
USHORT*, Jrd::jrd_tra*, Jrd::SelectivityList&);
|
||||
Jrd::IndexBlock* IDX_create_index_block(Jrd::thread_db*, Jrd::jrd_rel*, USHORT);
|
||||
void IDX_delete_index(Jrd::thread_db*, Jrd::jrd_rel*, USHORT);
|
||||
void IDX_delete_indices(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::RelationPages*);
|
||||
void IDX_delete_index(Jrd::thread_db*, Jrd::RelationPermanent*, USHORT);
|
||||
void IDX_delete_indices(Jrd::thread_db*, Jrd::RelationPermanent*, Jrd::RelationPages*);
|
||||
void IDX_erase(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
|
||||
void IDX_garbage_collect(Jrd::thread_db*, Jrd::record_param*, Jrd::RecordStack&, Jrd::RecordStack&);
|
||||
void IDX_modify(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*, Jrd::jrd_tra*);
|
||||
|
313
src/jrd/ini.epp
313
src/jrd/ini.epp
@ -208,6 +208,22 @@ namespace
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
unsigned getLatestFormat(thread_db* tdbb, int relId, int maxFieldId)
|
||||
{
|
||||
const auto* relation = MetadataCache::lookupRelation(tdbb, relId);
|
||||
fb_assert(relation && relation->rel_formats);
|
||||
fb_assert(relation->rel_formats->count());
|
||||
|
||||
const auto formatNumber = relation->rel_formats->count() - 1;
|
||||
fb_assert(formatNumber < MAX_TABLE_VERSIONS);
|
||||
|
||||
const auto format = (*relation->rel_formats)[formatNumber];
|
||||
fb_assert(format->fmt_count == maxFieldId);
|
||||
fb_assert(format->fmt_version == formatNumber);
|
||||
|
||||
return formatNumber;
|
||||
}
|
||||
|
||||
bool getCharsetByTextType(SSHORT& charSet, const USHORT subType)
|
||||
{
|
||||
switch (subType)
|
||||
@ -751,10 +767,7 @@ void INI_format(thread_db* tdbb, const string& charset)
|
||||
for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
|
||||
{
|
||||
if (relfld[RFLD_R_TYPE] == rel_persistent)
|
||||
{
|
||||
auto rel = MetadataCache::findRelation(tdbb, relfld[RFLD_R_ID]);
|
||||
DPM_create_relation(tdbb, rel.getPointer());
|
||||
}
|
||||
DPM_create_relation(tdbb, MetadataCache::lookup_relation_id(tdbb, relfld[RFLD_R_ID]));
|
||||
|
||||
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
|
||||
;
|
||||
@ -969,16 +982,9 @@ void INI_init(thread_db* tdbb)
|
||||
const int* fld;
|
||||
for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
|
||||
{
|
||||
extern CacheObject* TRAP;
|
||||
|
||||
const auto id = relfld[RFLD_R_ID];
|
||||
//fprintf(stderr, "INI_init %d %s\n", id, names[relfld[RFLD_R_NAME]]);
|
||||
jrd_rel* relation = MetadataCache::findRelation(tdbb, id);
|
||||
|
||||
if (id == 7) TRAP = relation.getPointer();
|
||||
|
||||
const bool isPersistent = (relfld[RFLD_R_TYPE] == rel_persistent);
|
||||
|
||||
auto* relation = MetadataCache::lookupRelation(tdbb, relfld[RFLD_R_ID]);
|
||||
relation->rel_flags |= REL_system;
|
||||
relation->rel_flags |= MET_get_rel_flags_from_TYPE(relfld[RFLD_R_TYPE]);
|
||||
relation->rel_name = names[relfld[RFLD_R_NAME]];
|
||||
@ -994,6 +1000,8 @@ void INI_init(thread_db* tdbb)
|
||||
}
|
||||
}
|
||||
|
||||
jrd_rel* relVers = FB_NEW_POOL(relation->getPool()) jrd_rel(relation->getPool(), relation);
|
||||
|
||||
HalfStaticArray<const char*, 64> fieldNames;
|
||||
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
|
||||
{
|
||||
@ -1001,7 +1009,7 @@ void INI_init(thread_db* tdbb)
|
||||
}
|
||||
|
||||
const auto fields = vec<jrd_fld*>::newVector(*pool, fieldNames.getCount());
|
||||
relation->rel_fields = fields;
|
||||
relVers->rel_fields = fields;
|
||||
|
||||
ULONG fieldPos = 0;
|
||||
for (auto iter = fields->begin(); iter != fields->end(); ++iter)
|
||||
@ -1014,12 +1022,11 @@ void INI_init(thread_db* tdbb)
|
||||
relation->rel_formats = vec<Format*>::newVector(*pool, 1);
|
||||
|
||||
const auto majorVersion = ODS_VERSION;
|
||||
const auto dbMinorVersion = dbb->dbb_ods_version ? dbb->dbb_minor_version : ODS_CURRENT;
|
||||
// We need only the latest format for virtual tables
|
||||
auto minorVersion = isPersistent ? ODS_RELEASED : ODS_CURRENT;
|
||||
unsigned formatNumber = 0, currentFormat = 0;
|
||||
|
||||
MetadataCache* mdc = tdbb->getDatabase()->dbb_mdc;
|
||||
|
||||
while (minorVersion <= ODS_CURRENT)
|
||||
{
|
||||
bool newFormat = false;
|
||||
@ -1046,9 +1053,6 @@ void INI_init(thread_db* tdbb)
|
||||
format->fmt_version = formatNumber;
|
||||
format->fmt_length = FLAG_BYTES(format->fmt_count);
|
||||
|
||||
if (minorVersion == dbb->dbb_minor_version)
|
||||
currentFormat = formatNumber;
|
||||
|
||||
relation->rel_formats->resize(formatNumber + 1);
|
||||
(*relation->rel_formats)[formatNumber] = format;
|
||||
|
||||
@ -1086,13 +1090,16 @@ void INI_init(thread_db* tdbb)
|
||||
format->fmt_length += desc->dsc_length;
|
||||
}
|
||||
|
||||
if (minorVersion == dbMinorVersion)
|
||||
currentFormat = formatNumber;
|
||||
|
||||
minorVersion++;
|
||||
formatNumber++;
|
||||
}
|
||||
|
||||
fb_assert(currentFormat < relation->rel_formats->count());
|
||||
relation->rel_current_fmt = currentFormat;
|
||||
relation->rel_current_format = (*relation->rel_formats)[currentFormat];
|
||||
relVers->rel_current_fmt = currentFormat;
|
||||
relVers->rel_current_format = (*relation->rel_formats)[currentFormat];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1290,7 +1297,7 @@ void INI_upgrade(thread_db* tdbb)
|
||||
for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
|
||||
{
|
||||
if (relfld[RFLD_R_TYPE] == rel_persistent && relfld[RFLD_R_ODS] > odsVersion)
|
||||
DPM_create_relation(tdbb, MET_relation(tdbb, relfld[RFLD_R_ID]));
|
||||
DPM_create_relation(tdbb, MetadataCache::lookup_relation_id(tdbb, relfld[RFLD_R_ID]));
|
||||
|
||||
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
|
||||
;
|
||||
@ -1333,17 +1340,12 @@ void INI_upgrade(thread_db* tdbb)
|
||||
{
|
||||
// New format number is the latest we're aware of
|
||||
|
||||
const auto relation = MET_relation(tdbb, relId);
|
||||
fb_assert(relation->rel_formats->count());
|
||||
const unsigned formatNumber = relation->rel_formats->count() - 1;
|
||||
fb_assert(formatNumber < MAX_TABLE_VERSIONS);
|
||||
|
||||
FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE transaction)
|
||||
REL IN RDB$RELATIONS
|
||||
WITH REL.RDB$RELATION_ID = relId
|
||||
{
|
||||
MODIFY REL USING
|
||||
REL.RDB$FORMAT = formatNumber;
|
||||
REL.RDB$FORMAT = getLatestFormat(tdbb, relId, fieldId);
|
||||
END_MODIFY
|
||||
}
|
||||
END_FOR
|
||||
@ -1435,7 +1437,7 @@ void INI_upgrade(thread_db* tdbb)
|
||||
{
|
||||
if (relfld[RFLD_R_TYPE] == rel_persistent && relfld[RFLD_R_ODS] > odsVersion)
|
||||
{
|
||||
const auto relation = MET_relation(tdbb, relfld[RFLD_R_ID]);
|
||||
const auto relation = MetadataCache::lookupRelation(tdbb, relfld[RFLD_R_ID]);
|
||||
if (relation && relation->getBasePages()->rel_pages)
|
||||
DPM_delete_relation(tdbb, relation);
|
||||
}
|
||||
@ -1502,253 +1504,7 @@ void INI_upgrade(thread_db* tdbb)
|
||||
|
||||
if (invalidate && dbb->dbb_relations.get(relName, relation))
|
||||
{
|
||||
MET_dsql_cache_use(tdbb, SYM_relation, relName);
|
||||
relation->rel_flags |= REL_dropped;
|
||||
dbb->dbb_relations.remove(relName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The caller used an UCHAR* to store the acl, it was converted to TEXT* to
|
||||
// be passed to this function, only to be converted to UCHAR* to be passed
|
||||
// to BLB_put_segment. Therefore, "acl" was changed to UCHAR* as param.
|
||||
static void add_security_to_sys_rel(thread_db* tdbb,
|
||||
AutoRequest& reqAddSC,
|
||||
AutoRequest& reqModObjSC,
|
||||
AutoRequest& reqInsUserPriv,
|
||||
const MetaName& user_name,
|
||||
const TEXT* rel_name,
|
||||
const USHORT acl_length,
|
||||
const UCHAR* acl)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* a d d _ s e c u r i t y _ t o _ s y s _ r e l
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
*
|
||||
* Add security to system relations. Only the owner of the
|
||||
* database has SELECT/INSERT/UPDATE/DELETE privileges on
|
||||
* any system relations. Any other users only has SELECT
|
||||
* privilege.
|
||||
*
|
||||
**************************************/
|
||||
MetaName security_class, default_class;
|
||||
|
||||
SET_TDBB(tdbb);
|
||||
Jrd::Attachment* attachment = tdbb->getAttachment();
|
||||
|
||||
security_class.printf("%s%" SQUADFORMAT, SQL_SECCLASS_PREFIX,
|
||||
DPM_gen_id(tdbb, MET_lookup_generator(tdbb, SQL_SECCLASS_GENERATOR), false, 1));
|
||||
|
||||
default_class.printf("%s%" SQUADFORMAT, DEFAULT_CLASS,
|
||||
DPM_gen_id(tdbb, MET_lookup_generator(tdbb, DEFAULT_CLASS), false, 1));
|
||||
|
||||
|
||||
add_security_class(tdbb, reqAddSC, security_class, acl_length, acl);
|
||||
add_security_class(tdbb, reqAddSC, default_class, acl_length, acl);
|
||||
|
||||
FOR(REQUEST_HANDLE reqModObjSC) REL IN RDB$RELATIONS
|
||||
WITH REL.RDB$RELATION_NAME EQ rel_name
|
||||
{
|
||||
MODIFY REL USING
|
||||
REL.RDB$SECURITY_CLASS.NULL = FALSE;
|
||||
PAD(security_class.c_str(), REL.RDB$SECURITY_CLASS);
|
||||
|
||||
REL.RDB$DEFAULT_CLASS.NULL = FALSE;
|
||||
PAD(default_class.c_str(), REL.RDB$DEFAULT_CLASS);
|
||||
END_MODIFY
|
||||
}
|
||||
END_FOR
|
||||
|
||||
for (int cnt = 0; cnt < 6; cnt++)
|
||||
{
|
||||
STORE(REQUEST_HANDLE reqInsUserPriv) PRIV IN RDB$USER_PRIVILEGES
|
||||
switch (cnt)
|
||||
=======
|
||||
if (relfld[RFLD_R_ODS] > odsVersion)
|
||||
>>>>>>> master
|
||||
{
|
||||
store_relation(tdbb, relId, relName, fieldId, relType, handle, relSec);
|
||||
}
|
||||
else if (newFormat)
|
||||
{
|
||||
// New format number is the latest we're aware of
|
||||
|
||||
const auto relation = MET_relation(tdbb, relId);
|
||||
fb_assert(relation->rel_formats->count());
|
||||
const unsigned formatNumber = relation->rel_formats->count() - 1;
|
||||
fb_assert(formatNumber < MAX_TABLE_VERSIONS);
|
||||
|
||||
FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE transaction)
|
||||
REL IN RDB$RELATIONS
|
||||
WITH REL.RDB$RELATION_ID = relId
|
||||
{
|
||||
MODIFY REL USING
|
||||
REL.RDB$FORMAT = formatNumber;
|
||||
END_MODIFY
|
||||
}
|
||||
END_FOR
|
||||
|
||||
// Schedule metadata cache to be updated at the commit time
|
||||
|
||||
dsc desc;
|
||||
desc.makeText(static_cast<USHORT>(strlen(relName)), CS_METADATA,
|
||||
(UCHAR*) relName);
|
||||
DFW_post_work(transaction, dfw_update_format, &desc, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NonRelationSecurity nonRelSec(ownerName, reqAddSC, false);
|
||||
|
||||
// Create global fields added after the original minor ODS
|
||||
|
||||
context = "domains";
|
||||
handle.reset();
|
||||
|
||||
for (const gfld* gfield = gfields; gfield->gfld_name; gfield++)
|
||||
{
|
||||
if (gfield->gfld_ods_version > odsVersion)
|
||||
store_global_field(tdbb, gfield, handle, nonRelSec);
|
||||
}
|
||||
|
||||
// Create new system indexes
|
||||
|
||||
context = "indices";
|
||||
store_indices(tdbb, odsVersion);
|
||||
|
||||
// Create new system triggers and their trigger messages
|
||||
|
||||
context = "triggers";
|
||||
handle.reset();
|
||||
|
||||
for (const jrd_trg* trigger = triggers; trigger->trg_relation; ++trigger)
|
||||
{
|
||||
if (trigger->trg_ods_version > odsVersion)
|
||||
store_trigger(tdbb, trigger, handle);
|
||||
}
|
||||
|
||||
context = "trigger messages";
|
||||
handle.reset();
|
||||
|
||||
for (const trigger_msg* message = trigger_messages; message->trigmsg_name; ++message)
|
||||
{
|
||||
if (message->trg_ods_version > odsVersion)
|
||||
store_message(tdbb, message, handle);
|
||||
}
|
||||
|
||||
// Create new system generators
|
||||
|
||||
context = "generators";
|
||||
handle.reset();
|
||||
|
||||
for (const gen* generator = generators; generator->gen_name; generator++)
|
||||
{
|
||||
if (generator->gen_ods_version > odsVersion)
|
||||
store_generator(tdbb, generator, handle, nonRelSec);
|
||||
}
|
||||
|
||||
// Create new system packages
|
||||
|
||||
// Reset nonRelSec for package permissions, it should be its last usage in this function
|
||||
new(&nonRelSec) NonRelationSecurity(ownerName, reqAddSC, true);
|
||||
|
||||
context = "packages";
|
||||
store_packages(tdbb, nonRelSec, odsVersion);
|
||||
|
||||
// There are no new built-in charsets and collations introduced in ODS 13.1.
|
||||
// But if it happens in some future minor ODS, the corresponding INTL structures
|
||||
// should have the ODS field added and here we need code that conditionally adds
|
||||
// the missing charsets/collations.
|
||||
//
|
||||
// The same about the new types being introduced in minor ODS versions.
|
||||
|
||||
TRA_commit(tdbb, transaction, false);
|
||||
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
TRA_rollback(tdbb, transaction, false, true);
|
||||
|
||||
// Delete relations we've just created
|
||||
|
||||
for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
|
||||
{
|
||||
if (relfld[RFLD_R_TYPE] == rel_persistent && relfld[RFLD_R_ODS] > odsVersion)
|
||||
{
|
||||
const auto relation = MET_relation(tdbb, relfld[RFLD_R_ID]);
|
||||
if (relation && relation->getBasePages()->rel_pages)
|
||||
DPM_delete_relation(tdbb, relation);
|
||||
}
|
||||
|
||||
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
|
||||
;
|
||||
}
|
||||
|
||||
string msg;
|
||||
msg.printf("Database: %s\n\t"
|
||||
"Failed upgrading ODS from version %u.%u to version %u.%u",
|
||||
attachment->att_filename.c_str(),
|
||||
majorVersion, minorVersion, majorVersion, ODS_CURRENT);
|
||||
iscLogException(msg.c_str(), ex);
|
||||
|
||||
if (context)
|
||||
{
|
||||
Arg::StatusVector error(ex);
|
||||
error.prepend(Arg::Gds(isc_ods_upgrade_err) << Arg::Str(context));
|
||||
error.raise();
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
// If the database was successfully updated, mark it with the current minor ODS
|
||||
|
||||
win window(HEADER_PAGE_NUMBER);
|
||||
auto header = (Ods::header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
|
||||
CCH_MARK(tdbb, &window);
|
||||
|
||||
dbb->dbb_minor_version = header->hdr_ods_minor = ODS_CURRENT;
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
|
||||
string msg;
|
||||
msg.printf("Database: %s\n\t"
|
||||
"Successfully upgraded ODS from version %u.%u to version %u.%u",
|
||||
attachment->att_filename.c_str(),
|
||||
majorVersion, minorVersion, majorVersion, ODS_CURRENT);
|
||||
gds__log(msg.c_str());
|
||||
|
||||
// Invalidate new/modified relations in the DSQL metadata cache,
|
||||
// thus forcing them to be reloaded on demand
|
||||
|
||||
if (const auto dbb = attachment->att_dsql_instance)
|
||||
{
|
||||
for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
|
||||
{
|
||||
bool invalidate = false;
|
||||
|
||||
if (relfld[RFLD_R_ODS] > odsVersion)
|
||||
invalidate = true;
|
||||
|
||||
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
|
||||
{
|
||||
if (fld[RFLD_F_ODS] > odsVersion)
|
||||
invalidate = true;
|
||||
}
|
||||
|
||||
// Code below is the same as METD_drop_relation() but without a transaction
|
||||
|
||||
const auto relName = names[relfld[RFLD_R_NAME]];
|
||||
dsql_rel* relation;
|
||||
|
||||
if (invalidate && dbb->dbb_relations.get(relName, relation))
|
||||
{
|
||||
MET_dsql_cache_use(tdbb, SYM_relation, relName);
|
||||
MetadataCache::dsql_cache_use(tdbb, SYM_relation, relName);
|
||||
relation->rel_flags |= REL_dropped;
|
||||
dbb->dbb_relations.remove(relName);
|
||||
}
|
||||
@ -2026,7 +1782,8 @@ static void store_indices(thread_db* tdbb, USHORT odsVersion)
|
||||
for (int n = 0; n < SYSTEM_INDEX_COUNT; n++)
|
||||
{
|
||||
const ini_idx_t* index = &indices[n];
|
||||
const auto relation = MET_relation(tdbb, index->ini_idx_relid);
|
||||
const auto* relVers = MetadataCache::lookup_relation_id(tdbb, index->ini_idx_relid);
|
||||
const auto* relation = relVers->rel_perm;
|
||||
|
||||
if (odsVersion && index->ini_idx_ods <= odsVersion)
|
||||
continue;
|
||||
@ -2065,7 +1822,7 @@ static void store_indices(thread_db* tdbb, USHORT odsVersion)
|
||||
STORE(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction)
|
||||
Y IN RDB$INDEX_SEGMENTS
|
||||
{
|
||||
jrd_fld* field = (*relation->rel_fields)[segment->ini_idx_rfld_id];
|
||||
jrd_fld* field = (*relVers->rel_fields)[segment->ini_idx_rfld_id];
|
||||
|
||||
Y.RDB$FIELD_POSITION = position;
|
||||
PAD(X.RDB$INDEX_NAME, Y.RDB$INDEX_NAME);
|
||||
@ -2244,7 +2001,7 @@ static void store_relation(thread_db* tdbb,
|
||||
X.RDB$DEFAULT_CLASS.NULL = FALSE;
|
||||
|
||||
X.RDB$FIELD_ID = fieldId;
|
||||
X.RDB$FORMAT = 0;
|
||||
X.RDB$FORMAT = getLatestFormat(tdbb, relId, fieldId);
|
||||
X.RDB$SYSTEM_FLAG = RDB_system;
|
||||
X.RDB$DBKEY_LENGTH = 8;
|
||||
}
|
||||
|
115
src/jrd/intl.cpp
115
src/jrd/intl.cpp
@ -93,6 +93,7 @@
|
||||
#include "firebird.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "../jrd/CharSetContainer.h"
|
||||
#include "../jrd/jrd.h"
|
||||
#include "../jrd/req.h"
|
||||
#include "../jrd/val.h"
|
||||
@ -139,7 +140,7 @@ static void lookup_texttype(texttype* tt, const SubtypeInfo* info);
|
||||
static GlobalPtr<Mutex> createCollationMtx;
|
||||
|
||||
// Classes and structures used internally to this file and intl implementation
|
||||
HazardPtr<CharSetContainer> CharSetContainer::lookupCharset(thread_db* tdbb, USHORT ttype)
|
||||
CharSetContainer* CharSetContainer::lookupCharset(thread_db* tdbb, USHORT ttype)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -173,18 +174,19 @@ HazardPtr<CharSetContainer> CharSetContainer::lookupCharset(thread_db* tdbb, USH
|
||||
}
|
||||
|
||||
|
||||
CharSetContainer* CharSetContainer::create(thread_db* tdbb, MetaId id)
|
||||
CharSetContainer* CharSetContainer::create(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
{
|
||||
SubtypeInfo info;
|
||||
|
||||
if (lookupInternalCharSet(id, &info) || MET_get_char_coll_subtype_info(tdbb, id, &info))
|
||||
{
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
CharSetContainer* csc = FB_NEW_POOL(*dbb->dbb_permanent) CharSetContainer(*dbb->dbb_permanent, id, &info);
|
||||
dbb->dbb_mdc->setCharSet(tdbb, id, csc);
|
||||
cs = dbb->dbb_mdc->getCharSet(tdbb, id);
|
||||
dbb->dbb_mdc->makeCharSet(tdbb, id, csc);
|
||||
return dbb->dbb_mdc->getCharSet(tdbb, id);
|
||||
}
|
||||
else
|
||||
ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(ttype));
|
||||
ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(id));
|
||||
}
|
||||
|
||||
|
||||
@ -256,7 +258,7 @@ Lock* CharSetContainer::createCollationLock(thread_db* tdbb, USHORT ttype, void*
|
||||
}
|
||||
|
||||
CharSetContainer::CharSetContainer(MemoryPool& p, USHORT cs_id, const SubtypeInfo* info)
|
||||
: charset_collations(p),
|
||||
: PermanentStorage(p),
|
||||
cs(NULL)
|
||||
{
|
||||
charset* csL = FB_NEW_POOL(p) charset;
|
||||
@ -286,34 +288,19 @@ CsConvert CharSetContainer::lookupConverter(thread_db* tdbb, CHARSET_ID toCsId)
|
||||
return CsConvert(cs->getStruct(), toCs->getStruct());
|
||||
}
|
||||
|
||||
Collation* CharSetContainer::lookupCollation(thread_db* tdbb, USHORT tt_id)
|
||||
Collation* CharSetVers::lookupCollation(thread_db* tdbb, USHORT tt_id)
|
||||
{
|
||||
const USHORT id = TTYPE_TO_COLLATION(tt_id);
|
||||
|
||||
Collation* coll(FB_FUNCTION);
|
||||
if (charset_collations.load(tdbb, id, coll))
|
||||
{
|
||||
if (!coll->obsolete)
|
||||
return coll;
|
||||
}
|
||||
if (Collation* coll = charset_collations[id])
|
||||
return coll;
|
||||
|
||||
CheckoutLockGuard guard(tdbb, createCollationMtx, FB_FUNCTION); // do we need it ?
|
||||
|
||||
Collation* to_delete(FB_FUNCTION);
|
||||
if (charset_collations.load(tdbb, id, coll))
|
||||
{
|
||||
if (!coll->obsolete)
|
||||
return coll;
|
||||
|
||||
to_delete = coll;
|
||||
bool rc = charset_collations.replace(tdbb, id, coll, nullptr);
|
||||
fb_assert(rc);
|
||||
}
|
||||
|
||||
SubtypeInfo info;
|
||||
if (MET_get_char_coll_subtype_info(tdbb, tt_id, &info))
|
||||
{
|
||||
CharSet* charset = INTL_charset_lookup(tdbb, TTYPE_TO_CHARSET(tt_id));
|
||||
CharSet* charset = perm->getCharSet();
|
||||
|
||||
if (TTYPE_TO_CHARSET(tt_id) != CS_METADATA)
|
||||
{
|
||||
@ -334,8 +321,8 @@ Collation* CharSetContainer::lookupCollation(thread_db* tdbb, USHORT tt_id)
|
||||
|
||||
lookup_texttype(tt, &info);
|
||||
|
||||
if (charset_collations.getCount(tdbb) <= id)
|
||||
charset_collations.grow(tdbb, id + 1);
|
||||
if (charset_collations.getCount() <= id)
|
||||
charset_collations.grow(id + 1);
|
||||
|
||||
fb_assert((tt->texttype_canonical_width == 0 && tt->texttype_fn_canonical == NULL) ||
|
||||
(tt->texttype_canonical_width != 0 && tt->texttype_fn_canonical != NULL));
|
||||
@ -358,45 +345,15 @@ Collation* CharSetContainer::lookupCollation(thread_db* tdbb, USHORT tt_id)
|
||||
|
||||
tt.release();
|
||||
|
||||
// we don't need a lock in the charset
|
||||
if (id != 0)
|
||||
{
|
||||
collation->existenceLock = CharSetContainer::createCollationLock(tdbb, tt_id, collation);
|
||||
|
||||
fb_assert(collation->useCount == 0);
|
||||
fb_assert(!collation->obsolete);
|
||||
}
|
||||
|
||||
if (id != 0)
|
||||
{
|
||||
LCK_lock(tdbb, collation->existenceLock, LCK_SR, LCK_WAIT);
|
||||
|
||||
// as we just obtained SR lock for new collation instance
|
||||
// we could safely delete obsolete instance
|
||||
if (to_delete)
|
||||
to_delete->destroy(tdbb);
|
||||
}
|
||||
//
|
||||
// We did not delete "to_delete" when id == 0. Why??????????????????
|
||||
//
|
||||
|
||||
coll = charset_collations.store(tdbb, id, collation);
|
||||
charset_collations[id] = collation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (to_delete)
|
||||
{
|
||||
LCK_lock(tdbb, to_delete->existenceLock, LCK_SR, LCK_WAIT);
|
||||
to_delete->destroy(tdbb);
|
||||
}
|
||||
|
||||
ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(tt_id));
|
||||
}
|
||||
|
||||
return coll;
|
||||
return charset_collations[id];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void CharSetContainer::unloadCollation(thread_db* tdbb, USHORT tt_id)
|
||||
{
|
||||
const USHORT id = TTYPE_TO_COLLATION(tt_id);
|
||||
@ -431,7 +388,7 @@ void CharSetContainer::unloadCollation(thread_db* tdbb, USHORT tt_id)
|
||||
LCK_release(tdbb, lock);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
static void lookup_texttype(texttype* tt, const SubtypeInfo* info)
|
||||
{
|
||||
@ -443,16 +400,14 @@ static void lookup_texttype(texttype* tt, const SubtypeInfo* info)
|
||||
|
||||
void Jrd::MetadataCache::releaseIntlObjects(thread_db* tdbb)
|
||||
{
|
||||
for (auto cs : mdc_charsets.snapshot())
|
||||
{
|
||||
if (cs)
|
||||
cs->release(tdbb);
|
||||
}
|
||||
mdc_charsets.cleanup();
|
||||
}
|
||||
|
||||
|
||||
void Jrd::MetadataCache::destroyIntlObjects(thread_db* tdbb)
|
||||
{
|
||||
mdc_charsets.cleanup();
|
||||
/*
|
||||
for (FB_SIZE_T i = 0; i < mdc_charsets.getCount(tdbb); i++)
|
||||
{
|
||||
HazardPtr<CharSetContainer> cs;
|
||||
@ -461,7 +416,7 @@ void Jrd::MetadataCache::destroyIntlObjects(thread_db* tdbb)
|
||||
cs->destroy(tdbb);
|
||||
mdc_charsets.store(tdbb, i, nullptr);
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
|
||||
@ -603,7 +558,7 @@ int INTL_compare(thread_db* tdbb, const dsc* pText1, const dsc* pText2, ErrorFun
|
||||
}
|
||||
}
|
||||
|
||||
HazardPtr<TextType> obj = INTL_texttype_lookup(tdbb, compare_type);
|
||||
TextType* obj = INTL_texttype_lookup(tdbb, compare_type);
|
||||
|
||||
return obj->compare(length1, p1, length2, p2);
|
||||
}
|
||||
@ -719,7 +674,7 @@ CsConvert INTL_convert_lookup(thread_db* tdbb, CHARSET_ID to_cs, CHARSET_ID from
|
||||
fb_assert(from_cs != CS_dynamic);
|
||||
fb_assert(to_cs != CS_dynamic);
|
||||
|
||||
HazardPtr<CharSetContainer> charset = CharSetContainer::lookupCharset(tdbb, from_cs);
|
||||
CharSetContainer* charset = CharSetContainer::lookupCharset(tdbb, from_cs);
|
||||
|
||||
return charset->lookupConverter(tdbb, to_cs);
|
||||
}
|
||||
@ -951,7 +906,7 @@ USHORT INTL_key_length(thread_db* tdbb, USHORT idxType, USHORT iLength)
|
||||
key_length = iLength;
|
||||
else
|
||||
{
|
||||
HazardPtr<TextType> obj = INTL_texttype_lookup(tdbb, ttype);
|
||||
TextType* obj = INTL_texttype_lookup(tdbb, ttype);
|
||||
key_length = obj->key_length(iLength);
|
||||
}
|
||||
|
||||
@ -989,7 +944,7 @@ CharSet* INTL_charset_lookup(thread_db* tdbb, USHORT parm1)
|
||||
* <never> - if error
|
||||
*
|
||||
**************************************/
|
||||
HazardPtr<CharSetContainer> csc = CharSetContainer::lookupCharset(tdbb, parm1);
|
||||
CharSetContainer* csc = CharSetContainer::lookupCharset(tdbb, parm1);
|
||||
return csc->getCharSet();
|
||||
}
|
||||
|
||||
@ -1021,15 +976,19 @@ Collation* INTL_texttype_lookup(thread_db* tdbb, USHORT parm1)
|
||||
if (parm1 == ttype_dynamic)
|
||||
parm1 = MAP_CHARSET_TO_TTYPE(tdbb->getCharSet());
|
||||
|
||||
HazardPtr<CharSetContainer> csc = CharSetContainer::lookupCharset(tdbb, parm1);
|
||||
auto* perm = MetadataCache::lookupCharset(tdbb, TTYPE_TO_CHARSET(parm1));
|
||||
if (!perm)
|
||||
return nullptr;
|
||||
|
||||
return csc->lookupCollation(tdbb, parm1);
|
||||
auto* vers = perm->getObject(tdbb);
|
||||
|
||||
return vers ? vers->lookupCollation(tdbb, TTYPE_TO_COLLATION(parm1)) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
void INTL_texttype_unload(thread_db* tdbb, USHORT ttype)
|
||||
/*void INTL_texttype_unload(thread_db* tdbb, USHORT ttype)
|
||||
{
|
||||
/**************************************
|
||||
**************************************
|
||||
*
|
||||
* I N T L _ t e x t t y p e _ u n l o a d
|
||||
*
|
||||
@ -1038,14 +997,14 @@ void INTL_texttype_unload(thread_db* tdbb, USHORT ttype)
|
||||
* Functional description
|
||||
* Unload a collation from memory.
|
||||
*
|
||||
**************************************/
|
||||
**************************************
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
HazardPtr<CharSetContainer> csc = CharSetContainer::lookupCharset(tdbb, ttype);
|
||||
CharSetContainer* csc = CharSetContainer::lookupCharset(tdbb, ttype);
|
||||
if (csc)
|
||||
csc->unloadCollation(tdbb, ttype);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
bool INTL_texttype_validate(Jrd::thread_db* tdbb, const SubtypeInfo* info)
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ bool INTL_defined_type(Jrd::thread_db*, USHORT);
|
||||
USHORT INTL_key_length(Jrd::thread_db*, USHORT, USHORT);
|
||||
Jrd::CharSet* INTL_charset_lookup(Jrd::thread_db* tdbb, USHORT parm1);
|
||||
Jrd::Collation* INTL_texttype_lookup(Jrd::thread_db* tdbb, USHORT parm1);
|
||||
void INTL_texttype_unload(Jrd::thread_db*, USHORT);
|
||||
//void INTL_texttype_unload(Jrd::thread_db*, USHORT);
|
||||
bool INTL_texttype_validate(Jrd::thread_db*, const SubtypeInfo*);
|
||||
void INTL_pad_spaces(Jrd::thread_db*, dsc*, UCHAR*, ULONG);
|
||||
USHORT INTL_string_to_key(Jrd::thread_db*, USHORT, const dsc*, dsc*, USHORT);
|
||||
|
@ -2126,8 +2126,8 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
|
||||
// load DDL triggers
|
||||
mdc->load_ddl_triggers(tdbb);
|
||||
|
||||
TrigVectorPtr* trig_connect = dbb->dbb_mdc->getTriggers(DB_TRIGGER_CONNECT | TRIGGER_TYPE_DB);
|
||||
if (trig_connect && trig_connect->load() && !trig_connect->load()->isEmpty(tdbb))
|
||||
auto* trig_connect = dbb->dbb_mdc->getTriggers(DB_TRIGGER_CONNECT | TRIGGER_TYPE_DB);
|
||||
if (trig_connect && *trig_connect)
|
||||
{
|
||||
// Start a transaction to execute ON CONNECT triggers.
|
||||
// Ensure this transaction can't trigger auto-sweep.
|
||||
@ -8202,11 +8202,11 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign
|
||||
{
|
||||
try
|
||||
{
|
||||
TrigVectorPtr* trig_disconnect = dbb->dbb_mdc->getTriggers(DB_TRIGGER_CONNECT | TRIGGER_TYPE_DB);
|
||||
auto* trig_disconnect = dbb->dbb_mdc->getTriggers(DB_TRIGGER_CONNECT | TRIGGER_TYPE_DB);
|
||||
|
||||
if (!forcedPurge &&
|
||||
!(attachment->att_flags & ATT_no_db_triggers) &&
|
||||
trig_disconnect && trig_disconnect->load() && !trig_disconnect->load()->isEmpty(tdbb))
|
||||
trig_disconnect && *trig_disconnect)
|
||||
{
|
||||
ThreadStatusGuard temp_status(tdbb);
|
||||
|
||||
@ -9686,7 +9686,7 @@ void JRD_cancel_operation(thread_db* /*tdbb*/, Jrd::Attachment* attachment, int
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ????????????
|
||||
bool TrigVector::hasActive() const
|
||||
{
|
||||
for (auto t : snapshot())
|
||||
@ -9723,3 +9723,5 @@ void TrigVector::release(thread_db* tdbb)
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
445
src/jrd/jrd.h
445
src/jrd/jrd.h
@ -32,6 +32,7 @@
|
||||
#define JRD_JRD_H
|
||||
|
||||
#include "../common/gdsassert.h"
|
||||
#include "../jrd/tdbb.h"
|
||||
#include "../common/dsc.h"
|
||||
#include "../jrd/err_proto.h"
|
||||
#include "../jrd/jrd_proto.h"
|
||||
@ -62,9 +63,6 @@
|
||||
#include "../jrd/Attachment.h"
|
||||
#include "firebird/Interface.h"
|
||||
|
||||
#include <cds/threading/model.h> // cds::threading::Manager
|
||||
|
||||
#define BUGCHECK(number) ERR_bugcheck(number, __FILE__, __LINE__)
|
||||
#define SOFT_BUGCHECK(number) ERR_soft_bugcheck(number, __FILE__, __LINE__)
|
||||
#define CORRUPT(number) ERR_corrupt(number)
|
||||
#define IBERROR(number) ERR_error(number)
|
||||
@ -205,445 +203,6 @@ const USHORT WIN_garbage_collector = 4; // garbage collector's window
|
||||
const USHORT WIN_garbage_collect = 8; // scan left a page for garbage collector
|
||||
|
||||
|
||||
#ifdef USE_ITIMER
|
||||
class TimeoutTimer final :
|
||||
public Firebird::RefCntIface<Firebird::ITimerImpl<TimeoutTimer, Firebird::CheckStatusWrapper> >
|
||||
{
|
||||
public:
|
||||
explicit TimeoutTimer()
|
||||
: m_started(0),
|
||||
m_expired(false),
|
||||
m_value(0),
|
||||
m_error(0)
|
||||
{ }
|
||||
|
||||
// ITimer implementation
|
||||
void handler();
|
||||
|
||||
bool expired() const
|
||||
{
|
||||
return m_expired;
|
||||
}
|
||||
|
||||
unsigned int getValue() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
unsigned int getErrCode() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
// milliseconds left before timer expiration
|
||||
unsigned int timeToExpire() const;
|
||||
|
||||
// evaluate expire timestamp using start timestamp
|
||||
bool getExpireTimestamp(const ISC_TIMESTAMP_TZ start, ISC_TIMESTAMP_TZ& exp) const;
|
||||
|
||||
// set timeout value in milliseconds and secondary error code
|
||||
void setup(unsigned int value, ISC_STATUS error)
|
||||
{
|
||||
m_value = value;
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
SINT64 m_started;
|
||||
bool m_expired;
|
||||
unsigned int m_value; // milliseconds
|
||||
ISC_STATUS m_error;
|
||||
};
|
||||
#else
|
||||
class TimeoutTimer : public Firebird::RefCounted
|
||||
{
|
||||
public:
|
||||
explicit TimeoutTimer()
|
||||
: m_start(0),
|
||||
m_value(0),
|
||||
m_error(0)
|
||||
{ }
|
||||
|
||||
bool expired() const;
|
||||
|
||||
unsigned int getValue() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
unsigned int getErrCode() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
// milliseconds left before timer expiration
|
||||
unsigned int timeToExpire() const;
|
||||
|
||||
// clock value when timer will expire
|
||||
bool getExpireClock(SINT64& clock) const;
|
||||
|
||||
// set timeout value in milliseconds and secondary error code
|
||||
void setup(unsigned int value, ISC_STATUS error)
|
||||
{
|
||||
m_start = 0;
|
||||
m_value = value;
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
SINT64 currTime() const
|
||||
{
|
||||
return fb_utils::query_performance_counter() * 1000 / fb_utils::query_performance_frequency();
|
||||
}
|
||||
|
||||
SINT64 m_start;
|
||||
unsigned int m_value; // milliseconds
|
||||
ISC_STATUS m_error;
|
||||
};
|
||||
#endif // USE_ITIMER
|
||||
|
||||
// Thread specific database block
|
||||
|
||||
// tdbb_flags
|
||||
|
||||
const ULONG TDBB_sweeper = 1; // Thread sweeper or garbage collector
|
||||
const ULONG TDBB_no_cache_unwind = 2; // Don't unwind page buffer cache
|
||||
const ULONG TDBB_backup_write_locked = 4; // BackupManager has write lock on LCK_backup_database
|
||||
const ULONG TDBB_stack_trace_done = 8; // PSQL stack trace is added into status-vector
|
||||
const ULONG TDBB_dont_post_dfw = 16; // dont post DFW tasks as deferred work is performed now
|
||||
const ULONG TDBB_sys_error = 32; // error shouldn't be handled by the looper
|
||||
const ULONG TDBB_verb_cleanup = 64; // verb cleanup is in progress
|
||||
const ULONG TDBB_use_db_page_space = 128; // use database (not temporary) page space in GTT operations
|
||||
const ULONG TDBB_detaching = 256; // detach is in progress
|
||||
const ULONG TDBB_wait_cancel_disable = 512; // don't cancel current waiting operation
|
||||
const ULONG TDBB_cache_unwound = 1024; // page cache was unwound
|
||||
const ULONG TDBB_reset_stack = 2048; // stack should be reset after stack overflow exception
|
||||
const ULONG TDBB_dfw_cleanup = 4096; // DFW cleanup phase is active
|
||||
const ULONG TDBB_repl_in_progress = 8192; // Prevent recursion in replication
|
||||
const ULONG TDBB_replicator = 16384; // Replicator
|
||||
|
||||
class thread_db : public Firebird::ThreadData
|
||||
{
|
||||
const static int QUANTUM = 100; // Default quantum
|
||||
const static int SWEEP_QUANTUM = 10; // Make sweeps less disruptive
|
||||
|
||||
private:
|
||||
MemoryPool* defaultPool;
|
||||
void setDefaultPool(MemoryPool* p)
|
||||
{
|
||||
defaultPool = p;
|
||||
}
|
||||
friend class Firebird::SubsystemContextPoolHolder <Jrd::thread_db, MemoryPool>;
|
||||
Database* database;
|
||||
Attachment* attachment;
|
||||
jrd_tra* transaction;
|
||||
Request* request;
|
||||
RuntimeStatistics *reqStat, *traStat, *attStat, *dbbStat;
|
||||
|
||||
public:
|
||||
explicit thread_db(FbStatusVector* status)
|
||||
: ThreadData(ThreadData::tddDBB),
|
||||
defaultPool(NULL),
|
||||
database(NULL),
|
||||
attachment(NULL),
|
||||
transaction(NULL),
|
||||
request(NULL),
|
||||
tdbb_status_vector(status),
|
||||
tdbb_quantum(QUANTUM),
|
||||
tdbb_flags(0),
|
||||
tdbb_temp_traid(0),
|
||||
tdbb_bdbs(*getDefaultMemoryPool()),
|
||||
tdbb_thread(Firebird::ThreadSync::getThread("thread_db"))
|
||||
{
|
||||
reqStat = traStat = attStat = dbbStat = RuntimeStatistics::getDummy();
|
||||
fb_utils::init_status(tdbb_status_vector);
|
||||
}
|
||||
|
||||
~thread_db()
|
||||
{
|
||||
resetStack();
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
for (FB_SIZE_T n = 0; n < tdbb_bdbs.getCount(); ++n)
|
||||
{
|
||||
fb_assert(tdbb_bdbs[n] == NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
FbStatusVector* tdbb_status_vector;
|
||||
SLONG tdbb_quantum; // Cycles remaining until voluntary schedule
|
||||
ULONG tdbb_flags;
|
||||
|
||||
TraNumber tdbb_temp_traid; // current temporary table scope
|
||||
|
||||
// BDB's held by thread
|
||||
Firebird::HalfStaticArray<BufferDesc*, 16> tdbb_bdbs;
|
||||
Firebird::ThreadSync* tdbb_thread;
|
||||
|
||||
MemoryPool* getDefaultPool()
|
||||
{
|
||||
return defaultPool;
|
||||
}
|
||||
|
||||
Database* getDatabase()
|
||||
{
|
||||
return database;
|
||||
}
|
||||
|
||||
const Database* getDatabase() const
|
||||
{
|
||||
return database;
|
||||
}
|
||||
|
||||
void setDatabase(Database* val);
|
||||
|
||||
Attachment* getAttachment()
|
||||
{
|
||||
return attachment;
|
||||
}
|
||||
|
||||
const Attachment* getAttachment() const
|
||||
{
|
||||
return attachment;
|
||||
}
|
||||
|
||||
void setAttachment(Attachment* val);
|
||||
|
||||
jrd_tra* getTransaction()
|
||||
{
|
||||
return transaction;
|
||||
}
|
||||
|
||||
const jrd_tra* getTransaction() const
|
||||
{
|
||||
return transaction;
|
||||
}
|
||||
|
||||
void setTransaction(jrd_tra* val);
|
||||
|
||||
Request* getRequest()
|
||||
{
|
||||
return request;
|
||||
}
|
||||
|
||||
const Request* getRequest() const
|
||||
{
|
||||
return request;
|
||||
}
|
||||
|
||||
void setRequest(Request* val);
|
||||
|
||||
SSHORT getCharSet() const;
|
||||
|
||||
void markAsSweeper()
|
||||
{
|
||||
tdbb_quantum = SWEEP_QUANTUM;
|
||||
tdbb_flags |= TDBB_sweeper;
|
||||
}
|
||||
|
||||
void bumpStats(const RuntimeStatistics::StatType index, SINT64 delta = 1)
|
||||
{
|
||||
reqStat->bumpValue(index, delta);
|
||||
traStat->bumpValue(index, delta);
|
||||
attStat->bumpValue(index, delta);
|
||||
dbbStat->bumpValue(index, delta);
|
||||
}
|
||||
|
||||
void bumpRelStats(const RuntimeStatistics::StatType index, SLONG relation_id, SINT64 delta = 1)
|
||||
{
|
||||
// We don't bump counters for dbbStat here, they're merged from attStats on demand
|
||||
|
||||
reqStat->bumpValue(index, delta);
|
||||
traStat->bumpValue(index, delta);
|
||||
attStat->bumpValue(index, delta);
|
||||
|
||||
const RuntimeStatistics* const dummyStat = RuntimeStatistics::getDummy();
|
||||
|
||||
// We expect that at least attStat is present (not a dummy object)
|
||||
|
||||
fb_assert(attStat != dummyStat);
|
||||
|
||||
// Relation statistics is a quite complex beast, so a conditional check
|
||||
// does not hurt. It also allows to avoid races while accessing the static
|
||||
// dummy object concurrently.
|
||||
|
||||
if (reqStat != dummyStat)
|
||||
reqStat->bumpRelValue(index, relation_id, delta);
|
||||
|
||||
if (traStat != dummyStat)
|
||||
traStat->bumpRelValue(index, relation_id, delta);
|
||||
|
||||
if (attStat != dummyStat)
|
||||
attStat->bumpRelValue(index, relation_id, delta);
|
||||
}
|
||||
|
||||
ISC_STATUS getCancelState(ISC_STATUS* secondary = NULL);
|
||||
void checkCancelState();
|
||||
void reschedule();
|
||||
const TimeoutTimer* getTimeoutTimer() const
|
||||
{
|
||||
return tdbb_reqTimer;
|
||||
}
|
||||
|
||||
// Returns minimum of passed wait timeout and time to expiration of reqTimer.
|
||||
// Timer value is rounded to the upper whole second.
|
||||
ULONG adjustWait(ULONG wait) const;
|
||||
|
||||
void registerBdb(BufferDesc* bdb)
|
||||
{
|
||||
if (tdbb_bdbs.isEmpty()) {
|
||||
tdbb_flags &= ~TDBB_cache_unwound;
|
||||
}
|
||||
fb_assert(!(tdbb_flags & TDBB_cache_unwound));
|
||||
|
||||
FB_SIZE_T pos;
|
||||
if (tdbb_bdbs.find(NULL, pos))
|
||||
tdbb_bdbs[pos] = bdb;
|
||||
else
|
||||
tdbb_bdbs.add(bdb);
|
||||
}
|
||||
|
||||
bool clearBdb(BufferDesc* bdb)
|
||||
{
|
||||
if (tdbb_bdbs.isEmpty())
|
||||
{
|
||||
// hvlad: the only legal case when thread holds no latches but someone
|
||||
// tried to release latch is when CCH_unwind was called (and released
|
||||
// all latches) but caller is unaware about it. See CORE-3034, for example.
|
||||
// Else it is bug and should be BUGCHECK'ed.
|
||||
|
||||
if (tdbb_flags & TDBB_cache_unwound)
|
||||
return false;
|
||||
}
|
||||
fb_assert(!(tdbb_flags & TDBB_cache_unwound));
|
||||
|
||||
FB_SIZE_T pos;
|
||||
if (!tdbb_bdbs.find(bdb, pos))
|
||||
BUGCHECK(300); // can't find shared latch
|
||||
|
||||
tdbb_bdbs[pos] = NULL;
|
||||
|
||||
if (pos == tdbb_bdbs.getCount() - 1)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (tdbb_bdbs[pos] != NULL)
|
||||
{
|
||||
tdbb_bdbs.shrink(pos + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos == 0)
|
||||
{
|
||||
tdbb_bdbs.shrink(0);
|
||||
break;
|
||||
}
|
||||
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void resetStack()
|
||||
{
|
||||
if (tdbb_flags & TDBB_reset_stack)
|
||||
{
|
||||
tdbb_flags &= ~TDBB_reset_stack;
|
||||
#ifdef WIN_NT
|
||||
_resetstkoflw();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
class TimerGuard
|
||||
{
|
||||
public:
|
||||
TimerGuard(thread_db* tdbb, TimeoutTimer* timer, bool autoStop)
|
||||
: m_tdbb(tdbb),
|
||||
m_autoStop(autoStop && timer),
|
||||
m_saveTimer(tdbb->tdbb_reqTimer)
|
||||
{
|
||||
m_tdbb->tdbb_reqTimer = timer;
|
||||
if (timer && timer->expired())
|
||||
m_tdbb->tdbb_quantum = 0;
|
||||
}
|
||||
|
||||
~TimerGuard()
|
||||
{
|
||||
if (m_autoStop)
|
||||
m_tdbb->tdbb_reqTimer->stop();
|
||||
|
||||
m_tdbb->tdbb_reqTimer = m_saveTimer;
|
||||
}
|
||||
|
||||
private:
|
||||
thread_db* m_tdbb;
|
||||
bool m_autoStop;
|
||||
Firebird::RefPtr<TimeoutTimer> m_saveTimer;
|
||||
};
|
||||
|
||||
private:
|
||||
Firebird::RefPtr<TimeoutTimer> tdbb_reqTimer;
|
||||
|
||||
};
|
||||
|
||||
class ThreadContextHolder
|
||||
{
|
||||
public:
|
||||
explicit ThreadContextHolder(Firebird::CheckStatusWrapper* status = NULL)
|
||||
: context(status ? status : &localStatus)
|
||||
{
|
||||
context.putSpecific();
|
||||
|
||||
if (!cds::threading::Manager::isThreadAttached())
|
||||
cds::threading::Manager::attachThread();
|
||||
}
|
||||
|
||||
ThreadContextHolder(Database* dbb, Jrd::Attachment* att, FbStatusVector* status = NULL)
|
||||
: context(status ? status : &localStatus)
|
||||
{
|
||||
context.putSpecific();
|
||||
context.setDatabase(dbb);
|
||||
context.setAttachment(att);
|
||||
|
||||
if (!cds::threading::Manager::isThreadAttached())
|
||||
cds::threading::Manager::attachThread();
|
||||
}
|
||||
|
||||
~ThreadContextHolder()
|
||||
{
|
||||
Firebird::ThreadData::restoreSpecific();
|
||||
}
|
||||
|
||||
thread_db* operator->()
|
||||
{
|
||||
return &context;
|
||||
}
|
||||
|
||||
operator thread_db*()
|
||||
{
|
||||
return &context;
|
||||
}
|
||||
|
||||
private:
|
||||
// copying is prohibited
|
||||
ThreadContextHolder(const ThreadContextHolder&);
|
||||
ThreadContextHolder& operator= (const ThreadContextHolder&);
|
||||
|
||||
Firebird::FbLocalStatus localStatus;
|
||||
thread_db context;
|
||||
};
|
||||
|
||||
|
||||
// Helper class to temporarily activate sweeper context
|
||||
class ThreadSweepGuard
|
||||
{
|
||||
@ -776,6 +335,8 @@ inline void SET_DBB(Jrd::Database*& dbb)
|
||||
// global variables for engine
|
||||
|
||||
namespace Jrd {
|
||||
void suspend();
|
||||
|
||||
typedef Firebird::SubsystemContextPoolHolder <Jrd::thread_db, MemoryPool> ContextPoolHolder;
|
||||
|
||||
class DatabaseContextHolder : public Jrd::ContextPoolHolder
|
||||
|
200
src/jrd/lck.cpp
200
src/jrd/lck.cpp
@ -1590,203 +1590,3 @@ Lock* Lock::detach()
|
||||
return next;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
*
|
||||
* Someone is trying to drop an object. If there
|
||||
* are outstanding interests in the existence of
|
||||
* that object then just mark as blocking and return.
|
||||
* and release the existence lock.
|
||||
*
|
||||
**************************************/
|
||||
void ExistenceLock::blockingAst()
|
||||
{
|
||||
AsyncContextHolder tdbb(lck->lck_dbb, FB_FUNCTION);
|
||||
|
||||
MutexLockGuard g(mutex, FB_FUNCTION);
|
||||
|
||||
unsigned fl = (flags |= unlocking);
|
||||
if ((fl & countMask) == 0)
|
||||
internalUnlock(tdbb, fl, fl & locked);
|
||||
else
|
||||
{
|
||||
flags |= blocking;
|
||||
flags &= ~unlocking;
|
||||
}
|
||||
}
|
||||
|
||||
void ExistenceLock::enter245(thread_db* tdbb)
|
||||
{
|
||||
Firebird::MutexLockGuard g(mutex, FB_FUNCTION);
|
||||
|
||||
unsigned fl = flags;
|
||||
fb_assert((fl & sharedMask) > 0);
|
||||
|
||||
if (!(fl & locked))
|
||||
{
|
||||
LCK_lock(tdbb, lck, LCK_SR, LCK_WAIT);
|
||||
|
||||
if (object)
|
||||
{
|
||||
Arg::StatusVector v;
|
||||
if (!object->checkObject(tdbb, v))
|
||||
{
|
||||
LCK_release(tdbb, lck);
|
||||
ERR_post(v);
|
||||
}
|
||||
}
|
||||
|
||||
flags |= locked;
|
||||
}
|
||||
}
|
||||
|
||||
void ExistenceLock::leave245(thread_db* tdbb, bool force)
|
||||
{
|
||||
Firebird::MutexLockGuard g(mutex, FB_FUNCTION);
|
||||
unsigned fl = (flags |= unlocking);
|
||||
fb_assert(fl & locked);
|
||||
|
||||
if ((((fl & countMask) == 0) && (fl & blocking)) | force)
|
||||
internalUnlock(tdbb, fl);
|
||||
else
|
||||
flags &= ~unlocking;
|
||||
}
|
||||
|
||||
bool ExistenceLock::exclLock(thread_db* tdbb)
|
||||
{
|
||||
Firebird::MutexLockGuard g(mutex, FB_FUNCTION);
|
||||
unsigned fl = (flags += exclusive);
|
||||
|
||||
if ((fl & countMask) != exclusive)
|
||||
{
|
||||
flags -= exclusive;
|
||||
printf("false1\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lckFunction = fl & locked ? LCK_convert : LCK_lock;
|
||||
if (!lckFunction(tdbb, lck, LCK_EX, getLockWait(tdbb)))
|
||||
{
|
||||
flags -= exclusive;
|
||||
printf("false2\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SSHORT ExistenceLock::getLockWait(thread_db* tdbb)
|
||||
{
|
||||
jrd_tra* transaction = tdbb->getTransaction();
|
||||
return transaction ? transaction->getLockWait() : 0;
|
||||
}
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
bool ExistenceLock::hasExclLock(thread_db*)
|
||||
{
|
||||
Firebird::MutexLockGuard g(mutex, FB_FUNCTION);
|
||||
return (flags & exclMask) == exclusive;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ExistenceLock::unlock(thread_db* tdbb)
|
||||
{
|
||||
Firebird::MutexLockGuard g(mutex, FB_FUNCTION);
|
||||
fb_assert(hasExclLock(tdbb));
|
||||
|
||||
unsigned fl = flags;
|
||||
unsigned newFlags;
|
||||
do
|
||||
{
|
||||
newFlags = (fl | unlocking) - exclusive;
|
||||
} while (!flags.compare_exchange_weak(fl, newFlags, std::memory_order_release, std::memory_order_acquire));
|
||||
|
||||
fb_assert((fl & exclMask) == 0);
|
||||
if ((fl & locked) && !(fl & blocking))
|
||||
{
|
||||
LCK_convert(tdbb, lck, LCK_SR, getLockWait(tdbb)); // always succeeds
|
||||
flags &= ~unlocking;
|
||||
}
|
||||
else
|
||||
internalUnlock(tdbb, fl);
|
||||
}
|
||||
|
||||
void ExistenceLock::internalUnlock(thread_db* tdbb, unsigned fl, bool flLockRelease)
|
||||
{
|
||||
fb_assert(mutex.locked());
|
||||
fb_assert((fl & countMask) == 0);
|
||||
|
||||
if (flLockRelease)
|
||||
{
|
||||
LCK_release(tdbb, lck); // repost ??????????????
|
||||
internalObjectDelete(tdbb, fl);
|
||||
}
|
||||
else
|
||||
fb_assert(!(fl & inCache));
|
||||
|
||||
flags &= ~(blocking | unlocking | locked);
|
||||
}
|
||||
|
||||
void ExistenceLock::internalObjectDelete(thread_db* tdbb, unsigned fl)
|
||||
{
|
||||
if (object)
|
||||
{
|
||||
object->afterUnlock(tdbb, fl);
|
||||
|
||||
if (!(fl & inCache))
|
||||
object->retire();
|
||||
}
|
||||
}
|
||||
|
||||
void ExistenceLock::incrementError [[noreturn]] ()
|
||||
{
|
||||
const char* objTypeName = "unknown object";
|
||||
switch(lck->lck_type)
|
||||
{
|
||||
case LCK_rel_exist:
|
||||
objTypeName = "relation";
|
||||
break;
|
||||
case LCK_idx_exist:
|
||||
objTypeName = "index";
|
||||
break;
|
||||
case LCK_prc_exist:
|
||||
objTypeName = "procedure";
|
||||
break;
|
||||
case LCK_tt_exist:
|
||||
objTypeName = "collation";
|
||||
break;
|
||||
case LCK_fun_exist:
|
||||
objTypeName = "function";
|
||||
break;
|
||||
default:
|
||||
fb_assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
fatal_exception::raiseFmt("Can not use %s %s which is going to be dropped in regular request",
|
||||
objTypeName, object->c_name());
|
||||
}
|
||||
|
||||
void ExistenceLock::releaseLock(thread_db* tdbb, ReleaseMethod rm)
|
||||
{
|
||||
Firebird::MutexLockGuard g(mutex, FB_FUNCTION);
|
||||
switch (rm)
|
||||
{
|
||||
case ReleaseMethod::Normal:
|
||||
if ((flags |= blocking) & locked)
|
||||
leave245(tdbb);
|
||||
else if (hasExclLock(tdbb))
|
||||
unlock(tdbb);
|
||||
else
|
||||
fb_assert(false);
|
||||
break;
|
||||
|
||||
case ReleaseMethod::DropObject:
|
||||
fb_assert(hasExclLock(tdbb));
|
||||
// fall through
|
||||
|
||||
case ReleaseMethod::CloseCache:
|
||||
LCK_release(tdbb, lck);
|
||||
internalObjectDelete(tdbb, flags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
128
src/jrd/lck.h
128
src/jrd/lck.h
@ -196,134 +196,6 @@ void LCK_write_data(Jrd::thread_db*, Jrd::Lock*, LOCK_DATA_T);
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
// fb_assert(tdbb->getDatabase()->dbb_mdc->mdc_use_mutex.locked());
|
||||
// fb_assert(!tdbb->getAttachment()->isSystem());
|
||||
|
||||
class ExistenceLock
|
||||
{
|
||||
public:
|
||||
ExistenceLock(MemoryPool& p, thread_db* tdbb, lck_t type, SLONG key, CacheObject* obj)
|
||||
: lck(FB_NEW_RPT(p, 0) Lock(tdbb, sizeof(SLONG), type, this, ast)),
|
||||
flags(inCache),
|
||||
object(obj)
|
||||
{
|
||||
lck->setKey(key);
|
||||
}
|
||||
|
||||
enum class ReleaseMethod {Normal, DropObject, CloseCache};
|
||||
|
||||
Resource::State inc(thread_db* tdbb)
|
||||
{
|
||||
unsigned fl = ++flags;
|
||||
fb_assert(!(fl & countChk));
|
||||
|
||||
if (fl & exclMask)
|
||||
{
|
||||
--flags;
|
||||
incrementError();
|
||||
}
|
||||
|
||||
if (fl & countMask)
|
||||
printf("inc1\n");
|
||||
|
||||
return (fl & locked) && !(fl & unlocking) ? Resource::State::Locked : Resource::State::Counted;
|
||||
}
|
||||
|
||||
// make sure we have SH existence lock
|
||||
void enter245(thread_db* tdbb);
|
||||
|
||||
Resource::State dec(thread_db* tdbb)
|
||||
{
|
||||
unsigned fl = --flags;
|
||||
fb_assert(!(fl & countChk));
|
||||
//fb_assert(((fl + 1) & count) > 0);
|
||||
|
||||
return ((fl & countMask) == 0) && (fl & blocking) ? Resource::State::Unlocking : Resource::State::Posted;
|
||||
}
|
||||
|
||||
// release shared lock if needed (or unconditionally when forced set)
|
||||
void leave245(thread_db* tdbb, bool force = false);
|
||||
|
||||
unsigned getUseCount() const
|
||||
{
|
||||
static_assert(sharedMask & 1); // Other cases shift is needed to return use count
|
||||
return flags & sharedMask;
|
||||
}
|
||||
|
||||
bool exclLock(thread_db* tdbb); // Take exclusive lock
|
||||
#ifdef DEV_BUILD
|
||||
bool hasExclLock(thread_db* tdbb); // Is object locked exclusively?
|
||||
#endif
|
||||
void unlock(thread_db* tdbb); // Release exclusive lock
|
||||
void releaseLock(thread_db* tdbb, ReleaseMethod rm); // Release any lock
|
||||
|
||||
private:
|
||||
static int ast(void* self)
|
||||
{
|
||||
reinterpret_cast<ExistenceLock*>(self)->blockingAst();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blockingAst();
|
||||
void incrementError [[noreturn]] ();
|
||||
SSHORT getLockWait(thread_db* tdbb);
|
||||
|
||||
void internalUnlock(thread_db* tdbb, unsigned fl, bool flLockRelease = true);
|
||||
void internalObjectDelete(thread_db* tdbb, unsigned fl);
|
||||
|
||||
public:
|
||||
Firebird::Mutex mutex;
|
||||
|
||||
private:
|
||||
Firebird::AutoPtr<Lock> lck;
|
||||
std::atomic<unsigned> flags;
|
||||
CacheObject* object;
|
||||
|
||||
public:
|
||||
static const unsigned sharedMask = 0x000FFFFF;
|
||||
static const unsigned exclMask = 0x07E00000;
|
||||
static const unsigned countMask = sharedMask | exclMask;
|
||||
static const unsigned countChk = 0x00100000;
|
||||
static const unsigned exclusive = 0x00200000;
|
||||
static const unsigned exCheck = 0x08000000;
|
||||
static const unsigned inCache = 0x10000000;
|
||||
static const unsigned unlocking = 0x20000000;
|
||||
static const unsigned locked = 0x40000000;
|
||||
static const unsigned blocking = 0x80000000;
|
||||
};
|
||||
|
||||
class ExistenceGuard
|
||||
{
|
||||
public:
|
||||
ExistenceGuard(thread_db* t, ExistenceLock& l)
|
||||
: tdbb(t), lck(&l)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
ExistenceGuard(thread_db* t, ExistenceLock* l)
|
||||
: tdbb(t), lck(l)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
~ExistenceGuard()
|
||||
{
|
||||
if (lck && (lck->dec(tdbb) == Resource::State::Unlocking))
|
||||
lck->leave245(tdbb);
|
||||
}
|
||||
|
||||
private:
|
||||
thread_db* tdbb;
|
||||
ExistenceLock* lck;
|
||||
|
||||
void init()
|
||||
{
|
||||
if (lck && lck->inc(tdbb) != Resource::State::Locked)
|
||||
lck->enter245(tdbb);
|
||||
}
|
||||
};
|
||||
|
||||
class AutoLock
|
||||
{
|
||||
public:
|
||||
|
542
src/jrd/met.epp
542
src/jrd/met.epp
File diff suppressed because it is too large
Load Diff
828
src/jrd/met.h
828
src/jrd/met.h
@ -35,6 +35,7 @@
|
||||
#include "../jrd/val.h"
|
||||
#include "../jrd/irq.h"
|
||||
#include "../jrd/drq.h"
|
||||
#include "../jrd/exe.h"
|
||||
|
||||
#include "../jrd/CharSetContainer.h"
|
||||
|
||||
@ -85,60 +86,6 @@ public:
|
||||
const int TFB_computed = 1;
|
||||
const int TFB_array = 2;
|
||||
|
||||
|
||||
const int TRIGGER_PRE_STORE = 1;
|
||||
const int TRIGGER_POST_STORE = 2;
|
||||
const int TRIGGER_PRE_MODIFY = 3;
|
||||
const int TRIGGER_POST_MODIFY = 4;
|
||||
const int TRIGGER_PRE_ERASE = 5;
|
||||
const int TRIGGER_POST_ERASE = 6;
|
||||
const int TRIGGER_MAX = 7;
|
||||
|
||||
// trigger type prefixes
|
||||
const int TRIGGER_PRE = 0;
|
||||
const int TRIGGER_POST = 1;
|
||||
|
||||
// trigger type suffixes
|
||||
const int TRIGGER_STORE = 1;
|
||||
const int TRIGGER_MODIFY = 2;
|
||||
const int TRIGGER_ERASE = 3;
|
||||
|
||||
// that's how trigger action types are encoded
|
||||
/*
|
||||
bit 0 = TRIGGER_PRE/TRIGGER_POST flag,
|
||||
bits 1-2 = TRIGGER_STORE/TRIGGER_MODIFY/TRIGGER_ERASE (slot #1),
|
||||
bits 3-4 = TRIGGER_STORE/TRIGGER_MODIFY/TRIGGER_ERASE (slot #2),
|
||||
bits 5-6 = TRIGGER_STORE/TRIGGER_MODIFY/TRIGGER_ERASE (slot #3),
|
||||
and finally the above calculated value is decremented
|
||||
|
||||
example #1:
|
||||
TRIGGER_POST_ERASE =
|
||||
= ((TRIGGER_ERASE << 1) | TRIGGER_POST) - 1 =
|
||||
= ((3 << 1) | 1) - 1 =
|
||||
= 0x00000110 (6)
|
||||
|
||||
example #2:
|
||||
TRIGGER_PRE_STORE_MODIFY =
|
||||
= ((TRIGGER_MODIFY << 3) | (TRIGGER_STORE << 1) | TRIGGER_PRE) - 1 =
|
||||
= ((2 << 3) | (1 << 1) | 0) - 1 =
|
||||
= 0x00010001 (17)
|
||||
|
||||
example #3:
|
||||
TRIGGER_POST_MODIFY_ERASE_STORE =
|
||||
= ((TRIGGER_STORE << 5) | (TRIGGER_ERASE << 3) | (TRIGGER_MODIFY << 1) | TRIGGER_POST) - 1 =
|
||||
= ((1 << 5) | (3 << 3) | (2 << 1) | 1) - 1 =
|
||||
= 0x00111100 (60)
|
||||
*/
|
||||
|
||||
// that's how trigger types are decoded
|
||||
#define TRIGGER_ACTION(value, shift) \
|
||||
(((((value + 1) >> shift) & 3) << 1) | ((value + 1) & 1)) - 1
|
||||
|
||||
#define TRIGGER_ACTION_SLOT(value, slot) \
|
||||
TRIGGER_ACTION(value, (slot * 2 - 1) )
|
||||
|
||||
const int TRIGGER_COMBINED_MAX = 128;
|
||||
|
||||
#include "../jrd/exe_proto.h"
|
||||
#include "../jrd/obj.h"
|
||||
#include "../dsql/sym.h"
|
||||
@ -159,8 +106,9 @@ public:
|
||||
private:
|
||||
const ExtEngineManager::Procedure* prc_external;
|
||||
|
||||
jrd_prc(MemoryPool& p, MetaId id)
|
||||
: Routine(p, id),
|
||||
public:
|
||||
explicit jrd_prc(RoutinePermanent* perm)
|
||||
: Routine(perm),
|
||||
prc_record_format(NULL),
|
||||
prc_type(prc_legacy),
|
||||
prc_external(NULL)
|
||||
@ -168,26 +116,17 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
explicit jrd_prc(MemoryPool& p)
|
||||
: Routine(p),
|
||||
prc_record_format(NULL),
|
||||
prc_type(prc_legacy),
|
||||
prc_external(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int getObjectType() const
|
||||
int getObjectType() const override
|
||||
{
|
||||
return obj_procedure;
|
||||
}
|
||||
|
||||
virtual SLONG getSclType() const
|
||||
SLONG getSclType() const override
|
||||
{
|
||||
return obj_procedures;
|
||||
}
|
||||
|
||||
virtual void releaseFormat()
|
||||
void releaseFormat() override
|
||||
{
|
||||
delete prc_record_format;
|
||||
prc_record_format = NULL;
|
||||
@ -199,19 +138,22 @@ private:
|
||||
delete prc_external;
|
||||
}
|
||||
|
||||
static int blockingAst(void* ast_object);
|
||||
|
||||
public:
|
||||
static jrd_prc* create(thread_db* tdbb, MetaId id, CacheObject::Flag flags);
|
||||
static jrd_prc* create(thread_db* tdbb, MemoryPool& p, MetaId id, CacheObject::Flag flags);
|
||||
static Lock* getLock(MemoryPool& p, thread_db* tdbb);
|
||||
|
||||
virtual bool checkCache(thread_db* tdbb) const;
|
||||
bool checkCache(thread_db* tdbb) const override;
|
||||
|
||||
virtual void releaseExternal()
|
||||
void releaseExternal() override
|
||||
{
|
||||
delete prc_external;
|
||||
prc_external = NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool reload(thread_db* tdbb); // impl is in met.epp
|
||||
bool reload(thread_db* tdbb) override; // impl is in met.epp
|
||||
};
|
||||
|
||||
|
||||
@ -257,681 +199,16 @@ enum IndexStatus
|
||||
|
||||
class CharSet;
|
||||
|
||||
class ObjectBase : public HazardObject
|
||||
{
|
||||
public:
|
||||
enum ResetType {Recompile, Mark, Commit, Rollback};
|
||||
|
||||
typedef SLONG ReturnedId; // enable '-1' as not found
|
||||
|
||||
public:
|
||||
virtual void resetDependentObject(thread_db* tdbb, ResetType rt) = 0;
|
||||
virtual void eraseObject(thread_db* tdbb) = 0; // erase object
|
||||
|
||||
public:
|
||||
void resetDependentObjects(thread_db* tdbb, TraNumber olderThan);
|
||||
void addDependentObject(thread_db* tdbb, ObjectBase* dep);
|
||||
void removeDependentObject(thread_db* tdbb, ObjectBase* dep);
|
||||
[[noreturn]] void busyError(thread_db* tdbb, MetaId id, const char* name);
|
||||
};
|
||||
|
||||
namespace CacheFlag
|
||||
{
|
||||
static const CacheObject::Flag COMMITTED = 0x01;
|
||||
static const CacheObject::Flag ERASED = 0x02;
|
||||
static const CacheObject::Flag NOSCAN = 0x04;
|
||||
static const CacheObject::Flag AUTOCREATE = 0x08;
|
||||
|
||||
static const CacheObject::Flag IGNORE_MASK = COMMITTED | ERASED;
|
||||
}
|
||||
|
||||
template <class OBJ>
|
||||
class CacheList : public HazardObject
|
||||
{
|
||||
public:
|
||||
CacheList(OBJ* obj, TraNumber currentTrans, CacheObject::Flag fl = 0)
|
||||
: object(obj), next(nullptr), traNumber(currentTrans), cacheFlags(fl)
|
||||
{ }
|
||||
|
||||
// find appropriate object in cache
|
||||
OBJ* getObject(TraNumber currentTrans, CacheObject::Flag flags) const
|
||||
{
|
||||
CacheObject::Flag f(cacheFlags.load() & ~(flags & CacheFlag::IGNORE_MASK));
|
||||
|
||||
// object deleted, good bye
|
||||
if (f & CacheFlag::ERASED)
|
||||
return nullptr;
|
||||
|
||||
// committed (i.e. confirmed) objects are freely available
|
||||
if (f & CacheFlag::COMMITTED)
|
||||
return object;
|
||||
|
||||
// transaction that created an object can always access it
|
||||
if ((traNumber == currentTrans) && currentTrans)
|
||||
return object;
|
||||
|
||||
// try next level
|
||||
CacheList* n = next.load(atomics::memory_order_acquire);
|
||||
return n ? n->getObject(currentTrans, flags) : nullptr;
|
||||
}
|
||||
|
||||
bool isBusy(TraNumber currentTrans) const
|
||||
{
|
||||
return traNumber != currentTrans && !(cacheFlags & CacheFlag::COMMITTED);
|
||||
}
|
||||
|
||||
// add new entry to the list
|
||||
static bool add(atomics::atomic<CacheList*>& list, CacheList* newVal)
|
||||
{
|
||||
HazardPtr<CacheList> oldVal(list);
|
||||
|
||||
do
|
||||
{
|
||||
if (oldVal && oldVal->isBusy(newVal->traNumber)) // modified in other transaction
|
||||
return false;
|
||||
newVal->next.store(oldVal.getPointer(), atomics::memory_order_relaxed);
|
||||
} while (! oldVal.replace2(list, newVal));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// remove too old objects - they are anyway can't be in use
|
||||
static TraNumber cleanup(atomics::atomic<CacheList*>& list, const TraNumber oldest)
|
||||
{
|
||||
TraNumber rc = 0;
|
||||
for (HazardPtr<CacheList> entry(list); entry; entry.set(entry->next))
|
||||
{
|
||||
if ((entry->cacheFlags & CacheFlag::COMMITTED) && entry->traNumber < oldest)
|
||||
{
|
||||
if (entry->cacheFlags.fetch_or(CacheFlag::ERASED) & CacheFlag::ERASED)
|
||||
break; // someone else also performs cleanup
|
||||
|
||||
// split remaining list off
|
||||
if (entry.replace2(list, nullptr))
|
||||
{
|
||||
while (entry && !(entry->cacheFlags.fetch_or(CacheFlag::ERASED) & CacheFlag::ERASED))
|
||||
{
|
||||
entry->retire();
|
||||
OBJ::destroy(entry->object);
|
||||
entry.set(entry->next);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// store traNumber of last not removed list element
|
||||
rc = entry->traNumber;
|
||||
}
|
||||
|
||||
return rc; // 0 is returned in a case when list becomes empty
|
||||
}
|
||||
|
||||
// created earlier object is OK and should become visible to the world
|
||||
void commit(TraNumber currentTrans, TraNumber nextTrans)
|
||||
{
|
||||
fb_assert(cacheFlags == 0);
|
||||
fb_assert(traNumber == currentTrans);
|
||||
traNumber = nextTrans;
|
||||
cacheFlags |= CacheFlag::COMMITTED;
|
||||
}
|
||||
|
||||
// created earlier object is bad and should be destroyed
|
||||
static void rollback(atomics::atomic<CacheList*>& list, const TraNumber currentTran)
|
||||
{
|
||||
// Take into an account that no other transaction except current (i.e. object creator)
|
||||
// can access uncommitted objects, only list entries may be accessed as hazard pointers.
|
||||
// Therefore rollback can retire such entries at once, a kind of pop() from stack.
|
||||
|
||||
HazardPtr<CacheList> entry(list);
|
||||
while (entry)
|
||||
{
|
||||
if (entry->cacheFlags & CacheFlag::COMMITTED)
|
||||
break;
|
||||
fb_assert(entry->traNumber == currentTran);
|
||||
|
||||
if (entry.replace2(list, entry->next))
|
||||
{
|
||||
entry->retire();
|
||||
OBJ::destroy(entry->object);
|
||||
entry = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark as erased
|
||||
void erase()
|
||||
{
|
||||
cacheFlags |= CacheFlag::ERASED;
|
||||
}
|
||||
|
||||
void assertCommitted()
|
||||
{
|
||||
fb_assert(cacheFlags & CacheFlag::COMMITTED);
|
||||
}
|
||||
|
||||
private:
|
||||
OBJ* object;
|
||||
atomics::atomic<CacheList*> next;
|
||||
TraNumber traNumber; // when COMMITTED not set - stores transaction that created this list element
|
||||
// when COMMITTED is set - stores transaction after which older elements are not needed
|
||||
// traNumber to be changed BEFORE setting COMMITTED
|
||||
MdcVersion version; // version of metadata cache when object was added
|
||||
atomics::atomic<CacheObject::Flag> cacheFlags;
|
||||
};
|
||||
|
||||
|
||||
class CurrentTransaction
|
||||
{
|
||||
public:
|
||||
static TraNumber getNumber(thread_db* tdbb);
|
||||
/* static TraNumber currentTraNumber(thread_db* tdbb)
|
||||
{
|
||||
jrd_tra* tra = tdbb->getTransaction();
|
||||
return tra ? tra->tra_number : 0;
|
||||
} */
|
||||
};
|
||||
|
||||
|
||||
template <class OBJ>
|
||||
class CacheElement : public ObjectBase
|
||||
{
|
||||
typedef CacheList<OBJ> CachedObj;
|
||||
|
||||
public:
|
||||
CacheElement(MetaId id) :
|
||||
list(nullptr), resetAt(0), myId(id)
|
||||
{ }
|
||||
|
||||
~CacheElement()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
OBJ* getObject(thread_db* tdbb, CacheObject::Flag flags = 0)
|
||||
{
|
||||
HazardPtr<CachedObj> l(list);
|
||||
if (!l)
|
||||
return nullptr;
|
||||
return l->getObject(CurrentTransaction::getNumber(tdbb), flags);
|
||||
}
|
||||
|
||||
bool storeObject(thread_db* tdbb, OBJ* obj, CacheObject::Flag fl = 0)
|
||||
{
|
||||
TraNumber oldest = tdbb->getDatabase()->dbb_oldest_active;
|
||||
TraNumber oldResetAt = resetAt.load(atomics::memory_order_acquire);
|
||||
if (oldResetAt && oldResetAt < oldest)
|
||||
setNewResetAt(oldResetAt, CachedObj::cleanup(list, oldest));
|
||||
|
||||
TraNumber current = CurrentTransaction::getNumber(tdbb);
|
||||
CachedObj* value = FB_NEW CachedObj(obj, current, fl & CacheFlag::IGNORE_MASK);
|
||||
if (!CachedObj::add(list, value))
|
||||
{
|
||||
delete value;
|
||||
return false;
|
||||
}
|
||||
|
||||
setNewResetAt(oldResetAt, current);
|
||||
return true;
|
||||
}
|
||||
|
||||
void commit(thread_db* tdbb)
|
||||
{
|
||||
HazardPtr<CachedObj> current(list);
|
||||
if (current)
|
||||
current->commit(CurrentTransaction::getNumber(tdbb), tdbb->getDatabase()->dbb_next_transaction);
|
||||
}
|
||||
|
||||
void rollback(thread_db* tdbb)
|
||||
{
|
||||
CacheList<OBJ>::rollback(list, CurrentTransaction::getNumber(tdbb));
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
list.load()->assertCommitted();
|
||||
CacheList<OBJ>::cleanup(list, MAX_TRA_NUMBER);
|
||||
}
|
||||
|
||||
void resetDependentObject(thread_db* tdbb, ResetType rt) override
|
||||
{
|
||||
switch (rt)
|
||||
{
|
||||
case ObjectBase::ResetType::Recompile:
|
||||
{
|
||||
OBJ* newObj = OBJ::create(tdbb, myId, 0);
|
||||
if (!storeObject(tdbb, newObj))
|
||||
{
|
||||
OBJ::destroy(newObj);
|
||||
OBJ* oldObj = getObject(tdbb);
|
||||
busyError(tdbb, myId, oldObj ? oldObj->c_name() : nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectBase::ResetType::Mark:
|
||||
// used in AST, therefore ignore error when saving empty object
|
||||
if (storeObject(tdbb, nullptr))
|
||||
commit(tdbb);
|
||||
break;
|
||||
|
||||
case ObjectBase::ResetType::Commit:
|
||||
commit(tdbb);
|
||||
break;
|
||||
|
||||
case ObjectBase::ResetType::Rollback:
|
||||
rollback(tdbb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void eraseObject(thread_db* tdbb) override
|
||||
{
|
||||
HazardPtr<CachedObj> l(list);
|
||||
fb_assert(l);
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
if (!storeObject(tdbb, nullptr, CacheFlag::ERASED))
|
||||
{
|
||||
OBJ* oldObj = getObject(tdbb);
|
||||
busyError(tdbb, myId, oldObj ? oldObj->c_name() : nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setNewResetAt(TraNumber oldVal, TraNumber newVal)
|
||||
{
|
||||
resetAt.compare_exchange_strong(oldVal, newVal,
|
||||
atomics::memory_order_release, atomics::memory_order_relaxed);
|
||||
}
|
||||
|
||||
atomics::atomic<CachedObj*> list;
|
||||
atomics::atomic<TraNumber> resetAt;
|
||||
MetaId myId;
|
||||
};
|
||||
|
||||
|
||||
template <class E, unsigned SUBARRAY_SHIFT = 8>
|
||||
class CacheVector : public Firebird::PermanentStorage
|
||||
{
|
||||
public:
|
||||
static const unsigned SUBARRAY_SIZE = 1 << SUBARRAY_SHIFT;
|
||||
static const unsigned SUBARRAY_MASK = SUBARRAY_SIZE - 1;
|
||||
|
||||
typedef CacheElement<E> StoredObject;
|
||||
typedef atomics::atomic<StoredObject*> SubArrayData;
|
||||
typedef atomics::atomic<SubArrayData*> ArrayData;
|
||||
typedef SharedReadVector<ArrayData, 4> Storage;
|
||||
|
||||
explicit CacheVector(MemoryPool& pool)
|
||||
: Firebird::PermanentStorage(pool),
|
||||
m_objects(getPool())
|
||||
{}
|
||||
|
||||
private:
|
||||
static FB_SIZE_T getCount(const HazardPtr<typename Storage::Generation>& v)
|
||||
{
|
||||
return v->getCount() << SUBARRAY_SHIFT;
|
||||
}
|
||||
|
||||
SubArrayData* getDataPointer(MetaId id) const
|
||||
{
|
||||
auto up = m_objects.readAccessor();
|
||||
if (id >= getCount(up))
|
||||
return nullptr;
|
||||
|
||||
auto sub = up->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
fb_assert(sub);
|
||||
return &sub[id & SUBARRAY_MASK];
|
||||
}
|
||||
|
||||
void grow(FB_SIZE_T reqSize)
|
||||
{
|
||||
fb_assert(reqSize > 0);
|
||||
reqSize = ((reqSize - 1) >> SUBARRAY_SHIFT) + 1;
|
||||
|
||||
Firebird::MutexLockGuard g(objectsGrowMutex, FB_FUNCTION);
|
||||
|
||||
m_objects.grow(reqSize);
|
||||
auto wa = m_objects.writeAccessor();
|
||||
fb_assert(wa->getCapacity() >= reqSize);
|
||||
while (wa->getCount() < reqSize)
|
||||
{
|
||||
SubArrayData* sub = FB_NEW_POOL(getPool()) SubArrayData[SUBARRAY_SIZE];
|
||||
memset(sub, 0, sizeof(SubArrayData) * SUBARRAY_SIZE);
|
||||
wa->add()->store(sub, atomics::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
StoredObject* getData(thread_db*, MetaId id)
|
||||
{
|
||||
auto ptr = getDataPointer(id);
|
||||
return ptr ? *ptr : nullptr;
|
||||
}
|
||||
|
||||
E* getObject(thread_db* tdbb, MetaId id, CacheObject::Flag flags)
|
||||
{
|
||||
// In theory that should be endless cycle - object may arrive/disappear again and again.
|
||||
// But in order to faster find devel problems we run it very limited number of times.
|
||||
#ifdef DEV_BUILD
|
||||
for (int i = 0; i < 2; ++i)
|
||||
#else
|
||||
for (;;)
|
||||
#endif
|
||||
{
|
||||
auto ptr = getDataPointer(id);
|
||||
if (ptr)
|
||||
{
|
||||
HazardPtr<StoredObject> data(*ptr);
|
||||
if (data)
|
||||
{
|
||||
auto rc = data->getObject(tdbb, flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & CacheFlag::AUTOCREATE))
|
||||
return nullptr;
|
||||
|
||||
auto val = E::create(tdbb, id, flags);
|
||||
if (!val)
|
||||
(Firebird::Arg::Gds(isc_random) << "Object create failed").raise();
|
||||
|
||||
if (storeObject(tdbb, id, val))
|
||||
return val;
|
||||
|
||||
E::destroy(val);
|
||||
}
|
||||
#ifdef DEV_BUILD
|
||||
(Firebird::Arg::Gds(isc_random) << "Object suddenly disappeared").raise();
|
||||
#endif
|
||||
}
|
||||
|
||||
StoredObject* storeObject(thread_db* tdbb, MetaId id, E* const val)
|
||||
{
|
||||
if (id >= getCount())
|
||||
grow(id + 1);
|
||||
|
||||
auto ptr = getDataPointer(id);
|
||||
fb_assert(ptr);
|
||||
|
||||
HazardPtr<StoredObject> data(*ptr);
|
||||
if (!data)
|
||||
{
|
||||
MemoryPool* pool = tdbb->getDatabase()->dbb_permanent;
|
||||
fb_assert(pool);
|
||||
StoredObject* newData = FB_NEW_POOL(*pool) StoredObject(id);
|
||||
if (!data.replace2(*ptr, newData))
|
||||
delete newData;
|
||||
else
|
||||
data.set(*ptr);
|
||||
}
|
||||
|
||||
if (!data->storeObject(tdbb, val))
|
||||
data.clear();
|
||||
return data.getPointer();
|
||||
}
|
||||
|
||||
StoredObject* lookup(thread_db* tdbb, std::function<bool(E* val)> cmp, MetaId* foundId = nullptr) const
|
||||
{
|
||||
auto a = m_objects.readAccessor();
|
||||
for (FB_SIZE_T i = 0; i < a->getCount(); ++i)
|
||||
{
|
||||
SubArrayData* const sub = a->value(i).load(atomics::memory_order_relaxed);
|
||||
if (!sub)
|
||||
continue;
|
||||
|
||||
for (SubArrayData* end = &sub[SUBARRAY_SIZE]; sub < end--;)
|
||||
{
|
||||
StoredObject* ptr = end->load(atomics::memory_order_relaxed);
|
||||
if (!ptr)
|
||||
continue;
|
||||
|
||||
E* val = ptr->getObject(tdbb);
|
||||
if (val && cmp(val))
|
||||
{
|
||||
if (foundId)
|
||||
*foundId = (i << SUBARRAY_SHIFT) + (end - sub);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
~CacheVector()
|
||||
{
|
||||
auto a = m_objects.writeAccessor();
|
||||
for (FB_SIZE_T i = 0; i < a->getCount(); ++i)
|
||||
{
|
||||
SubArrayData* const sub = a->value(i).load(atomics::memory_order_relaxed);
|
||||
if (!sub)
|
||||
continue;
|
||||
|
||||
for (SubArrayData* end = &sub[SUBARRAY_SIZE]; sub < end--;)
|
||||
delete *end; // no need using release here in CacheVector's dtor
|
||||
|
||||
delete[] sub;
|
||||
}
|
||||
|
||||
delete a;
|
||||
}
|
||||
|
||||
FB_SIZE_T getCount() const
|
||||
{
|
||||
return m_objects.readAccessor()->getCount() << SUBARRAY_SHIFT;
|
||||
}
|
||||
|
||||
bool replace2(MetaId id, HazardPtr<E>& oldVal, E* const newVal)
|
||||
{
|
||||
if (id >= getCount())
|
||||
grow(id + 1);
|
||||
|
||||
auto a = m_objects.readAccessor();
|
||||
SubArrayData* sub = a->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
fb_assert(sub);
|
||||
sub = &sub[id & SUBARRAY_MASK];
|
||||
|
||||
return oldVal.replace2(sub, newVal);
|
||||
}
|
||||
|
||||
bool clear(MetaId id)
|
||||
{
|
||||
if (id >= getCount())
|
||||
return false;
|
||||
|
||||
auto a = m_objects.readAccessor();
|
||||
SubArrayData* sub = a->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
fb_assert(sub);
|
||||
sub = &sub[id & SUBARRAY_MASK];
|
||||
|
||||
sub->store(nullptr, atomics::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load(MetaId id, HazardPtr<E>& val) const
|
||||
{
|
||||
auto a = m_objects.readAccessor();
|
||||
if (id < getCount(a))
|
||||
{
|
||||
SubArrayData* sub = a->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
if (sub)
|
||||
{
|
||||
val.set(sub[id & SUBARRAY_MASK]);
|
||||
if (val && val->hasData())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HazardPtr<E> load(MetaId id) const
|
||||
{
|
||||
HazardPtr<E> val;
|
||||
if (!load(id, val))
|
||||
val.clear();
|
||||
return val;
|
||||
}
|
||||
|
||||
HazardPtr<typename Storage::Generation> readAccessor() const
|
||||
{
|
||||
return m_objects.readAccessor();
|
||||
}
|
||||
|
||||
class Snapshot;
|
||||
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
HazardPtr<E> operator*()
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
HazardPtr<E> operator->()
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
index = snap->locateData(index + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator& itr) const
|
||||
{
|
||||
fb_assert(snap == itr.snap);
|
||||
return index == itr.index;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& itr) const
|
||||
{
|
||||
fb_assert(snap == itr.snap);
|
||||
return index != itr.index;
|
||||
}
|
||||
|
||||
private:
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
|
||||
public:
|
||||
enum class Location {Begin, End};
|
||||
Iterator(const Snapshot* s, Location loc)
|
||||
: snap(s),
|
||||
index(loc == Location::Begin ? snap->locateData(0) :
|
||||
snap->data->getCount() << SUBARRAY_SHIFT)
|
||||
{ }
|
||||
|
||||
HazardPtr<E> get()
|
||||
{
|
||||
HazardPtr<E> rc;
|
||||
if (!snap->load(index, rc))
|
||||
rc.clear();
|
||||
return rc;
|
||||
}
|
||||
|
||||
private:
|
||||
const Snapshot* snap;
|
||||
FB_SIZE_T index;
|
||||
};
|
||||
|
||||
class Snapshot
|
||||
{
|
||||
private:
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
|
||||
public:
|
||||
Snapshot(const CacheVector* array)
|
||||
: data(array->readAccessor())
|
||||
{ }
|
||||
|
||||
Iterator begin() const
|
||||
{
|
||||
return Iterator(this, Iterator::Location::Begin);
|
||||
}
|
||||
|
||||
Iterator end() const
|
||||
{
|
||||
return Iterator(this, Iterator::Location::End);
|
||||
}
|
||||
|
||||
FB_SIZE_T locateData(FB_SIZE_T index) const
|
||||
{
|
||||
for (FB_SIZE_T i = index >> SUBARRAY_SHIFT; i < data->getCount(); ++i, index = 0)
|
||||
{
|
||||
SubArrayData* const sub = data->value(i).load(atomics::memory_order_acquire);
|
||||
if (!sub)
|
||||
continue;
|
||||
|
||||
for (FB_SIZE_T j = index & SUBARRAY_MASK; j < SUBARRAY_SIZE; ++j)
|
||||
{
|
||||
auto p = sub[j].load(atomics::memory_order_acquire);
|
||||
if (p && p->hasData())
|
||||
return (i << SUBARRAY_SHIFT) + j;
|
||||
}
|
||||
}
|
||||
return data->getCount() << SUBARRAY_SHIFT;
|
||||
}
|
||||
|
||||
bool load(MetaId id, HazardPtr<E>& val) const
|
||||
{
|
||||
if (id < (data->getCount() << SUBARRAY_SHIFT))
|
||||
{
|
||||
SubArrayData* sub = data->value(id >> SUBARRAY_SHIFT).load(atomics::memory_order_acquire);
|
||||
if (sub)
|
||||
{
|
||||
val.set(sub[id & SUBARRAY_MASK]);
|
||||
if (val && val->hasData())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HazardPtr<typename Storage::Generation> data;
|
||||
};
|
||||
|
||||
Snapshot snapshot() const
|
||||
{
|
||||
return Snapshot(this);
|
||||
}
|
||||
|
||||
private:
|
||||
Storage m_objects;
|
||||
Firebird::Mutex objectsGrowMutex;
|
||||
};
|
||||
|
||||
typedef CacheElement<DbTriggers, NullClass> TriggersSet;
|
||||
|
||||
class MetadataCache : public Firebird::PermanentStorage
|
||||
{
|
||||
friend class CharSetContainer;
|
||||
/*
|
||||
class ListNodeAllocator
|
||||
{
|
||||
public:
|
||||
typedef int value_type;
|
||||
|
||||
T* allocate(std::size_t n);
|
||||
void deallocate(T* p, std::size_t n);
|
||||
};
|
||||
|
||||
struct MetaTraits : public cds::container::michael_list::traits
|
||||
{
|
||||
typedef ListNodeAllocator allocator;
|
||||
};
|
||||
|
||||
template <typename C>
|
||||
using MetaList = cds::container::MichaelList<cds::gc::DHP, C, MetaTraits>;
|
||||
*/
|
||||
|
||||
public:
|
||||
typedef CacheVector<CharSetVers, CharSetContainer> Charsets; // intl character set descriptions
|
||||
typedef Charsets::StoredObject Charset; // character set stored in cache vector
|
||||
|
||||
MetadataCache(MemoryPool& pool)
|
||||
: Firebird::PermanentStorage(pool),
|
||||
mdc_relations(getPool()),
|
||||
@ -939,17 +216,14 @@ public:
|
||||
mdc_functions(getPool()),
|
||||
mdc_charsets(getPool()),
|
||||
mdc_charset_ids(getPool())
|
||||
{
|
||||
memset(mdc_triggers, 0, sizeof(mdc_triggers));
|
||||
mdc_ddl_triggers = nullptr;
|
||||
}
|
||||
{ }
|
||||
|
||||
~MetadataCache();
|
||||
|
||||
/*
|
||||
// Objects are placed to this list after DROP OBJECT
|
||||
// and wait for current OAT >= NEXT when DDL committed
|
||||
atomics::atomic<CacheList<ObjectBase>*> dropList;
|
||||
atomics::atomic<Cache List<ObjectBase>*> dropList;
|
||||
{
|
||||
public:
|
||||
void drop(
|
||||
@ -969,7 +243,7 @@ public:
|
||||
jrd_rel* getRelation(thread_db* tdbb, ULONG rel_id);
|
||||
void setRelation(thread_db* tdbb, ULONG rel_id, jrd_rel* rel);
|
||||
void releaseTrigger(thread_db* tdbb, USHORT triggerId, const MetaName& name);
|
||||
TrigVectorPtr* getTriggers(USHORT triggerId);
|
||||
const Triggers* getTriggers(USHORT tType);
|
||||
|
||||
MetaId relCount()
|
||||
{
|
||||
@ -1002,18 +276,8 @@ public:
|
||||
return mdc_procedures.storeObject(tdbb, id, p);
|
||||
}
|
||||
|
||||
CharSetContainer* getCharSet(thread_db* tdbb, MetaId id)
|
||||
{
|
||||
if (id >= mdc_charsets.getCount())
|
||||
return nullptr;
|
||||
|
||||
return mdc_charsets.getObject(tdbb, id, 0);
|
||||
}
|
||||
|
||||
bool makeCharSet(thread_db* tdbb, MetaId id, CharSetContainer* cs)
|
||||
{
|
||||
return mdc_charsets.storeObject(tdbb, id, cs);
|
||||
}
|
||||
CharSetContainer* getCharSet(thread_db* tdbb, MetaId id);
|
||||
bool makeCharSet(thread_db* tdbb, MetaId id, CharSetContainer* cs);
|
||||
|
||||
// former met_proto.h
|
||||
#ifdef DEV_BUILD
|
||||
@ -1023,17 +287,19 @@ public:
|
||||
#endif
|
||||
static void clear_cache(thread_db* tdbb);
|
||||
static void update_partners(thread_db* tdbb);
|
||||
static bool routine_in_use(thread_db* tdbb, HazardPtr<Routine> routine);
|
||||
void load_db_triggers(thread_db* tdbb, int type);
|
||||
void load_ddl_triggers(thread_db* tdbb);
|
||||
void load_db_triggers(thread_db* tdbb, int type, bool force = false);
|
||||
void load_ddl_triggers(thread_db* tdbb, bool force = false);
|
||||
static jrd_prc* lookup_procedure(thread_db* tdbb, const QualifiedName& name, bool noscan);
|
||||
static jrd_prc* lookup_procedure_id(thread_db* tdbb, MetaId id, bool return_deleted, bool noscan, USHORT flags);
|
||||
static CacheElement<jrd_prc>* lookupProcedure(thread_db* tdbb, const MetaName& name);
|
||||
static CacheElement<jrd_prc>* lookupProcedure(thread_db* tdbb, MetaId id, bool noscan = false);
|
||||
static CacheElement<jrd_prc, RoutinePermanent>* lookupProcedure(thread_db* tdbb, const QualifiedName& name);
|
||||
static CacheElement<jrd_prc, RoutinePermanent>* lookupProcedure(thread_db* tdbb, MetaId id, bool noscan = false);
|
||||
static CacheElement<jrd_prc, RoutinePermanent>* lookupFunction(thread_db* tdbb, const QualifiedName& name);
|
||||
static CacheElement<jrd_prc, RoutinePermanent>* lookupFunction(thread_db* tdbb, MetaId id, bool noscan = false);
|
||||
static jrd_rel* lookup_relation(thread_db*, const MetaName&);
|
||||
static jrd_rel* lookup_relation_id(thread_db*, MetaId, bool);
|
||||
static CacheElement<jrd_rel>* lookupRelation(thread_db* tdbb, const MetaName& name);
|
||||
static CacheElement<jrd_rel>* lookupRelation(thread_db* tdbb, MetaId id, bool noscan = false);
|
||||
static jrd_rel* lookup_relation_id(thread_db*, MetaId, bool noscan = false);
|
||||
static CachedRelation* lookupRelation(thread_db* tdbb, const MetaName& name);
|
||||
static CachedRelation* lookupRelation(thread_db* tdbb, MetaId id, bool noscan = false);
|
||||
CachedRelation* lookupRelation(MetaId id);
|
||||
static void lookup_index(thread_db* tdbb, MetaName& index_name, const MetaName& relation_name, USHORT number);
|
||||
static ObjectBase::ReturnedId lookup_index_name(thread_db* tdbb, const MetaName& index_name,
|
||||
MetaId* relation_id, IndexStatus* status);
|
||||
@ -1049,18 +315,32 @@ public:
|
||||
// end of met_proto.h
|
||||
static bool checkRelation(thread_db* tdbb, jrd_rel* relation);
|
||||
|
||||
static Charset* lookupCharset(thread_db* tdbb, USHORT tt_id);
|
||||
|
||||
MdcVersion getVersion()
|
||||
{
|
||||
return mdc_version.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
MdcVersion nextVersion()
|
||||
{
|
||||
return ++mdc_version;
|
||||
}
|
||||
|
||||
private:
|
||||
CacheVector<jrd_rel> mdc_relations;
|
||||
CacheVector<jrd_prc> mdc_procedures;
|
||||
TrigVectorPtr mdc_triggers[DB_TRIGGER_MAX];
|
||||
TrigVectorPtr mdc_ddl_triggers;
|
||||
CacheVector<Function> mdc_functions; // User defined functions
|
||||
CacheVector<CharSetContainer> mdc_charsets; // intl character set descriptions
|
||||
CacheVector<jrd_rel, RelationPermanent> mdc_relations;
|
||||
CacheVector<jrd_prc, RoutinePermanent> mdc_procedures;
|
||||
TriggersSet mdc_triggers[DB_TRIGGER_MAX];
|
||||
TriggersSet mdc_ddl_triggers;
|
||||
CacheVector<Function, RoutinePermanent> mdc_functions; // User defined functions
|
||||
Charsets mdc_charsets; // intl character set descriptions
|
||||
Firebird::GenericMap<Firebird::Pair<Firebird::Left<
|
||||
MetaName, USHORT> > > mdc_charset_ids; // Character set ids
|
||||
|
||||
std::atomic<MdcVersion> mdc_version; // Current version of metadata cache
|
||||
|
||||
public:
|
||||
Firebird::Mutex mdc_db_triggers_mutex, // Also used for load DDL triggers
|
||||
Firebird::Mutex
|
||||
mdc_charset_mutex; // Protects mdc_charset_ids
|
||||
};
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#ifndef JRD_MET_PROTO_H
|
||||
#define JRD_MET_PROTO_H
|
||||
|
||||
#include "../common/classes/array.h"
|
||||
#include "../jrd/MetaName.h"
|
||||
#include "../jrd/HazardPtr.h"
|
||||
|
||||
@ -38,6 +39,7 @@ namespace Jrd
|
||||
class Format;
|
||||
class jrd_rel;
|
||||
class CompilerScratch;
|
||||
struct Dependency;
|
||||
class DmlNode;
|
||||
class Database;
|
||||
struct bid;
|
||||
@ -47,6 +49,10 @@ namespace Jrd
|
||||
class DeferredWork;
|
||||
struct FieldInfo;
|
||||
class ExceptionItem;
|
||||
class GeneratorItem;
|
||||
class BlobFilter;
|
||||
|
||||
class Triggers;
|
||||
}
|
||||
|
||||
struct SubtypeInfo
|
||||
@ -72,7 +78,7 @@ Jrd::Format* MET_current(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
void MET_delete_dependencies(Jrd::thread_db*, const Jrd::MetaName&, int, Jrd::jrd_tra*);
|
||||
void MET_delete_shadow(Jrd::thread_db*, USHORT);
|
||||
void MET_error(const TEXT*, ...);
|
||||
Jrd::Format* MET_format(Jrd::thread_db*, Jrd::jrd_rel*, USHORT);
|
||||
Jrd::Format* MET_format(Jrd::thread_db*, Jrd::RelationPermanent*, USHORT);
|
||||
bool MET_get_char_coll_subtype_info(Jrd::thread_db*, USHORT, SubtypeInfo* info);
|
||||
Jrd::DmlNode* MET_get_dependencies(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR*, const ULONG,
|
||||
Jrd::CompilerScratch*, Jrd::bid*, Jrd::Statement**,
|
||||
@ -83,7 +89,7 @@ ULONG MET_get_rel_flags_from_TYPE(USHORT);
|
||||
bool MET_get_repl_state(Jrd::thread_db*, const Jrd::MetaName&);
|
||||
void MET_get_shadow_files(Jrd::thread_db*, bool);
|
||||
bool MET_load_exception(Jrd::thread_db*, Jrd::ExceptionItem&);
|
||||
void MET_load_trigger(Jrd::thread_db*, Jrd::jrd_rel*, const Jrd::MetaName&, Jrd::TrigVectorPtr*);
|
||||
void MET_load_trigger(Jrd::thread_db*, Jrd::jrd_rel*, const Jrd::MetaName&, Jrd::Triggers&);
|
||||
void MET_lookup_cnstrt_for_index(Jrd::thread_db*, Jrd::MetaName& constraint, const Jrd::MetaName& index_name);
|
||||
void MET_lookup_cnstrt_for_trigger(Jrd::thread_db*, Jrd::MetaName&, Jrd::MetaName&, const Jrd::MetaName&);
|
||||
void MET_lookup_exception(Jrd::thread_db*, SLONG, /* OUT */ Jrd::MetaName&, /* OUT */ Firebird::string*);
|
||||
@ -93,19 +99,20 @@ bool MET_load_generator(Jrd::thread_db*, Jrd::GeneratorItem&, bool* sysGen = 0,
|
||||
SLONG MET_lookup_generator(Jrd::thread_db*, const Jrd::MetaName&, bool* sysGen = 0, SLONG* step = 0);
|
||||
bool MET_lookup_generator_id(Jrd::thread_db*, SLONG, Jrd::MetaName&, bool* sysGen = 0);
|
||||
void MET_update_generator_increment(Jrd::thread_db* tdbb, SLONG gen_id, SLONG step);
|
||||
void MET_lookup_index_condition(Jrd::thread_db* tdbb, Jrd::jrd_rel* relation, Jrd::index_desc* idx);
|
||||
void MET_lookup_index_expression(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*);
|
||||
void MET_lookup_index_expression_blr(Jrd::thread_db*, Jrd::MetaName index_name, Jrd::bid& expr_blob_id);
|
||||
bool MET_lookup_partner(Jrd::thread_db* tdbb, Jrd::jrd_rel* relation, Jrd::index_desc* idx, const TEXT* index_name);
|
||||
Jrd::DmlNode* MET_parse_blob(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::bid*, Jrd::CompilerScratch**,
|
||||
Jrd::Statement**, bool, bool);
|
||||
void MET_parse_sys_trigger(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
void MET_prepare(Jrd::thread_db*, Jrd::jrd_tra*, USHORT, const UCHAR*);
|
||||
void MET_release_existence(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
void MET_release_trigger(Jrd::thread_db*, Jrd::TrigVectorPtr*, const Jrd::MetaName&);
|
||||
void MET_release_triggers(Jrd::thread_db*, Jrd::TrigVectorPtr*, bool);
|
||||
void MET_revoke(Jrd::thread_db*, Jrd::jrd_tra*, const Jrd::MetaName&,
|
||||
const Jrd::MetaName&, const Firebird::string&);
|
||||
void MET_scan_partners(Jrd::thread_db*, Jrd::jrd_rel*);
|
||||
void MET_scan_relation(Jrd::thread_db*, Jrd::jrd_rel*&);
|
||||
void MET_store_dependencies(Jrd::thread_db*, Firebird::Array<Jrd::Dependency>&, Jrd::jrd_rel*,
|
||||
const Jrd::MetaName&, int, Jrd::jrd_tra*);
|
||||
void MET_trigger_msg(Jrd::thread_db*, Firebird::string&, const Jrd::MetaName&, USHORT);
|
||||
void MET_update_shadow(Jrd::thread_db*, Jrd::Shadow*, USHORT);
|
||||
void MET_update_transaction(Jrd::thread_db*, Jrd::jrd_tra*, const bool);
|
||||
|
@ -1022,8 +1022,8 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
|
||||
fb_assert(tail->csb_relation);
|
||||
|
||||
CMP_post_access(tdbb, csb, tail->csb_relation->rel_security_name,
|
||||
tail->csb_view ? tail->csb_view->rel_id : 0,
|
||||
SCL_update, obj_relations, tail->csb_relation->rel_name);
|
||||
tail->csb_view ? tail->csb_view->getId() : 0,
|
||||
SCL_update, obj_relations, tail->csb_relation->getName());
|
||||
}
|
||||
|
||||
rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb, rse->hasSkipLocked());
|
||||
@ -1076,7 +1076,7 @@ void Optimizer::compileRelation(StreamType stream)
|
||||
tail->csb_idx = FB_NEW_POOL(getPool()) IndexDescList(getPool(), idxList);
|
||||
|
||||
if (tail->csb_plan)
|
||||
markIndices(tail, relation->rel_id);
|
||||
markIndices(tail, relation->getId());
|
||||
}
|
||||
|
||||
const auto format = CMP_format(tdbb, csb, stream);
|
||||
@ -1656,7 +1656,7 @@ void Optimizer::checkIndices()
|
||||
((idx.idx_runtime_flags & idx_plan_navigate) && !(idx.idx_runtime_flags & idx_navigate)))
|
||||
{
|
||||
if (relation)
|
||||
MetadataCache::lookup_index(tdbb, index_name, relation->rel_name, (USHORT) (idx.idx_id + 1));
|
||||
MetadataCache::lookup_index(tdbb, index_name, relation->getName(), (USHORT) (idx.idx_id + 1));
|
||||
else
|
||||
index_name = "";
|
||||
|
||||
@ -2707,7 +2707,7 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream,
|
||||
else if (relation->isVirtual())
|
||||
{
|
||||
// Virtual table: monitoring or security
|
||||
switch (relation->rel_id)
|
||||
switch (relation->getId())
|
||||
{
|
||||
case rel_global_auth_mapping:
|
||||
rsb = FB_NEW_POOL(getPool()) GlobalMappingScan(csb, alias, stream, relation);
|
||||
@ -2948,7 +2948,7 @@ string Optimizer::getStreamName(StreamType stream)
|
||||
string name;
|
||||
|
||||
if (relation)
|
||||
name = relation->rel_name.c_str();
|
||||
name = relation->getName().c_str();
|
||||
else if (procedure)
|
||||
name = procedure->getName().toString();
|
||||
|
||||
@ -2983,7 +2983,7 @@ string Optimizer::makeAlias(StreamType stream)
|
||||
if (csb_tail->csb_alias)
|
||||
alias_list.push(*csb_tail->csb_alias);
|
||||
else if (csb_tail->csb_relation)
|
||||
alias_list.push(csb_tail->csb_relation->rel_name.c_str());
|
||||
alias_list.push(csb_tail->csb_relation->getName().c_str());
|
||||
|
||||
if (!csb_tail->csb_view)
|
||||
break;
|
||||
@ -3000,7 +3000,7 @@ string Optimizer::makeAlias(StreamType stream)
|
||||
}
|
||||
}
|
||||
else if (csb_tail->csb_relation)
|
||||
alias = csb_tail->csb_relation->rel_name.c_str();
|
||||
alias = csb_tail->csb_relation->getName().c_str();
|
||||
else if (csb_tail->csb_procedure)
|
||||
alias = csb_tail->csb_procedure->getName().toString();
|
||||
//// TODO: LocalTableSourceNode
|
||||
|
@ -1076,7 +1076,7 @@ InversionNode* Retrieval::makeIndexScanNode(IndexScratch* indexScratch) const
|
||||
// For external requests, determine index name (to be reported in plans)
|
||||
MetaName indexName;
|
||||
if (!(csb->csb_g_flags & csb_internal))
|
||||
MetadataCache::lookup_index(tdbb, indexName, relation->rel_name, idx->idx_id + 1);
|
||||
MetadataCache::lookup_index(tdbb, indexName, relation->getName(), idx->idx_id + 1);
|
||||
|
||||
const auto retrieval =
|
||||
FB_NEW_POOL(getPool()) IndexRetrieval(getPool(), relation, idx, indexName);
|
||||
|
@ -108,14 +108,14 @@ namespace
|
||||
CompilerScratch::csb_repeat* t1 = CMP_csb_element(m_csb, 0);
|
||||
t1->csb_flags |= csb_used | csb_active | csb_trigger;
|
||||
t1->csb_relation = m_csb->csb_resources.registerResource(tdbb, Resource::rsc_relation,
|
||||
relation, relation->rel_id);
|
||||
relation, relation->getId());
|
||||
t1->csb_stream = stream;
|
||||
|
||||
stream = m_csb->nextStream();
|
||||
t1 = CMP_csb_element(m_csb, 1);
|
||||
t1->csb_flags |= csb_used | csb_active | csb_trigger;
|
||||
t1->csb_relation = m_csb->csb_resources.registerResource(tdbb, Resource::rsc_relation,
|
||||
relation, relation->rel_id);
|
||||
relation, relation->getId());
|
||||
t1->csb_stream = stream;
|
||||
}
|
||||
else if (relation)
|
||||
@ -123,7 +123,7 @@ namespace
|
||||
CompilerScratch::csb_repeat* t1 = CMP_csb_element(m_csb, 0);
|
||||
t1->csb_stream = m_csb->nextStream();
|
||||
t1->csb_relation = m_csb->csb_resources.registerResource(tdbb, Resource::rsc_relation,
|
||||
relation, relation->rel_id);
|
||||
relation, relation->getId());
|
||||
t1->csb_flags = csb_used | csb_active;
|
||||
}
|
||||
|
||||
@ -479,7 +479,7 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
|
||||
|
||||
if (csb->collectingDependencies())
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_field);
|
||||
Dependency dependency(obj_field);
|
||||
dependency.name = name;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
@ -544,9 +544,9 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
|
||||
|
||||
if (csb->collectingDependencies())
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_relation);
|
||||
Dependency dependency(obj_relation);
|
||||
jrd_rel* rel = MetadataCache::lookup_relation(tdbb, *relationName);
|
||||
dependency.relation = csb->csb_resources.registerResource(tdbb, Resource::rsc_relation, rel, rel->rel_id);
|
||||
dependency.relation = csb->csb_resources.registerResource(tdbb, Resource::rsc_relation, rel, rel->getId());
|
||||
dependency.subName = fieldName;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
@ -562,7 +562,7 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
|
||||
|
||||
if (csb->collectingDependencies() && desc->getTextType() != CS_NONE)
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_collation);
|
||||
Dependency dependency(obj_collation);
|
||||
dependency.number = INTL_TEXT_TYPE(*desc);
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
@ -609,7 +609,7 @@ ValueExprNode* PAR_make_field(thread_db* tdbb, CompilerScratch* csb, USHORT cont
|
||||
*
|
||||
* Functional description
|
||||
* Make up a field node in the permanent pool. This is used
|
||||
* by MET_scan_relation to handle view fields.
|
||||
* by relation scan to handle view fields.
|
||||
*
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
@ -892,7 +892,7 @@ void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SS
|
||||
if (!csb->collectingDependencies())
|
||||
return;
|
||||
|
||||
CompilerScratch::Dependency dependency(0);
|
||||
Dependency dependency(0);
|
||||
|
||||
if (csb->csb_rpt[stream].csb_relation)
|
||||
{
|
||||
@ -1054,12 +1054,12 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
if (isGbak)
|
||||
{
|
||||
PAR_warning(Arg::Warning(isc_indexname) << Arg::Str(name) <<
|
||||
Arg::Str(relation->rel_name));
|
||||
Arg::Str(relation->getName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
|
||||
Arg::Str(relation->rel_name));
|
||||
Arg::Str(relation->getName()));
|
||||
}
|
||||
}
|
||||
else if (idx_status == MET_object_deferred_active)
|
||||
@ -1067,7 +1067,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
if (!isGbak)
|
||||
{
|
||||
PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
|
||||
Arg::Str(relation->rel_name));
|
||||
Arg::Str(relation->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1082,7 +1082,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
|
||||
if (csb->collectingDependencies())
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_index);
|
||||
Dependency dependency(obj_index);
|
||||
dependency.name = &item.indexName;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
@ -1123,12 +1123,12 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
if (isGbak)
|
||||
{
|
||||
PAR_warning(Arg::Warning(isc_indexname) << Arg::Str(name) <<
|
||||
Arg::Str(relation->rel_name));
|
||||
Arg::Str(relation->getName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
|
||||
Arg::Str(relation->rel_name));
|
||||
Arg::Str(relation->getName()));
|
||||
}
|
||||
}
|
||||
else if (idx_status == MET_object_deferred_active)
|
||||
@ -1136,7 +1136,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
if (!isGbak)
|
||||
{
|
||||
PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
|
||||
Arg::Str(relation->rel_name));
|
||||
Arg::Str(relation->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1151,7 +1151,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
|
||||
if (csb->collectingDependencies())
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_index);
|
||||
Dependency dependency(obj_index);
|
||||
dependency.name = &item.indexName;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
@ -1390,7 +1390,6 @@ RseNode* PAR_rse(thread_db* tdbb, CompilerScratch* csb, SSHORT rse_op)
|
||||
break;
|
||||
|
||||
case blr_writelock:
|
||||
// PAR_parseRecordSource() called RelationSourceNode::parse() => MET_scan_relation().
|
||||
for (FB_SIZE_T iter = 0; iter < rse->rse_relations.getCount(); ++iter)
|
||||
{
|
||||
const RelationSourceNode* subNode = nodeAs<RelationSourceNode>(rse->rse_relations[iter]);
|
||||
@ -1400,11 +1399,11 @@ RseNode* PAR_rse(thread_db* tdbb, CompilerScratch* csb, SSHORT rse_op)
|
||||
const jrd_rel* relation = relNode->relation;
|
||||
fb_assert(relation);
|
||||
if (relation->isVirtual())
|
||||
PAR_error(csb, Arg::Gds(isc_forupdate_virtualtbl) << relation->rel_name, false);
|
||||
PAR_error(csb, Arg::Gds(isc_forupdate_virtualtbl) << relation->getName(), false);
|
||||
if (relation->isSystem())
|
||||
PAR_error(csb, Arg::Gds(isc_forupdate_systbl) << relation->rel_name, false);
|
||||
PAR_error(csb, Arg::Gds(isc_forupdate_systbl) << relation->getName(), false);
|
||||
if (relation->isTemporary())
|
||||
PAR_error(csb, Arg::Gds(isc_forupdate_temptbl) << relation->rel_name, false);
|
||||
PAR_error(csb, Arg::Gds(isc_forupdate_temptbl) << relation->getName(), false);
|
||||
}
|
||||
rse->flags |= RseNode::FLAG_WRITELOCK;
|
||||
break;
|
||||
|
@ -132,7 +132,7 @@ void BitmapTableScan::print(thread_db* tdbb, string& plan,
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||
printName(tdbb, m_relation->getName().c_str(), m_alias) + " Access By ID";
|
||||
|
||||
printOptInfo(plan);
|
||||
printInversion(tdbb, m_inversion, plan, true, level);
|
||||
|
@ -125,7 +125,7 @@ void ExternalTableScan::print(thread_db* tdbb, string& plan,
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||
printName(tdbb, m_relation->getName().c_str(), m_alias) + " Full Scan";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
else
|
||||
|
@ -189,7 +189,7 @@ void FullTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned
|
||||
bounds += " (upper bound)";
|
||||
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds;
|
||||
printName(tdbb, m_relation->getName().c_str(), m_alias) + " Full Scan" + bounds;
|
||||
printOptInfo(plan);
|
||||
}
|
||||
else
|
||||
|
@ -312,7 +312,7 @@ void IndexTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigne
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||
printName(tdbb, m_relation->getName().c_str(), m_alias) + " Access By ID";
|
||||
|
||||
printOptInfo(plan);
|
||||
printInversion(tdbb, m_index, plan, true, level, true);
|
||||
|
@ -361,7 +361,7 @@ namespace Jrd
|
||||
|
||||
public:
|
||||
ProcedureScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream,
|
||||
const jrd_prc* procedure, const ValueListNode* sourceList,
|
||||
const SubRoutine<jrd_prc>& procedure, const ValueListNode* sourceList,
|
||||
const ValueListNode* targetList, MessageNode* message);
|
||||
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
@ -478,7 +478,7 @@ void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const
|
||||
if (!DPM_get(tdbb, &temp, LCK_read))
|
||||
Arg::Gds(isc_no_cur_rec).raise();
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_RPT_READS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_RPT_READS, relation->getId());
|
||||
|
||||
if (VIO_chase_record_version(tdbb, &temp, transaction, tdbb->getDefaultPool(), false, false))
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ void VirtualTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsig
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||
printName(tdbb, m_relation->getName().c_str(), m_alias) + " Full Scan";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
else
|
||||
|
@ -553,9 +553,6 @@ void Applier::insertRecord(thread_db* tdbb, TraNumber traNum,
|
||||
if (!rel)
|
||||
raiseError("Table %s is not found", relName.c_str());
|
||||
|
||||
if (!(rel->rel_flags & REL_scanned))
|
||||
MET_scan_relation(tdbb, rel);
|
||||
|
||||
const auto relation = rel.getPointer();
|
||||
const auto format = findFormat(tdbb, relation, length);
|
||||
|
||||
@ -690,9 +687,6 @@ void Applier::updateRecord(thread_db* tdbb, TraNumber traNum,
|
||||
if (!rel)
|
||||
raiseError("Table %s is not found", relName.c_str());
|
||||
|
||||
if (!(rel->rel_flags & REL_scanned))
|
||||
MET_scan_relation(tdbb, rel);
|
||||
|
||||
const auto relation = rel.getPointer();
|
||||
const auto orgFormat = findFormat(tdbb, relation, orgLength);
|
||||
|
||||
@ -832,9 +826,6 @@ void Applier::deleteRecord(thread_db* tdbb, TraNumber traNum,
|
||||
if (!rel)
|
||||
raiseError("Table %s is not found", relName.c_str());
|
||||
|
||||
if (!(rel->rel_flags & REL_scanned))
|
||||
MET_scan_relation(tdbb, rel);
|
||||
|
||||
const auto relation = rel.getPointer();
|
||||
const auto format = findFormat(tdbb, relation, length);
|
||||
|
||||
@ -1076,7 +1067,7 @@ bool Applier::lookupRecord(thread_db* tdbb,
|
||||
RecordBitmap::reset(m_bitmap);
|
||||
|
||||
// Special case: RDB$DATABASE has no keys but it's guaranteed to have only one record
|
||||
if (relation->rel_id == rel_database)
|
||||
if (relation->getId() == rel_database)
|
||||
{
|
||||
RBM_SET(tdbb->getDefaultPool(), &m_bitmap, 0);
|
||||
return false;
|
||||
@ -1106,7 +1097,7 @@ bool Applier::lookupRecord(thread_db* tdbb,
|
||||
{
|
||||
const auto tab = &NO_KEY_TABLES[i];
|
||||
|
||||
if (tab->rel_id == relation->rel_id)
|
||||
if (tab->rel_id == relation->getId())
|
||||
{
|
||||
table = tab;
|
||||
break;
|
||||
@ -1114,7 +1105,7 @@ bool Applier::lookupRecord(thread_db* tdbb,
|
||||
}
|
||||
|
||||
if (!table)
|
||||
raiseError("Table %s has no unique key", relation->rel_name.c_str());
|
||||
raiseError("Table %s has no unique key", relation->getName().c_str());
|
||||
|
||||
const auto transaction = tdbb->getTransaction();
|
||||
|
||||
@ -1168,7 +1159,7 @@ const Format* Applier::findFormat(thread_db* tdbb, jrd_rel* relation, ULONG leng
|
||||
if (format->fmt_length != length)
|
||||
{
|
||||
raiseError("Record format with length %u is not found for table %s",
|
||||
length, relation->rel_name.c_str());
|
||||
length, relation->getName().c_str());
|
||||
}
|
||||
|
||||
return format;
|
||||
@ -1210,7 +1201,7 @@ void Applier::doInsert(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
fb_assert(blob);
|
||||
blob->blb_sub_type = desc.getBlobSubType();
|
||||
blob->blb_charset = desc.getCharSet();
|
||||
blobId->set_permanent(relation->rel_id, DPM_store_blob(tdbb, blob, relation, record));
|
||||
blobId->set_permanent(relation->getId(), DPM_store_blob(tdbb, blob, relation, record));
|
||||
current->bli_materialized = true;
|
||||
current->bli_blob_id = *blobId;
|
||||
transaction->tra_blobs->fastRemove();
|
||||
@ -1223,7 +1214,7 @@ void Applier::doInsert(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
const ULONG num1 = blobId->bid_quad.bid_quad_high;
|
||||
const ULONG num2 = blobId->bid_quad.bid_quad_low;
|
||||
raiseError("Blob %u.%u is not found for table %s",
|
||||
num1, num2, relation->rel_name.c_str());
|
||||
num1, num2, relation->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1305,7 +1296,7 @@ void Applier::doUpdate(thread_db* tdbb, record_param* orgRpb, record_param* newR
|
||||
fb_assert(blob);
|
||||
blob->blb_sub_type = desc.getBlobSubType();
|
||||
blob->blb_charset = desc.getCharSet();
|
||||
dstBlobId->set_permanent(relation->rel_id, DPM_store_blob(tdbb, blob, relation, newRecord));
|
||||
dstBlobId->set_permanent(relation->getId(), DPM_store_blob(tdbb, blob, relation, newRecord));
|
||||
current->bli_materialized = true;
|
||||
current->bli_blob_id = *dstBlobId;
|
||||
transaction->tra_blobs->fastRemove();
|
||||
@ -1318,7 +1309,7 @@ void Applier::doUpdate(thread_db* tdbb, record_param* orgRpb, record_param* newR
|
||||
const ULONG num1 = dstBlobId->bid_quad.bid_quad_high;
|
||||
const ULONG num2 = dstBlobId->bid_quad.bid_quad_low;
|
||||
raiseError("Blob %u.%u is not found for table %s",
|
||||
num1, num2, relation->rel_name.c_str());
|
||||
num1, num2, relation->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -498,15 +498,15 @@ void REPL_store(thread_db* tdbb, const record_param* rpb, jrd_tra* transaction)
|
||||
const auto relation = rpb->rpb_relation;
|
||||
fb_assert(relation);
|
||||
|
||||
if (relation->isTemporary())
|
||||
if (relation->rel_perm->isTemporary())
|
||||
return;
|
||||
|
||||
if (!relation->isSystem())
|
||||
if (!relation->rel_perm->isSystem())
|
||||
{
|
||||
if (!relation->isReplicating(tdbb))
|
||||
return;
|
||||
|
||||
if (!matchTable(tdbb, relation->rel_name))
|
||||
if (!matchTable(tdbb, relation->getName()))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -525,7 +525,7 @@ void REPL_store(thread_db* tdbb, const record_param* rpb, jrd_tra* transaction)
|
||||
ReplicatedRecordImpl replRecord(tdbb, relation, record);
|
||||
|
||||
replicator->insertRecord(&status,
|
||||
relation->rel_name.c_str(),
|
||||
relation->getName().c_str(),
|
||||
&replRecord);
|
||||
|
||||
checkStatus(tdbb, status, transaction);
|
||||
@ -540,15 +540,15 @@ void REPL_modify(thread_db* tdbb, const record_param* orgRpb,
|
||||
const auto relation = newRpb->rpb_relation;
|
||||
fb_assert(relation);
|
||||
|
||||
if (relation->isTemporary())
|
||||
if (relation->rel_perm->isTemporary())
|
||||
return;
|
||||
|
||||
if (!relation->isSystem())
|
||||
if (!relation->rel_perm->isSystem())
|
||||
{
|
||||
if (!relation->isReplicating(tdbb))
|
||||
return;
|
||||
|
||||
if (!matchTable(tdbb, relation->rel_name))
|
||||
if (!matchTable(tdbb, relation->getName()))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -583,7 +583,7 @@ void REPL_modify(thread_db* tdbb, const record_param* orgRpb,
|
||||
ReplicatedRecordImpl replNewRecord(tdbb, relation, newRecord);
|
||||
|
||||
replicator->updateRecord(&status,
|
||||
relation->rel_name.c_str(),
|
||||
relation->getName().c_str(),
|
||||
&replOrgRecord, &replNewRecord);
|
||||
|
||||
checkStatus(tdbb, status, transaction);
|
||||
@ -598,15 +598,15 @@ void REPL_erase(thread_db* tdbb, const record_param* rpb, jrd_tra* transaction)
|
||||
const auto relation = rpb->rpb_relation;
|
||||
fb_assert(relation);
|
||||
|
||||
if (relation->isTemporary())
|
||||
if (relation->rel_perm->isTemporary())
|
||||
return;
|
||||
|
||||
if (!relation->isSystem())
|
||||
if (!relation->rel_perm->isSystem())
|
||||
{
|
||||
if (!relation->isReplicating(tdbb))
|
||||
return;
|
||||
|
||||
if (!matchTable(tdbb, relation->rel_name))
|
||||
if (!matchTable(tdbb, relation->getName()))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -625,7 +625,7 @@ void REPL_erase(thread_db* tdbb, const record_param* rpb, jrd_tra* transaction)
|
||||
ReplicatedRecordImpl replRecord(tdbb, relation, record);
|
||||
|
||||
replicator->deleteRecord(&status,
|
||||
relation->rel_name.c_str(),
|
||||
relation->getName().c_str(),
|
||||
&replRecord);
|
||||
|
||||
checkStatus(tdbb, status, transaction);
|
||||
|
@ -51,12 +51,13 @@ class Savepoint;
|
||||
class Cursor;
|
||||
class thread_db;
|
||||
|
||||
|
||||
// record parameter block
|
||||
|
||||
struct record_param
|
||||
struct RecordParameterBase
|
||||
{
|
||||
record_param()
|
||||
: rpb_transaction_nr(0), rpb_relation(0), rpb_record(NULL), rpb_prior(NULL),
|
||||
RecordParameterBase()
|
||||
: rpb_transaction_nr(0), rpb_record(NULL), rpb_prior(NULL),
|
||||
rpb_undo(NULL), rpb_format_number(0),
|
||||
rpb_page(0), rpb_line(0),
|
||||
rpb_f_page(0), rpb_f_line(0),
|
||||
@ -69,11 +70,10 @@ struct record_param
|
||||
|
||||
RecordNumber rpb_number; // record number in relation
|
||||
TraNumber rpb_transaction_nr; // transaction number
|
||||
jrd_rel* rpb_relation; // relation of record
|
||||
Record* rpb_record; // final record block
|
||||
Record* rpb_prior; // prior record block if this is a delta record
|
||||
Record* rpb_undo; // our first version of data if this is a second modification
|
||||
USHORT rpb_format_number; // format number in relation
|
||||
USHORT rpb_format_number; // format number in relation
|
||||
|
||||
ULONG rpb_page; // page number
|
||||
USHORT rpb_line; // line number on page
|
||||
@ -91,17 +91,37 @@ struct record_param
|
||||
USHORT rpb_runtime_flags; // runtime flags
|
||||
SSHORT rpb_org_scans; // relation scan count at stream open
|
||||
|
||||
protected:
|
||||
struct win rpb_window;
|
||||
};
|
||||
|
||||
struct record_param : public RecordParameterBase
|
||||
{
|
||||
record_param()
|
||||
: RecordParameterBase(), rpb_relation(nullptr)
|
||||
{ }
|
||||
|
||||
inline WIN& getWindow(thread_db* tdbb)
|
||||
{
|
||||
if (rpb_relation) {
|
||||
rpb_window.win_page.setPageSpaceID(rpb_relation->getPages(tdbb)->rel_pg_space_id);
|
||||
rpb_window.win_page.setPageSpaceID(rpb_relation->rel_perm->getPages(tdbb)->rel_pg_space_id);
|
||||
}
|
||||
|
||||
return rpb_window;
|
||||
}
|
||||
|
||||
private:
|
||||
struct win rpb_window;
|
||||
jrd_rel* rpb_relation; // relation of record
|
||||
};
|
||||
|
||||
struct RecordParameter : public RecordParameterBase
|
||||
{
|
||||
RecordParameter()
|
||||
: RecordParameterBase(), rpb_relation()
|
||||
{ }
|
||||
|
||||
WIN& getWindow(thread_db* tdbb); // in Statement.cpp
|
||||
|
||||
Rsc::Rel rpb_relation; // relation of record
|
||||
};
|
||||
|
||||
// Record flags must be an exact replica of ODS record header flags
|
||||
@ -162,38 +182,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// Set of objects cached per particular MDC version
|
||||
|
||||
class CacheObject;
|
||||
|
||||
class VersionedObjects : public pool_alloc_rpt<CacheObject*>,
|
||||
public Firebird::RefCounted
|
||||
{
|
||||
public:
|
||||
VersionedObjects(FB_SIZE_T cnt, MdcVersion ver);
|
||||
|
||||
FB_SIZE_T push(CacheObject* obj);
|
||||
|
||||
template <class OBJ>
|
||||
OBJ* get(FB_SIZE_T n)
|
||||
{
|
||||
fb_assert(count == capacity);
|
||||
fb_assert(n < count);
|
||||
// ????? fb_assert(dynamic_cast<OBJ*>(data[n]));
|
||||
return reinterpret_cast<OBJ*>(data[n]);
|
||||
}
|
||||
|
||||
MdcVersion version; // version when created
|
||||
|
||||
private:
|
||||
FB_SIZE_T count;
|
||||
#ifdef DEV_BUILD
|
||||
FB_SIZE_T capacity;
|
||||
#endif
|
||||
CacheObject* data[1];
|
||||
};
|
||||
|
||||
|
||||
// request block
|
||||
|
||||
class Request : public pool_alloc<type_req>
|
||||
@ -434,6 +422,7 @@ public:
|
||||
SnapshotData req_snapshot;
|
||||
StatusXcp req_last_xcp; // last known exception
|
||||
bool req_batch_mode;
|
||||
Firebird::RefPtr<VersionedObjects> resources;
|
||||
|
||||
enum req_s {
|
||||
req_evaluate,
|
||||
@ -445,6 +434,7 @@ public:
|
||||
req_unwind
|
||||
} req_operation; // operation for next node
|
||||
|
||||
|
||||
template <typename T> T* getImpure(unsigned offset)
|
||||
{
|
||||
return reinterpret_cast<T*>(&impureArea[offset]);
|
||||
@ -516,9 +506,6 @@ public:
|
||||
{
|
||||
req_timeStampCache.validate(req_attachment->att_current_timezone);
|
||||
}
|
||||
|
||||
private:
|
||||
Firebird::RefPtr<VersionedObjects> currentVersion;
|
||||
};
|
||||
|
||||
// Flags for req_flags
|
||||
|
@ -81,7 +81,7 @@ Lock* RLCK_reserve_relation(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rela
|
||||
!(tdbb->tdbb_flags & TDBB_repl_in_progress))
|
||||
{
|
||||
// This condition is a workaround for nbackup
|
||||
if (relation->rel_id != rel_backup_history)
|
||||
if (relation->getId() != rel_backup_history)
|
||||
ERR_post(Arg::Gds(isc_read_only_trans));
|
||||
}
|
||||
}
|
||||
@ -123,7 +123,7 @@ Lock* RLCK_reserve_relation(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rela
|
||||
if (!result)
|
||||
{
|
||||
string err;
|
||||
err.printf("Acquire lock for relation (%s) failed", relation->rel_name.c_str());
|
||||
err.printf("Acquire lock for relation (%s) failed", relation->getName().c_str());
|
||||
|
||||
ERR_append_status(tdbb->tdbb_status_vector, Arg::Gds(isc_random) << Arg::Str(err));
|
||||
ERR_punt();
|
||||
@ -148,7 +148,7 @@ Lock* RLCK_transaction_relation_lock(thread_db* tdbb, jrd_tra* transaction, jrd_
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
const ULONG relId = relation->rel_id;
|
||||
const ULONG relId = relation->getId();
|
||||
|
||||
Lock* lock;
|
||||
vec<Lock*>* vector = transaction->tra_relation_locks;
|
||||
|
@ -47,7 +47,7 @@ int traRpbList::PushRpb(record_param* value)
|
||||
if (pos-- > 0)
|
||||
{
|
||||
traRpbListElement& prev = (*this)[pos];
|
||||
if (prev.lr_rpb->rpb_relation->rel_id == value->rpb_relation->rel_id &&
|
||||
if (prev.lr_rpb->rpb_relation->getId() == value->rpb_relation->getId() &&
|
||||
prev.lr_rpb->rpb_number == value->rpb_number)
|
||||
{
|
||||
// we got the same record once more - mark for refetch
|
||||
|
@ -46,8 +46,8 @@ public:
|
||||
|
||||
static inline bool greaterThan(const traRpbListElement& i1, const traRpbListElement& i2)
|
||||
{
|
||||
return i1.lr_rpb->rpb_relation->rel_id != i2.lr_rpb->rpb_relation->rel_id ?
|
||||
i1.lr_rpb->rpb_relation->rel_id > i2.lr_rpb->rpb_relation->rel_id :
|
||||
return i1.lr_rpb->rpb_relation->rel_perm->rel_id != i2.lr_rpb->rpb_relation->rel_perm->rel_id ?
|
||||
i1.lr_rpb->rpb_relation->rel_perm->rel_id > i2.lr_rpb->rpb_relation->rel_perm->rel_id :
|
||||
i1.lr_rpb->rpb_number != i2.lr_rpb->rpb_number ?
|
||||
i1.lr_rpb->rpb_number > i2.lr_rpb->rpb_number :
|
||||
i1.level > i2.level;
|
||||
|
@ -910,7 +910,6 @@ SecurityClass::flags_t SCL_get_mask(thread_db* tdbb, const TEXT* relation_name,
|
||||
jrd_rel* relation;
|
||||
if (relation_name && (relation = MetadataCache::lookup_relation(tdbb, relation_name)))
|
||||
{
|
||||
MET_scan_relation(tdbb, relation);
|
||||
const SecurityClass* s_class;
|
||||
if ( (s_class = SCL_get_class(tdbb, relation->rel_security_name.c_str())) )
|
||||
{
|
||||
|
511
src/jrd/tdbb.h
Normal file
511
src/jrd/tdbb.h
Normal file
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* PROGRAM: JRD access method
|
||||
* MODULE: tdbb.h
|
||||
* DESCRIPTION: Thread specific database block
|
||||
*
|
||||
* 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): ______________________________________.
|
||||
*
|
||||
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
|
||||
*
|
||||
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
|
||||
* Claudio Valderrama C.
|
||||
* Adriano dos Santos Fernandes
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef JRD_TDBB_H
|
||||
#define JRD_TDBB_H
|
||||
|
||||
#include <cds/threading/model.h> // cds::threading::Manager
|
||||
|
||||
#include "../common/gdsassert.h"
|
||||
|
||||
#include "../common/classes/Synchronize.h"
|
||||
|
||||
#include "../jrd/RuntimeStatistics.h"
|
||||
#include "../jrd/status.h"
|
||||
#include "../jrd/err_proto.h"
|
||||
|
||||
#define BUGCHECK(number) ERR_bugcheck(number, __FILE__, __LINE__)
|
||||
|
||||
|
||||
namespace Firebird {
|
||||
|
||||
class MemoryPool;
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
|
||||
class Database;
|
||||
class Attachment;
|
||||
class jrd_tra;
|
||||
class Request;
|
||||
class BufferDesc;
|
||||
class Lock;
|
||||
|
||||
class NullClass
|
||||
{
|
||||
public:
|
||||
NullClass(MemoryPool&, MetaId, Lock*) { }
|
||||
NullClass() { }
|
||||
};
|
||||
template <class OBJ, class EXT> class CacheElement;
|
||||
|
||||
#ifdef USE_ITIMER
|
||||
class TimeoutTimer final :
|
||||
public Firebird::RefCntIface<Firebird::ITimerImpl<TimeoutTimer, Firebird::CheckStatusWrapper> >
|
||||
{
|
||||
public:
|
||||
explicit TimeoutTimer()
|
||||
: m_started(0),
|
||||
m_expired(false),
|
||||
m_value(0),
|
||||
m_error(0)
|
||||
{ }
|
||||
|
||||
// ITimer implementation
|
||||
void handler();
|
||||
|
||||
bool expired() const
|
||||
{
|
||||
return m_expired;
|
||||
}
|
||||
|
||||
unsigned int getValue() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
unsigned int getErrCode() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
// milliseconds left before timer expiration
|
||||
unsigned int timeToExpire() const;
|
||||
|
||||
// evaluate expire timestamp using start timestamp
|
||||
bool getExpireTimestamp(const ISC_TIMESTAMP_TZ start, ISC_TIMESTAMP_TZ& exp) const;
|
||||
|
||||
// set timeout value in milliseconds and secondary error code
|
||||
void setup(unsigned int value, ISC_STATUS error)
|
||||
{
|
||||
m_value = value;
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
SINT64 m_started;
|
||||
bool m_expired;
|
||||
unsigned int m_value; // milliseconds
|
||||
ISC_STATUS m_error;
|
||||
};
|
||||
#else
|
||||
class TimeoutTimer : public Firebird::RefCounted
|
||||
{
|
||||
public:
|
||||
explicit TimeoutTimer()
|
||||
: m_start(0),
|
||||
m_value(0),
|
||||
m_error(0)
|
||||
{ }
|
||||
|
||||
bool expired() const;
|
||||
|
||||
unsigned int getValue() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
unsigned int getErrCode() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
// milliseconds left before timer expiration
|
||||
unsigned int timeToExpire() const;
|
||||
|
||||
// clock value when timer will expire
|
||||
bool getExpireClock(SINT64& clock) const;
|
||||
|
||||
// set timeout value in milliseconds and secondary error code
|
||||
void setup(unsigned int value, ISC_STATUS error)
|
||||
{
|
||||
m_start = 0;
|
||||
m_value = value;
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
SINT64 currTime() const
|
||||
{
|
||||
return fb_utils::query_performance_counter() * 1000 / fb_utils::query_performance_frequency();
|
||||
}
|
||||
|
||||
SINT64 m_start;
|
||||
unsigned int m_value; // milliseconds
|
||||
ISC_STATUS m_error;
|
||||
};
|
||||
#endif // USE_ITIMER
|
||||
|
||||
|
||||
// tdbb_flags
|
||||
|
||||
const ULONG TDBB_sweeper = 1; // Thread sweeper or garbage collector
|
||||
const ULONG TDBB_no_cache_unwind = 2; // Don't unwind page buffer cache
|
||||
const ULONG TDBB_backup_write_locked = 4; // BackupManager has write lock on LCK_backup_database
|
||||
const ULONG TDBB_stack_trace_done = 8; // PSQL stack trace is added into status-vector
|
||||
const ULONG TDBB_dont_post_dfw = 16; // dont post DFW tasks as deferred work is performed now
|
||||
const ULONG TDBB_sys_error = 32; // error shouldn't be handled by the looper
|
||||
const ULONG TDBB_verb_cleanup = 64; // verb cleanup is in progress
|
||||
const ULONG TDBB_use_db_page_space = 128; // use database (not temporary) page space in GTT operations
|
||||
const ULONG TDBB_detaching = 256; // detach is in progress
|
||||
const ULONG TDBB_wait_cancel_disable = 512; // don't cancel current waiting operation
|
||||
const ULONG TDBB_cache_unwound = 1024; // page cache was unwound
|
||||
const ULONG TDBB_reset_stack = 2048; // stack should be reset after stack overflow exception
|
||||
const ULONG TDBB_dfw_cleanup = 4096; // DFW cleanup phase is active
|
||||
const ULONG TDBB_repl_in_progress = 8192; // Prevent recursion in replication
|
||||
const ULONG TDBB_replicator = 16384; // Replicator
|
||||
|
||||
class thread_db : public Firebird::ThreadData
|
||||
{
|
||||
const static int QUANTUM = 100; // Default quantum
|
||||
const static int SWEEP_QUANTUM = 10; // Make sweeps less disruptive
|
||||
|
||||
private:
|
||||
MemoryPool* defaultPool;
|
||||
void setDefaultPool(MemoryPool* p)
|
||||
{
|
||||
defaultPool = p;
|
||||
}
|
||||
friend class Firebird::SubsystemContextPoolHolder <Jrd::thread_db, MemoryPool>;
|
||||
Database* database;
|
||||
Attachment* attachment;
|
||||
jrd_tra* transaction;
|
||||
Request* request;
|
||||
RuntimeStatistics *reqStat, *traStat, *attStat, *dbbStat;
|
||||
|
||||
public:
|
||||
explicit thread_db(FbStatusVector* status)
|
||||
: ThreadData(ThreadData::tddDBB),
|
||||
defaultPool(NULL),
|
||||
database(NULL),
|
||||
attachment(NULL),
|
||||
transaction(NULL),
|
||||
request(NULL),
|
||||
tdbb_status_vector(status),
|
||||
tdbb_quantum(QUANTUM),
|
||||
tdbb_flags(0),
|
||||
tdbb_temp_traid(0),
|
||||
tdbb_bdbs(*getDefaultMemoryPool()),
|
||||
tdbb_thread(Firebird::ThreadSync::getThread("thread_db"))
|
||||
{
|
||||
reqStat = traStat = attStat = dbbStat = RuntimeStatistics::getDummy();
|
||||
fb_utils::init_status(tdbb_status_vector);
|
||||
}
|
||||
|
||||
~thread_db()
|
||||
{
|
||||
resetStack();
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
for (FB_SIZE_T n = 0; n < tdbb_bdbs.getCount(); ++n)
|
||||
{
|
||||
fb_assert(tdbb_bdbs[n] == NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
FbStatusVector* tdbb_status_vector;
|
||||
SLONG tdbb_quantum; // Cycles remaining until voluntary schedule
|
||||
ULONG tdbb_flags;
|
||||
|
||||
TraNumber tdbb_temp_traid; // current temporary table scope
|
||||
|
||||
// BDB's held by thread
|
||||
Firebird::HalfStaticArray<BufferDesc*, 16> tdbb_bdbs;
|
||||
Firebird::ThreadSync* tdbb_thread;
|
||||
|
||||
MemoryPool* getDefaultPool()
|
||||
{
|
||||
return defaultPool;
|
||||
}
|
||||
|
||||
Database* getDatabase()
|
||||
{
|
||||
return database;
|
||||
}
|
||||
|
||||
const Database* getDatabase() const
|
||||
{
|
||||
return database;
|
||||
}
|
||||
|
||||
void setDatabase(Database* val);
|
||||
|
||||
Attachment* getAttachment()
|
||||
{
|
||||
return attachment;
|
||||
}
|
||||
|
||||
const Attachment* getAttachment() const
|
||||
{
|
||||
return attachment;
|
||||
}
|
||||
|
||||
void setAttachment(Attachment* val);
|
||||
|
||||
jrd_tra* getTransaction()
|
||||
{
|
||||
return transaction;
|
||||
}
|
||||
|
||||
const jrd_tra* getTransaction() const
|
||||
{
|
||||
return transaction;
|
||||
}
|
||||
|
||||
void setTransaction(jrd_tra* val);
|
||||
|
||||
Request* getRequest()
|
||||
{
|
||||
return request;
|
||||
}
|
||||
|
||||
const Request* getRequest() const
|
||||
{
|
||||
return request;
|
||||
}
|
||||
|
||||
void setRequest(Request* val);
|
||||
|
||||
SSHORT getCharSet() const;
|
||||
|
||||
void markAsSweeper()
|
||||
{
|
||||
tdbb_quantum = SWEEP_QUANTUM;
|
||||
tdbb_flags |= TDBB_sweeper;
|
||||
}
|
||||
|
||||
void bumpStats(const RuntimeStatistics::StatType index, SINT64 delta = 1)
|
||||
{
|
||||
reqStat->bumpValue(index, delta);
|
||||
traStat->bumpValue(index, delta);
|
||||
attStat->bumpValue(index, delta);
|
||||
dbbStat->bumpValue(index, delta);
|
||||
}
|
||||
|
||||
void bumpRelStats(const RuntimeStatistics::StatType index, SLONG relation_id, SINT64 delta = 1)
|
||||
{
|
||||
// We don't bump counters for dbbStat here, they're merged from attStats on demand
|
||||
|
||||
reqStat->bumpValue(index, delta);
|
||||
traStat->bumpValue(index, delta);
|
||||
attStat->bumpValue(index, delta);
|
||||
|
||||
const RuntimeStatistics* const dummyStat = RuntimeStatistics::getDummy();
|
||||
|
||||
// We expect that at least attStat is present (not a dummy object)
|
||||
|
||||
fb_assert(attStat != dummyStat);
|
||||
|
||||
// Relation statistics is a quite complex beast, so a conditional check
|
||||
// does not hurt. It also allows to avoid races while accessing the static
|
||||
// dummy object concurrently.
|
||||
|
||||
if (reqStat != dummyStat)
|
||||
reqStat->bumpRelValue(index, relation_id, delta);
|
||||
|
||||
if (traStat != dummyStat)
|
||||
traStat->bumpRelValue(index, relation_id, delta);
|
||||
|
||||
if (attStat != dummyStat)
|
||||
attStat->bumpRelValue(index, relation_id, delta);
|
||||
}
|
||||
|
||||
ISC_STATUS getCancelState(ISC_STATUS* secondary = NULL);
|
||||
void checkCancelState();
|
||||
void reschedule();
|
||||
const TimeoutTimer* getTimeoutTimer() const
|
||||
{
|
||||
return tdbb_reqTimer;
|
||||
}
|
||||
|
||||
// Returns minimum of passed wait timeout and time to expiration of reqTimer.
|
||||
// Timer value is rounded to the upper whole second.
|
||||
ULONG adjustWait(ULONG wait) const;
|
||||
|
||||
void registerBdb(BufferDesc* bdb)
|
||||
{
|
||||
if (tdbb_bdbs.isEmpty()) {
|
||||
tdbb_flags &= ~TDBB_cache_unwound;
|
||||
}
|
||||
fb_assert(!(tdbb_flags & TDBB_cache_unwound));
|
||||
|
||||
FB_SIZE_T pos;
|
||||
if (tdbb_bdbs.find(NULL, pos))
|
||||
tdbb_bdbs[pos] = bdb;
|
||||
else
|
||||
tdbb_bdbs.add(bdb);
|
||||
}
|
||||
|
||||
bool clearBdb(BufferDesc* bdb)
|
||||
{
|
||||
if (tdbb_bdbs.isEmpty())
|
||||
{
|
||||
// hvlad: the only legal case when thread holds no latches but someone
|
||||
// tried to release latch is when CCH_unwind was called (and released
|
||||
// all latches) but caller is unaware about it. See CORE-3034, for example.
|
||||
// Else it is bug and should be BUGCHECK'ed.
|
||||
|
||||
if (tdbb_flags & TDBB_cache_unwound)
|
||||
return false;
|
||||
}
|
||||
fb_assert(!(tdbb_flags & TDBB_cache_unwound));
|
||||
|
||||
FB_SIZE_T pos;
|
||||
if (!tdbb_bdbs.find(bdb, pos))
|
||||
BUGCHECK(300); // can't find shared latch
|
||||
|
||||
tdbb_bdbs[pos] = NULL;
|
||||
|
||||
if (pos == tdbb_bdbs.getCount() - 1)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (tdbb_bdbs[pos] != NULL)
|
||||
{
|
||||
tdbb_bdbs.shrink(pos + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos == 0)
|
||||
{
|
||||
tdbb_bdbs.shrink(0);
|
||||
break;
|
||||
}
|
||||
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void resetStack()
|
||||
{
|
||||
if (tdbb_flags & TDBB_reset_stack)
|
||||
{
|
||||
tdbb_flags &= ~TDBB_reset_stack;
|
||||
#ifdef WIN_NT
|
||||
_resetstkoflw();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
class TimerGuard
|
||||
{
|
||||
public:
|
||||
TimerGuard(thread_db* tdbb, TimeoutTimer* timer, bool autoStop)
|
||||
: m_tdbb(tdbb),
|
||||
m_autoStop(autoStop && timer),
|
||||
m_saveTimer(tdbb->tdbb_reqTimer)
|
||||
{
|
||||
m_tdbb->tdbb_reqTimer = timer;
|
||||
if (timer && timer->expired())
|
||||
m_tdbb->tdbb_quantum = 0;
|
||||
}
|
||||
|
||||
~TimerGuard()
|
||||
{
|
||||
if (m_autoStop)
|
||||
m_tdbb->tdbb_reqTimer->stop();
|
||||
|
||||
m_tdbb->tdbb_reqTimer = m_saveTimer;
|
||||
}
|
||||
|
||||
private:
|
||||
thread_db* m_tdbb;
|
||||
bool m_autoStop;
|
||||
Firebird::RefPtr<TimeoutTimer> m_saveTimer;
|
||||
};
|
||||
|
||||
private:
|
||||
Firebird::RefPtr<TimeoutTimer> tdbb_reqTimer;
|
||||
|
||||
};
|
||||
|
||||
class ThreadContextHolder
|
||||
{
|
||||
public:
|
||||
explicit ThreadContextHolder(Firebird::CheckStatusWrapper* status = NULL)
|
||||
: context(status ? status : &localStatus)
|
||||
{
|
||||
context.putSpecific();
|
||||
|
||||
if (!cds::threading::Manager::isThreadAttached())
|
||||
cds::threading::Manager::attachThread();
|
||||
}
|
||||
|
||||
ThreadContextHolder(Database* dbb, Jrd::Attachment* att, FbStatusVector* status = NULL)
|
||||
: context(status ? status : &localStatus)
|
||||
{
|
||||
context.putSpecific();
|
||||
context.setDatabase(dbb);
|
||||
context.setAttachment(att);
|
||||
|
||||
if (!cds::threading::Manager::isThreadAttached())
|
||||
cds::threading::Manager::attachThread();
|
||||
}
|
||||
|
||||
~ThreadContextHolder()
|
||||
{
|
||||
Firebird::ThreadData::restoreSpecific();
|
||||
}
|
||||
|
||||
thread_db* operator->()
|
||||
{
|
||||
return &context;
|
||||
}
|
||||
|
||||
operator thread_db*()
|
||||
{
|
||||
return &context;
|
||||
}
|
||||
|
||||
private:
|
||||
// copying is prohibited
|
||||
ThreadContextHolder(const ThreadContextHolder&);
|
||||
ThreadContextHolder& operator= (const ThreadContextHolder&);
|
||||
|
||||
Firebird::FbLocalStatus localStatus;
|
||||
thread_db context;
|
||||
};
|
||||
|
||||
} // namespace Jrd
|
||||
|
||||
#endif // JRD_TDBB_H
|
@ -2179,11 +2179,11 @@ static void expand_view_lock(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rel
|
||||
Arg::Gds(isc_tpb_reserv_max_recursion) << Arg::Num(30));
|
||||
}
|
||||
|
||||
const char* const relation_name = relation->rel_name.c_str();
|
||||
const char* const relation_name = relation->getName().c_str();
|
||||
|
||||
// LCK_none < LCK_SR < LCK_PR < LCK_SW < LCK_EX
|
||||
UCHAR oldlock;
|
||||
const bool found = lockmap.get(relation->rel_id, oldlock);
|
||||
const bool found = lockmap.get(relation->getId(), oldlock);
|
||||
|
||||
if (found && oldlock > lock_type)
|
||||
{
|
||||
@ -2258,7 +2258,7 @@ static void expand_view_lock(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rel
|
||||
lock->lck_logical = lock_type;
|
||||
|
||||
if (!found)
|
||||
*lockmap.put(relation->rel_id) = lock_type;
|
||||
*lockmap.put(relation->getId()) = lock_type;
|
||||
|
||||
const ViewContexts& ctx = relation->rel_view_contexts;
|
||||
|
||||
@ -2277,9 +2277,6 @@ static void expand_view_lock(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rel
|
||||
Arg::Str(option_name));
|
||||
}
|
||||
|
||||
// force a scan to read view information
|
||||
MET_scan_relation(tdbb, base_rel);
|
||||
|
||||
expand_view_lock(tdbb, transaction, base_rel, lock_type, option_name, lockmap, level + 1);
|
||||
}
|
||||
}
|
||||
@ -3202,9 +3199,6 @@ static void transaction_options(thread_db* tdbb,
|
||||
Arg::Str(option_name));
|
||||
}
|
||||
|
||||
// force a scan to read view information
|
||||
MET_scan_relation(tdbb, relation);
|
||||
|
||||
UCHAR lock_type = (op == isc_tpb_lock_read) ? LCK_none : LCK_SW;
|
||||
if (tpb < end)
|
||||
{
|
||||
@ -4065,11 +4059,7 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool
|
||||
{
|
||||
const MetaName security_name = (fld && fld->fld_security_name.hasData()) ?
|
||||
fld->fld_security_name : blobRelation->rel_security_name;
|
||||
if (security_name.isEmpty())
|
||||
{
|
||||
MET_scan_relation(tdbb, blobRelation);
|
||||
security_name = blb_relation->rel_security_name;
|
||||
}
|
||||
fb_assert(security_name.hasData());
|
||||
|
||||
SecurityClass* s_class = SCL_get_class(tdbb, security_name.c_str());
|
||||
if (!s_class)
|
||||
@ -4086,12 +4076,12 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool
|
||||
if (fld)
|
||||
{
|
||||
SCL_check_access(tdbb, s_class, 0, 0, SCL_select, obj_column,
|
||||
false, fld->fld_name, blobRelation->rel_name);
|
||||
false, fld->fld_name, blobRelation->getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
SCL_check_access(tdbb, s_class, 0, 0, SCL_select, obj_relations,
|
||||
false, blobRelation->rel_name);
|
||||
false, blobRelation->getName());
|
||||
}
|
||||
|
||||
s_class->scl_blb_access = SecurityClass::BA_SUCCESS;
|
||||
@ -4121,7 +4111,7 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("SELECT") <<
|
||||
(fld ? Arg::Str("COLUMN") : Arg::Str("TABLE")) <<
|
||||
(fld ? Arg::Str(fld->fld_name) : Arg::Str(blobRelation->rel_name)));
|
||||
(fld ? Arg::Str(fld->fld_name) : Arg::Str(blobRelation->getName())));
|
||||
}
|
||||
else
|
||||
tra_fetched_blobs.add(*blob_id);
|
||||
@ -4187,10 +4177,10 @@ void TraceSweepEvent::beginSweepRelation(const jrd_rel* relation)
|
||||
if (!m_need_trace)
|
||||
return;
|
||||
|
||||
if (relation && relation->rel_name.isEmpty())
|
||||
if (relation && relation->getName().isEmpty())
|
||||
{
|
||||
// don't accumulate per-relation stats for metadata query below
|
||||
MetadataCache::lookup_relation_id(m_tdbb, relation->rel_id, false);
|
||||
MetadataCache::lookup_relation_id(m_tdbb, relation->getId(), false);
|
||||
}
|
||||
|
||||
m_relation_clock = fb_utils::query_performance_counter();
|
||||
|
@ -67,6 +67,7 @@ class UserManagement;
|
||||
class MappingList;
|
||||
class DbCreatorsList;
|
||||
class thread_db;
|
||||
class Resources;
|
||||
|
||||
class SecDbContext
|
||||
{
|
||||
@ -404,6 +405,8 @@ public:
|
||||
|
||||
return tra_gen_ids;
|
||||
}
|
||||
|
||||
void postResources(thread_db* tdbb, const Resources* resources);
|
||||
};
|
||||
|
||||
// System transaction is always transaction 0.
|
||||
|
@ -499,7 +499,7 @@ const char* TraceTriggerImpl::getRelationName()
|
||||
return NULL;
|
||||
|
||||
const jrd_rel* rel = m_trig->req_rpb[0].rpb_relation;
|
||||
return rel ? rel->rel_name.c_str() : NULL;
|
||||
return rel ? rel->getName().c_str() : NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1134,7 +1134,7 @@ Validation::RTN Validation::corrupt(int err_code, const jrd_rel* relation, ...)
|
||||
if (relation)
|
||||
{
|
||||
fprintf(stdout, "LOG:\tDatabase: %s\n\t%s in table %s (%d)\n",
|
||||
fn, s.c_str(), relation->rel_name.c_str(), relation->rel_id);
|
||||
fn, s.c_str(), relation->getName().c_str(), relation->getId());
|
||||
}
|
||||
else
|
||||
fprintf(stdout, "LOG:\tDatabase: %s\n\t%s\n", fn, s.c_str());
|
||||
@ -1154,7 +1154,7 @@ Validation::RTN Validation::corrupt(int err_code, const jrd_rel* relation, ...)
|
||||
if (relation)
|
||||
{
|
||||
gds__log("Database: %s\n\t%s in table %s (%d)",
|
||||
fn, s.c_str(), relation->rel_name.c_str(), relation->rel_id);
|
||||
fn, s.c_str(), relation->getName().c_str(), relation->getId());
|
||||
}
|
||||
else
|
||||
gds__log("Database: %s\n\t%s", fn, s.c_str());
|
||||
@ -1558,7 +1558,7 @@ Validation::RTN Validation::walk_chain(jrd_rel* relation, const rhd* header,
|
||||
data_page* page = 0;
|
||||
fetch_page(true, page_number, pag_data, &window, &page);
|
||||
|
||||
if (page->dpg_relation != relation->rel_id)
|
||||
if (page->dpg_relation != relation->getId())
|
||||
{
|
||||
release_page(&window);
|
||||
return corrupt(VAL_DATA_PAGE_CONFUSED, relation, page_number, page->dpg_sequence);
|
||||
@ -1647,13 +1647,13 @@ void Validation::walk_database()
|
||||
|
||||
if (vdr_tab_incl)
|
||||
{
|
||||
if (!vdr_tab_incl->matches(relation->rel_name.c_str(), relation->rel_name.length()))
|
||||
if (!vdr_tab_incl->matches(relation->getName().c_str(), relation->getName().length()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vdr_tab_excl)
|
||||
{
|
||||
if (vdr_tab_excl->matches(relation->rel_name.c_str(), relation->rel_name.length()))
|
||||
if (vdr_tab_excl->matches(relation->getName().c_str(), relation->getName().length()))
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1663,7 +1663,7 @@ void Validation::walk_database()
|
||||
vdr_page_bitmap->clear();
|
||||
|
||||
string relName;
|
||||
relName.printf("Relation %d (%s)", relation->rel_id, relation->rel_name.c_str());
|
||||
relName.printf("Relation %d (%s)", relation->getId(), relation->getName().c_str());
|
||||
output("%s\n", relName.c_str());
|
||||
|
||||
int errs = vdr_errors;
|
||||
@ -1713,7 +1713,7 @@ Validation::RTN Validation::walk_data_page(jrd_rel* relation, ULONG page_number,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (page->dpg_relation != relation->rel_id || page->dpg_sequence != sequence)
|
||||
if (page->dpg_relation != relation->getId() || page->dpg_sequence != sequence)
|
||||
{
|
||||
release_page(&window);
|
||||
return corrupt(VAL_DATA_PAGE_CONFUSED, relation, page_number, sequence);
|
||||
@ -2064,7 +2064,7 @@ Validation::RTN Validation::walk_index(jrd_rel* relation, index_root_page& root_
|
||||
|
||||
const bool leafPage = (page->btr_level == 0);
|
||||
|
||||
if (page->btr_relation != relation->rel_id || page->btr_id != (UCHAR) (id % 256))
|
||||
if (page->btr_relation != relation->getId() || page->btr_id != (UCHAR) (id % 256))
|
||||
{
|
||||
corrupt(VAL_INDEX_PAGE_CORRUPT, relation, id + 1,
|
||||
next, page->btr_level, 0, __FILE__, __LINE__);
|
||||
@ -2583,13 +2583,13 @@ Validation::RTN Validation::walk_pointer_page(jrd_rel* relation, ULONG sequence)
|
||||
if (VAL_debug_level)
|
||||
{
|
||||
fprintf(stdout, "walk_pointer_page: page %d relation %d sequence %d\n",
|
||||
(*vector)[sequence], relation->rel_id, sequence);
|
||||
(*vector)[sequence], relation->getId(), sequence);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Give the page a quick once over
|
||||
|
||||
if (page->ppg_relation != relation->rel_id || page->ppg_sequence != sequence)
|
||||
if (page->ppg_relation != relation->getId() || page->ppg_sequence != sequence)
|
||||
{
|
||||
release_page(&window);
|
||||
return corrupt(VAL_P_PAGE_INCONSISTENT, relation, (*vector)[sequence], sequence);
|
||||
@ -2809,7 +2809,7 @@ Validation::RTN Validation::walk_record(jrd_rel* relation, const rhd* header, US
|
||||
|
||||
const data_page::dpg_repeat* line = &page->dpg_rpt[line_number];
|
||||
|
||||
if (page->dpg_relation != relation->rel_id ||
|
||||
if (page->dpg_relation != relation->getId() ||
|
||||
line_number >= page->dpg_count || !(length = line->dpg_length))
|
||||
{
|
||||
corrupt(VAL_REC_FRAGMENT_CORRUPT, relation, number.getValue());
|
||||
@ -3005,7 +3005,7 @@ void Validation::checkDPinPIP(jrd_rel* relation, ULONG page_number)
|
||||
release_page(&pip_window);
|
||||
}
|
||||
|
||||
Validation::RTN Validation::walk_relation(jrd_rel* rel)
|
||||
Validation::RTN Validation::walk_relation(jrd_rel* relation)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -3020,14 +3020,6 @@ Validation::RTN Validation::walk_relation(jrd_rel* rel)
|
||||
|
||||
try {
|
||||
|
||||
// If relation hasn't been scanned, do so now
|
||||
|
||||
if (!(rel->rel_flags & REL_scanned) || (rel->rel_flags & REL_being_scanned))
|
||||
{
|
||||
MET_scan_relation(vdr_tdbb, rel);
|
||||
}
|
||||
jrd_rel* relation = rel.getPointer();
|
||||
|
||||
// skip deleted relations
|
||||
if (relation->rel_flags & (REL_deleted | REL_deleting)) {
|
||||
return rtn_ok;
|
||||
@ -3036,8 +3028,8 @@ Validation::RTN Validation::walk_relation(jrd_rel* rel)
|
||||
#ifdef DEBUG_VAL_VERBOSE
|
||||
if (VAL_debug_level)
|
||||
fprintf(stdout, "walk_relation: id %d Format %d %s %s\n",
|
||||
relation->rel_id, relation->rel_current_fmt,
|
||||
relation->rel_name.c_str(), relation->rel_owner_name.c_str());
|
||||
relation->getId(), relation->rel_current_fmt,
|
||||
relation->getName().c_str(), relation->rel_owner_name.c_str());
|
||||
#endif
|
||||
|
||||
// If it's a view, external file or virtual table, skip this
|
||||
@ -3164,16 +3156,16 @@ Validation::RTN Validation::walk_relation(jrd_rel* rel)
|
||||
{
|
||||
if (!(vdr_flags & VDR_online))
|
||||
{
|
||||
const char* msg = rel->rel_name.length() > 0 ?
|
||||
const char* msg = rel->getName().length() > 0 ?
|
||||
"bugcheck during scan of table %d (%s)" :
|
||||
"bugcheck during scan of table %d";
|
||||
gds__log(msg, rel->rel_id, rel->rel_name.c_str());
|
||||
gds__log(msg, rel->getId(), rel->getName().c_str());
|
||||
}
|
||||
#ifdef DEBUG_VAL_VERBOSE
|
||||
if (VAL_debug_level)
|
||||
{
|
||||
char s[256];
|
||||
SNPRINTF(s, sizeof(s), msg, rel->rel_id, rel->rel_name.c_str());
|
||||
SNPRINTF(s, sizeof(s), msg, rel->getId(), rel->getName().c_str());
|
||||
fprintf(stdout, "LOG:\t%s\n", s);
|
||||
}
|
||||
#endif
|
||||
@ -3216,7 +3208,7 @@ Validation::RTN Validation::walk_root(jrd_rel* relation, bool getInfo)
|
||||
MetaName index;
|
||||
|
||||
release_page(&window);
|
||||
MetadataCache::lookup_index(vdr_tdbb, index, relation->rel_name, i + 1);
|
||||
MetadataCache::lookup_index(vdr_tdbb, index, relation->getName(), i + 1);
|
||||
fetch_page(false, relPages->rel_index_root, pag_root, &window, &page);
|
||||
|
||||
if (vdr_idx_incl)
|
||||
|
132
src/jrd/vio.cpp
132
src/jrd/vio.cpp
@ -442,7 +442,7 @@ bool SweepTask::handler(WorkItem& _item)
|
||||
if (!gcGuard.gcEnabled())
|
||||
{
|
||||
string str;
|
||||
str.printf("Acquire garbage collection lock failed (%s)", relation->rel_name.c_str());
|
||||
str.printf("Acquire garbage collection lock failed (%s)", relation->getName().c_str());
|
||||
status_exception::raise(Arg::Gds(isc_random) << Arg::Str(str));
|
||||
}
|
||||
|
||||
@ -634,10 +634,10 @@ static bool assert_gc_enabled(const jrd_tra* transaction, const jrd_rel* relatio
|
||||
return false;
|
||||
|
||||
vec<Lock*>* vector = transaction->tra_relation_locks;
|
||||
if (!vector || relation->rel_id >= vector->count())
|
||||
if (!vector || relation->getId() >= vector->count())
|
||||
return false;
|
||||
|
||||
Lock* lock = (*vector)[relation->rel_id];
|
||||
Lock* lock = (*vector)[relation->getId()];
|
||||
if (!lock)
|
||||
return false;
|
||||
|
||||
@ -659,7 +659,7 @@ inline void check_gbak_cheating_insupd(thread_db* tdbb, const jrd_rel* relation,
|
||||
!request->hasInternalStatement())
|
||||
{
|
||||
status_exception::raise(Arg::Gds(isc_protect_sys_tab) <<
|
||||
Arg::Str(op) << Arg::Str(relation->rel_name));
|
||||
Arg::Str(op) << Arg::Str(relation->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,7 +679,7 @@ inline void check_gbak_cheating_delete(thread_db* tdbb, const jrd_rel* relation)
|
||||
// There are 2 tables whose contents gbak might delete:
|
||||
// - RDB$INDEX_SEGMENTS if it detects inconsistencies while restoring
|
||||
// - RDB$FILES if switch -k is set
|
||||
switch(relation->rel_id)
|
||||
switch(relation->getId())
|
||||
{
|
||||
case rel_segments:
|
||||
case rel_files:
|
||||
@ -694,7 +694,7 @@ inline void check_gbak_cheating_delete(thread_db* tdbb, const jrd_rel* relation)
|
||||
inline int wait(thread_db* tdbb, jrd_tra* transaction, const record_param* rpb)
|
||||
{
|
||||
if (transaction->getLockWait())
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_WAITS, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_WAITS, rpb->rpb_relation->getId());
|
||||
|
||||
return TRA_wait(tdbb, transaction, rpb->rpb_transaction_nr, jrd_tra::tra_wait);
|
||||
}
|
||||
@ -833,7 +833,7 @@ void VIO_backout(thread_db* tdbb, record_param* rpb, const jrd_tra* transaction)
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"VIO_backout (rel_id %u, record_param %" SQUADFORMAT", transaction %" SQUADFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
#endif
|
||||
|
||||
// If there is data in the record, fetch it now. If the old version
|
||||
@ -1018,7 +1018,7 @@ void VIO_backout(thread_db* tdbb, record_param* rpb, const jrd_tra* transaction)
|
||||
gcLockGuard.release();
|
||||
delete_record(tdbb, rpb, 0, NULL);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_BACKOUTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_BACKOUTS, relation->getId());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1108,7 +1108,7 @@ void VIO_backout(thread_db* tdbb, record_param* rpb, const jrd_tra* transaction)
|
||||
delete_record(tdbb, &temp, rpb->rpb_page, NULL);
|
||||
}
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_BACKOUTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_BACKOUTS, relation->getId());
|
||||
}
|
||||
|
||||
|
||||
@ -1144,7 +1144,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"VIO_chase_record_version (rel_id %u, record_param %" QUADFORMAT"d, transaction %"
|
||||
SQUADFORMAT", pool %p)\n",
|
||||
relation->rel_id,
|
||||
relation->getId(),
|
||||
rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
(void*) pool);
|
||||
|
||||
@ -1308,7 +1308,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
|
||||
|
||||
if (state == tra_active)
|
||||
{
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->getId());
|
||||
|
||||
// Cannot use Arg::Num here because transaction number is 64-bit unsigned integer
|
||||
ERR_post(Arg::Gds(isc_deadlock) <<
|
||||
@ -1775,7 +1775,7 @@ void VIO_data(thread_db* tdbb, record_param* rpb, MemoryPool* pool)
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_READS,
|
||||
"VIO_data (rel_id %u, record_param %" QUADFORMAT"d, pool %p)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), (void*)pool);
|
||||
relation->getId(), rpb->rpb_number.getValue(), (void*)pool);
|
||||
|
||||
|
||||
VIO_trace(DEBUG_READS_INFO,
|
||||
@ -1960,7 +1960,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"VIO_erase (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction->tra_number);
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction->tra_number);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -2007,7 +2007,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
USHORT id;
|
||||
DeferredWork* work;
|
||||
|
||||
switch ((RIDS) relation->rel_id)
|
||||
switch ((RIDS) relation->getId())
|
||||
{
|
||||
case rel_database:
|
||||
case rel_log:
|
||||
@ -2060,9 +2060,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
}
|
||||
EVL_field(0, rpb->rpb_record, f_rel_name, &desc);
|
||||
DFW_post_work(transaction, dfw_delete_relation, &desc, id);
|
||||
jrd_rel* rel_drop = MetadataCache::lookup_relation_id(tdbb, id, false);
|
||||
if (rel_drop)
|
||||
MET_scan_relation(tdbb, rel_drop);
|
||||
MetadataCache::lookup_relation_id(tdbb, id, false);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2135,7 +2133,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
// hvlad: lets add index name to the DFW item even if we add it again later within
|
||||
// additional argument. This is needed to make DFW work items different for different
|
||||
// indexes dropped at the same transaction and to not merge them at DFW_merge_work.
|
||||
work = DFW_post_work(transaction, dfw_delete_index, &idx_name, r2->rel_id);
|
||||
work = DFW_post_work(transaction, dfw_delete_index, &idx_name, r2->getId());
|
||||
|
||||
// add index id and name (the latter is required to delete dependencies correctly)
|
||||
DFW_post_work_arg(transaction, work, &idx_name, id, dfw_arg_index_name);
|
||||
@ -2156,7 +2154,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
MET_lookup_partner(tdbb, r2.getPointer(), &idx, index_name.nullStr()) &&
|
||||
(partner = MetadataCache::lookup_relation_id(tdbb, idx.idx_primary_relation, false)) )
|
||||
{
|
||||
DFW_post_work_arg(transaction, work, 0, partner->rel_id,
|
||||
DFW_post_work_arg(transaction, work, 0, partner->getId(),
|
||||
dfw_arg_partner_rel_id);
|
||||
}
|
||||
else
|
||||
@ -2179,7 +2177,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
EVL_field(0, rpb->rpb_record, f_rfr_fname, &desc2);
|
||||
MOV_get_metaname(tdbb, &desc, object_name);
|
||||
if ( (r2 = MetadataCache::lookup_relation(tdbb, object_name)) )
|
||||
DFW_post_work(transaction, dfw_delete_rfr, &desc2, r2->rel_id);
|
||||
DFW_post_work(transaction, dfw_delete_rfr, &desc2, r2->getId());
|
||||
|
||||
EVL_field(0, rpb->rpb_record, f_rfr_sname, &desc2);
|
||||
MOV_get_metaname(tdbb, &desc2, object_name);
|
||||
@ -2344,7 +2342,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
if ((dbb->dbb_flags & DBB_gc_background) && !rpb->rpb_relation->isTemporary() && !backVersion)
|
||||
notify_garbage_collector(tdbb, rpb, transaction->tra_number);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_DELETES, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_DELETES, relation->getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2382,7 +2380,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
|
||||
// Check to see if recursive revoke needs to be propagated
|
||||
|
||||
if ((RIDS) relation->rel_id == rel_priv)
|
||||
if ((RIDS) relation->getId() == rel_priv)
|
||||
{
|
||||
EVL_field(0, rpb->rpb_record, f_prv_rname, &desc);
|
||||
MOV_get_metaname(tdbb, &desc, object_name);
|
||||
@ -2401,7 +2399,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
if (transaction->tra_save_point && transaction->tra_save_point->isChanging())
|
||||
verb_post(tdbb, transaction, rpb, 0);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_DELETES, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_DELETES, relation->getId());
|
||||
|
||||
// for an autocommit transaction, mark a commit as necessary
|
||||
|
||||
@ -2732,7 +2730,7 @@ void VIO_intermediate_gc(thread_db* tdbb, record_param* rpb, jrd_tra* transactio
|
||||
clearRecordStack(staying);
|
||||
clearRecordStack(going);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_IMGC, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_IMGC, rpb->rpb_relation->getId());
|
||||
}
|
||||
|
||||
bool VIO_garbage_collect(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
@ -2759,7 +2757,7 @@ bool VIO_garbage_collect(thread_db* tdbb, record_param* rpb, jrd_tra* transactio
|
||||
VIO_trace(DEBUG_TRACE,
|
||||
"VIO_garbage_collect (rel_id %u, record_param %" QUADFORMAT"d, transaction %"
|
||||
SQUADFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
|
||||
VIO_trace(DEBUG_TRACE_INFO,
|
||||
" record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -2894,7 +2892,7 @@ bool VIO_get(thread_db* tdbb, record_param* rpb, jrd_tra* transaction, MemoryPoo
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_READS,
|
||||
"VIO_get (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT", pool %p)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
(void*) pool);
|
||||
#endif
|
||||
|
||||
@ -2936,7 +2934,7 @@ bool VIO_get(thread_db* tdbb, record_param* rpb, jrd_tra* transaction, MemoryPoo
|
||||
VIO_data(tdbb, rpb, pool);
|
||||
}
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_IDX_READS, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_IDX_READS, rpb->rpb_relation->getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2972,7 +2970,7 @@ bool VIO_get_current(thread_db* tdbb,
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_TRACE,
|
||||
"VIO_get_current (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT", pool %p)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
(void*) pool);
|
||||
#endif
|
||||
|
||||
@ -3011,7 +3009,7 @@ bool VIO_get_current(thread_db* tdbb,
|
||||
|
||||
if (!counted)
|
||||
{
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_IDX_READS, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_IDX_READS, rpb->rpb_relation->getId());
|
||||
counted = true;
|
||||
}
|
||||
|
||||
@ -3250,7 +3248,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"VIO_modify (rel_id %u, org_rpb %" QUADFORMAT"d, new_rpb %" QUADFORMAT"d, "
|
||||
"transaction %" SQUADFORMAT")\n",
|
||||
relation->rel_id, org_rpb->rpb_number.getValue(), new_rpb->rpb_number.getValue(),
|
||||
relation->getId(), org_rpb->rpb_number.getValue(), new_rpb->rpb_number.getValue(),
|
||||
transaction ? transaction->tra_number : 0);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
@ -3294,7 +3292,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
|
||||
if (transaction->tra_flags & TRA_system)
|
||||
{
|
||||
VIO_update_in_place(tdbb, transaction, org_rpb, new_rpb);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3309,7 +3307,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
|
||||
{
|
||||
const SLONG nullLinger = 0;
|
||||
|
||||
switch ((RIDS) relation->rel_id)
|
||||
switch ((RIDS) relation->getId())
|
||||
{
|
||||
case rel_segments:
|
||||
case rel_vrel:
|
||||
@ -3654,7 +3652,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
|
||||
verb_post(tdbb, transaction, org_rpb, org_rpb->rpb_undo);
|
||||
}
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3687,7 +3685,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
|
||||
verb_post(tdbb, transaction, org_rpb, 0);
|
||||
}
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->getId());
|
||||
|
||||
// for an autocommit transaction, mark a commit as necessary
|
||||
|
||||
@ -3745,7 +3743,7 @@ bool VIO_next_record(thread_db* tdbb,
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_TRACE,
|
||||
"VIO_next_record (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT", pool %p)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
(void*) pool);
|
||||
|
||||
VIO_trace(DEBUG_TRACE_INFO,
|
||||
@ -3790,7 +3788,7 @@ bool VIO_next_record(thread_db* tdbb,
|
||||
rpb->rpb_f_page, rpb->rpb_f_line);
|
||||
#endif
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_SEQ_READS, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_SEQ_READS, rpb->rpb_relation->getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3813,7 +3811,7 @@ Record* VIO_record(thread_db* tdbb, record_param* rpb, const Format* format, Mem
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_TRACE,
|
||||
"VIO_record (rel_id %u, record_param %" QUADFORMAT"d, format %d, pool %p)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), format ? format->fmt_version : 0,
|
||||
relation->getId(), rpb->rpb_number.getValue(), format ? format->fmt_version : 0,
|
||||
(void*) pool);
|
||||
#endif
|
||||
|
||||
@ -3856,7 +3854,7 @@ bool VIO_refetch_record(thread_db* tdbb, record_param* rpb, jrd_tra* transaction
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_READS,
|
||||
"VIO_refetch_record (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
#endif
|
||||
|
||||
const TraNumber tid_fetch = rpb->rpb_transaction_nr;
|
||||
@ -3882,7 +3880,7 @@ bool VIO_refetch_record(thread_db* tdbb, record_param* rpb, jrd_tra* transaction
|
||||
VIO_data(tdbb, rpb, tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_RPT_READS, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_RPT_READS, rpb->rpb_relation->getId());
|
||||
|
||||
// If record is present, and the transaction is read committed,
|
||||
// make sure the record has not been updated. Also, punt after
|
||||
@ -3897,7 +3895,7 @@ bool VIO_refetch_record(thread_db* tdbb, record_param* rpb, jrd_tra* transaction
|
||||
// dimitr: reads using the undo log are also OK
|
||||
!(rpb->rpb_runtime_flags & RPB_undo_read))
|
||||
{
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, rpb->rpb_relation->getId());
|
||||
|
||||
// Cannot use Arg::Num here because transaction number is 64-bit unsigned integer
|
||||
ERR_post(Arg::Gds(isc_deadlock) <<
|
||||
@ -3933,7 +3931,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"VIO_store (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT
|
||||
")\n", relation->rel_id, rpb->rpb_number.getValue(),
|
||||
")\n", relation->getId(), rpb->rpb_number.getValue(),
|
||||
transaction ? transaction->tra_number : 0);
|
||||
#endif
|
||||
|
||||
@ -3944,7 +3942,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
|
||||
if (needDfw(tdbb, transaction))
|
||||
{
|
||||
switch ((RIDS) relation->rel_id)
|
||||
switch ((RIDS) relation->getId())
|
||||
{
|
||||
case rel_pages:
|
||||
case rel_formats:
|
||||
@ -4268,7 +4266,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
}
|
||||
|
||||
// this should be scheduled even in database creation (system transaction)
|
||||
switch ((RIDS) relation->rel_id)
|
||||
switch ((RIDS) relation->getId())
|
||||
{
|
||||
case rel_collations:
|
||||
{
|
||||
@ -4310,7 +4308,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
verb_post(tdbb, transaction, rpb, 0);
|
||||
}
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_INSERTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_INSERTS, relation->getId());
|
||||
|
||||
// for an autocommit transaction, mark a commit as necessary
|
||||
|
||||
@ -4404,7 +4402,7 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
|
||||
traceSweep->beginSweepRelation(relation);
|
||||
|
||||
if (gc) {
|
||||
gc->sweptRelation(transaction->tra_oldest_active, relation->rel_id);
|
||||
gc->sweptRelation(transaction->tra_oldest_active, relation->getId());
|
||||
}
|
||||
|
||||
while (VIO_next_record(tdbb, &rpb, transaction, 0, DPM_next_all))
|
||||
@ -4465,7 +4463,7 @@ WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* t
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"VIO_writelock (rel_id %u, org_rpb %" QUADFORMAT"d, transaction %" SQUADFORMAT")\n",
|
||||
relation->rel_id, org_rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
relation->getId(), org_rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" old record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -4604,7 +4602,7 @@ WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* t
|
||||
if (transaction->tra_flags & TRA_autocommit)
|
||||
transaction->tra_flags |= TRA_perform_autocommit;
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_LOCKS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_LOCKS, relation->getId());
|
||||
|
||||
// VIO_writelock
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
@ -4869,7 +4867,7 @@ static void delete_record(thread_db* tdbb, record_param* rpb, ULONG prior_page,
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"delete_record (rel_id %u, record_param %" QUADFORMAT"d, prior_page %" SLONGFORMAT", pool %p)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), prior_page, (void*)pool);
|
||||
relation->getId(), rpb->rpb_number.getValue(), prior_page, (void*)pool);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" delete_record record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -4944,7 +4942,7 @@ static UCHAR* delete_tail(thread_db* tdbb,
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"delete_tail (rel_id %u, record_param %" QUADFORMAT"d, prior_page %" SLONGFORMAT", tail %p, length %u)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), prior_page, tail, tail_length);
|
||||
relation->getId(), rpb->rpb_number.getValue(), prior_page, tail, tail_length);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" tail of record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -5043,7 +5041,7 @@ static void expunge(thread_db* tdbb, record_param* rpb, const jrd_tra* transacti
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"expunge (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT
|
||||
", prior_page %" SLONGFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0,
|
||||
prior_page);
|
||||
#endif
|
||||
|
||||
@ -5099,7 +5097,7 @@ static void expunge(thread_db* tdbb, record_param* rpb, const jrd_tra* transacti
|
||||
RecordStack empty_staying;
|
||||
garbage_collect(tdbb, &temp, rpb->rpb_page, empty_staying);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_EXPUNGES, rpb->rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_EXPUNGES, rpb->rpb_relation->getId());
|
||||
}
|
||||
|
||||
|
||||
@ -5128,7 +5126,7 @@ static void garbage_collect(thread_db* tdbb, record_param* rpb, ULONG prior_page
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_WRITES,
|
||||
"garbage_collect (rel_id %u, record_param %" QUADFORMAT"d, prior_page %" SLONGFORMAT", staying)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), prior_page);
|
||||
relation->getId(), rpb->rpb_number.getValue(), prior_page);
|
||||
|
||||
VIO_trace(DEBUG_WRITES_INFO,
|
||||
" record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -5595,7 +5593,7 @@ static void invalidate_cursor_records(jrd_tra* transaction, record_param* mod_rp
|
||||
|
||||
if (org_rpb != mod_rpb &&
|
||||
org_rpb->rpb_relation && org_rpb->rpb_number.isValid() &&
|
||||
org_rpb->rpb_relation->rel_id == mod_rpb->rpb_relation->rel_id &&
|
||||
org_rpb->rpb_relation->getId() == mod_rpb->rpb_relation->getId() &&
|
||||
org_rpb->rpb_number == mod_rpb->rpb_number)
|
||||
{
|
||||
org_rpb->rpb_runtime_flags |= RPB_refetch;
|
||||
@ -5716,7 +5714,7 @@ static void list_staying_fast(thread_db* tdbb, record_param* rpb, RecordStack& s
|
||||
|
||||
garbage_collect(tdbb, &temp2, temp.rpb_page, staying);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_PURGES, temp.rpb_relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_PURGES, temp.rpb_relation->getId());
|
||||
|
||||
if (back_rpb && back_rpb->rpb_page == page && back_rpb->rpb_line == line)
|
||||
{
|
||||
@ -5963,7 +5961,7 @@ static void notify_garbage_collector(thread_db* tdbb, record_param* rpb, TraNumb
|
||||
|
||||
const ULONG dp_sequence = rpb->rpb_number.getValue() / dbb->dbb_max_records;
|
||||
|
||||
const TraNumber minTranId = gc->addPage(relation->rel_id, dp_sequence, tranid);
|
||||
const TraNumber minTranId = gc->addPage(relation->getId(), dp_sequence, tranid);
|
||||
if (tranid > minTranId)
|
||||
tranid = minTranId;
|
||||
|
||||
@ -6004,7 +6002,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"prepare_update (rel_id %u, transaction %" SQUADFORMAT
|
||||
", commit_tid read %" SQUADFORMAT", record_param %" QUADFORMAT"d, ",
|
||||
relation->rel_id, transaction ? transaction->tra_number : 0, commit_tid_read,
|
||||
relation->getId(), transaction ? transaction->tra_number : 0, commit_tid_read,
|
||||
rpb ? rpb->rpb_number.getValue() : 0);
|
||||
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
@ -6128,7 +6126,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
|
||||
|
||||
delete_record(tdbb, temp, 0, NULL);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->getId());
|
||||
return PrepareResult::DELETED;
|
||||
}
|
||||
}
|
||||
@ -6173,7 +6171,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
|
||||
|
||||
if (writeLockSkipLocked.isAssigned() || (transaction->tra_flags & TRA_read_consistency))
|
||||
{
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->getId());
|
||||
return PrepareResult::DELETED;
|
||||
}
|
||||
|
||||
@ -6194,7 +6192,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
|
||||
|
||||
delete_record(tdbb, temp, 0, NULL);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->getId());
|
||||
return PrepareResult::CONFLICT;
|
||||
}
|
||||
|
||||
@ -6299,7 +6297,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
|
||||
// For SNAPSHOT mode transactions raise error early
|
||||
if (!(transaction->tra_flags & TRA_read_committed))
|
||||
{
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->getId());
|
||||
|
||||
if (writeLockSkipLocked == true)
|
||||
return PrepareResult::SKIP_LOCKED;
|
||||
@ -6374,7 +6372,7 @@ static void protect_system_table_insert(thread_db* tdbb,
|
||||
}
|
||||
|
||||
status_exception::raise(Arg::Gds(isc_protect_sys_tab) <<
|
||||
Arg::Str("INSERT") << Arg::Str(relation->rel_name));
|
||||
Arg::Str("INSERT") << Arg::Str(relation->getName()));
|
||||
}
|
||||
|
||||
|
||||
@ -6406,7 +6404,7 @@ static void protect_system_table_delupd(thread_db* tdbb,
|
||||
}
|
||||
|
||||
status_exception::raise(Arg::Gds(isc_protect_sys_tab) <<
|
||||
Arg::Str(operation) << Arg::Str(relation->rel_name));
|
||||
Arg::Str(operation) << Arg::Str(relation->getName()));
|
||||
}
|
||||
|
||||
|
||||
@ -6436,7 +6434,7 @@ static void purge(thread_db* tdbb, record_param* rpb)
|
||||
#ifdef VIO_DEBUG
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"purge (rel_id %u, record_param %" QUADFORMAT"d)\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue());
|
||||
relation->getId(), rpb->rpb_number.getValue());
|
||||
|
||||
VIO_trace(DEBUG_TRACE_ALL_INFO,
|
||||
" record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -6488,7 +6486,7 @@ static void purge(thread_db* tdbb, record_param* rpb)
|
||||
staying.push(record);
|
||||
garbage_collect(tdbb, &temp, rpb->rpb_page, staying);
|
||||
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_PURGES, relation->rel_id);
|
||||
tdbb->bumpRelStats(RuntimeStatistics::RECORD_PURGES, relation->getId());
|
||||
return; // true;
|
||||
}
|
||||
|
||||
@ -6515,7 +6513,7 @@ static void replace_record(thread_db* tdbb,
|
||||
jrd_rel* relation = rpb->rpb_relation;
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"replace_record (rel_id %u, record_param %" QUADFORMAT"d, transaction %" SQUADFORMAT")\n",
|
||||
relation->rel_id, rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
relation->getId(), rpb->rpb_number.getValue(), transaction ? transaction->tra_number : 0);
|
||||
|
||||
VIO_trace(DEBUG_TRACE_ALL_INFO,
|
||||
" record %" SLONGFORMAT":%d, rpb_trans %" SQUADFORMAT
|
||||
@ -6573,7 +6571,7 @@ static void refresh_fk_fields(thread_db* tdbb, Record* old_rec, record_param* cu
|
||||
for (FB_SIZE_T i = 0; i < frgnCount; i++)
|
||||
{
|
||||
// We need self-referenced FK's only
|
||||
if ((*relation->rel_foreign_refs.frgn_relations)[i] == relation->rel_id)
|
||||
if ((*relation->rel_foreign_refs.frgn_relations)[i] == relation->getId())
|
||||
{
|
||||
index_desc idx;
|
||||
idx.idx_id = idx_invalid;
|
||||
@ -6773,7 +6771,7 @@ void VIO_update_in_place(thread_db* tdbb,
|
||||
VIO_trace(DEBUG_TRACE_ALL,
|
||||
"update_in_place (rel_id %u, transaction %" SQUADFORMAT", org_rpb %" QUADFORMAT"d, "
|
||||
"new_rpb %" QUADFORMAT"d)\n",
|
||||
relation->rel_id, transaction ? transaction->tra_number : 0, org_rpb->rpb_number.getValue(),
|
||||
relation->getId(), transaction ? transaction->tra_number : 0, org_rpb->rpb_number.getValue(),
|
||||
new_rpb ? new_rpb->rpb_number.getValue() : 0);
|
||||
|
||||
VIO_trace(DEBUG_TRACE_ALL_INFO,
|
||||
|
Loading…
Reference in New Issue
Block a user