8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 10:00:38 +01:00

WIP (saved before fbconf)

This commit is contained in:
AlexPeshkoff 2024-06-05 20:05:25 +03:00
parent af683fc5b8
commit c801683372
16 changed files with 648 additions and 123 deletions

View File

@ -2813,6 +2813,8 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq
METD_drop_procedure(transaction, QualifiedName(name, package));
MetadataCache::dsql_cache_release(tdbb, SYM_procedure, name, package);
}
}
void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
@ -4130,6 +4132,14 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
storePrivileges(tdbb, transaction, name, obj_collation, USAGE_PRIVILEGES);
auto* cs = MetadataCache::getCharSet(tdbb, forCharSetId, CacheFlag::AUTOCREATE | CacheFlag::NOCOMMIT);
if (!cs)
{
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_charset_not_found) << Arg::Num(forCharSetId));
}
cs->resetDependentObject(tdbb, ElementBase::ResetType::Recompile);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER,
DDL_TRIGGER_CREATE_COLLATION, name, NULL);

View File

@ -4844,7 +4844,7 @@ DmlNode* DefaultNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch*
{
Dependency dependency(obj_relation);
dependency.relation = MetadataCache::lookupRelation(tdbb, relationName, CacheFlag::AUTOCREATE);
dependency.subName = FB_NEW_POOL(pool) MetaName(fieldName);
dependency.subName = fieldName;
csb->addDependency(dependency);
}

View File

@ -333,7 +333,7 @@ void DDL_resolve_intl_type(DsqlCompilerScratch* dsqlScratch, dsql_fld* field,
// If field is not specified with NATIONAL, or CHARACTER SET
// treat it as a single-byte-per-character field of character set NONE.
assign_field_length(field, 1);
field->textType = 0;
field->textType = ttype_none;
if (collation_name.isEmpty())
return;

View File

@ -240,7 +240,7 @@ public:
USHORT charLength; // Length of field in characters
Nullable<CSetId> charSetId;
CollId collationId;
SSHORT textType;
TTypeId textType;
bool fullDomain; // Domain name without TYPE OF prefix
bool notNull; // NOT NULL was explicit specified
MetaName fieldSource;

View File

@ -128,7 +128,7 @@ public:
static Lock* makeLock(thread_db* tdbb, MemoryPool& p);
bool scan(thread_db* tdbb, ObjectBase::Flag flags);
Collation* getCollation(TTypeId tt_id);
Collation* getCollation(CollId id);
Collation* getCollation(MetaName name);
Cached::CharSet* getContainer() const
{

View File

@ -44,7 +44,7 @@ namespace Jrd
static int blockingAst(void* ast_object);
static Function* lookup(thread_db* tdbb, MetaId id, ObjectBase::Flag flags);
static Function* lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags = 0);
static Function* lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags);
private:
explicit Function(Cached::Function* perm)

View File

@ -33,6 +33,7 @@
#include "../jrd/Database.h"
#include "../jrd/tra.h"
#include "../jrd/met.h"
#include "../jrd/tpc_proto.h"
using namespace Jrd;
using namespace Firebird;
@ -64,6 +65,11 @@ TraNumber TransactionNumber::next(thread_db* tdbb)
return tdbb->getDatabase()->dbb_next_transaction;
}
bool TransactionNumber::isDead(thread_db* tdbb, TraNumber traNumber)
{
return TPC_cache_state(tdbb, traNumber) == tra_dead;
}
// class VersionSupport

View File

@ -125,7 +125,7 @@ public:
// atomically replaces 'where' with 'newVal', using *this as old value for comparison
// sets *this to actual data from 'where' if replace failed
bool replace2(atomics::atomic<T*>& where, T* newVal)
bool replace(atomics::atomic<T*>& where, T* newVal)
{
T* val = get<T>();
bool rc = where.compare_exchange_strong(val, newVal,
@ -452,6 +452,7 @@ namespace CacheFlag
static const ObjectBase::Flag NOSCAN = 0x04;
static const ObjectBase::Flag AUTOCREATE = 0x08;
static const ObjectBase::Flag NOCOMMIT = 0x10;
static const ObjectBase::Flag RET_ERASED = 0x20;
static const ObjectBase::Flag IGNORE_MASK = COMMITTED | ERASED;
}
@ -469,6 +470,16 @@ public:
static MemoryPool& get(thread_db* tdbb);
};
class TransactionNumber
{
public:
static TraNumber current(thread_db* tdbb);
static TraNumber oldestActive(thread_db* tdbb);
static TraNumber next(thread_db* tdbb);
static bool isDead(thread_db* tdbb, TraNumber traNumber);
};
class StartupBarrier
{
public:
@ -562,7 +573,9 @@ class ListEntry : public HazardObject
public:
ListEntry(OBJ* obj, TraNumber currentTrans, ObjectBase::Flag fl)
: object(obj), traNumber(currentTrans), cacheFlags(fl)
{ }
{
//printf("%s %lld\n", object->c_name(), traNumber);
}
~ListEntry()
{
@ -591,6 +604,10 @@ public:
{
ObjectBase::Flag f(listEntry->getFlags());
//printf("gO %s %02x %lld (%lld)\n", listEntry->object->c_name(), f, listEntry->traNumber, currentTrans);
if ((!(f & CacheFlag::COMMITTED)) && (listEntry->traNumber != currentTrans))
printf("Oblom\n");
if ((f & CacheFlag::COMMITTED) ||
// committed (i.e. confirmed) objects are freely available
(listEntry->traNumber == currentTrans))
@ -609,6 +626,7 @@ public:
// required entry found in the list
auto* obj = listEntry->object;
//printf("found\n");
if (obj)
{
listEntry->scan(
@ -627,7 +645,7 @@ public:
bool isBusy(TraNumber currentTrans) const noexcept
{
return traNumber != currentTrans && !(getFlags() & CacheFlag::COMMITTED);
return !((getFlags() & CacheFlag::COMMITTED) || (traNumber == currentTrans));
}
ObjectBase::Flag getFlags() const noexcept
@ -636,16 +654,26 @@ public:
}
// add new entry to the list
static bool add(atomics::atomic<ListEntry*>& list, ListEntry* newVal)
static bool add(thread_db* tdbb, atomics::atomic<ListEntry*>& list, ListEntry* newVal)
{
HazardPtr<ListEntry> 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));
while(oldVal && oldVal->isBusy(oldVal->traNumber))
{
// modified in transaction oldVal->traNumber
if (TransactionNumber::isDead(tdbb, oldVal->traNumber))
{
rollback(tdbb, list, oldVal->traNumber);
oldVal.set(list);
}
else
return false;
}
newVal->next.store(oldVal.getPointer());
} while (!oldVal.replace(list, newVal));
return true;
}
@ -672,7 +700,7 @@ public:
break; // someone else also performs GC
// split remaining list off
if (entry.replace2(list, nullptr))
if (entry.replace(list, nullptr))
{
while (entry && !(entry->cacheFlags.fetch_or(CacheFlag::ERASED) & CacheFlag::ERASED))
{
@ -696,6 +724,7 @@ public:
{
fb_assert((getFlags() & CacheFlag::IGNORE_MASK) == 0);
fb_assert(traNumber == currentTrans);
printf("commit %s %lld=>%lld\n", object->c_name(), traNumber, nextTrans);
traNumber = nextTrans;
version = VersionSupport::next(tdbb);
@ -716,7 +745,7 @@ public:
break;
fb_assert(entry->traNumber == currentTran);
if (entry.replace2(list, entry->next))
if (entry.replace(list, entry->next))
{
entry->retire();
OBJ::destroy(tdbb, entry->object);
@ -768,15 +797,6 @@ private:
};
class TransactionNumber
{
public:
static TraNumber current(thread_db* tdbb);
static TraNumber oldestActive(thread_db* tdbb);
static TraNumber next(thread_db* tdbb);
};
typedef class Lock* MakeLock(thread_db*, MemoryPool&);
template <class V, class P>
@ -787,11 +807,11 @@ public:
typedef P Permanent;
CacheElement(thread_db* tdbb, MemoryPool& p, MetaId id, MakeLock* makeLock) :
Permanent(tdbb, p, id, makeLock), list(nullptr), resetAt(0), myId(id)
Permanent(tdbb, p, id, makeLock), list(nullptr), resetAt(0)
{ }
CacheElement(MemoryPool& p) :
Permanent(p), list(nullptr), resetAt(0), myId(0)
Permanent(p), list(nullptr), resetAt(0)
{ }
static void cleanup(thread_db* tdbb, CacheElement* element)
@ -896,7 +916,7 @@ public:
TraNumber cur = TransactionNumber::current(tdbb);
ListEntry<Versioned>* newEntry = FB_NEW_POOL(*getDefaultMemoryPool()) ListEntry<Versioned>(obj, cur, fl);
if (!ListEntry<Versioned>::add(list, newEntry))
if (!ListEntry<Versioned>::add(tdbb, list, newEntry))
{
newEntry->cleanup(tdbb);
delete newEntry;
@ -1013,10 +1033,6 @@ private:
private:
atomics::atomic<ListEntry<Versioned>*> list;
atomics::atomic<TraNumber> resetAt;
public:
//atomics::atomic<ULONG> flags; // control non-versioned features (like foreign keys)
const MetaId myId;
};
@ -1226,20 +1242,6 @@ public:
m_objects.clear();
}
/*
bool replace2(MetaId id, HazardPtr<Versioned>& oldVal, Versioned* 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())
@ -1253,32 +1255,7 @@ public:
sub->store(nullptr, atomics::memory_order_release);
return true;
}
/*
bool load(MetaId id, HazardPtr<Versioned>& 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<Versioned> load(MetaId id) const
{
HazardPtr<Versioned> val;
if (!load(id, val))
val.clear();
return val;
}
*/
HazardPtr<typename Storage::Generation> readAccessor() const
{
return m_objects.readAccessor();
@ -1295,11 +1272,6 @@ public:
return get();
}
/* StoredElement& operator->()
{
return get();
}
*/
Iterator& operator++()
{
index = locateData(index + 1);

View File

@ -678,6 +678,480 @@ struct deferred_task
dfw_task_routine task_routine;
};
namespace
{
template <typename Self, typename T, int objType,
T* (*lookupById)(thread_db*, MetaId, ObjectBase::Flag),
T* (*lookupByName)(Jrd::thread_db*, const QualifiedName&, ObjectBase::Flag)
>
class RoutineManager
{
public:
// Create a new routine.
static bool createRoutine(thread_db* tdbb, SSHORT phase, DeferredWork* work,
jrd_tra* transaction)
{
SET_TDBB(tdbb);
switch (phase)
{
case 1:
case 2:
case 3:
case 4:
return true;
case 5:
{
const bool compile = !work->findArg(dfw_arg_check_blr);
getDependencies(work, compile, transaction);
T* routine = lookupByName(tdbb,
QualifiedName(work->dfw_name, work->dfw_package), compile ? CacheFlag::NOSCAN : 0);
if (!routine)
return false;
break;
}
}
return false;
}
// Perform required actions when modifying a routine.
static bool modifyRoutine(thread_db* tdbb, SSHORT phase, DeferredWork* work,
jrd_tra* transaction)
{
SET_TDBB(tdbb);
const QualifiedName name(work->dfw_name, work->dfw_package);
Routine* routine;
fprintf(stderr, "routine %d %s ph %d\n", work->dfw_id, name.c_str(), phase);
switch (phase)
{
case 0:
routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN);
if (routine && routine->getPermanent()->existenceLock)
LCK_release(tdbb, routine->getPermanent()->existenceLock);
return false;
case 1:
case 2:
return true;
case 3:
routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN);
if (!routine)
return false;
if (routine->getPermanent()->existenceLock)
{
// Let routine be deleted if only this transaction is using it
//if (!routine->getPermanent()->existenceLock->exclLock(tdbb))
// !!!!!!!!!!!!!!!!!!!!!!!!!!! raiseRoutineInUseError(routine, name);
}
// If we are in a multi-client server, someone else may have marked
// routine obsolete. Unmark and we will remark it later.
//routine->flags &= ~Routine::FLAG_OBSOLETE;
return true;
case 4:
{
routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN);
if (!routine)
return false;
if (routine->getStatement())
{
//if (routine->getStatement()->isActive())
// raiseRoutineInUseError(routine, name);
// release the request
routine->releaseStatement(tdbb);
}
// delete dependency lists
if (work->dfw_package.isEmpty())
MET_delete_dependencies(tdbb, work->dfw_name, objType, transaction);
/* the routine has just been scanned by lookupById
and its Routine::FLAG_SCANNED flag is set. We are going to reread it
from file (create all new dependencies) and do not want this
flag to be set. That is why we do not add Routine::FLAG_OBSOLETE and
Routine::FLAG_BEING_ALTERED flags, we set only these two flags
*/
// routine->flags = (Routine::FLAG_OBSOLETE | Routine::FLAG_BEING_ALTERED);
if (routine->getPermanent()->existenceLock)
LCK_release(tdbb, routine->getPermanent()->existenceLock);
// Now handle the new definition
bool compile = !work->findArg(dfw_arg_check_blr);
getDependencies(work, compile, transaction);
// routine->flags &= ~(Routine::FLAG_OBSOLETE | Routine::FLAG_BEING_ALTERED);
return true;
}
case 5:
if (work->findArg(dfw_arg_check_blr))
{
SSHORT validBlr = FALSE;
Jrd::Database* dbb = tdbb->getDatabase();
MemoryPool* newPool = dbb->createPool();
try
{
Jrd::ContextPoolHolder context(tdbb, newPool);
// compile the routine to know if the BLR is still valid
if (lookupById(tdbb, work->dfw_id, CacheFlag::AUTOCREATE))
validBlr = TRUE;
}
catch (const Firebird::Exception&)
{
fb_utils::init_status(tdbb->tdbb_status_vector);
}
dbb->deletePool(newPool);
Self::validate(tdbb, transaction, work, validBlr);
}
return true;
case 6:
Self::checkOutParamDependencies(tdbb, work, transaction);
break;
}
return false;
}
// Check if it is allowed to delete a routine, and if so, clean up after it.
static bool deleteRoutine(thread_db* tdbb, SSHORT phase, DeferredWork* work,
jrd_tra* transaction)
{
SET_TDBB(tdbb);
const QualifiedName name(work->dfw_name, work->dfw_package);
T* routine;
switch (phase)
{
case 0:
routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN);
if (!routine)
return false;
if (routine->getPermanent()->existenceLock)
LCK_release(tdbb, routine->getPermanent()->existenceLock);
return false;
case 1:
check_dependencies(tdbb, work->dfw_name.c_str(), NULL, work->dfw_package.c_str(),
objType, transaction);
return true;
case 2:
routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN);
if (!routine)
return false;
return true;
case 3:
return true;
case 4:
{
routine = lookupById(tdbb, work->dfw_id, CacheFlag::RET_ERASED | CacheFlag::NOSCAN);
if (!routine)
return false;
if (routine->getStatement())
{
routine->releaseStatement(tdbb);
}
// delete dependency lists
if (work->dfw_package.isEmpty())
MET_delete_dependencies(tdbb, work->dfw_name, objType, transaction);
//if (routine->getPermanent()->existenceLock)
// routine->getPermanent()->existenceLock->releaseLock(tdbb, ExistenceLock::ReleaseMethod::DropObject);
break;
}
} // switch
return false;
}
private:
// Get relations and fields on which this routine depends, either when it's being
// created or when it's modified.
static void getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction)
{
thread_db* tdbb = JRD_get_thread_data();
Jrd::Database* dbb = tdbb->getDatabase();
if (compile)
compile = !tdbb->getAttachment()->isGbak();
bid blobId;
blobId.clear();
Routine* routine = Self::lookupBlobId(tdbb, work, blobId, compile);
MetadataCache::verify_cache(tdbb);
// get any dependencies now by parsing the blr
if (!routine)
return;
const MetaName depName(work->dfw_package.isEmpty() ?
MetaName(work->dfw_name) : work->dfw_package);
if (!blobId.isEmpty())
{
Statement* statement = NULL;
// Nickolay Samofatov: allocate statement memory pool...
MemoryPool* new_pool = dbb->createPool();
// block is used to ensure verify_cache()
// works in not deleted context
{
Jrd::ContextPoolHolder context(tdbb, new_pool);
MET_get_dependencies(tdbb, nullptr, NULL, 0, NULL, &blobId,
(compile ? &statement : NULL),
NULL, depName,
(work->dfw_package.isEmpty() ? objType : obj_package_body),
0, transaction);
if (statement)
statement->release(tdbb);
else
dbb->deletePool(new_pool);
}
}
else
{
Array<Dependency> dependencies;
const auto allParameters = {&routine->getInputFields(), &routine->getOutputFields()};
for (const auto parameters : allParameters)
{
for (const auto parameter : *parameters)
{
if (parameter->prm_type_of_table.hasData())
{
Dependency dependency(obj_relation);
dependency.relation = MetadataCache::lookupRelation(tdbb,
parameter->prm_type_of_table, CacheFlag::AUTOCREATE);
dependency.subName = parameter->prm_type_of_column;
dependencies.push(dependency);
}
else if (!fb_utils::implicit_domain(parameter->prm_field_source.c_str()))
{
Dependency dependency(obj_field);
dependency.name = parameter->prm_field_source;
dependencies.push(dependency);
}
if (parameter->prm_text_type.isAssigned())
{
Dependency dependency(obj_collation);
dependency.number = parameter->prm_text_type.value;
dependencies.push(dependency);
}
}
}
MET_store_dependencies(tdbb, dependencies, nullptr, depName,
(work->dfw_package.isEmpty() ? objType : obj_package_header),
transaction);
}
MetadataCache::verify_cache(tdbb);
}
};
class FunctionManager : public RoutineManager<FunctionManager, Function, obj_udf,
Function::lookup, Function::lookup>
{
public:
static const char* const getTypeStr()
{
return "function";
}
static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile);
static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
SSHORT validBlr);
static void checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction);
};
class ProcedureManager : public RoutineManager<ProcedureManager, jrd_prc, obj_procedure,
MetadataCache::lookup_procedure_id, MetadataCache::lookup_procedure>
{
public:
static const char* const getTypeStr()
{
return "procedure";
}
static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile);
static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
SSHORT validBlr);
static void checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction);
};
// These methods cannot be defined inline, because GPRE generates wrong code.
Routine* FunctionManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId,
bool compile)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest handle(tdbb, irq_c_fun_dpd, IRQ_REQUESTS);
Routine* routine = nullptr;
FOR(REQUEST_HANDLE handle)
X IN RDB$FUNCTIONS WITH
X.RDB$FUNCTION_NAME EQ work->dfw_name.c_str() AND
X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '')
{
blobId = X.RDB$FUNCTION_BLR;
routine = Function::lookup(tdbb,
QualifiedName(work->dfw_name, work->dfw_package), !compile);
}
END_FOR
return routine;
}
void FunctionManager::validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
SSHORT validBlr)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest request(tdbb, irq_fun_validate, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
FUN IN RDB$FUNCTIONS
WITH FUN.RDB$FUNCTION_ID EQ work->dfw_id AND
FUN.RDB$FUNCTION_BLR NOT MISSING
{
MODIFY FUN USING
FUN.RDB$VALID_BLR = validBlr;
FUN.RDB$VALID_BLR.NULL = FALSE;
END_MODIFY
}
END_FOR
}
void FunctionManager::checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction)
{
// Do nothing, as function output is unnamed.
}
Routine* ProcedureManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId,
bool compile)
{
Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest handle(tdbb, irq_c_prc_dpd, IRQ_REQUESTS);
Routine* routine = nullptr;
FOR(REQUEST_HANDLE handle)
X IN RDB$PROCEDURES WITH
X.RDB$PROCEDURE_NAME EQ work->dfw_name.c_str() AND
X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '')
{
blobId = X.RDB$PROCEDURE_BLR;
routine = MetadataCache::lookup_procedure(tdbb,
QualifiedName(work->dfw_name, work->dfw_package), !compile);
}
END_FOR
return routine;
}
void ProcedureManager::validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
SSHORT validBlr)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest request(tdbb, irq_prc_validate, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
PRC IN RDB$PROCEDURES
WITH PRC.RDB$PROCEDURE_ID EQ work->dfw_id AND
PRC.RDB$PROCEDURE_BLR NOT MISSING
{
MODIFY PRC USING
PRC.RDB$VALID_BLR = validBlr;
PRC.RDB$VALID_BLR.NULL = FALSE;
END_MODIFY
}
END_FOR
}
void ProcedureManager::checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest handle(tdbb, irq_out_proc_param_dep, IRQ_REQUESTS);
ObjectsArray<string> names;
int depCount = 0;
FOR (REQUEST_HANDLE handle)
DEP IN RDB$DEPENDENCIES
WITH DEP.RDB$DEPENDED_ON_NAME EQ work->dfw_name.c_str() AND
DEP.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '') AND
DEP.RDB$DEPENDED_ON_TYPE = obj_procedure AND
NOT DEP.RDB$FIELD_NAME MISSING AND
NOT ANY PP IN RDB$PROCEDURE_PARAMETERS
WITH PP.RDB$PROCEDURE_NAME EQ DEP.RDB$DEPENDED_ON_NAME AND
PP.RDB$PACKAGE_NAME EQUIV DEP.RDB$PACKAGE_NAME AND
PP.RDB$PARAMETER_NAME EQ DEP.RDB$FIELD_NAME AND
PP.RDB$PARAMETER_TYPE EQ 1
{
// If the found object is also being deleted, there's no dependency
if (!find_depend_in_dfw(tdbb, DEP.RDB$DEPENDENT_NAME, DEP.RDB$DEPENDENT_TYPE, 0, transaction))
{
string& name = names.add();
name.printf("%s.%s", work->dfw_name.c_str(), DEP.RDB$FIELD_NAME);
++depCount;
}
}
END_FOR
if (names.hasData())
{
Arg::StatusVector status;
status << Arg::Gds(isc_no_meta_update) << Arg::Gds(isc_no_delete);
for (auto& name : names)
status << Arg::Gds(isc_parameter_name) << Arg::Str(name);
status << Arg::Gds(isc_dependency) << Arg::Num(depCount);
ERR_post(status);
}
}
} // namespace
static const deferred_task task_table[] =
{
/*
@ -709,6 +1183,7 @@ static const deferred_task task_table[] =
{ dfw_drop_package_header, drop_package_header }, // packages should be before procedures
{ dfw_modify_package_header, modify_package_header }, // packages should be before procedures
{ dfw_drop_package_body, drop_package_body }, // packages should be before procedures
*/
{ dfw_create_procedure, ProcedureManager::createRoutine },
{ dfw_create_function, FunctionManager::createRoutine },
{ dfw_delete_procedure, ProcedureManager::deleteRoutine },
@ -716,7 +1191,7 @@ static const deferred_task task_table[] =
{ dfw_modify_procedure, ProcedureManager::modifyRoutine },
{ dfw_modify_function, FunctionManager::modifyRoutine },
{ dfw_delete_prm, delete_parameter },
*/
{ dfw_create_collation, create_collation },
{ dfw_delete_collation, delete_collation },
/*
@ -2223,18 +2698,7 @@ static bool create_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work,
return true;
case 2:
return true;
case 3:
{
auto* cs = MetadataCache::getCharSet(tdbb, TTypeId(work->dfw_id), CacheFlag::NOCOMMIT);
if (!cs)
{
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_charset_not_found) << Arg::Num(CSetId(TTypeId(work->dfw_id))));
}
cs->resetDependentObject(tdbb, ElementBase::ResetType::Recompile);
}
return true;
case 4:
@ -2296,3 +2760,44 @@ static bool delete_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work,
}
static bool delete_parameter(thread_db* tdbb, SSHORT phase, DeferredWork*, jrd_tra*)
{
/**************************************
*
* d e l e t e _ p a r a m e t e r
*
**************************************
*
* Functional description
* Return an error if someone attempts to
* delete a field from a procedure and it is
* used by a view or procedure.
*
**************************************/
SET_TDBB(tdbb);
switch (phase)
{
case 1:
/* hvlad: temporary disable procedure parameters dependency check
until proper solution (something like dyn_mod_parameter)
will be implemented. This check never worked properly
so no harm is done
if (MetadataCache::lookup_procedure_id(tdbb, work->dfw_id, CacheFlag::NOSCAN))
{
const DeferredWork* arg = work->dfw_args;
fb_assert(arg && (arg->dfw_type == dfw_arg_proc_name));
check_dependencies(tdbb, arg->dfw_name.c_str(), work->dfw_name.c_str(),
obj_procedure, transaction);
}
*/
break;
}
return false;
}

View File

@ -409,11 +409,11 @@ struct Dependency
Cached::Relation* relation;
Cached::Function* function;
Cached::Procedure* procedure;
MetaName* name;
MetaName name;
SLONG number;
};
const MetaName* subName;
MetaName subName;
SLONG subNumber;
};

View File

@ -223,6 +223,8 @@ CharSetContainer::CharSetContainer(thread_db* tdbb, MemoryPool& p, MetaId id, Ma
cs(NULL),
cs_lock(nullptr)
{
printf("CharSetContainer::CharSetContainer(..., %04x)\n", id);
SubtypeInfo info;
CSetId cs_id(id);
@ -265,11 +267,10 @@ CsConvert CharSetContainer::lookupConverter(thread_db* tdbb, CSetId toCsId)
return CsConvert(cs->getStruct(), toCs->getStruct());
}
Collation* CharSetVers::getCollation(TTypeId tt_id)
Collation* CharSetVers::getCollation(CollId id)
{
const auto id = CollId(tt_id);
if (!charset_collations[id])
ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(tt_id));
if (USHORT(id) >= charset_collations.getCount() || !charset_collations[id])
return nullptr;
return charset_collations[id];
}
@ -879,7 +880,14 @@ Collation* INTL_texttype_lookup(thread_db* tdbb, TTypeId parm1)
auto* vers = MetadataCache::lookup_charset(tdbb, parm1, CacheFlag::AUTOCREATE);
return vers ? vers->getCollation(parm1) : nullptr;
if (vers)
{
auto* coll = vers->getCollation(parm1);
if (coll)
return coll;
}
ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(parm1));
}

View File

@ -44,7 +44,7 @@ struct IdStorage
struct TTypeId : public IdStorage
{
TTypeId() : IdStorage(0) { }
explicit TTypeId(USHORT id) : IdStorage(id & 0xFF) { }
explicit TTypeId(USHORT id) : IdStorage(id) { }
constexpr TTypeId(CSetId id);
TTypeId(CSetId cs, CollId col);
};

View File

@ -295,9 +295,11 @@ void MetadataCache::update_partners(thread_db* tdbb)
}
#ifdef NEVERDEF //DEV_BUILD
void MetadataCache::verify_cache(thread_db* tdbb)
{
#ifndef NEVERDEF //DEV_BUILD
}
#else // NEVERDEF
/**************************************
*
* M E T _ v e r i f y _ c a c h e
@ -467,7 +469,7 @@ void MetadataCache::verify_cache(thread_db* tdbb)
routine->intUseCount = 0;
}
}
#endif
#endif // NEVERDEF
// Done before MDC is deleted
@ -4592,7 +4594,7 @@ void MET_store_dependencies(thread_db* tdbb,
Dependency dependency = dependencies.pop();
if (!dependency.relation && !dependency.function && !dependency.procedure &&
!dependency.name && !dependency.number)
!dependency.name.hasData() && !dependency.number)
{
continue;
}
@ -4662,7 +4664,7 @@ void MET_store_dependencies(thread_db* tdbb,
break;
case obj_field:
dpdo_name = *(dependency.name);
dpdo_name = dependency.name;
break;
case obj_generator:
@ -4686,14 +4688,14 @@ void MET_store_dependencies(thread_db* tdbb,
break;
case obj_index:
name = *dependency.name;
name = dependency.name;
dpdo_name = name;
break;
}
MetaName field_name;
if (dependency.subNumber || dependency.subName)
if (dependency.subNumber || dependency.subName.hasData())
{
if (dependency.subNumber)
{
@ -4715,7 +4717,7 @@ void MET_store_dependencies(thread_db* tdbb,
}
}
else
field_name = *dependency.subName;
field_name = dependency.subName;
}
if (field_name.hasData())

View File

@ -292,7 +292,7 @@ public:
static Function* lookup_function(thread_db* tdbb, const QualifiedName& name);
static Function* lookup_function(thread_db* tdbb, MetaId id, ObjectBase::Flag flags);
static Cached::Procedure* lookupProcedure(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags);
static Cached::Procedure* lookupProcedure(thread_db* tdbb, MetaId id, ObjectBase::Flag flags = 0);
static Cached::Procedure* lookupProcedure(thread_db* tdbb, MetaId id, ObjectBase::Flag flags);
static Cached::Function* lookupFunction(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags);
//static Cached::Function* lookupFunction(thread_db* tdbb, MetaId id, ObjectBase::Flag flags);
static jrd_rel* lookup_relation(thread_db*, const MetaName&);

View File

@ -69,6 +69,7 @@
#include "../dsql/BoolNodes.h"
#include "../dsql/ExprNodes.h"
#include "../dsql/StmtNodes.h"
#include "../jrd/intl_proto.h"
using namespace Jrd;
@ -163,6 +164,27 @@ namespace
AutoPtr<CompilerScratch> m_csb;
CompilerScratch** const m_csbPtr;
};
class VldTTypeId : public TTypeId
{
public:
VldTTypeId(thread_db* tdbb, USHORT a_id) : TTypeId(a_id)
{
TTypeId parm1(a_id);
if (parm1 == ttype_dynamic)
parm1 = tdbb->getCharSet();
auto* vers = MetadataCache::lookup_charset(tdbb, parm1, CacheFlag::AUTOCREATE);
if (vers)
{
CollId coll(parm1);
if (USHORT(coll) == 0 || vers->getCollation(coll))
return;
}
ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(parm1));
}
};
} // namespace
@ -251,7 +273,7 @@ BoolExprNode* PAR_validation_blr(thread_db* tdbb, Cached::Relation* relation, co
// Parse a BLR datatype. Return the alignment requirements of the datatype.
USHORT PAR_datatype(BlrReader& blrReader, dsc* desc)
USHORT PAR_datatype(thread_db* tdbb, BlrReader& blrReader, dsc* desc)
{
desc->clear();
@ -278,18 +300,18 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc)
break;
case blr_text2:
textType = TTypeId(blrReader.getWord());
textType = VldTTypeId(tdbb, blrReader.getWord());
desc->makeText(blrReader.getWord(), textType);
break;
case blr_cstring2:
desc->dsc_dtype = dtype_cstring;
desc->setTextType(TTypeId(blrReader.getWord()));
desc->setTextType(VldTTypeId(tdbb, blrReader.getWord()));
desc->dsc_length = blrReader.getWord();
break;
case blr_varying2:
textType = TTypeId(blrReader.getWord());
textType = VldTTypeId(tdbb, blrReader.getWord());
desc->makeVarying(blrReader.getWord(), textType);
break;
@ -383,7 +405,7 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc)
desc->dsc_dtype = dtype_blob;
desc->dsc_length = sizeof(ISC_QUAD);
desc->dsc_sub_type = blrReader.getWord();
textType = TTypeId(blrReader.getWord());
textType = VldTTypeId(tdbb, blrReader.getWord());
desc->dsc_scale = textType & 0xFF; // BLOB character set
desc->dsc_flags = textType & 0xFF00; // BLOB collation
break;
@ -426,14 +448,14 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
case blr_domain_name2:
{
const bool fullDomain = (csb->csb_blr_reader.getByte() == blr_domain_full);
MetaName* name = FB_NEW_POOL(csb->csb_pool) MetaName(csb->csb_pool);
csb->csb_blr_reader.getMetaName(*name);
MetaName name;
csb->csb_blr_reader.getMetaName(name);
MetaNamePair namePair(*name, "");
MetaNamePair namePair(name, "");
FieldInfo fieldInfo;
bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
MET_get_domain(tdbb, csb->csb_pool, *name, desc, (exist ? NULL : &fieldInfo));
MET_get_domain(tdbb, csb->csb_pool, name, desc, (exist ? NULL : &fieldInfo));
if (!exist)
csb->csb_map_field_info.put(namePair, fieldInfo);
@ -453,7 +475,7 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
if (dtype == blr_domain_name2)
{
const auto ttype = TTypeId(csb->csb_blr_reader.getWord());
const auto ttype = VldTTypeId(tdbb, csb->csb_blr_reader.getWord());
switch (desc->dsc_dtype)
{
@ -486,14 +508,14 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
const bool fullDomain = (csb->csb_blr_reader.getByte() == blr_domain_full);
MetaName* relationName = FB_NEW_POOL(csb->csb_pool) MetaName(csb->csb_pool);
csb->csb_blr_reader.getMetaName(*relationName);
MetaName* fieldName = FB_NEW_POOL(csb->csb_pool) MetaName(csb->csb_pool);
csb->csb_blr_reader.getMetaName(*fieldName);
MetaName fieldName;
csb->csb_blr_reader.getMetaName(fieldName);
MetaNamePair namePair(*relationName, *fieldName);
MetaNamePair namePair(*relationName, fieldName);
FieldInfo fieldInfo;
bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
MET_get_relation_field(tdbb, csb->csb_pool, *relationName, *fieldName, desc,
MET_get_relation_field(tdbb, csb->csb_pool, *relationName, fieldName, desc,
(exist ? NULL : &fieldInfo));
if (!exist)
@ -514,7 +536,7 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
if (dtype == blr_column_name2)
{
const auto ttype = TTypeId(csb->csb_blr_reader.getWord());
const auto ttype = VldTTypeId(tdbb, csb->csb_blr_reader.getWord());
switch (desc->dsc_dtype)
{
@ -547,7 +569,7 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item
default:
csb->csb_blr_reader.seekBackward(1);
PAR_datatype(csb->csb_blr_reader, desc);
PAR_datatype(tdbb, csb->csb_blr_reader, desc);
break;
}
@ -906,7 +928,7 @@ void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SS
}
if (field_name.length() > 0)
dependency.subName = FB_NEW_POOL(*tdbb->getDefaultPool()) MetaName(*tdbb->getDefaultPool(), field_name);
dependency.subName = field_name;
else if (id >= 0)
dependency.subNumber = id;
@ -1076,7 +1098,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
if (csb->collectingDependencies())
{
Dependency dependency(obj_index);
dependency.name = &item.indexName;
dependency.name = item.indexName;
csb->addDependency(dependency);
}
@ -1146,7 +1168,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
if (csb->collectingDependencies())
{
Dependency dependency(obj_index);
dependency.name = &item.indexName;
dependency.name = item.indexName;
csb->addDependency(dependency);
}
}

View File

@ -54,7 +54,7 @@ Jrd::BoolExprNode* PAR_validation_blr(Jrd::thread_db*, Jrd::Cached::Relation*, c
StreamType PAR_context(Jrd::CompilerScratch*, SSHORT*);
void PAR_dependency(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, StreamType stream,
SSHORT id, const Jrd::MetaName& field_name);
USHORT PAR_datatype(Firebird::BlrReader&, dsc*);
USHORT PAR_datatype(Jrd::thread_db*, Firebird::BlrReader&, dsc*);
USHORT PAR_desc(Jrd::thread_db*, Jrd::CompilerScratch*, dsc*, Jrd::ItemInfo* = NULL);
void PAR_error(Jrd::CompilerScratch*, const Firebird::Arg::StatusVector&, bool isSyntaxError = true);
SSHORT PAR_find_proc_field(const Jrd::jrd_prc*, const Jrd::MetaName&);