diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 875960c7906..37bc7523689 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,6 @@ name: CI -on: [push, pull_request] +on: [pull_request] jobs: diff --git a/src/alice/exe.cpp b/src/alice/exe.cpp index bd56dc06e62..c02abb1ceaa 100644 --- a/src/alice/exe.cpp +++ b/src/alice/exe.cpp @@ -71,7 +71,7 @@ static const TEXT val_errors[] = int EXE_action(const TEXT* database, const SINT64 switches) { bool error = false; - Firebird::AutoMemoryPool newPool(MemoryPool::createPool()); + Firebird::AutoMemoryPool newPool(MemoryPool::createPool(ALLOC_ARGS0)); { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); AliceContextPoolHolder context(tdgbl, newPool); @@ -141,7 +141,7 @@ int EXE_action(const TEXT* database, const SINT64 switches) int EXE_two_phase(const TEXT* database, const SINT64 switches) { bool error = false; - Firebird::AutoMemoryPool newPool(MemoryPool::createPool()); + Firebird::AutoMemoryPool newPool(MemoryPool::createPool(ALLOC_ARGS0)); { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); AliceContextPoolHolder context(tdgbl, newPool); diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 4adad932700..b612e9f6789 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -746,13 +746,13 @@ burp_fld* get_fields( burp_rel* relation) if (!Y.RDB$CHARACTER_SET_ID.NULL) { - field->fld_character_set_id = Y.RDB$CHARACTER_SET_ID; + field->fld_character_set_id = CSetId(Y.RDB$CHARACTER_SET_ID); field->fld_flags |= FLD_charset_flag; } if (!X.RDB$COLLATION_ID.NULL) { - field->fld_collation_id = X.RDB$COLLATION_ID; + field->fld_collation_id = CollId(X.RDB$COLLATION_ID); field->fld_flags |= FLD_collate_flag; } @@ -865,13 +865,13 @@ burp_fld* get_fields( burp_rel* relation) if (!Y.RDB$CHARACTER_SET_ID.NULL) { - field->fld_character_set_id = Y.RDB$CHARACTER_SET_ID; + field->fld_character_set_id = CSetId(Y.RDB$CHARACTER_SET_ID); field->fld_flags |= FLD_charset_flag; } if (!X.RDB$COLLATION_ID.NULL) { - field->fld_collation_id = X.RDB$COLLATION_ID; + field->fld_collation_id = CollId(X.RDB$COLLATION_ID); field->fld_flags |= FLD_collate_flag; } diff --git a/src/burp/burp.h b/src/burp/burp.h index 1e4e08c49e3..db2eb067bea 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -47,6 +47,7 @@ #include "../common/status.h" #include "../common/sha.h" #include "../common/classes/ImplementHelper.h" +#include "../jrd/intl.h" #ifdef HAVE_UNISTD_H #include @@ -724,8 +725,8 @@ struct burp_fld SSHORT fld_null_flag; ISC_QUAD fld_default_value; ISC_QUAD fld_default_source; - SSHORT fld_character_set_id; - SSHORT fld_collation_id; + CSetId fld_character_set_id; + CollId fld_collation_id; RCRD_OFFSET fld_sql; RCRD_OFFSET fld_null; }; @@ -944,7 +945,7 @@ class GblPool } explicit GblPool(bool ownPool) - : gbl_pool(ownPool ? MemoryPool::createPool(getDefaultMemoryPool()) : getDefaultMemoryPool()) + : gbl_pool(ownPool ? MemoryPool::createPool(ALLOC_ARGS1 getDefaultMemoryPool()) : getDefaultMemoryPool()) { } ~GblPool() diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 7198aaeaca4..1cd645d604d 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -3906,11 +3906,11 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation) break; case att_field_character_set: - field->fld_character_set_id = (USHORT) get_int32(tdgbl); + field->fld_character_set_id = CSetId(get_int32(tdgbl)); break; case att_field_collation_id: - field->fld_collation_id = (USHORT) get_int32(tdgbl); + field->fld_collation_id = CollId(get_int32(tdgbl)); X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = field->fld_collation_id; break; @@ -4110,11 +4110,11 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation) break; case att_field_character_set: - field->fld_character_set_id = (USHORT) get_int32(tdgbl); + field->fld_character_set_id = CSetId(get_int32(tdgbl)); break; case att_field_collation_id: - field->fld_collation_id = (USHORT) get_int32(tdgbl); + field->fld_collation_id = CollId(get_int32(tdgbl)); X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = field->fld_collation_id; break; @@ -11913,13 +11913,13 @@ bool WriteRelationMeta::prepareBatch(BurpGlobals* tdgbl) SLONG sqlLength, sqlSubType, sqlScale, sqlType; desc.getSqlInfo(&sqlLength, &sqlSubType, &sqlScale, &sqlType); - SLONG characterSetId = field->fld_character_set_id; + CSetId characterSetId = field->fld_character_set_id; if (tdgbl->gbl_sw_fix_fss_data && field->fld_character_set_id == CS_UNICODE_FSS && ((sqlType == SQL_BLOB && field->fld_sub_type == isc_blob_text && !(field->fld_flags & FLD_array)) || sqlType == SQL_TEXT || sqlType == SQL_VARYING)) { - characterSetId = tdgbl->gbl_sw_fix_fss_data_id; + characterSetId = CSetId(tdgbl->gbl_sw_fix_fss_data_id); } else if (field->fld_flags & FLD_array) { diff --git a/src/common/CharSet.h b/src/common/CharSet.h index bae77455899..7606c41871c 100644 --- a/src/common/CharSet.h +++ b/src/common/CharSet.h @@ -32,18 +32,19 @@ #include "CsConvert.h" #include "IntlUtil.h" +#include "../jrd/intl.h" namespace Firebird { - template <> - inline void SimpleDelete::clear(charset* cs) +template <> +inline void SimpleDelete::clear(charset* cs) +{ + if (cs) { - if (cs) - { - Firebird::IntlUtil::finiCharset(cs); - delete cs; - } + Firebird::IntlUtil::finiCharset(cs); + delete cs; } +} class CharSet { @@ -84,7 +85,7 @@ class CharSet public: virtual ~CharSet() {} - USHORT getId() const { return id; } + CSetId getId() const { return id; } const char* getName() const { return cs->charset_name; } UCHAR minBytesPerChar() const { return cs->charset_min_bytes_per_char; } UCHAR maxBytesPerChar() const { return cs->charset_max_bytes_per_char; } @@ -133,7 +134,7 @@ class CharSet const ULONG startPos, const ULONG length) const = 0; private: - USHORT id; + CSetId id; charset* cs; UCHAR sqlMatchAny[sizeof(ULONG)]; UCHAR sqlMatchOne[sizeof(ULONG)]; diff --git a/src/common/CvtFormat.cpp b/src/common/CvtFormat.cpp index c0485e20871..0dc092e263a 100644 --- a/src/common/CvtFormat.cpp +++ b/src/common/CvtFormat.cpp @@ -1660,7 +1660,7 @@ ISC_TIMESTAMP_TZ CVT_format_string_to_datetime(const dsc* desc, const Firebird:: if (format.isEmpty()) cb->err(Arg::Gds(isc_sysf_invalid_null_empty) << Arg::Str(STRINGIZE(format))); - USHORT dtype; + TTypeId dtype; UCHAR* sourceString; const USHORT stringLength = CVT_get_string_ptr_common(desc, &dtype, &sourceString, nullptr, 0, 0, cb); diff --git a/src/common/TextType.cpp b/src/common/TextType.cpp index 4d8353655e3..9be44a758b7 100644 --- a/src/common/TextType.cpp +++ b/src/common/TextType.cpp @@ -94,6 +94,7 @@ #include "firebird.h" #include "iberror.h" #include "../jrd/intl_classes.h" +#include "../common/TextType.h" #include "../common/IntlUtil.h" #include "../common/classes/Aligner.h" @@ -101,7 +102,7 @@ namespace Firebird { -TextType::TextType(TTYPE_ID _type, texttype *_tt, USHORT _attributes, CharSet* _cs) +TextType::TextType(TTypeId _type, texttype *_tt, USHORT _attributes, CharSet* _cs) : tt(_tt), cs(_cs), type(_type), attributes(_attributes) { if (cs->getSqlMatchAnyLength() != 0) diff --git a/src/common/TextType.h b/src/common/TextType.h index 858b9fb5c8a..a2e6d8c20e0 100644 --- a/src/common/TextType.h +++ b/src/common/TextType.h @@ -31,6 +31,7 @@ #define COMMON_TEXTTYPE_H #include "../common/classes/MetaString.h" +#include "../jrd/intl.h" struct texttype; @@ -41,7 +42,7 @@ class CharSet; class TextType { public: - TextType(TTYPE_ID _type, texttype* _tt, USHORT _attributes, CharSet* _cs); + TextType(TTypeId _type, texttype* _tt, USHORT _attributes, CharSet* _cs); private: TextType(const TextType&); // Not implemented @@ -77,7 +78,7 @@ class TextType ULONG dstLen, UCHAR* dst); - USHORT getType() const + TTypeId getType() const { return type; } @@ -95,6 +96,11 @@ class TextType BYTE getCanonicalWidth() const; USHORT getFlags() const; + const char* c_name() const + { + return name.c_str(); + } + public: Firebird::MetaString name; @@ -103,7 +109,7 @@ class TextType CharSet* cs; private: - TTYPE_ID type; + TTypeId type; USHORT attributes; public: diff --git a/src/common/classes/Bits.h b/src/common/classes/Bits.h new file mode 100644 index 00000000000..aee8fc763d8 --- /dev/null +++ b/src/common/classes/Bits.h @@ -0,0 +1,136 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: Bits.h + * DESCRIPTION: Arbitrary size bitmask + * + * The contents of this file are subject to the Initial + * Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * 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 Alexander Peshkoff + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2016, 2022 Alexander Peshkoff + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + */ + +#ifndef COMMON_CLASSES_BITS_H +#define COMMON_CLASSES_BITS_H + +namespace Firebird { + + // Arbitrary size bitmask + template + class Bits + { + static const unsigned shift = 3; + static const unsigned bitmask = (1 << shift) - 1; + + static const unsigned L = (N >> shift) + (N & bitmask ? 1 : 0); + + public: + static const unsigned BYTES_COUNT = L; + + Bits() + { + clearAll(); + } + + Bits(const Bits& b) + { + assign(b); + } + + Bits& operator=(const Bits& b) + { + assign(b); + return *this; + } + + Bits& set(unsigned i) + { + fb_assert(i < N); + if (i < N) + data[index(i)] |= mask(i); + return *this; + } + + Bits& setAll() + { + memset(data, ~0, sizeof data); + return *this; + } + + Bits& clear(unsigned i) + { + fb_assert(i < N); + if (i < N) + data[index(i)] &= ~mask(i); + return *this; + } + + Bits& clearAll() + { + memset(data, 0, sizeof data); + return *this; + } + + bool test(unsigned int i) const + { + fb_assert(i < N); + if (i >= N) + return false; + return data[index(i)] & mask(i); + } + + void load(const void* from) + { + memcpy(data, from, sizeof data); + } + + void store(void* to) const + { + memcpy(to, data, sizeof data); + } + + Bits& operator|=(const Bits& b) + { + for (unsigned n = 0; n < L; ++n) + data[n] |= b.data[n]; + return *this; + } + + private: + UCHAR data[L]; + + void assign(const Bits& b) + { + memcpy(data, b.data, sizeof data); + } + + static unsigned index(unsigned i) + { + return i >> shift; + } + + static UCHAR mask(unsigned i) + { + return 1U << (i & bitmask); + } + }; + +} // namespace Firebird + +#endif // COMMON_CLASSES_BITS_H + diff --git a/src/common/classes/BlrReader.h b/src/common/classes/BlrReader.h index 8a8f9b9eaca..8b487a66188 100644 --- a/src/common/classes/BlrReader.h +++ b/src/common/classes/BlrReader.h @@ -28,8 +28,8 @@ #include "../common/StatusArg.h" #include "../jrd/constants.h" -namespace Firebird { +namespace Firebird { class BlrReader { @@ -183,7 +183,6 @@ class BlrReader const UCHAR* pos; }; - } // namespace #endif // COMMON_CLASSES_BLR_READER_H diff --git a/src/common/classes/MetaString.h b/src/common/classes/MetaString.h index 6c954d3c07f..eaa84e52bb4 100644 --- a/src/common/classes/MetaString.h +++ b/src/common/classes/MetaString.h @@ -60,7 +60,7 @@ class MetaString MetaString() { init(); count = 0; } MetaString(const char* s) { assign(s); } MetaString(const char* s, FB_SIZE_T l) { assign(s, l); } - MetaString(const MetaString& m) { set(m); } + MetaString(const MetaString& m) = default; //{ set(m); } MetaString(const AbstractString& s) { assign(s.c_str(), s.length()); } explicit MetaString(MemoryPool&) { init(); count = 0; } MetaString(MemoryPool&, const char* s) { assign(s); } @@ -73,7 +73,7 @@ class MetaString MetaString& clear() { return assign(nullptr, 0); } MetaString& operator=(const char* s) { return assign(s); } MetaString& operator=(const AbstractString& s) { return assign(s.c_str(), s.length()); } - MetaString& operator=(const MetaString& m) { return set(m); } + MetaString& operator=(const MetaString& m) = default; //{ return set(m); } char* getBuffer(const FB_SIZE_T l); FB_SIZE_T length() const { return count; } diff --git a/src/common/classes/Nullable.h b/src/common/classes/Nullable.h new file mode 100644 index 00000000000..57151406021 --- /dev/null +++ b/src/common/classes/Nullable.h @@ -0,0 +1,169 @@ +/* + * The contents of this file are subject to the Initial + * Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * 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 Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2010 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef CLASSES_NULLABLE_H +#define CLASSES_NULLABLE_H + +#include "firebird.h" +#include "../common/classes/fb_string.h" +#include "../jrd/constants.h" + + +// Auxiliary template to build an empty value. +template // Generic NullableClear +class NullableClear +{ +public: + static void clear(T& v) + { + v = T(); + } +}; + + +// Nullable support without constructor, to allow usage in unions (used in the parser). +template class BaseNullable +{ +public: + static BaseNullable val(const T& v) + { + BaseNullable nullable; + nullable.value = v; + nullable.specified = true; + return nullable; + } + + static BaseNullable empty() + { + BaseNullable nullable; + NullableClear::clear(nullable.value); + nullable.specified = false; + return nullable; + } + + T orElse(T elseValue) const + { + return specified ? value : elseValue; + } + + bool operator ==(const BaseNullable& o) const + { + return (!specified && !o.specified) || (specified == o.specified && value == o.value); + } + + bool operator ==(const T& o) const + { + return specified && value == o; + } + + void operator =(const T& v) + { + this->value = v; + this->specified = true; + } + + bool isUnknown() const + { + return !specified; + } + + bool isAssigned() const + { + return specified; + } + +public: + T value; + bool specified; +}; + + +// NullableClear specializations. + +template +class NullableClear > +{ +public: + static void clear(BaseNullable& v) + { + v.specified = false; + } +}; + +template <> +class NullableClear +{ +public: + static void clear(rel_t& v) + { + v = rel_persistent; + } +}; + +// Actual Nullable template. +template class Nullable : public BaseNullable +{ +public: + explicit Nullable(const T& v) + { + this->value = v; + this->specified = true; + } + + Nullable(const Nullable& o) = default; + + Nullable() + { + invalidate(); + } + + void operator =(const BaseNullable& o) + { + this->value = o.value; + this->specified = o.specified; + } + + void operator =(const T& v) + { + this->value = v; + this->specified = true; + } + + bool assignOnce(const T& v) + { + if (this->specified) + return false; + + *this = v; + return true; + } + + void invalidate() + { + NullableClear::clear(this->value); + this->specified = false; + } +}; + +typedef Nullable TriState; + +#endif // CLASSES_NULLABLE_H diff --git a/src/common/classes/alloc.cpp b/src/common/classes/alloc.cpp index f3869386b88..5b91abc7aeb 100644 --- a/src/common/classes/alloc.cpp +++ b/src/common/classes/alloc.cpp @@ -1710,9 +1710,6 @@ class MemPool static void deallocate(void* block) noexcept; bool validate(char* buf, FB_SIZE_T size); - // Create memory pool instance - static MemPool* createPool(MemPool* parent, MemoryStats& stats); - MemoryStats& getStatsGroup() noexcept { return *stats; @@ -2060,6 +2057,13 @@ MemPool::~MemPool(void) } #ifdef MEM_DEBUG +#ifdef DEBUG_LOST_POOLS + for (auto* c = child; c; c = c->child) + fprintf(stderr, "%p: child = %p\n", this, c); +#endif + + fb_assert(!child); + if (parent) { MutexLockGuard unlinkGuard(parent->mutex, FB_FUNCTION); @@ -2114,12 +2118,12 @@ void MemPool::newExtent(size_t& size, Extent** linkedList) size = extent->spaceRemaining; } -MemoryPool* MemoryPool::createPool(MemoryPool* parentPool, MemoryStats& stats) +MemoryPool* MemoryPool::createPool(ALLOC_PARAMS1 MemoryPool* parentPool, MemoryStats& stats) { if (!parentPool) parentPool = getDefaultMemoryPool(); - MemPool* p = FB_NEW_POOL(*parentPool) MemPool(*(parentPool->pool), stats, &defaultExtentsCache); + MemPool* p = new(*parentPool ALLOC_PASS_ARGS) MemPool(*(parentPool->pool), stats, &defaultExtentsCache); return FB_NEW_POOL(*parentPool) MemoryPool(p); } diff --git a/src/common/classes/alloc.h b/src/common/classes/alloc.h index 622d95fce34..d73155ba127 100644 --- a/src/common/classes/alloc.h +++ b/src/common/classes/alloc.h @@ -178,22 +178,39 @@ friend class ExternalMemoryHandler; static MemoryPool* defaultMemoryManager; static MemoryPool* externalMemoryManager; -public: - // Create memory pool instance - static MemoryPool* createPool(MemoryPool* parent = NULL, MemoryStats& stats = *default_stats_group); - // Delete memory pool instance - static void deletePool(MemoryPool* pool); + const void* mp() const + { + return pool; + } +public: #ifdef DEBUG_GDS_ALLOC #define ALLOC_ARGS , __FILE__, __LINE__ +#define ALLOC_ARGS1 __FILE__, __LINE__, +#define ALLOC_ARGS0 __FILE__, __LINE__ #define ALLOC_PARAMS , const char* file, int line +#define ALLOC_PARAMS1 const char* file, int line, +#define ALLOC_PARAMS0 const char* file, int line #define ALLOC_PASS_ARGS , file, line +#define ALLOC_PASS_ARGS1 file, line, +#define ALLOC_PASS_ARGS0 file, line #else #define ALLOC_ARGS #define ALLOC_PARAMS #define ALLOC_PASS_ARGS +#define ALLOC_ARGS1 +#define ALLOC_PARAMS1 +#define ALLOC_PASS_ARGS1 +#define ALLOC_ARGS0 +#define ALLOC_PARAMS0 +#define ALLOC_PASS_ARGS0 #endif // DEBUG_GDS_ALLOC + // Create memory pool instance + static MemoryPool* createPool(ALLOC_PARAMS1 MemoryPool* parent = NULL, MemoryStats& stats = *default_stats_group); + // Delete memory pool instance + static void deletePool(MemoryPool* pool); + void* calloc(size_t size ALLOC_PARAMS); static void* globalAlloc(size_t s ALLOC_PARAMS) @@ -348,6 +365,7 @@ class SubsystemContextPoolHolder : public ContextPoolHolder savedThreadData(subThreadData), savedPool(savedThreadData->getDefaultPool()) { + fb_assert(newPool); savedThreadData->setDefaultPool(newPool); } @@ -356,6 +374,13 @@ class SubsystemContextPoolHolder : public ContextPoolHolder savedThreadData->setDefaultPool(savedPool); } +/* ?????????????????? + operator SubsystemPool&() + { + return *savedThreadData->getDefaultPool(); + } + */ + private: SubsystemThreadData* savedThreadData; SubsystemPool* savedPool; diff --git a/src/common/classes/array.h b/src/common/classes/array.h index 8b3ff24d9d1..93030c66e4d 100644 --- a/src/common/classes/array.h +++ b/src/common/classes/array.h @@ -29,6 +29,7 @@ #include "../common/gdsassert.h" #include +#include #include #include "../common/classes/vector.h" #include "../common/classes/alloc.h" @@ -69,8 +70,10 @@ class EmptyStorage : public AutoStorage // Dynamic array of simple types template > -class Array : protected Storage +class Array : public Storage { +// !!!!! temp commemnted out - FastLoadLevel failure static_assert(std::is_trivially_copyable(), "Only simple (trivially copyable) types supported in array"); + public: typedef FB_SIZE_T size_type; typedef FB_SSIZE_T difference_type; @@ -82,6 +85,8 @@ class Array : protected Storage typedef pointer iterator; typedef const_pointer const_iterator; + static const size_type npos = ~size_type(0); + explicit Array(MemoryPool& p) : Storage(p), count(0), @@ -190,6 +195,15 @@ class Array : protected Storage return *this; } + template + Array& operator=(const Array& source) + { + ensureCapacity(source.getCount(), false); + for (size_type index = 0; index < count; ++index) + data[index] = source[index]; + return *this; + } + const T& operator[](size_type index) const noexcept { return getElement(index); @@ -444,7 +458,7 @@ class Array : protected Storage 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 { @@ -459,6 +473,19 @@ class Array : protected Storage return false; } + bool find(std::function 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; @@ -481,7 +508,19 @@ class Array : protected Storage { if (count != op.count) return false; - return memcmp(data, op.data, count) == 0; + + // return memcmp(data, op.data, count) == 0; + // fast but wrong - imagine array element with non-dense elements + + auto my = begin(); + const auto my_end = end(); + for (auto him = op.begin(); my != my_end; ++my, ++him) + { + if (! (*my == *him)) + return false; + } + + return true; } // Member function only for some debugging tests. Hope nobody is bothered. @@ -616,6 +655,18 @@ class SortedArray : public Array return pos; } + size_type addUniq(const Value& item) + { + size_type pos; + fb_assert(sortMode == FB_ARRAY_SORT_WHEN_ADD); + if (!find(KeyOfValue::generate(item), pos)) + { + this->insert(pos, item); + return pos; + } + return this->npos; + } + void setSortMode(int sm) { if (sortMode != FB_ARRAY_SORT_WHEN_ADD && sm == FB_ARRAY_SORT_WHEN_ADD && !sorted) diff --git a/src/common/classes/auto.h b/src/common/classes/auto.h index fa286c709ef..05ad8d1985c 100644 --- a/src/common/classes/auto.h +++ b/src/common/classes/auto.h @@ -233,11 +233,11 @@ class AutoSetRestore : public AutoSaveRestore }; -template +template class AutoSetRestoreFlag { public: - AutoSetRestoreFlag(T* aValue, T newBit, bool set) + AutoSetRestoreFlag(T* aValue, T2 newBit, bool set) : value(aValue), bit(newBit), oldValue((*value) & bit) @@ -254,7 +254,7 @@ class AutoSetRestoreFlag *value |= oldValue; } - void release(T cleanBit) + void release(T2 cleanBit) { bit &= ~cleanBit; oldValue &= ~cleanBit; @@ -266,7 +266,7 @@ class AutoSetRestoreFlag AutoSetRestoreFlag& operator =(const AutoSetRestoreFlag&); T* value; - T bit; + T2 bit; T oldValue; }; diff --git a/src/common/classes/fb_pair.h b/src/common/classes/fb_pair.h index ac202a43ecc..3ed2f5221ef 100644 --- a/src/common/classes/fb_pair.h +++ b/src/common/classes/fb_pair.h @@ -47,6 +47,7 @@ template : first(v1), second(v2) { } explicit NonPooled(MemoryPool&, const NonPooled& lp) : first(lp.first), second(lp.second) { } + NonPooled(const NonPooled& lp) = default; parLeft first; parRight second; }; @@ -64,6 +65,8 @@ template : first(v1), second(p, v2) { } explicit Right(MemoryPool& p, const Right& lp) : first(lp.first), second(p, lp.second) { } + Right(const Right& lp) + : first(lp.first), second(AutoStorage::getAutoMemoryPool(), lp.second) { } parLeft first; parRight second; }; @@ -81,6 +84,8 @@ template : first(p, v1), second(v2) { } explicit Left(MemoryPool& p, const Left& lp) : first(p, lp.first), second(lp.second) { } + Left(const Left& lp) + : first(AutoStorage::getAutoMemoryPool(), lp.first), second(lp.second) { } parLeft first; parRight second; }; @@ -97,6 +102,9 @@ template : first(p, v1), second(p, v2) { } explicit Full(MemoryPool& p, const Full& lp) : first(p, lp.first), second(p, lp.second) { } + Full(const Full& lp) + : first(AutoStorage::getAutoMemoryPool(), lp.first), + second(AutoStorage::getAutoMemoryPool(), lp.second) { } parLeft first; parRight second; }; @@ -116,8 +124,7 @@ template Pair() : BasePair(AutoStorage::getAutoMemoryPool()) { } Pair(const Pair_first_type& v1, const Pair_second_type& v2) : BasePair(AutoStorage::getAutoMemoryPool(), v1, v2) { } - Pair(const Pair& lp) - : BasePair(AutoStorage::getAutoMemoryPool(), lp) { } + Pair(const Pair& lp) = default; bool operator==(const Pair& v) const { return this->first == v.first && this->second == v.second; diff --git a/src/common/classes/sparse_bitmap.h b/src/common/classes/sparse_bitmap.h index e08bbc22f78..3b2d632f73b 100644 --- a/src/common/classes/sparse_bitmap.h +++ b/src/common/classes/sparse_bitmap.h @@ -31,6 +31,7 @@ #define SPARSE_BITMAP_H #include "../common/classes/alloc.h" +#include "../common/classes/tree.h" namespace Firebird { diff --git a/src/common/classes/vector.h b/src/common/classes/vector.h index d142f4033e1..dfa49ebac4f 100644 --- a/src/common/classes/vector.h +++ b/src/common/classes/vector.h @@ -32,6 +32,7 @@ #include "../common/gdsassert.h" #include +#include namespace Firebird { @@ -114,6 +115,14 @@ class Vector return data; } + void grow(FB_SIZE_T cntL) noexcept + { + fb_assert(cntL <= Capacity); + fb_assert(cntL > count); + memset(data + count, 0, sizeof(T) * (cntL - count)); + count = cntL; + } + void push(const T& item) { add(item); @@ -177,6 +186,16 @@ class DefaultComparator } }; +template +class DefaultComparator +{ +public: + static bool greaterThan(const T* i1, const T* i2) + { + return std::greater{}(i1, i2); + } +}; + // Template to convert value to index directly template class DefaultKeyValue diff --git a/src/common/common.h b/src/common/common.h index 14cd295e35b..b4edd6cb1dc 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -756,6 +756,7 @@ extern "C" int remove(const char* path); #define MAX_USHORT ((USHORT)0xFFFF) #define MIN_USHORT 0x0000 +#define MAX_META_ID MAX_USHORT #define MAX_SSHORT 0x7FFF #define MIN_SSHORT (-MAX_SSHORT - 1) @@ -1038,4 +1039,6 @@ namespace Firebird { static IMessageMetadata* const DELAYED_OUT_FORMAT = reinterpret_cast(1); } +//#define DEBUG_LOST_POOLS 1 + #endif /* COMMON_COMMON_H */ diff --git a/src/common/cvt.cpp b/src/common/cvt.cpp index 788d473a8a8..10ac0159cc7 100644 --- a/src/common/cvt.cpp +++ b/src/common/cvt.cpp @@ -143,7 +143,7 @@ static void integer_to_text(const dsc*, dsc*, Callbacks*); static void int128_to_text(const dsc*, dsc*, Callbacks* cb); static void localError(const Firebird::Arg::StatusVector&); static SSHORT cvt_get_short(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err); -static void make_null_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); +static void make_null_string(const dsc*, TTypeId, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); namespace { class RetPtr; @@ -412,7 +412,7 @@ static void float_to_text(const dsc* from, dsc* to, Callbacks* cb) dsc intermediate; intermediate.dsc_dtype = dtype_text; - intermediate.dsc_ttype() = ttype_ascii; + intermediate.setTextType(ttype_ascii); // CVC: If you think this is dangerous, replace the "else" with a call to // MEMMOVE(temp, temp + 1, chars_printed) or something cleverer. // Paranoid assumption: @@ -457,7 +457,7 @@ static void decimal_float_to_text(const dsc* from, dsc* to, DecimalStatus decSt, dsc intermediate; intermediate.dsc_dtype = dtype_text; - intermediate.dsc_ttype() = ttype_ascii; + intermediate.setTextType(ttype_ascii); intermediate.dsc_address = reinterpret_cast(temp); intermediate.dsc_length = strlen(temp); @@ -485,7 +485,7 @@ static void int128_to_text(const dsc* from, dsc* to, Callbacks* cb) dsc intermediate; intermediate.dsc_dtype = dtype_text; - intermediate.dsc_ttype() = ttype_ascii; + intermediate.setTextType(ttype_ascii); intermediate.dsc_address = reinterpret_cast(temp); intermediate.dsc_length = strlen(temp); @@ -628,7 +628,7 @@ static void integer_to_text(const dsc* from, dsc* to, Callbacks* cb) ULONG trailing = ULONG(to->dsc_length) - length; if (trailing > 0) { - CHARSET_ID chid = cb->getChid(to); // : DSC_GET_CHARSET(to); + CSetId chid = cb->getChid(to); // : DSC_GET_CHARSET(to); const char pad = chid == ttype_binary ? '\0' : ' '; memset(q, pad, trailing); @@ -1681,11 +1681,11 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c if ((from->dsc_dtype == dtype_text && to->dsc_dtype == dtype_dbkey && - from->dsc_ttype() == ttype_binary && + from->getTextType() == ttype_binary && from->dsc_length == to->dsc_length) || (to->dsc_dtype == dtype_text && from->dsc_dtype == dtype_dbkey && - to->dsc_ttype() == ttype_binary && + to->getTextType() == ttype_binary && from->dsc_length == to->dsc_length)) { memcpy(p, q, length); @@ -1979,12 +1979,12 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c * unless really required is a good optimization. */ - CHARSET_ID charset2; + CSetId charset2; if (cb->transliterate(from, to, charset2)) return; { // scope - USHORT strtype_unused; + TTypeId strtype_unused; UCHAR *ptr; length = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, decSt, cb); q = ptr; @@ -2074,7 +2074,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c dsc intermediate; intermediate.dsc_dtype = dtype_text; - intermediate.dsc_ttype() = ttype_ascii; + intermediate.setTextType(ttype_ascii); intermediate.makeText(static_cast(strlen(text)), CS_ASCII, reinterpret_cast(text)); @@ -2145,7 +2145,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_dbkey: if (from->isText()) { - USHORT strtype_unused; + TTypeId strtype_unused; UCHAR* ptr; USHORT len = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, decSt, cb); @@ -2423,7 +2423,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb) MOVE_CLEAR(&desc, sizeof(desc)); desc.dsc_address = (UCHAR*) temp; desc.dsc_dtype = dtype_text; - desc.dsc_ttype() = ttype_ascii; + desc.setTextType(ttype_ascii); desc.dsc_length = (p - temp); if (from->isTimeStamp() && version4) @@ -2443,7 +2443,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb) void make_null_string(const dsc* desc, - USHORT to_interp, + TTypeId to_interp, const char** address, vary* temp, USHORT length, @@ -2491,7 +2491,7 @@ void make_null_string(const dsc* desc, USHORT CVT_make_string(const dsc* desc, - USHORT to_interp, + TTypeId to_interp, const char** address, vary* temp, USHORT length, @@ -2627,7 +2627,7 @@ static SSHORT cvt_decompose(const char* string, dsc errd; MOVE_CLEAR(&errd, sizeof(errd)); errd.dsc_dtype = dtype_text; - errd.dsc_ttype() = ttype_ascii; + errd.setTextType(ttype_ascii); errd.dsc_length = length; errd.dsc_address = reinterpret_cast(const_cast(string)); @@ -2893,7 +2893,7 @@ Int128 CVT_hex_to_int128(const char* str, USHORT len) } -USHORT CVT_get_string_ptr_common(const dsc* desc, USHORT* ttype, UCHAR** address, +USHORT CVT_get_string_ptr_common(const dsc* desc, TTypeId* ttype, UCHAR** address, vary* temp, USHORT length, DecimalStatus decSt, Callbacks* cb) { /************************************** @@ -3656,11 +3656,11 @@ namespace } public: - virtual bool transliterate(const dsc* from, dsc* to, CHARSET_ID&); - virtual CHARSET_ID getChid(const dsc* d); - virtual CharSet* getToCharset(CHARSET_ID charset2); - virtual void validateData(CharSet* toCharset, SLONG length, const UCHAR* q); - virtual ULONG validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start, + virtual bool transliterate(const dsc* from, dsc* to, CSetId&); + virtual CSetId getChid(const dsc* d); + virtual Firebird::CharSet* getToCharset(CSetId charset2); + virtual void validateData(Firebird::CharSet* toCharset, SLONG length, const UCHAR* q); + virtual ULONG validateLength(Firebird::CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start, const USHORT size); virtual SLONG getLocalDate(); virtual ISC_TIMESTAMP getCurrentGmtTimeStamp(); @@ -3668,13 +3668,13 @@ namespace virtual void isVersion4(bool& v4); } commonCallbacks(status_exception::raise); - bool CommonCallbacks::transliterate(const dsc*, dsc* to, CHARSET_ID& charset2) + bool CommonCallbacks::transliterate(const dsc*, dsc* to, CSetId& charset2) { charset2 = INTL_TTYPE(to); return false; } - CharSet* CommonCallbacks::getToCharset(CHARSET_ID) + Firebird::CharSet* CommonCallbacks::getToCharset(CSetId) { return NULL; } @@ -3683,7 +3683,7 @@ namespace { } - ULONG CommonCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start, + ULONG CommonCallbacks::validateLength(Firebird::CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start, const USHORT size) { if (length > size) @@ -3711,7 +3711,7 @@ namespace return MIN(length, size); } - CHARSET_ID CommonCallbacks::getChid(const dsc* d) + CSetId CommonCallbacks::getChid(const dsc* d) { return INTL_TTYPE(d); } @@ -3740,7 +3740,7 @@ namespace Firebird { Callbacks* CVT_commonCallbacks = &commonCallbacks; } -USHORT CVT_get_string_ptr(const dsc* desc, USHORT* ttype, UCHAR** address, +USHORT CVT_get_string_ptr(const dsc* desc, TTypeId* ttype, UCHAR** address, vary* temp, USHORT length, DecimalStatus decSt, ErrorFunction err) { /************************************** diff --git a/src/common/cvt.h b/src/common/cvt.h index da95c1bc734..c814f230354 100644 --- a/src/common/cvt.h +++ b/src/common/cvt.h @@ -30,6 +30,7 @@ #define COMMON_CVT_H #include "../common/DecFloat.h" +#include "../jrd/intl.h" namespace Firebird { @@ -54,11 +55,11 @@ class Callbacks } public: - virtual bool transliterate(const dsc* from, dsc* to, CHARSET_ID&) = 0; - virtual CHARSET_ID getChid(const dsc* d) = 0; - virtual CharSet* getToCharset(CHARSET_ID charset2) = 0; - virtual void validateData(CharSet* toCharset, SLONG length, const UCHAR* q) = 0; - virtual ULONG validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start, + virtual bool transliterate(const dsc* from, dsc* to, CSetId&) = 0; + virtual CSetId getChid(const dsc* d) = 0; + virtual Firebird::CharSet* getToCharset(CSetId charset2) = 0; + virtual void validateData(Firebird::CharSet* toCharset, SLONG length, const UCHAR* q) = 0; + virtual ULONG validateLength(Firebird::CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start, const USHORT size) = 0; virtual SLONG getLocalDate() = 0; virtual ISC_TIMESTAMP getCurrentGmtTimeStamp() = 0; @@ -94,12 +95,12 @@ Firebird::Decimal64 CVT_get_dec64(const dsc*, Firebird::DecimalStatus, ErrorFunc Firebird::Decimal128 CVT_get_dec128(const dsc*, Firebird::DecimalStatus, ErrorFunction); Firebird::Int128 CVT_get_int128(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); Firebird::Int128 CVT_hex_to_int128(const char* str, USHORT len); -USHORT CVT_make_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); +USHORT CVT_make_string(const dsc*, TTypeId, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); void CVT_move_common(const dsc*, dsc*, Firebird::DecimalStatus, Firebird::Callbacks*); void CVT_move(const dsc*, dsc*, Firebird::DecimalStatus, ErrorFunction); SSHORT CVT_decompose(const char*, USHORT, Firebird::Int128*, ErrorFunction); -USHORT CVT_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); -USHORT CVT_get_string_ptr_common(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*); +USHORT CVT_get_string_ptr(const dsc*, TTypeId*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); +USHORT CVT_get_string_ptr_common(const dsc*, TTypeId*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*); SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); void CVT_string_to_datetime(const dsc*, ISC_TIMESTAMP_TZ*, bool*, const Firebird::EXPECT_DATETIME, diff --git a/src/common/dsc.cpp b/src/common/dsc.cpp index ee867be24bf..b0cbe4ac00f 100644 --- a/src/common/dsc.cpp +++ b/src/common/dsc.cpp @@ -1376,8 +1376,8 @@ bool DSC_make_descriptor(DSC* desc, SSHORT scale, USHORT length, SSHORT sub_type, - SSHORT charset, - SSHORT collation) + CSetId charset, + CollId collation) { /************************************** * @@ -1412,13 +1412,13 @@ bool DSC_make_descriptor(DSC* desc, { case blr_text: desc->dsc_dtype = dtype_text; - desc->setTextType(INTL_CS_COLL_TO_TTYPE(charset, collation)); + desc->setTextType(TTypeId(charset, collation)); break; case blr_varying: desc->dsc_dtype = dtype_varying; desc->dsc_length += sizeof(USHORT); - desc->setTextType(INTL_CS_COLL_TO_TTYPE(charset, collation)); + desc->setTextType(TTypeId(charset, collation)); break; case blr_short: @@ -1507,15 +1507,15 @@ bool DSC_make_descriptor(DSC* desc, desc->dsc_dtype = dtype_blob; if (sub_type == isc_blob_text) { - fb_assert(charset <= MAX_SCHAR); - desc->dsc_scale = (SCHAR) charset; - desc->dsc_flags = collation << 8; // collation of blob + fb_assert(USHORT(charset) <= MAX_SCHAR); + desc->dsc_scale = (SCHAR) USHORT(charset); + desc->dsc_flags = USHORT(collation) << 8; // collation of blob } break; case blr_cstring: desc->dsc_dtype = dtype_cstring; - desc->setTextType(INTL_CS_COLL_TO_TTYPE(charset, collation)); + desc->setTextType(TTypeId(charset, collation)); break; case blr_bool: diff --git a/src/common/dsc.h b/src/common/dsc.h index b39779b7a3b..b16c361f6c8 100644 --- a/src/common/dsc.h +++ b/src/common/dsc.h @@ -30,6 +30,7 @@ #include "firebird/impl/dsc_pub.h" #include "firebird/impl/consts_pub.h" #include "../jrd/ods.h" +#include "../jrd/intl.h" #include "../intl/charsets.h" #include "../common/DecFloat.h" #include "../common/Int128.h" @@ -105,10 +106,6 @@ typedef struct dsc UCHAR* dsc_address = nullptr; // Used either as offset in a message or as a pointer #ifdef __cplusplus - SSHORT dsc_blob_ttype() const { return dsc_scale | (dsc_flags & 0xFF00);} - SSHORT& dsc_ttype() { return dsc_sub_type;} - SSHORT dsc_ttype() const { return dsc_sub_type;} - bool isNullable() const { return dsc_flags & DSC_nullable; @@ -228,12 +225,9 @@ typedef struct dsc return dsc_dtype == dtype_unknown; } - SSHORT getBlobSubType() const + UCHAR getType() const { - if (isBlob()) - return dsc_sub_type; - - return isc_blob_text; + return dsc_dtype; } SSHORT getSubType() const @@ -244,21 +238,29 @@ typedef struct dsc return 0; } + SSHORT getBlobSubType() const + { + if (isBlob()) + return dsc_sub_type; + + return isc_blob_text; + } + void setBlobSubType(SSHORT subType) { if (isBlob()) dsc_sub_type = subType; } - UCHAR getCharSet() const + CSetId getCharSet() const { if (isText()) - return dsc_sub_type & 0xFF; + return CSetId(dsc_sub_type); if (isBlob()) { if (dsc_sub_type == isc_blob_text) - return dsc_scale; + return CSetId(dsc_scale); return CS_BINARY; } @@ -269,39 +271,49 @@ typedef struct dsc return CS_NONE; } - USHORT getTextType() const + TTypeId getTextType() const { if (isText()) - return dsc_sub_type; + return TTypeId(dsc_sub_type); if (isBlob()) { if (dsc_sub_type == isc_blob_text) - return dsc_scale | (dsc_flags & 0xFF00); + return TTypeId(CSetId(dsc_scale), CollId(dsc_flags > 8)); - return CS_BINARY; + return TTypeId(CS_BINARY); } if (isDbKey()) - return CS_BINARY; + return TTypeId(CS_BINARY); - return CS_NONE; + return TTypeId(CS_NONE); } - void setTextType(USHORT ttype) + void setTextType(TTypeId ttype) { if (isText()) dsc_sub_type = ttype; else if (isBlob() && dsc_sub_type == isc_blob_text) { - dsc_scale = ttype & 0xFF; - dsc_flags = (dsc_flags & 0xFF) | (ttype & 0xFF00); + dsc_scale = CSetId(ttype); + dsc_flags = (dsc_flags & 0xFF) | (CollId(ttype) < 8); } } - USHORT getCollation() const + CollId getCollation() const + { + return CollId(getTextType()); + } + + UCHAR getLength() const { - return getTextType() >> 8; + return dsc_length; + } + + UCHAR getScale() const + { + return dsc_scale; } void clear() @@ -317,7 +329,7 @@ typedef struct dsc dsc_flags = 0; } - void makeBlob(SSHORT subType, USHORT ttype, ISC_QUAD* address = NULL) + void makeBlob(SSHORT subType, TTypeId ttype, ISC_QUAD* address = NULL) { clear(); dsc_dtype = dtype_blob; @@ -422,7 +434,7 @@ typedef struct dsc dsc_address = (UCHAR*) address; } - void makeText(USHORT length, USHORT ttype, UCHAR* address = NULL) + void makeText(USHORT length, TTypeId ttype, UCHAR* address = NULL) { clear(); dsc_dtype = dtype_text; @@ -485,7 +497,7 @@ typedef struct dsc dsc_address = (UCHAR*) address; } - void makeVarying(USHORT length, USHORT ttype, UCHAR* address = NULL) + void makeVarying(USHORT length, TTypeId ttype, UCHAR* address = NULL) { clear(); dsc_dtype = dtype_varying; @@ -499,6 +511,16 @@ typedef struct dsc dsc_address = address; } + bool operator==(const dsc& v) const + { + return dsc_dtype == v.dsc_dtype && + dsc_scale == v.dsc_scale && + dsc_length == v.dsc_length && + dsc_sub_type == v.dsc_sub_type && + dsc_flags == v.dsc_flags && + dsc_address == v.dsc_address; + } + USHORT getStringLength() const; operator Ods::Descriptor() const @@ -525,14 +547,14 @@ typedef struct dsc #endif // __cpluplus } DSC; -inline SSHORT DSC_GET_CHARSET(const dsc* desc) +inline CSetId DSC_GET_CHARSET(const dsc* desc) { - return (desc->dsc_sub_type & 0x00FF); + return desc->getCharSet(); } -inline SSHORT DSC_GET_COLLATE(const dsc* desc) +inline CollId DSC_GET_COLLATE(const dsc* desc) { - return (desc->dsc_sub_type >> 8); + return desc->getCollation(); } struct alt_dsc diff --git a/src/common/dsc_proto.h b/src/common/dsc_proto.h index b8372d1abfc..c8a6f0dadd8 100644 --- a/src/common/dsc_proto.h +++ b/src/common/dsc_proto.h @@ -25,12 +25,13 @@ #define JRD_DSC_PROTO_H #include "../common/dsc.h" +#include "../jrd/intl.h" USHORT DSC_string_length(const struct dsc*); const TEXT* DSC_dtype_tostring(UCHAR); void DSC_get_dtype_name(const dsc*, TEXT*, USHORT); bool DSC_make_descriptor(dsc*, USHORT, SSHORT, - USHORT, SSHORT, SSHORT, SSHORT); + USHORT, SSHORT, CSetId, CollId); USHORT DSC_convert_to_text_length(USHORT dsc_type); extern const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX]; diff --git a/src/common/sdl.cpp b/src/common/sdl.cpp index 0059a6bda39..29ef7e6b1af 100644 --- a/src/common/sdl.cpp +++ b/src/common/sdl.cpp @@ -780,7 +780,7 @@ static const UCHAR* sdl_desc(const UCHAR* ptr, DSC* desc) { case blr_text2: desc->dsc_dtype = dtype_text; - desc->setTextType(get_word(sdl)); + desc->setTextType(TTypeId(get_word(sdl))); break; case blr_text: @@ -791,7 +791,7 @@ static const UCHAR* sdl_desc(const UCHAR* ptr, DSC* desc) case blr_cstring2: desc->dsc_dtype = dtype_cstring; - desc->setTextType(get_word(sdl)); + desc->setTextType(TTypeId(get_word(sdl))); break; case blr_cstring: @@ -802,7 +802,7 @@ static const UCHAR* sdl_desc(const UCHAR* ptr, DSC* desc) case blr_varying2: desc->dsc_dtype = dtype_cstring; - desc->setTextType(get_word(sdl)); + desc->setTextType(TTypeId(get_word(sdl))); desc->dsc_length = sizeof(USHORT); break; diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp index f65dc7122bb..a4b68546aeb 100644 --- a/src/dsql/AggNodes.cpp +++ b/src/dsql/AggNodes.cpp @@ -407,7 +407,7 @@ bool AggNode::aggPass(thread_db* tdbb, Request* request) const to.dsc_flags = 0; to.dsc_sub_type = 0; to.dsc_scale = 0; - to.dsc_ttype() = ttype_sort_key; + to.setTextType(ttype_sort_key); to.dsc_length = asb->keyItems[0].getSkdLength(); to.dsc_address = data; INTL_string_to_key(tdbb, INTL_TEXT_TO_INDEX(desc->getTextType()), diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index ded5e5ad749..172f7cf73ca 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -942,18 +942,11 @@ bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, Request* request, dsc* { SET_TDBB(tdbb); - USHORT type1; + TTypeId type1 = desc1->getTextType(); - if (!desc1->isBlob()) - type1 = INTL_TEXT_TYPE(*desc1); - else - { - // No MATCHES support for blob - if (blrOp == blr_matching) - return false; - - type1 = desc1->dsc_sub_type == isc_blob_text ? desc1->dsc_blob_ttype() : ttype_none; - } + // No MATCHES support for blob + if (desc1->isBlob() && (blrOp == blr_matching)) + return false; Collation* obj = INTL_texttype_lookup(tdbb, type1); CharSet* charset = obj->getCharSet(); @@ -1004,7 +997,7 @@ bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, Request* request, dsc* } UCHAR* patternStr = nullptr; - SLONG patternLen = 0; + ULONG patternLen = 0; MoveBuffer patternBuffer; auto createMatcher = [&]() @@ -1150,17 +1143,7 @@ bool ComparativeBoolNode::sleuth(thread_db* tdbb, Request* request, const dsc* d // Choose interpretation for the operation - USHORT ttype; - if (desc1->isBlob()) - { - if (desc1->dsc_sub_type == isc_blob_text) - ttype = desc1->dsc_blob_ttype(); // Load blob character set and collation - else - ttype = INTL_TTYPE(desc2); - } - else - ttype = INTL_TTYPE(desc1); - + auto ttype = (desc1->isBlob() && (desc1->dsc_sub_type != isc_blob_text) ? desc2 : desc1)->getTextType(); Collation* obj = INTL_texttype_lookup(tdbb, ttype); // Get operator definition string (control string) @@ -1289,7 +1272,9 @@ string InListBoolNode::internalPrint(NodePrinter& printer) const { BoolExprNode::internalPrint(printer); +#ifndef TRIVIAL_NODE_PRINTER NODE_PRINT(printer, blrOp); +#endif NODE_PRINT(printer, arg); NODE_PRINT(printer, list); diff --git a/src/dsql/DSqlDataTypeUtil.cpp b/src/dsql/DSqlDataTypeUtil.cpp index 53c3311216f..8d87a1dde5a 100644 --- a/src/dsql/DSqlDataTypeUtil.cpp +++ b/src/dsql/DSqlDataTypeUtil.cpp @@ -27,7 +27,7 @@ #include "../dsql/DsqlCompilerScratch.h" #include "../dsql/metd_proto.h" -UCHAR Jrd::DSqlDataTypeUtil::maxBytesPerChar(UCHAR charSet) +UCHAR Jrd::DSqlDataTypeUtil::maxBytesPerChar(CSetId charSet) { return METD_get_charset_bpc(dsqlScratch->getTransaction(), charSet); } diff --git a/src/dsql/DSqlDataTypeUtil.h b/src/dsql/DSqlDataTypeUtil.h index 7a11ea1aba5..bf5ef3093f2 100644 --- a/src/dsql/DSqlDataTypeUtil.h +++ b/src/dsql/DSqlDataTypeUtil.h @@ -40,7 +40,7 @@ namespace Jrd { } public: - virtual UCHAR maxBytesPerChar(UCHAR charSet); + virtual UCHAR maxBytesPerChar(CSetId charSet); virtual USHORT getDialect() const; private: diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 6b1a76f6180..ccc9e692d41 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -34,12 +34,15 @@ #include "../jrd/obj.h" #include "../jrd/ods.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "../common/os/path_utils.h" #include "../jrd/CryptoManager.h" #include "../jrd/IntlManager.h" #include "../jrd/PreparedStatement.h" #include "../jrd/ResultSet.h" #include "../jrd/UserManagement.h" +#include "../jrd/Statement.h" +#include "../jrd/ProtectRelations.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/dfw_proto.h" @@ -48,10 +51,11 @@ #include "../jrd/exe_proto.h" #include "../jrd/intl_proto.h" #include "../common/isc_f_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/scl_proto.h" #include "../jrd/vio_proto.h" +#include "../jrd/idx_proto.h" #include "../dsql/ddl_proto.h" #include "../dsql/errd_proto.h" #include "../dsql/gen_proto.h" @@ -64,11 +68,31 @@ #include "../auth/SecureRemotePassword/Message.h" #include "../jrd/Mapping.h" #include "../jrd/extds/ExtDS.h" +#include "../jrd/cch_proto.h" +#include "../jrd/btr_proto.h" +#include "../jrd/tra_proto.h" +#include "../jrd/mov_proto.h" +#include "../jrd/ini.h" +#include "../jrd/GarbageCollector.h" namespace Jrd { +// Define range of user relation ids + +const int MIN_RELATION_ID = rel_MAX; +const int MAX_RELATION_ID = 32767; + using namespace Firebird; +static const UCHAR nonnull_validation_blr[] = +{ + blr_version5, + blr_not, + blr_missing, + blr_fid, 0, 0, 0, + blr_eoc +}; + static void checkForeignKeyTempScope(thread_db* tdbb, jrd_tra* transaction, const MetaName& childRelName, const MetaName& masterIndexName); static void checkSpTrigDependency(thread_db* tdbb, jrd_tra* transaction, @@ -109,6 +133,7 @@ static void updateRdbFields(const TypeClause* type, SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision, SSHORT& collationIdNull, SSHORT& collationId, SSHORT& segmentLengthNull, SSHORT& segmentLength); +static ISC_STATUS getErrorCodeByObjectType(int obj_type); static const char* const CHECK_CONSTRAINT_EXCEPTION = "check_constraint"; @@ -392,7 +417,8 @@ void defineComputed(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relati field->collationId = DSC_GET_COLLATE(&desc); const USHORT adjust = field->dtype == dtype_varying ? sizeof(USHORT) : 0; - const USHORT bpc = METD_get_charset_bpc(dsqlScratch->getTransaction(), field->charSetId.value_or(CS_NONE)); + const USHORT bpc = METD_get_charset_bpc(dsqlScratch->getTransaction(), + field->charSetId.value_or(CSetId(CS_NONE))); field->charLength = (field->length - adjust) / bpc; } else if (field->dtype == dtype_blob) @@ -452,7 +478,7 @@ void definePartial(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relatio // // (B) post delete trigger: post_delete_constraint will: // -// 1. also delete a record from RDB$INDICES where +// 1. also delete a record from RDB$INDICES where /// !!!!!!!!!!!!!!!!!!!! // RDB$INDICES.RDB$INDEX_NAME = // RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME // @@ -736,6 +762,8 @@ static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType) return relationTypeNull ? rel_persistent : rel_t(relationType); } +// ???????????????????????? cleanup + // Save the name of a field in the relation or view currently being defined. This is done to support // definition of triggers which will depend on the metadata created in this statement. static void saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName) @@ -778,6 +806,7 @@ static void saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, dsqlScratch->relation = relation; } + // Update RDB$FIELDS received by reference. static void updateRdbFields(const TypeClause* type, SSHORT& fieldType, @@ -1123,7 +1152,7 @@ void AlterCharSetNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch jrd_tra* transaction) { METD_drop_charset(transaction, charSet); - MET_dsql_cache_release(tdbb, SYM_intlsym_charset, charSet); + MetadataCache::dsql_cache_release(tdbb, SYM_intlsym_charset, charSet); bool charSetFound = false; bool collationFound = false; @@ -1716,11 +1745,16 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql compile(tdbb, dsqlScratch); - // second pass - if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); - else - executeAlter(tdbb, dsqlScratch, transaction, true, false); + { // scope + // avoid modify routine dfw during second pass on CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, altered ? 0 : TDBB_dont_post_dfw, true); + + // second pass + if (alterIndividualParameters) + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); + else + executeAlter(tdbb, dsqlScratch, transaction, true, false); + } if (package.isEmpty()) { @@ -1729,13 +1763,6 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql } savePoint.release(); // everything is ok - - if (alter) - { - // Update DSQL cache - METD_drop_function(transaction, QualifiedName(name, package)); - MET_dsql_cache_release(tdbb, SYM_udf, name, package); - } } bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -1821,6 +1848,9 @@ bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch if (package.isEmpty()) storePrivileges(tdbb, transaction, name, obj_udf, EXEC_PRIVILEGES); + // avoid modify routine dfw when execute CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, TDBB_dont_post_dfw, true); + executeAlter(tdbb, dsqlScratch, transaction, false, false); return true; @@ -1833,6 +1863,7 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* bool modified = false; unsigned returnPos = 0; + MetaId id; AutoCacheRequest requestHandle(tdbb, drq_m_funcs2, DYN_REQUESTS); @@ -1847,10 +1878,17 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* Arg::Gds(isc_dyn_cannot_mod_sysfunc) << MetaName(FUN.RDB$FUNCTION_NAME)); } - if (!secondPass && runTriggers && package.isEmpty()) + id = FUN.RDB$FUNCTION_ID; + + if (!secondPass && runTriggers) { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, - DDL_TRIGGER_ALTER_FUNCTION, name, NULL); + if (package.isEmpty()) + { + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, + DDL_TRIGGER_ALTER_FUNCTION, name, NULL); + } + + MetadataCache::oldVersion(tdbb, id); } MODIFY FUN @@ -2038,6 +2076,9 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* } } + if (secondPass && modified) + MetadataCache::newVersion(tdbb, id); + return modified; } @@ -2427,6 +2468,7 @@ void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds AutoCacheRequest request(tdbb, drq_m_fun, DYN_REQUESTS); bool found = false; + MetaId id; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) FUN IN RDB$FUNCTIONS @@ -2441,6 +2483,8 @@ void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds if (!FUN.RDB$ENGINE_NAME.NULL || !FUN.RDB$FUNCTION_BLR.NULL) status_exception::raise(Arg::Gds(isc_dyn_newfc_oldsyntax) << name); + MetadataCache::oldVersion(tdbb, (id = FUN.RDB$FUNCTION_ID)); + MODIFY FUN if (clauses.name.hasData()) { @@ -2465,6 +2509,7 @@ void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds if (found) { + MetadataCache::newVersion(tdbb, id); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_FUNCTION, name, NULL); } @@ -2475,10 +2520,6 @@ void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds } savePoint.release(); // everything is ok - - // Update DSQL cache - METD_drop_function(transaction, QualifiedName(name, "")); - MET_dsql_cache_release(tdbb, SYM_udf, name, ""); } @@ -2551,6 +2592,10 @@ void DropFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); bool found = false; + MetaId id; + + //MetadataCache::oldVersion(tdbb, id); -- missing ID in the node + MetadataCache::lookup_function(tdbb, QualifiedName(name, ""), CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); dropArguments(tdbb, transaction, name, package); @@ -2574,12 +2619,12 @@ void DropFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch name, NULL); } + id = FUN.RDB$FUNCTION_ID; ERASE FUN; + found = true; if (!FUN.RDB$SECURITY_CLASS.NULL) deleteSecurityClass(tdbb, transaction, FUN.RDB$SECURITY_CLASS); - - found = true; } END_FOR @@ -2602,17 +2647,18 @@ void DropFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch END_FOR } - if (found && package.isEmpty()) + if (found) { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_FUNCTION, - name, NULL); + MetadataCache::erase(tdbb, id); + + if (package.isEmpty()) + { + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_FUNCTION, + name, NULL); + } } savePoint.release(); // everything is ok - - // Update DSQL cache - METD_drop_function(transaction, QualifiedName(name, package)); - MET_dsql_cache_release(tdbb, SYM_udf, name, package); } @@ -2736,11 +2782,16 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq compile(tdbb, dsqlScratch); - // second pass - if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); - else - executeAlter(tdbb, dsqlScratch, transaction, true, false); + { // scope + // avoid modify routine dfw during second pass on CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, altered ? 0 : TDBB_dont_post_dfw, true); + + // second pass + if (alterIndividualParameters) + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); + else + executeAlter(tdbb, dsqlScratch, transaction, true, false); + } if (package.isEmpty()) { @@ -2749,13 +2800,6 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq } savePoint.release(); // everything is ok - - if (alter) - { - // Update DSQL cache - METD_drop_procedure(transaction, QualifiedName(name, package)); - MET_dsql_cache_release(tdbb, SYM_procedure, name, package); - } } bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -2833,6 +2877,9 @@ bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc if (package.isEmpty()) storePrivileges(tdbb, transaction, name, obj_procedure, EXEC_PRIVILEGES); + // avoid modify routine dfw when execute CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, TDBB_dont_post_dfw, true); + executeAlter(tdbb, dsqlScratch, transaction, false, false); return true; @@ -2844,6 +2891,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch Attachment* const attachment = transaction->getAttachment(); AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS); bool modified = false; + MetaId id; DsqlStatement* statement = dsqlScratch->getDsqlStatement(); @@ -2859,10 +2907,17 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch MetaName(P.RDB$PROCEDURE_NAME)); } - if (!secondPass && runTriggers && package.isEmpty()) + id = P.RDB$PROCEDURE_ID; + + if (!secondPass && runTriggers) { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, - DDL_TRIGGER_ALTER_PROCEDURE, name, NULL); + if (package.isEmpty()) + { + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, + DDL_TRIGGER_ALTER_PROCEDURE, name, NULL); + } + + MetadataCache::oldVersion(tdbb, id); } MODIFY P @@ -3004,8 +3059,28 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch END_MODIFY } END_FOR + + static const CachedRequestId rq3id; + AutoCacheRequest request3(tdbb, rq3id); + + FOR (REQUEST_HANDLE request3 TRANSACTION_HANDLE transaction) + P IN RDB$PROCEDURES + WITH P.RDB$PROCEDURE_NAME EQ name.c_str() AND + P.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '') + { + MODIFY P + { + P.RDB$PROCEDURE_INPUTS = (USHORT) parameters.getCount(); + P.RDB$PROCEDURE_OUTPUTS = (USHORT) returns.getCount(); + } + END_MODIFY + } + END_FOR } + if (secondPass && modified) + MetadataCache::newVersion(tdbb, id); + return modified; } @@ -3363,9 +3438,13 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc AutoSavePoint savePoint(tdbb, transaction); bool found = false; + //MetadataCache::oldVersion(tdbb, id); missing ID in the node + MetadataCache::lookup_procedure(tdbb, QualifiedName(name, package), CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); + dropParameters(tdbb, transaction, name, package); AutoCacheRequest requestHandle(tdbb, drq_e_prcs2, DYN_REQUESTS); + MetaId id; FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) PRC IN RDB$PROCEDURES @@ -3385,12 +3464,12 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc DDL_TRIGGER_DROP_PROCEDURE, name, NULL); } + id = PRC.RDB$PROCEDURE_ID; ERASE PRC; + found = true; if (!PRC.RDB$SECURITY_CLASS.NULL) deleteSecurityClass(tdbb, transaction, PRC.RDB$SECURITY_CLASS); - - found = true; } END_FOR @@ -3413,17 +3492,18 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc END_FOR } - if (found && package.isEmpty()) + if (found) { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE, - name, NULL); + MetadataCache::erase(tdbb, id); + + if (package.isEmpty()) + { + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE, + name, NULL); + } } savePoint.release(); // everything is ok - - // Update DSQL cache - METD_drop_procedure(transaction, QualifiedName(name, package)); - MET_dsql_cache_release(tdbb, SYM_procedure, name, package); } @@ -3660,8 +3740,6 @@ void CreateAlterTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS { fb_assert(create || alter); - Attachment* const attachment = transaction->getAttachment(); - source.ltrim("\n\r\t "); // run all statements under savepoint control @@ -3674,6 +3752,9 @@ void CreateAlterTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS if (alter) { + // DB-wide triggers are preloaded in a cache during attachDatabase(), + // i.e. no need in something like oldVersion<>() call here. + if (!modify(tdbb, dsqlScratch, transaction)) { if (create) // create or alter @@ -3988,6 +4069,8 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_COLLATION, name, NULL); + MetadataCache::oldVersion(tdbb, forCharSetId); + DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_collation); AutoCacheRequest request(tdbb, drq_s_colls, DYN_REQUESTS); @@ -4011,7 +4094,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra if (fromName.hasData()) { if (MET_get_char_coll_subtype_info(tdbb, - INTL_CS_COLL_TO_TTYPE(forCharSetId, fromCollationId), &info) && + TTypeId(forCharSetId, fromCollationId), &info) && forCharSetId != CS_METADATA && info.specificAttributes.hasData()) { @@ -4046,7 +4129,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra specificAttributes = temp; } - info.charsetName = forCharSet.c_str(); + info.charsetName.push(forCharSet.c_str()); info.collationName = name; if (X.RDB$BASE_COLLATION_NAME.NULL) info.baseCollationName = info.collationName; @@ -4055,11 +4138,11 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra info.ignoreAttributes = false; if (!IntlManager::collationInstalled(info.baseCollationName.c_str(), - info.charsetName.c_str())) + info.charsetName[0].c_str())) { // msg: 223: "Collation @1 not installed for character set @2" status_exception::raise( - Arg::PrivateDyn(223) << info.baseCollationName << info.charsetName); + Arg::PrivateDyn(223) << info.baseCollationName << info.charsetName[0]); } IntlUtil::SpecificAttributesMap map; @@ -4077,7 +4160,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra string newSpecificAttributes; if (!IntlManager::setupCollationAttributes( - info.baseCollationName.c_str(), info.charsetName.c_str(), s, + info.baseCollationName.c_str(), info.charsetName[0].c_str(), s, newSpecificAttributes)) { // msg: 222: "Invalid collation attributes" @@ -4137,6 +4220,8 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra storePrivileges(tdbb, transaction, name, obj_collation, USAGE_PRIVILEGES); + MetadataCache::newVersion(tdbb, forCharSetId); + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_COLLATION, name, NULL); @@ -4144,7 +4229,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra // Update DSQL cache METD_drop_collation(transaction, name); - MET_dsql_cache_release(tdbb, SYM_intlsym_collation, name); + MetadataCache::dsql_cache_release(tdbb, SYM_intlsym_collation, name); } DdlNode* CreateCollationNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) @@ -4323,7 +4408,7 @@ void DropCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc // Update DSQL cache METD_drop_collation(transaction, name); - MET_dsql_cache_release(tdbb, SYM_intlsym_collation, name); + MetadataCache::dsql_cache_release(tdbb, SYM_intlsym_collation, name); } @@ -4937,8 +5022,8 @@ void AlterDomainNode::getDomainType(thread_db* tdbb, jrd_tra* transaction, dyn_f WITH FLD.RDB$FIELD_NAME EQ dynFld.dyn_fld_source.c_str(); { DSC_make_descriptor(&dynFld.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, - FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, FLD.RDB$CHARACTER_SET_ID, - FLD.RDB$COLLATION_ID); + FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); dynFld.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; dynFld.dyn_dtype = FLD.RDB$FIELD_TYPE; @@ -5153,8 +5238,8 @@ void AlterDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, dyn_fld origDom, newDom; DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, - FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, FLD.RDB$CHARACTER_SET_ID, - FLD.RDB$COLLATION_ID); + FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); origDom.dyn_fld_name = name; origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; @@ -6034,8 +6119,97 @@ RelationNode::RelationNode(MemoryPool& p, RelationSourceNode* aDsqlNode) : DdlNode(p), dsqlNode(aDsqlNode), name(p, dsqlNode->dsqlName), - clauses(p) + clauses(p), + indexList(p) +{ +} + + +// Assign a relation ID and dbkey length to the new relation. +// Probe the candidate relation ID returned from the system +// relation RDB$DATABASE to make sure it isn't already assigned. +// This can happen from nefarious manipulation of RDB$DATABASE +// or wraparound of the next relation ID. Keep looking for a +// usable relation ID until the search space is exhausted. + +MetaId RelationNode::generateIdDbKey(thread_db* tdbb, jrd_tra* transaction) { + MetaId rel_id = 0; + AutoRequest handle; + AutoCacheRequest request; + request.reset(tdbb, irq_c_relation, IRQ_REQUESTS); + const USHORT local_min_relation_id = USER_DEF_REL_INIT_ID; + Attachment* attachment = tdbb->getAttachment(); + + bid blob_id; + blob_id.clear(); + + // Take a relation lock on rel id == -1 before actually generating a relation id. + AutoLock lock(tdbb, FB_NEW_RPT(*tdbb->getDefaultPool(), 0) + Lock(tdbb, sizeof(SLONG), LCK_relation)); + lock->setKey(-1); + LCK_lock(tdbb, lock, LCK_EX, LCK_WAIT); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + X IN RDB$DATABASE CROSS Y IN RDB$RELATIONS WITH + Y.RDB$RELATION_NAME EQ name.c_str() + { + blob_id = Y.RDB$VIEW_BLR; + + MODIFY X USING + rel_id = X.RDB$RELATION_ID; + + if (rel_id < local_min_relation_id || rel_id > MAX_RELATION_ID) + rel_id = X.RDB$RELATION_ID = local_min_relation_id; + + // Roman Simakov: We need to return deleted relations to skip them. + // This maybe result of cleanup failure after phase 3. + while (auto relation = MetadataCache::lookup_relation_id(tdbb, rel_id++, + CacheFlag::RET_ERASED | CacheFlag::AUTOCREATE)) + { + if (rel_id < local_min_relation_id || rel_id > MAX_RELATION_ID) + rel_id = local_min_relation_id; + + if (rel_id == X.RDB$RELATION_ID) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_table_name) << Arg::Str(name) << + Arg::Gds(isc_imp_exc)); + } + } + + X.RDB$RELATION_ID = (rel_id > MAX_RELATION_ID) ? local_min_relation_id : rel_id; + + MODIFY Y USING + Y.RDB$RELATION_ID = --rel_id; + if (blob_id.isEmpty()) + Y.RDB$DBKEY_LENGTH = 8; + else + { + // update the dbkey length to include each of the base relations + Y.RDB$DBKEY_LENGTH = 0; + + handle.reset(); + + FOR(REQUEST_HANDLE handle) + Z IN RDB$VIEW_RELATIONS CROSS + R IN RDB$RELATIONS OVER RDB$RELATION_NAME + WITH Z.RDB$VIEW_NAME = name.c_str() AND + (Z.RDB$CONTEXT_TYPE = VCT_TABLE OR + Z.RDB$CONTEXT_TYPE = VCT_VIEW) + { + Y.RDB$DBKEY_LENGTH += R.RDB$DBKEY_LENGTH; + } + END_FOR + } + END_MODIFY + END_MODIFY + } + END_FOR + + lock.release(); + + return rel_id; } void RelationNode::FieldDefinition::modify(thread_db* tdbb, jrd_tra* transaction) @@ -6820,8 +6994,8 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc definition.refRelation = constraint.refRelation; definition.refColumns = constraint.refColumns; - CreateIndexNode::store(tdbb, transaction, constraint.index->name, - definition, &referredIndexName); + indexList.push(CreateIndexNode::store(tdbb, indexList.getPool(), transaction, + constraint.index->name, definition, &referredIndexName)); CRT.RDB$INDEX_NAME.NULL = FALSE; strcpy(CRT.RDB$INDEX_NAME, constraint.index->name.c_str()); @@ -7444,2174 +7618,3819 @@ void RelationNode::dropFromPublication(thread_db* tdbb, } -//---------------------- +void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, MetaName relName) +{ +/************************************** + * + * m a k e _ v e r s i o n + * + ************************************** + * + * Functional description + * Make a new format version for a relation. While we're at it, make + * sure all fields have id's. If the relation is a view, make a + * a format anyway -- used for view updates. + * + * While we're in the vicinity, also check the updatability of fields. + * + **************************************/ + TemporaryField* stack = nullptr; + TemporaryField* external = nullptr; + jrd_rel* relation = nullptr; + int physical_fields = 0; + bool external_flag = false; + bool computed_field = false; + TrigArray* triggers = nullptr; + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + bool null_view; + USHORT n; -string CreateRelationNode::internalPrint(NodePrinter& printer) const -{ - RelationNode::internalPrint(printer); + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, TDBB_dont_post_dfw, true); - NODE_PRINT(printer, externalFile); - NODE_PRINT(printer, relationType); + { + AutoCacheRequest request_fmt1(tdbb, irq_format1, IRQ_REQUESTS); - return "CreateRelationNode"; -} + FOR(REQUEST_HANDLE request_fmt1 TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ relName.c_str() + { + relation = MetadataCache::lookup_relation_id(tdbb, REL.RDB$RELATION_ID, CacheFlag::AUTOCREATE); + if (!relation) + return; -void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) -{ - SCL_check_create_access(tdbb, obj_relations); -} + const bid blob_id = REL.RDB$VIEW_BLR; + null_view = blob_id.isEmpty(); + external_flag = REL.RDB$EXTERNAL_FILE[0]; + triggers = &relation->rel_triggers; -void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction) -{ - if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation)) - return; + if (REL.RDB$FORMAT == MAX_TABLE_VERSIONS) + raiseTooManyVersionsError(REL.RDB$VIEW_BLR.NULL ? obj_relation : obj_view, relName); - saveRelation(tdbb, dsqlScratch, name, false, true); + MODIFY REL USING + blb* blob = blb::create(tdbb, transaction, &REL.RDB$RUNTIME); + AutoCacheRequest request_fmtx(tdbb, irq_format2, IRQ_REQUESTS); - if (externalFile) - { - fb_assert(dsqlScratch->relation); - dsqlScratch->relation->rel_flags |= REL_external; - } + FOR(REQUEST_HANDLE request_fmtx TRANSACTION_HANDLE transaction) + RFR IN RDB$RELATION_FIELDS CROSS + FLD IN RDB$FIELDS WITH + RFR.RDB$RELATION_NAME EQ relName.c_str() AND + RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME + { + // Update RFR to reflect new fields id - // run all statements under savepoint control - AutoSavePoint savePoint(tdbb, transaction); + if (!RFR.RDB$FIELD_ID.NULL && RFR.RDB$FIELD_ID >= REL.RDB$FIELD_ID) + REL.RDB$FIELD_ID = RFR.RDB$FIELD_ID + 1; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TABLE, - name, NULL); + // force recalculation of RDB$UPDATE_FLAG if field is calculated + if (!FLD.RDB$COMPUTED_BLR.isEmpty()) + { + RFR.RDB$UPDATE_FLAG.NULL = TRUE; + computed_field = true; + } - DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation); + if (RFR.RDB$FIELD_ID.NULL || RFR.RDB$UPDATE_FLAG.NULL) + { + MODIFY RFR USING + if (RFR.RDB$FIELD_ID.NULL) + { + if (external_flag) + { + RFR.RDB$FIELD_ID = RFR.RDB$FIELD_POSITION; + // RFR.RDB$FIELD_POSITION.NULL is + // needed to be referenced in the + // code somewhere for GPRE to include + // this field in the structures that + // it generates at the top of this func. + RFR.RDB$FIELD_ID.NULL = RFR.RDB$FIELD_POSITION.NULL; + } + else + { + RFR.RDB$FIELD_ID = REL.RDB$FIELD_ID; + RFR.RDB$FIELD_ID.NULL = FALSE; + + // If the table is being altered, check validity of NOT NULL fields. + if (!REL.RDB$FORMAT.NULL) + { + bool notNull = (RFR.RDB$NULL_FLAG.NULL ? + (FLD.RDB$NULL_FLAG.NULL ? false : (bool) FLD.RDB$NULL_FLAG) : + (bool) RFR.RDB$NULL_FLAG); + + if (notNull) + { + dsc desc; + desc.makeText(static_cast(strlen(REL.RDB$RELATION_NAME)), + CS_METADATA, (UCHAR*) REL.RDB$RELATION_NAME); + + DeferredWork* work = DFW_post_work(transaction, + dfw_check_not_null, &desc, 0); + SortedArray& ids = DFW_get_ids(work); + + FB_SIZE_T pos; + if (!ids.find(RFR.RDB$FIELD_ID, pos)) + ids.insert(pos, RFR.RDB$FIELD_ID); + } + } + } - fb_assert(relationType.has_value()); + REL.RDB$FIELD_ID++; + } + if (RFR.RDB$UPDATE_FLAG.NULL) + { + RFR.RDB$UPDATE_FLAG.NULL = FALSE; + RFR.RDB$UPDATE_FLAG = 1; + if (!FLD.RDB$COMPUTED_BLR.isEmpty()) + { + RFR.RDB$UPDATE_FLAG = 0; + } + if (!null_view && REL.RDB$DBKEY_LENGTH > 8) + { + AutoRequest temp; + RFR.RDB$UPDATE_FLAG = 0; + FOR(REQUEST_HANDLE temp) X IN RDB$TRIGGERS WITH + X.RDB$RELATION_NAME EQ relName.c_str() AND + X.RDB$TRIGGER_TYPE EQ 1 + { + RFR.RDB$UPDATE_FLAG = 1; + } + END_FOR + } + } + END_MODIFY + } - checkRelationTempScope(tdbb, transaction, name, relationType.value()); + // Store stuff in field summary - AutoCacheRequest request(tdbb, drq_s_rels2, DYN_REQUESTS); + n = RFR.RDB$FIELD_ID; + putSummaryRecord(tdbb, blob, RSR_field_id, (UCHAR*)&n, sizeof(n)); + putSummaryRecord(tdbb, blob, RSR_field_name, (UCHAR*) RFR.RDB$FIELD_NAME, + fb_utils::name_length(RFR.RDB$FIELD_NAME)); + putSummaryRecord(tdbb, blob, RSR_field_source, (UCHAR*) RFR.RDB$FIELD_SOURCE, + fb_utils::name_length(RFR.RDB$FIELD_SOURCE)); + if (!FLD.RDB$FIELD_LENGTH.NULL) + { + n = FLD.RDB$FIELD_LENGTH; + putSummaryRecord(tdbb, blob, RSR_field_length, (UCHAR*)&n, sizeof(n)); + } + if (!FLD.RDB$CHARACTER_LENGTH.NULL) + { + n = FLD.RDB$CHARACTER_LENGTH; + putSummaryRecord(tdbb, blob, RSR_character_length, (UCHAR*)&n, sizeof(n)); + } + if (!FLD.RDB$COMPUTED_BLR.isEmpty() && !RFR.RDB$VIEW_CONTEXT) + { + putSummaryBlob(tdbb, blob, RSR_computed_blr, &FLD.RDB$COMPUTED_BLR, transaction); + } + else if (!null_view) + { + n = RFR.RDB$VIEW_CONTEXT; + putSummaryRecord(tdbb, blob, RSR_view_context, (UCHAR*)&n, sizeof(n)); + putSummaryRecord(tdbb, blob, RSR_base_field, (UCHAR*) RFR.RDB$BASE_FIELD, + fb_utils::name_length(RFR.RDB$BASE_FIELD)); + } + putSummaryBlob(tdbb, blob, RSR_missing_value, &FLD.RDB$MISSING_VALUE, transaction); - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - REL IN RDB$RELATIONS - { - strcpy(REL.RDB$RELATION_NAME, name.c_str()); - REL.RDB$SYSTEM_FLAG = 0; - REL.RDB$FLAGS = REL_sql; - REL.RDB$RELATION_TYPE = relationType.value(); + { + USHORT len = RFR.RDB$FIELD_POSITION; + putSummaryRecord(tdbb, blob, RSR_field_pos, (UCHAR*) &len, sizeof(len)); + } - if (ssDefiner.isAssigned()) - { - REL.RDB$SQL_SECURITY.NULL = FALSE; - REL.RDB$SQL_SECURITY = ssDefiner.asBool() ? FB_TRUE : FB_FALSE; - } - else - REL.RDB$SQL_SECURITY.NULL = TRUE; + bid* defaultValue = RFR.RDB$DEFAULT_VALUE.isEmpty() ? + &FLD.RDB$DEFAULT_VALUE : &RFR.RDB$DEFAULT_VALUE; - REL.RDB$VIEW_BLR.NULL = TRUE; - REL.RDB$VIEW_SOURCE.NULL = TRUE; - REL.RDB$EXTERNAL_FILE.NULL = TRUE; + putSummaryBlob(tdbb, blob, RSR_default_value, defaultValue, transaction); + putSummaryBlob(tdbb, blob, RSR_validation_blr, &FLD.RDB$VALIDATION_BLR, transaction); - if (externalFile) - { - if (externalFile->length() >= sizeof(REL.RDB$EXTERNAL_FILE)) - status_exception::raise(Arg::Gds(isc_dyn_name_longer)); + bool notNull = (RFR.RDB$NULL_FLAG.NULL ? + (FLD.RDB$NULL_FLAG.NULL ? false : (bool) FLD.RDB$NULL_FLAG) : + (bool) RFR.RDB$NULL_FLAG); - if (ISC_check_if_remote(externalFile->c_str(), false)) - status_exception::raise(Arg::PrivateDyn(163)); + if (notNull) + { + putSummaryRecord(tdbb, blob, RSR_field_not_null, + nonnull_validation_blr, sizeof(nonnull_validation_blr)); + } - REL.RDB$EXTERNAL_FILE.NULL = FALSE; - strcpy(REL.RDB$EXTERNAL_FILE, externalFile->c_str()); - REL.RDB$RELATION_TYPE = rel_external; - } - } - END_STORE + n = fb_utils::name_length(RFR.RDB$SECURITY_CLASS); + if (!RFR.RDB$SECURITY_CLASS.NULL && n) + { + putSummaryRecord(tdbb, blob, RSR_security_class, + (UCHAR*) RFR.RDB$SECURITY_CLASS, n); + } - bool replicationEnabled; + n = fb_utils::name_length(RFR.RDB$GENERATOR_NAME); + if (!RFR.RDB$GENERATOR_NAME.NULL && n) + { + putSummaryRecord(tdbb, blob, RSR_field_generator_name, + (UCHAR*) RFR.RDB$GENERATOR_NAME, n); + } - if (replicationState.isAssigned()) - replicationEnabled = replicationState.asBool(); - else - { - // Apply the default replication state to the table being created + n = RFR.RDB$IDENTITY_TYPE; + if (!RFR.RDB$IDENTITY_TYPE.NULL) + putSummaryRecord(tdbb, blob, RSR_field_identity_type, (UCHAR*) &n, sizeof(n)); - AutoCacheRequest request2(tdbb, drq_l_pub_mode, DYN_REQUESTS); + // Make a temporary field block - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - PUB IN RDB$PUBLICATIONS - WITH PUB.RDB$PUBLICATION_NAME EQ DEFAULT_PUBLICATION - { - replicationEnabled = (PUB.RDB$AUTO_ENABLE != 0); - } - END_FOR - } + TemporaryField* tfb = FB_NEW_POOL(*tdbb->getDefaultPool()) TemporaryField; + tfb->tfb_next = stack; + stack = tfb; - if (replicationEnabled) - { - // Add table to the default publication - RelationNode::addToPublication(tdbb, transaction, name, DEFAULT_PUBLICATION); - } + // for text data types, grab the CHARACTER_SET and + // COLLATION to give the type of international text - storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES); + if (FLD.RDB$CHARACTER_SET_ID.NULL) + FLD.RDB$CHARACTER_SET_ID = CS_NONE; - ObjectsArray constraints; - const ObjectsArray* pkCols = findPkColumns(); - SSHORT position = 0; + CollId collation((!FLD.RDB$COLLATION_ID.NULL) ? FLD.RDB$COLLATION_ID : + (!RFR.RDB$COLLATION_ID.NULL) ? RFR.RDB$COLLATION_ID : COLLATE_NONE); // codepoint collation - for (NestConst* i = clauses.begin(); i != clauses.end(); ++i) - { - switch ((*i)->type) - { - case Clause::TYPE_ADD_COLUMN: - defineField(tdbb, dsqlScratch, transaction, - static_cast(i->getObject()), position, pkCols); - ++position; - break; + if (!DSC_make_descriptor(&tfb->tfb_desc, FLD.RDB$FIELD_TYPE, + FLD.RDB$FIELD_SCALE, + FLD.RDB$FIELD_LENGTH, + FLD.RDB$FIELD_SUB_TYPE, + CSetId(FLD.RDB$CHARACTER_SET_ID), collation)) + { + if (null_view && REL.RDB$FORMAT.NULL) + DPM_delete_relation(tdbb, getPermanent(relation)); - case Clause::TYPE_ADD_CONSTRAINT: - makeConstraint(tdbb, dsqlScratch, transaction, - static_cast(i->getObject()), constraints); - break; + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_random) << Arg::Str(relName)); + } - default: - fb_assert(false); - break; - } - } + // Make sure the text type specified is implemented + if (!validateTextType(tdbb, tfb)) + { + if (null_view && REL.RDB$FORMAT.NULL) + DPM_delete_relation(tdbb, getPermanent(relation)); - for (ObjectsArray::iterator constraint(constraints.begin()); - constraint != constraints.end(); - ++constraint) - { - defineConstraint(tdbb, dsqlScratch, transaction, constraint->name, *constraint->create); - } + ERR_post_nothrow(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_random) << Arg::Str(relName)); + INTL_texttype_lookup(tdbb, tfb->tfb_desc.getTextType()); // should punt + ERR_punt(); // if INTL_texttype_lookup hasn't punt + } - dsqlScratch->relation->rel_flags &= ~REL_creating; + // Blob specific + if (tfb->tfb_desc.isBlob()) + { + USHORT len = FLD.RDB$SEGMENT_LENGTH; + putSummaryRecord(tdbb, blob, RSR_segment_length, (UCHAR*) &len, sizeof(len)); + } - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TABLE, - name, NULL); + // Store the default value. - savePoint.release(); // everything is ok + memset(&tfb->tfb_default, 0, sizeof(tfb->tfb_default)); - // Update DSQL cache - METD_drop_relation(transaction, name); - MET_dsql_cache_release(tdbb, SYM_relation, name); -} + if (notNull && !defaultValue->isEmpty()) + { + Jrd::ContextPoolHolder context(tdbb, dbb->createPool(ALLOC_ARGS0)); + Statement* defaultStatement = NULL; + try + { + ValueExprNode* defaultNode = static_cast(MET_parse_blob( + tdbb, getPermanent(relation), defaultValue, NULL, &defaultStatement, false, false)); -// Starting from the elements in a table definition, locate the PK columns if given in a -// separate table constraint declaration. -const ObjectsArray* CreateRelationNode::findPkColumns() -{ - for (const NestConst* i = clauses.begin(); i != clauses.end(); ++i) - { - if ((*i)->type == Clause::TYPE_ADD_CONSTRAINT) - { - const AddConstraintClause* clause = static_cast(i->getObject()); + AutoPtr defaultRequest(defaultStatement->findRequest(tdbb)); - if (clause->constraintType == AddConstraintClause::CTYPE_PK) - return &clause->columns; - } - } + // Attention: this is scoped to the end of this "try". + AutoSetRestore2 autoRequest(tdbb, + &thread_db::getRequest, &thread_db::setRequest, defaultRequest); - return NULL; -} + defaultRequest->validateTimeStamp(); + dsc* result = nullptr; + { // scope + Firebird::Cleanup detach([&defaultRequest] {TRA_detach_request(defaultRequest);}); + TRA_attach_request(transaction, defaultRequest); + result = EVL_expr(tdbb, defaultRequest, defaultNode); + } -//---------------------- + if (result) + { + dsc desc = *result; + MoveBuffer buffer; + if (desc.isText() || desc.isBlob()) + { + UCHAR* ptr = NULL; + const int len = MOV_make_string2(tdbb, &desc, tfb->tfb_desc.getCharSet(), + &ptr, buffer, true); + fb_assert(ULONG(len) < ULONG(MAX_USHORT)); + desc.makeText(len, tfb->tfb_desc.getCharSet(), ptr); + } -string AlterRelationNode::internalPrint(NodePrinter& printer) const -{ - RelationNode::internalPrint(printer); - return "AlterRelationNode"; -} + impure_value tempValue; + MoveBuffer tempBuffer; -void AlterRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) -{ - dsc dscName; - dscName.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str()); - SCL_check_relation(tdbb, &dscName, SCL_alter); -} + if (!tfb->tfb_desc.isBlob() && !DSC_EQUIV(result, &tfb->tfb_desc, false)) + { + tempValue.vlu_desc = tfb->tfb_desc; + + if (tfb->tfb_desc.isText()) + tempValue.vlu_desc.dsc_address = tempBuffer.getBuffer(tfb->tfb_desc.dsc_length); + else + tempValue.vlu_desc.dsc_address = (UCHAR*) &tempValue.vlu_misc; + + try + { + MOV_move(tdbb, &desc, &tempValue.vlu_desc); + desc = tempValue.vlu_desc; + } + catch (const status_exception&) + { + if (!tdbb->getAttachment()->isGbak()) + throw; + + // If we're restoring a database, ignore the error and use the original desc. + fb_utils::init_status(tdbb->tdbb_status_vector); + } + } -void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction) -{ - saveRelation(tdbb, dsqlScratch, name, false, false); + EVL_make_value(tdbb, &desc, &tfb->tfb_default, relation->rel_pool); + } + } + catch (const Exception&) + { + if (defaultStatement) + defaultStatement->release(tdbb); + throw; + } - dsql_rel* relation; - relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name); + defaultStatement->release(tdbb); + } - if (!relation || (relation->rel_flags & REL_view)) - { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_table_not_found) << name); - } + // dimitr: view fields shouldn't be marked as computed + if (null_view && !FLD.RDB$COMPUTED_BLR.isEmpty()) + tfb->tfb_flags |= TFB_computed; + else + ++physical_fields; - if (!dsqlScratch->relation) - { - //// TODO: - /*** - char linecol[64]; - sprintf(linecol, "At line %d, column %d.", (int) dsqlNode->line, (int) dsqlNode->column); - ***/ + tfb->tfb_id = RFR.RDB$FIELD_ID; - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-204) << - Arg::Gds(isc_dsql_relation_err) << - Arg::Gds(isc_random) << name /***<< - Arg::Gds(isc_random) << linecol***/); - } + if ((n = FLD.RDB$DIMENSIONS)) + setupArray(tdbb, blob, FLD.RDB$FIELD_NAME, n, tfb); - bool beforeTriggerWasExecuted = false; + if (external_flag) + { + tfb = FB_NEW_POOL(*tdbb->getDefaultPool()) TemporaryField; + tfb->tfb_next = external; + external = tfb; + fb_assert(FLD.RDB$EXTERNAL_TYPE <= MAX_UCHAR); + tfb->tfb_desc.dsc_dtype = (UCHAR)FLD.RDB$EXTERNAL_TYPE; + fb_assert(FLD.RDB$EXTERNAL_SCALE >= MIN_SCHAR && + FLD.RDB$EXTERNAL_SCALE <= MAX_SCHAR); + tfb->tfb_desc.dsc_scale = (SCHAR)FLD.RDB$EXTERNAL_SCALE; + tfb->tfb_desc.dsc_length = FLD.RDB$EXTERNAL_LENGTH; + tfb->tfb_id = RFR.RDB$FIELD_ID; + } + } + END_FOR - const auto executeBeforeTrigger = [&]() - { - if (!beforeTriggerWasExecuted) - { - beforeTriggerWasExecuted = true; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_TABLE, name, nullptr); - } - }; + if (null_view && !physical_fields) + { + if (REL.RDB$FORMAT.NULL) + DPM_delete_relation(tdbb, getPermanent(relation)); - // If there is an error, get rid of the cached data. + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_table_name) << Arg::Str(relName) << + Arg::Gds(isc_must_have_phys_field)); + } - try - { - // run all statements under savepoint control - AutoSavePoint savePoint(tdbb, transaction); + blob = setupTriggers(tdbb, relation, null_view, triggers, blob); - ObjectsArray constraints; + blob->BLB_close(tdbb); + USHORT version = REL.RDB$FORMAT.NULL ? 0 : REL.RDB$FORMAT; + version++; + relation->rel_current_format = makeFormat(tdbb, transaction, getPermanent(relation), + &version, stack); + REL.RDB$FORMAT.NULL = FALSE; + REL.RDB$FORMAT = version; - for (NestConst* i = clauses.begin(); i != clauses.end(); ++i) - { - switch ((*i)->type) - { - case Clause::TYPE_ADD_COLUMN: + if (!null_view) { - const auto addColumnClause = static_cast(i->getObject()); - bool createColumn = true; - - if (addColumnClause->createIfNotExistsOnly) - { - AutoCacheRequest request(tdbb, drq_l_rel_fld_name, DYN_REQUESTS); + // update the dbkey length to include each of the base relations - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RFL IN RDB$RELATION_FIELDS - WITH RFL.RDB$RELATION_NAME = relation->rel_name.c_str() AND - RFL.RDB$FIELD_NAME = addColumnClause->field->fld_name.c_str() - { - createColumn = false; - break; - } - END_FOR - } + REL.RDB$DBKEY_LENGTH = 0; - if (createColumn) + AutoRequest handle; + FOR(REQUEST_HANDLE handle) + Z IN RDB$VIEW_RELATIONS + CROSS R IN RDB$RELATIONS OVER RDB$RELATION_NAME + WITH Z.RDB$VIEW_NAME = relName.c_str() { - executeBeforeTrigger(); - defineField(tdbb, dsqlScratch, transaction, addColumnClause, -1, nullptr); + REL.RDB$DBKEY_LENGTH += R.RDB$DBKEY_LENGTH; } - - break; + END_FOR } + END_MODIFY + } + END_FOR - case Clause::TYPE_ALTER_COL_TYPE: - executeBeforeTrigger(); - modifyField(tdbb, dsqlScratch, transaction, static_cast(i->getObject())); - break; + // If we didn't find the relation, it is probably being dropped - case Clause::TYPE_ALTER_COL_NAME: - { - executeBeforeTrigger(); + if (!relation) + return; - const AlterColNameClause* clause = - static_cast(i->getObject()); - AutoRequest request; - bool found = false; + // in case somebody changed the view definition or a computed + // field, reset the dependencies by deleting the current ones + // and setting a flag for MET_scan_relation to find the new ones - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RFL IN RDB$RELATION_FIELDS - WITH RFL.RDB$FIELD_NAME EQ clause->fromName.c_str() AND - RFL.RDB$RELATION_NAME EQ name.c_str() - { - found = true; + if (!null_view) + MET_delete_dependencies(tdbb, relName, obj_view, transaction); +/* // !!!!!!!!!!!!!! commented in order to get first of all working create table + { // begin scope + const DeferredWork* arg = work->findArg(dfw_arg_force_computed); + if (arg) + { + computed_field = true; + MET_delete_dependencies(tdbb, arg->dfw_name, obj_computed, transaction); + } + } // end scope + */ + if (!null_view || computed_field) + getPermanent(relation)->rel_flags |= REL_get_dependencies; - MODIFY RFL - checkViewDependency(tdbb, transaction, name, clause->fromName); - checkSpTrigDependency(tdbb, transaction, name, clause->fromName); + if (external_flag) + { + AutoRequest temp; + FOR(REQUEST_HANDLE temp) FMTS IN RDB$FORMATS WITH + FMTS.RDB$RELATION_ID EQ relation->getId() AND + FMTS.RDB$FORMAT EQ 0 + { + ERASE FMTS; + } + END_FOR - if (!fieldExists(tdbb, transaction, name, clause->toName)) - { - strcpy(RFL.RDB$FIELD_NAME, clause->toName.c_str()); - AlterDomainNode::modifyLocalFieldIndex(tdbb, transaction, name, - clause->fromName, clause->toName); - } - else - { - // msg 205: Cannot rename field %s to %s. A field with that name - // already exists in table %s. - status_exception::raise( - Arg::PrivateDyn(205) << clause->fromName << clause->toName << name); - } - END_MODIFY - } - END_FOR + makeFormat(tdbb, transaction, getPermanent(relation), 0, external); + } - if (!found) - { - // msg 176: "column %s does not exist in table/view %s" - status_exception::raise(Arg::PrivateDyn(176) << clause->fromName << name); - } + // signal others about new format presence +/* LCK_lock(tdbb, relation->rel_rescan_lock, LCK_EX, LCK_WAIT); + LCK_release(tdbb, relation->rel_rescan_lock); !!!!!!classic!!! */ + } +} - AutoRequest request2; - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - RCL IN RDB$RELATION_CONSTRAINTS CROSS - CHK IN RDB$CHECK_CONSTRAINTS - WITH RCL.RDB$RELATION_NAME EQ name.c_str() AND - RCL.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT AND - CHK.RDB$CONSTRAINT_NAME EQ RCL.RDB$CONSTRAINT_NAME AND - CHK.RDB$TRIGGER_NAME EQ clause->fromName.c_str() - { - MODIFY CHK - strcpy(CHK.RDB$TRIGGER_NAME, clause->toName.c_str()); - END_MODIFY - } - END_FOR +blb* RelationNode::setupTriggers(thread_db* tdbb, jrd_rel* relation, bool null_view, + TrigArray* triggers, blb* blob) +{ +/************************************** + * + * s e t u p _ t r i g g e r s + * + ************************************** + * + * Functional description + * + * Get the triggers in the right order, which appears + * to be system triggers first, then user triggers, + * then triggers that implement check constraints. + * + * BUG #8458: Check constraint triggers have to be loaded + * (and hence executed) after the user-defined + * triggers because user-defined triggers can modify + * the values being inserted or updated so that + * the end values stored in the database don't + * fulfill the check constraint. + * + **************************************/ + if (!relation) + return blob; - break; - } + Attachment* attachment = tdbb->getAttachment(); - case Clause::TYPE_ALTER_COL_NULL: - { - executeBeforeTrigger(); + // system triggers - const AlterColNullClause* clause = - static_cast(i->getObject()); + AutoCacheRequest request_fmtx(tdbb, irq_format4, IRQ_REQUESTS); - AutoRequest request; - bool found = false; + FOR (REQUEST_HANDLE request_fmtx) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$RELATION_NAME = relation->getName().c_str() + AND TRG.RDB$SYSTEM_FLAG = 1 + SORTED BY TRG.RDB$TRIGGER_SEQUENCE + { + if (!TRG.RDB$TRIGGER_INACTIVE) + setupTriggerDetails(tdbb, relation, blob, triggers, TRG.RDB$TRIGGER_NAME, null_view); + } + END_FOR - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RFL IN RDB$RELATION_FIELDS - WITH RFL.RDB$FIELD_NAME EQ clause->name.c_str() AND - RFL.RDB$RELATION_NAME EQ name.c_str() - { - found = true; + // user triggers - MODIFY RFL - { - if (!clause->notNullFlag && !RFL.RDB$GENERATOR_NAME.NULL) - { - // msg 274: Identity column @1 of table @2 cannot be changed to NULLable - status_exception::raise(Arg::PrivateDyn(274) << clause->name << name); - } + request_fmtx.reset(tdbb, irq_format5, IRQ_REQUESTS); - if (clause->notNullFlag) - { - RFL.RDB$NULL_FLAG.NULL = FALSE; - RFL.RDB$NULL_FLAG = TRUE; + FOR (REQUEST_HANDLE request_fmtx) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$RELATION_NAME EQ relation->getName().c_str() + AND TRG.RDB$SYSTEM_FLAG = 0 + AND (NOT ANY + CHK IN RDB$CHECK_CONSTRAINTS CROSS + RCN IN RDB$RELATION_CONSTRAINTS + WITH TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME + AND CHK.RDB$CONSTRAINT_NAME EQ RCN.RDB$CONSTRAINT_NAME + AND (RCN.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT + OR RCN.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) + ) + SORTED BY TRG.RDB$TRIGGER_SEQUENCE + { + if (!TRG.RDB$TRIGGER_INACTIVE) + setupTriggerDetails(tdbb, relation, blob, triggers, TRG.RDB$TRIGGER_NAME, null_view); + } + END_FOR - MetaName dummyName; - Constraint nullConstraint(*tdbb->getDefaultPool()); - nullConstraint.type = Constraint::TYPE_NOT_NULL; - nullConstraint.columns.add(clause->name); - defineConstraint(tdbb, dsqlScratch, transaction, dummyName, - nullConstraint); - } - else - { - RFL.RDB$NULL_FLAG.NULL = TRUE; + // check constraint triggers. We're looking for triggers that belong + // to the table and are system triggers (i.e. system flag in (3, 4, 5)) + // or a user looking trigger that's involved in a check constraint - AutoRequest request2; + request_fmtx.reset(tdbb, irq_format6, IRQ_REQUESTS); - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - RCL IN RDB$RELATION_CONSTRAINTS CROSS - CHK IN RDB$CHECK_CONSTRAINTS - WITH RCL.RDB$RELATION_NAME EQ name.c_str() AND - RCL.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT AND - CHK.RDB$CONSTRAINT_NAME EQ RCL.RDB$CONSTRAINT_NAME AND - CHK.RDB$TRIGGER_NAME EQ clause->name.c_str() - { - // ASF: Record in RDB$CHECK_CONSTRAINTS is deleted by a - // system trigger. - ERASE RCL; - } - END_FOR - } - } - END_MODIFY - } - END_FOR + FOR (REQUEST_HANDLE request_fmtx) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$RELATION_NAME = relation->getName().c_str() + AND (TRG.RDB$SYSTEM_FLAG BT fb_sysflag_check_constraint AND fb_sysflag_view_check + OR (TRG.RDB$SYSTEM_FLAG = 0 AND ANY + CHK IN RDB$CHECK_CONSTRAINTS CROSS + RCN IN RDB$RELATION_CONSTRAINTS + WITH TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME + AND CHK.RDB$CONSTRAINT_NAME EQ RCN.RDB$CONSTRAINT_NAME + AND (RCN.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT + OR RCN.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) + ) + ) + SORTED BY TRG.RDB$TRIGGER_SEQUENCE + { + if (!TRG.RDB$TRIGGER_INACTIVE) + setupTriggerDetails(tdbb, relation, blob, triggers, TRG.RDB$TRIGGER_NAME, null_view); + } + END_FOR - if (!found) - { - // msg 176: "column %s does not exist in table/view %s" - status_exception::raise(Arg::PrivateDyn(176) << clause->name << name); - } + return blob; +} - break; - } - case Clause::TYPE_ALTER_COL_POS: - { - executeBeforeTrigger(); +void RelationNode::setupTriggerDetails(thread_db* tdbb, jrd_rel* relation, blb* blob, + TrigArray* triggers, const TEXT* trigger_name, bool null_view) +{ +/************************************** + * + * s e t u p _ t r i g g e r _ d e t a i l s + * + ************************************** + * + * Functional description + * Stuff trigger details in places. + * + * for a view, load the trigger temporarily -- + * this is inefficient since it will just be reloaded + * in MET_scan_relation () but it needs to be done + * in case the view would otherwise be non-updatable + * + **************************************/ - const AlterColPosClause* clause = - static_cast(i->getObject()); - // CVC: Since now the parser accepts pos=1..N, let's subtract one here. - const SSHORT pos = clause->newPos - 1; + putSummaryRecord(tdbb, blob, RSR_trigger_name, + (const UCHAR*) trigger_name, fb_utils::name_length(trigger_name)); - modifyLocalFieldPosition(tdbb, transaction, name, clause->name, pos); + if (!null_view) { + MET_load_trigger(tdbb, relation, trigger_name, [&](int t)->Triggers& {return (*triggers)[t];}); + } +} - break; - } - case Clause::TYPE_DROP_COLUMN: - { - // Fix for bug 8054: - // [CASCADE | RESTRICT] syntax is available in IB4.5, but not - // required until v5.0. - // - // Option CASCADE causes an error: unsupported DSQL construct. - // Option RESTRICT is default behaviour. +void RelationNode::raiseTooManyVersionsError(const int obj_type, const MetaName& obj_name) +{ + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(getErrorCodeByObjectType(obj_type)) << Arg::Str(obj_name) << + Arg::Gds(isc_version_err)); +} - const DropColumnClause* clause = - static_cast(i->getObject()); - if (clause->cascade) - { - // Unsupported DSQL construct - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_construct_err)); - } +void RelationNode::putSummaryRecord(thread_db* tdbb, blb* blob, rsr_t type, const UCHAR* data, ULONG length) +{ +/************************************** + * + * p u t _ s u m m a r y _ r e c o r d + * + ************************************** + * + * Functional description + * Put an attribute record to the relation summary blob. + * + **************************************/ + // We cannot deal with chunks longer than a max blob segment (minus one) + fb_assert(length < MAX_USHORT); - deleteLocalField(tdbb, transaction, name, clause->name, clause->silent, executeBeforeTrigger); - break; - } + SET_TDBB(tdbb); - case Clause::TYPE_ADD_CONSTRAINT: - case Clause::TYPE_DROP_CONSTRAINT: - { - const bool silent = (*i)->type == Clause::TYPE_ADD_CONSTRAINT ? - static_cast(i->getObject())->createIfNotExistsOnly : - static_cast(i->getObject())->silent; - bool found = false; + HalfStaticArray buffer; + UCHAR* p = buffer.getBuffer(length + 1); + *p++ = (UCHAR) type; + memcpy(p, data, length); - if (silent) - { - const auto& constraintName = (*i)->type == Clause::TYPE_ADD_CONSTRAINT ? - static_cast(i->getObject())->name : - static_cast(i->getObject())->name; + blob->BLB_put_segment(tdbb, buffer.begin(), length + 1); +} - AutoCacheRequest request(tdbb, drq_l_rel_con, DYN_REQUESTS); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RC IN RDB$RELATION_CONSTRAINTS - WITH RC.RDB$CONSTRAINT_NAME EQ constraintName.c_str() AND - RC.RDB$RELATION_NAME EQ name.c_str() - { - found = true; - break; - } - END_FOR - } +void RelationNode::putSummaryBlob(thread_db* tdbb, blb* blob, rsr_t type, bid* blob_id, jrd_tra* transaction) +{ +/************************************** + * + * p u t _ s u m m a r y _ b l o b + * + ************************************** + * + * Functional description + * Put an attribute record to the relation summary blob. + * + **************************************/ - if ((*i)->type == Clause::TYPE_ADD_CONSTRAINT && !(silent && found)) - { - executeBeforeTrigger(); - makeConstraint(tdbb, dsqlScratch, transaction, - static_cast(i->getObject()), constraints); - } - else if ((*i)->type == Clause::TYPE_DROP_CONSTRAINT && !(silent && !found)) - { - executeBeforeTrigger(); - CreateDropConstraint& dropConstraint = constraints.add(); - dropConstraint.name = static_cast(i->getObject())->name; - dropConstraint.silent = static_cast(i->getObject())->silent; - } + SET_TDBB(tdbb); - break; - } + if (blob_id->isEmpty()) // If blob is null, don't bother. + return; - case Clause::TYPE_ALTER_SQL_SECURITY: - { - executeBeforeTrigger(); + // Go ahead and open blob + blb* blr = blb::open(tdbb, transaction, blob_id); - AutoRequest request; + ULONG length = blr->blb_length; + // We cannot deal with chunks longer than a max blob segment (minus one) + fb_assert(length < MAX_USHORT); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - REL IN RDB$RELATIONS - WITH REL.RDB$RELATION_NAME EQ name.c_str() - { - MODIFY REL - { - if (ssDefiner.isAssigned()) - { - REL.RDB$SQL_SECURITY.NULL = FALSE; - REL.RDB$SQL_SECURITY = ssDefiner.asBool() ? FB_TRUE : FB_FALSE; - } - else - REL.RDB$SQL_SECURITY.NULL = TRUE; - } - END_MODIFY - } - END_FOR + HalfStaticArray buffer; + UCHAR* p = buffer.getBuffer(length + 1); + *p++ = (UCHAR) type; - break; - } + length = blr->BLB_get_data(tdbb, p, length); - case Clause::TYPE_ALTER_PUBLICATION: - { - fb_assert(replicationState.isAssigned()); + blob->BLB_put_segment(tdbb, buffer.begin(), length + 1); +} - executeBeforeTrigger(); - if (replicationState.asBool()) - { - // Add table to the publication +bool RelationNode::validateTextType(thread_db* tdbb, const TemporaryField* tfb) +{ +/************************************** + * + * v a l i d a t e _ t e x t _ t y p e + * + ************************************** + * + * Functional description + * Make sure the text type specified is implemented + * + **************************************/ - try - { - RelationNode::addToPublication(tdbb, transaction, - name, DEFAULT_PUBLICATION); - } - catch (const status_exception& ex) - { - if (ex.value()[1] != isc_unique_key_violation) - throw; + TTypeId ttId = tfb->tfb_desc.getTextType(); + switch(ttId) + { + case TTypeId(CS_NONE): + case TTypeId(CS_BINARY): + break; - // Ignore duplicated records - fb_utils::init_status(tdbb->tdbb_status_vector); - } - } - else - { - // Drop table from the publication + default: + return INTL_defined_type(tdbb, ttId); + } - RelationNode::dropFromPublication(tdbb, transaction, - name, DEFAULT_PUBLICATION); - } + return true; +} - break; - } - default: - fb_assert(false); - break; - } - } +void RelationNode::setupArray(thread_db* tdbb, blb* blob, const TEXT* field_name, USHORT n, + TemporaryField* tfb) +{ +/************************************** + * + * s e t u p _ a r r a y + * + ************************************** + * + * Functional description + * + * setup an array descriptor in a tfb + * + **************************************/ - for (ObjectsArray::iterator constraint(constraints.begin()); - constraint != constraints.end(); - ++constraint) - { - if (constraint->create) - { - defineConstraint(tdbb, dsqlScratch, transaction, - constraint->name, *constraint->create); - } - else - { - AutoCacheRequest request(tdbb, drq_e_rel_con, DYN_REQUESTS); - bool found = false; + SLONG stuff[256]; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RC IN RDB$RELATION_CONSTRAINTS - WITH RC.RDB$CONSTRAINT_NAME EQ constraint->name.c_str() AND - RC.RDB$RELATION_NAME EQ name.c_str() - { - found = true; - ERASE RC; - } - END_FOR + putSummaryRecord(tdbb, blob, RSR_dimensions, (UCHAR*) &n, sizeof(n)); + tfb->tfb_flags |= TFB_array; + Ods::InternalArrayDesc* array = reinterpret_cast(stuff); + MOVE_CLEAR(array, (SLONG) sizeof(Ods::InternalArrayDesc)); + array->iad_dimensions = n; + array->iad_struct_count = 1; + array->iad_rpt[0].iad_desc = tfb->tfb_desc; + getArrayDesc(tdbb, field_name, array); + putSummaryRecord(tdbb, blob, RSR_array_desc, (UCHAR*) array, array->iad_length); +} - if (!found) - { - // msg 130: "CONSTRAINT %s does not exist." - status_exception::raise(Arg::PrivateDyn(130) << constraint->name); - } - } - } - if (beforeTriggerWasExecuted) - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_TABLE, name, nullptr); +void RelationNode::getArrayDesc(thread_db* tdbb, const TEXT* field_name, Ods::InternalArrayDesc* desc) +{ +/************************************** + * + * g e t _ a r r a y _ d e s c + * + ************************************** + * + * Functional description + * Get array descriptor for an array. + * + **************************************/ + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); - savePoint.release(); // everything is ok + AutoCacheRequest request(tdbb, irq_r_fld_dim, IRQ_REQUESTS); - // Update DSQL cache - METD_drop_relation(transaction, name); - MET_dsql_cache_release(tdbb, SYM_relation, name); + Ods::InternalArrayDesc::iad_repeat* ranges = 0; + FOR (REQUEST_HANDLE request) + D IN RDB$FIELD_DIMENSIONS WITH D.RDB$FIELD_NAME EQ field_name + { + if (D.RDB$DIMENSION >= 0 && D.RDB$DIMENSION < desc->iad_dimensions) + { + ranges = desc->iad_rpt + D.RDB$DIMENSION; + ranges->iad_lower = D.RDB$LOWER_BOUND; + ranges->iad_upper = D.RDB$UPPER_BOUND; + } } - catch (const Exception&) + END_FOR + + desc->iad_count = 1; + + for (ranges = desc->iad_rpt + desc->iad_dimensions; --ranges >= desc->iad_rpt;) { - METD_drop_relation(transaction, name); - dsqlScratch->relation = NULL; - throw; + ranges->iad_length = desc->iad_count; + desc->iad_count *= ranges->iad_upper - ranges->iad_lower + 1; } + + desc->iad_version = Ods::IAD_VERSION_1; + desc->iad_length = IAD_LEN(MAX(desc->iad_struct_count, desc->iad_dimensions)); + desc->iad_element_length = desc->iad_rpt[0].iad_desc.dsc_length; + desc->iad_total_length = desc->iad_element_length * desc->iad_count; } -// Modify a field, as part of an alter table statement. -// -// If there are dependencies on the field, abort the operation -// unless the dependency is an index. In this case, rebuild the -// index once the operation has completed. -// -// If the original datatype of the field was a domain: -// if the new type is a domain, just make the change to the new domain -// if it exists -// -// if the new type is a base type, just make the change -// -// If the original datatype of the field was a base type: -// if the new type is a base type, just make the change -// -// if the new type is a domain, make the change to the field -// definition and remove the entry for RDB$FIELD_SOURCE from the original -// field. In other words ... clean up after ourselves -// -// The following conversions are not allowed: -// Blob to anything -// Array to anything -// Date to anything -// Char to any numeric -// Varchar to any numeric -// Anything to Blob -// Anything to Array -void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, AlterColTypeClause* clause) + +Format* RelationNode::makeFormat(thread_db* tdbb, jrd_tra* transaction, Cached::Relation* relation, + USHORT* version, TemporaryField* stack) { - Attachment* const attachment = transaction->tra_attachment; +/************************************** + * + * m a k e _ f o r m a t + * + ************************************** + * + * Functional description + * Make a format block for a relation. + * + **************************************/ + TemporaryField* tfb; - dsql_fld* field = clause->field; + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* sysTransaction = attachment->getSysTransaction(); + Database* dbb = tdbb->getDatabase(); - // Add the field to the relation being defined for parsing purposes. - bool permanent = false; - dsql_rel* relation = dsqlScratch->relation; + // Figure out the highest field id and allocate a format block - if (relation) + USHORT count = 0; + for (tfb = stack; tfb; tfb = tfb->tfb_next) + count = MAX(count, tfb->tfb_id); + + // For system tables, all supported formats are predefined and preloaded + // into the metadata cache, so we don't need them stored in RDB$FORMATS + + if (relation->isSystem()) { - if (!(relation->rel_flags & REL_new_relation)) + // Find and return the format matching new number of fields + + fb_assert(relation->rel_formats); + for (const auto format : *relation->rel_formats) { - dsql_fld* permField = FB_NEW_POOL(dsqlScratch->getAttachment()->dbb_pool) - dsql_fld(dsqlScratch->getAttachment()->dbb_pool); - *permField = *field; + if (format->fmt_count == count + 1) + { + if (version) + *version = format->fmt_version; - field = permField; - permanent = true; + return format; + } } - field->fld_next = relation->rel_fields; - relation->rel_fields = field; + // We should never get here + fb_assert(false); } - try + Format* format = Format::newFormat(relation->getPool(), count + 1); + format->fmt_version = version ? *version : 0; + + // Fill in the format block from the temporary field blocks + + for (tfb = stack; tfb; tfb = tfb->tfb_next) { - bool found = false; - AutoRequest request; + dsc* desc = &format->fmt_desc[tfb->tfb_id]; + if (tfb->tfb_flags & TFB_array) + { + desc->dsc_dtype = dtype_array; + desc->dsc_length = sizeof(ISC_QUAD); + } + else + *desc = tfb->tfb_desc; + if (tfb->tfb_flags & TFB_computed) + desc->dsc_dtype |= DSC_computed; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RFR IN RDB$RELATION_FIELDS CROSS - REL IN RDB$RELATIONS CROSS - FLD IN RDB$FIELDS - WITH REL.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME AND - FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE AND - RFR.RDB$RELATION_NAME = name.c_str() AND - RFR.RDB$FIELD_NAME = field->fld_name.c_str() + impure_value& defRef = format->fmt_defaults[tfb->tfb_id]; + defRef = tfb->tfb_default; + + if (tfb->tfb_default.vlu_string) { - found = true; + fb_assert(defRef.vlu_desc.dsc_dtype == dtype_text); + defRef.vlu_desc.dsc_address = defRef.vlu_string->str_data; + } + else + defRef.vlu_desc.dsc_address = (UCHAR*) &defRef.vlu_misc; + } - bool isView = !REL.RDB$VIEW_BLR.NULL; + // Compute the offsets of the various fields - if (!isView && (!FLD.RDB$COMPUTED_BLR.NULL != (clause->computed != NULL))) - { - // Cannot add or remove COMPUTED from column @1 - status_exception::raise(Arg::PrivateDyn(249) << field->fld_name); - } + ULONG offset = FLAG_BYTES(count); - dyn_fld origDom; + count = 0; + for (Format::fmt_desc_iterator desc2 = format->fmt_desc.begin(); + count < format->fmt_count; + ++count, ++desc2) + { + if (desc2->dsc_dtype & DSC_computed) + { + desc2->dsc_dtype &= ~DSC_computed; + continue; + } + if (desc2->dsc_dtype) + { + offset = MET_align(&(*desc2), offset); + desc2->dsc_address = (UCHAR *) (IPTR) offset; + offset += desc2->dsc_length; + } + } - DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, - FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, FLD.RDB$CHARACTER_SET_ID, - FLD.RDB$COLLATION_ID); + // Release the temporary field blocks - origDom.dyn_fld_name = field->fld_name; - origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; - origDom.dyn_dtype = FLD.RDB$FIELD_TYPE; - origDom.dyn_precision = FLD.RDB$FIELD_PRECISION; - origDom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE; - origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; - origDom.dyn_collation = FLD.RDB$COLLATION_ID; - origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0; - origDom.dyn_fld_source = RFR.RDB$FIELD_SOURCE; + while ( (tfb = stack) ) + { + stack = tfb->tfb_next; + delete tfb; + } - // If the original field type is an array, force its blr type to blr_blob. - const bool hasDimensions = FLD.RDB$DIMENSIONS != 0; - if (hasDimensions) - origDom.dyn_dtype = blr_blob; + if (offset > MAX_RECORD_SIZE) + { + delete format; + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_rec_size_err) << Arg::Num(offset) << + Arg::Gds(isc_table_name) << Arg::Str(relation->getName())); + // Msg361: new record size of %ld bytes is too big + } - const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.c_str()) && - RFR.RDB$BASE_FIELD.NULL; - string computedSource; - BlrDebugWriter::BlrData computedValue; + format->fmt_length = offset; - if (clause->computed) - { - field->flags |= FLD_computed; + Format* old_format; + if (format->fmt_version && + (old_format = MET_format(tdbb, relation, (format->fmt_version - 1))) && + (*old_format == *format)) + { + delete format; + *version = old_format->fmt_version; + return old_format; + } - defineComputed(dsqlScratch, dsqlNode, field, clause->computed, computedSource, - computedValue); - } + // Link the format block into the world - if (clause->defaultValue) - { - MODIFY RFR - if (!RFR.RDB$GENERATOR_NAME.NULL) - { - // msg 275: Identity column @1 of table @2 cannot have default value - status_exception::raise(Arg::PrivateDyn(275) << field->fld_name << name); - } + vec* vector = relation->rel_formats = + vec::newVector(relation->getPool(), relation->rel_formats, format->fmt_version + 1); + (*vector)[format->fmt_version] = format; - if (hasDimensions) - { - // msg 225: "Default value is not allowed for array type in field %s" - status_exception::raise(Arg::PrivateDyn(225) << field->fld_name); - } + // Store format in system relation - if (clause->computed) - { - // msg 233: "Local column %s is computed, cannot set a default value" - status_exception::raise(Arg::PrivateDyn(233) << field->fld_name); - } + AutoCacheRequest request(tdbb, irq_format3, IRQ_REQUESTS); - string defaultSource; - BlrDebugWriter::BlrData defaultValue; + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FMTS IN RDB$FORMATS + { + FMTS.RDB$FORMAT = format->fmt_version; + FMTS.RDB$RELATION_ID = relation->getId(); + blb* blob = blb::create(tdbb, transaction, &FMTS.RDB$DESCRIPTOR); - defineDefault(tdbb, dsqlScratch, field, clause->defaultValue, - defaultSource, defaultValue); + // Use generic representation of formats with 32-bit offsets - RFR.RDB$DEFAULT_SOURCE.NULL = FALSE; - attachment->storeMetaDataBlob(tdbb, transaction, - &RFR.RDB$DEFAULT_SOURCE, defaultSource); + Firebird::Array odsDescs; + Ods::Descriptor* odsDesc = odsDescs.getBuffer(format->fmt_count); - RFR.RDB$DEFAULT_VALUE.NULL = FALSE; - attachment->storeBinaryBlob(tdbb, transaction, - &RFR.RDB$DEFAULT_VALUE, defaultValue); - END_MODIFY - } - else if (clause->dropDefault) - { - MODIFY RFR - if (RFR.RDB$DEFAULT_VALUE.NULL) - { - if (FLD.RDB$DEFAULT_VALUE.NULL) - { - // msg 229: "Local column %s doesn't have a default" - status_exception::raise(Arg::PrivateDyn(229) << field->fld_name); - } - else - { - // msg 230: "Local column %s default belongs to domain %s" - status_exception::raise( - Arg::PrivateDyn(230) << - field->fld_name << MetaName(FLD.RDB$FIELD_NAME)); - } - } - else - { - RFR.RDB$DEFAULT_SOURCE.NULL = TRUE; - RFR.RDB$DEFAULT_VALUE.NULL = TRUE; - } - END_MODIFY - } - else if (clause->dropIdentity) - { - if (RFR.RDB$GENERATOR_NAME.NULL) - { - // msg 285: "Column @1 is not an identity column" - status_exception::raise(Arg::PrivateDyn(285) << field->fld_name); - } + for (Format::fmt_desc_const_iterator desc = format->fmt_desc.begin(); + desc < format->fmt_desc.end(); ++desc, ++odsDesc) + { + *odsDesc = *desc; + } - DropSequenceNode::deleteIdentity(tdbb, transaction, RFR.RDB$GENERATOR_NAME); + HalfStaticArray buffer; - MODIFY RFR - RFR.RDB$GENERATOR_NAME.NULL = TRUE; - RFR.RDB$IDENTITY_TYPE.NULL = TRUE; - END_MODIFY - } - else if (clause->identityOptions) + buffer.add(UCHAR(format->fmt_count)); + buffer.add(UCHAR(format->fmt_count >> 8)); + + buffer.add((UCHAR*) odsDescs.begin(), odsDescs.getCount() * sizeof(Ods::Descriptor)); + + const FB_SIZE_T pos = buffer.getCount(); + buffer.add(0); + buffer.add(0); + + USHORT i = 0, dflCount = 0; + for (Format::fmt_defaults_iterator impure = format->fmt_defaults.begin(); + impure != format->fmt_defaults.end(); ++impure, ++i) + { + if (!impure->vlu_desc.isUnknown()) { - bool found = false; - AutoRequest request2; + dsc desc = impure->vlu_desc; + desc.dsc_address = NULL; - FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - GEN IN RDB$GENERATORS - WITH GEN.RDB$GENERATOR_NAME EQ RFR.RDB$GENERATOR_NAME - { - const SLONG id = GEN.RDB$GENERATOR_ID; - const MetaName genName(RFR.RDB$GENERATOR_NAME); + Ods::Descriptor odsDflDesc = desc; - if (clause->identityOptions->restart) - { - const SINT64 val = - clause->identityOptions->startValue - .value_or(!GEN.RDB$INITIAL_VALUE.NULL ? GEN.RDB$INITIAL_VALUE : 0) - - clause->identityOptions->increment - .value_or(!GEN.RDB$GENERATOR_INCREMENT.NULL ? GEN.RDB$GENERATOR_INCREMENT : 1); + buffer.add(UCHAR(i)); + buffer.add(UCHAR(i >> 8)); + buffer.add((UCHAR*) &odsDflDesc, sizeof(odsDflDesc)); + buffer.add(impure->vlu_desc.dsc_address, impure->vlu_desc.dsc_length); - transaction->getGenIdCache()->put(id, val); - } + ++dflCount; + } + } - if (clause->identityOptions->type.has_value()) - { - MODIFY RFR - RFR.RDB$IDENTITY_TYPE = clause->identityOptions->type.value(); - END_MODIFY - } + buffer[pos] = UCHAR(dflCount); + buffer[pos + 1] = UCHAR(dflCount >> 8); - if (clause->identityOptions->increment.has_value()) - { - if (clause->identityOptions->increment.value() == 0) - { - status_exception::raise(Arg::Gds(isc_dyn_cant_use_zero_inc_ident) << - Arg::Str(field->fld_name) << - Arg::Str(name)); - } + blob->BLB_put_data(tdbb, buffer.begin(), buffer.getCount()); + blob->BLB_close(tdbb); + } + END_STORE - MET_update_generator_increment(tdbb, id, - clause->identityOptions->increment.value()); - } + return format; +} - dsc desc; - desc.makeText((USHORT) genName.length(), ttype_metadata, - (UCHAR*) genName.c_str()); - DFW_post_work(transaction, dfw_set_generator, &desc, id); - found = true; - } - END_FOR +//---------------------- - if (!found) - { - // msg 285: "Column @1 is not an identity column" - status_exception::raise(Arg::PrivateDyn(285) << field->fld_name); - } - } - else - { - // We have the type. Default and type/domain are exclusive for now. - MetaName newDomainName; - dyn_fld newDom; +string CreateRelationNode::internalPrint(NodePrinter& printer) const +{ + RelationNode::internalPrint(printer); - if (field->typeOfName.hasData()) - { - // Case a1: Internal domain -> domain. - // Case a2: Domain -> domain. + NODE_PRINT(printer, externalFile); + NODE_PRINT(printer, relationType); - newDomainName = field->typeOfName; + return "CreateRelationNode"; +} + +void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + SCL_check_create_access(tdbb, obj_relations); +} + +void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + jrd_tra* transaction) +{ + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + + if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation)) + return; + + saveRelation(tdbb, dsqlScratch, name, false, true); + + if (externalFile) + { + fb_assert(dsqlScratch->relation); + dsqlScratch->relation->rel_flags |= REL_external; + } + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TABLE, + name, NULL); + + DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation); + + fb_assert(relationType.has_value()); + + checkRelationTempScope(tdbb, transaction, name, relationType.value()); + + AutoCacheRequest requestStore(tdbb, drq_s_rels2, DYN_REQUESTS); + + STORE(REQUEST_HANDLE requestStore TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS + { + strcpy(REL.RDB$RELATION_NAME, name.c_str()); + REL.RDB$SYSTEM_FLAG = 0; + REL.RDB$FLAGS = REL_sql; + REL.RDB$RELATION_TYPE = relationType.value(); + + if (ssDefiner.isAssigned()) + { + REL.RDB$SQL_SECURITY.NULL = FALSE; + REL.RDB$SQL_SECURITY = ssDefiner.asBool() ? FB_TRUE : FB_FALSE; + } + else + REL.RDB$SQL_SECURITY.NULL = TRUE; + + REL.RDB$VIEW_BLR.NULL = TRUE; + REL.RDB$VIEW_SOURCE.NULL = TRUE; + REL.RDB$EXTERNAL_FILE.NULL = TRUE; + + if (externalFile) + { + if (externalFile->length() >= sizeof(REL.RDB$EXTERNAL_FILE)) + status_exception::raise(Arg::Gds(isc_dyn_name_longer)); + + if (ISC_check_if_remote(externalFile->c_str(), false)) + status_exception::raise(Arg::PrivateDyn(163)); + + REL.RDB$EXTERNAL_FILE.NULL = FALSE; + strcpy(REL.RDB$EXTERNAL_FILE, externalFile->c_str()); + REL.RDB$RELATION_TYPE = rel_external; + } + } + END_STORE + + auto rel_id = generateIdDbKey(tdbb, transaction); + + bool replicationEnabled = false; + + if (replicationState.isAssigned()) + replicationEnabled = replicationState.asBool(); + else + { + // Apply the default replication state to the table being created + + AutoCacheRequest request2(tdbb, drq_l_pub_mode, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + PUB IN RDB$PUBLICATIONS + WITH PUB.RDB$PUBLICATION_NAME EQ DEFAULT_PUBLICATION + { + replicationEnabled = (PUB.RDB$AUTO_ENABLE != 0); + } + END_FOR + } + + if (replicationEnabled) + { + // Add table to the default publication + RelationNode::addToPublication(tdbb, transaction, name, DEFAULT_PUBLICATION); + } + + storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES); + + ObjectsArray constraints; + const ObjectsArray* pkCols = findPkColumns(); + SSHORT position = 0; + + for (NestConst* i = clauses.begin(); i != clauses.end(); ++i) + { + switch ((*i)->type) + { + case Clause::TYPE_ADD_COLUMN: + defineField(tdbb, dsqlScratch, transaction, + static_cast(i->getObject()), position, pkCols); + ++position; + break; + + case Clause::TYPE_ADD_CONSTRAINT: + makeConstraint(tdbb, dsqlScratch, transaction, + static_cast(i->getObject()), constraints); + break; + + default: + fb_assert(false); + break; + } + } + + for (ObjectsArray::iterator constraint(constraints.begin()); + constraint != constraints.end(); + ++constraint) + { + defineConstraint(tdbb, dsqlScratch, transaction, constraint->name, *constraint->create); + } + + try + { + // create the table + + if (rel_id && !externalFile) + { + auto* relation = MetadataCache::newVersion(tdbb, rel_id); + DPM_create_relation(tdbb, relation); + indexList.exec(tdbb, relation, transaction); + } + } + catch(const Exception&) + { + // We need to cleanup RDB$PAGES and pages if they were added at phase 3. + AutoCacheRequest request; + request.reset(tdbb, irq_c_relation3, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + X IN RDB$RELATIONS WITH + X.RDB$RELATION_NAME EQ name.c_str() AND + X.RDB$RELATION_ID NOT MISSING + { + auto* relation = MetadataCache::lookupRelation(tdbb, X.RDB$RELATION_ID, 0); + if (relation) + { + RelationPages* const relPages = relation->getBasePages(); + + if (relPages->rel_index_root) + IDX_delete_indices(tdbb, relation, relPages); + + if (relPages->rel_pages) + DPM_delete_relation(tdbb, relation); + } + } + END_FOR + + throw; + } + + dsqlScratch->relation->rel_flags &= ~REL_creating; + + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TABLE, + name, NULL); + + savePoint.release(); // everything is ok +} + +// Starting from the elements in a table definition, locate the PK columns if given in a +// separate table constraint declaration. +const ObjectsArray* CreateRelationNode::findPkColumns() +{ + for (const NestConst* i = clauses.begin(); i != clauses.end(); ++i) + { + if ((*i)->type == Clause::TYPE_ADD_CONSTRAINT) + { + const AddConstraintClause* clause = static_cast(i->getObject()); + + if (clause->constraintType == AddConstraintClause::CTYPE_PK) + return &clause->columns; + } + } + + return NULL; +} + + +//---------------------- + + +string AlterRelationNode::internalPrint(NodePrinter& printer) const +{ + RelationNode::internalPrint(printer); + return "AlterRelationNode"; +} + +void AlterRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str()); + SCL_check_relation(tdbb, &dscName, SCL_alter); +} + +void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + jrd_tra* transaction) +{ + saveRelation(tdbb, dsqlScratch, name, false, false); + + // also ensures oldVersion is present in cache + auto* rel = MetadataCache::lookupRelation(tdbb, name, CacheFlag::AUTOCREATE); + + dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name); + + if (!relation || (relation->rel_flags & REL_view)) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_table_not_found) << name); + } + + if (!dsqlScratch->relation) + { + char linecol[64]; + sprintf(linecol, "At line %d, column %d.", (int) dsqlNode->line, (int) dsqlNode->column); + + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-204) << + Arg::Gds(isc_dsql_relation_err) << + Arg::Gds(isc_random) << name << + Arg::Gds(isc_random) << linecol); + } + + bool beforeTriggerWasExecuted = false; + + const auto executeBeforeTrigger = [&]() + { + if (!beforeTriggerWasExecuted) + { + beforeTriggerWasExecuted = true; + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_TABLE, name, nullptr); + } + }; + + // If there is an error, get rid of the cached data. + + try + { + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + ObjectsArray constraints; + + for (NestConst* i = clauses.begin(); i != clauses.end(); ++i) + { + switch ((*i)->type) + { + case Clause::TYPE_ADD_COLUMN: + { + const auto addColumnClause = static_cast(i->getObject()); + bool createColumn = true; + + if (addColumnClause->createIfNotExistsOnly) + { + AutoCacheRequest request(tdbb, drq_l_rel_fld_name, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS + WITH RFL.RDB$RELATION_NAME = relation->rel_name.c_str() AND + RFL.RDB$FIELD_NAME = addColumnClause->field->fld_name.c_str() + { + createColumn = false; + break; + } + END_FOR + } + + if (createColumn) + { + executeBeforeTrigger(); + defineField(tdbb, dsqlScratch, transaction, addColumnClause, -1, nullptr); + } + + break; + } + + case Clause::TYPE_ALTER_COL_TYPE: + executeBeforeTrigger(); + modifyField(tdbb, dsqlScratch, transaction, static_cast(i->getObject())); + break; + + case Clause::TYPE_ALTER_COL_NAME: + { + executeBeforeTrigger(); + + const AlterColNameClause* clause = + static_cast(i->getObject()); + AutoRequest request; + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS + WITH RFL.RDB$FIELD_NAME EQ clause->fromName.c_str() AND + RFL.RDB$RELATION_NAME EQ name.c_str() + { + found = true; + + MODIFY RFL + checkViewDependency(tdbb, transaction, name, clause->fromName); + checkSpTrigDependency(tdbb, transaction, name, clause->fromName); + + if (!fieldExists(tdbb, transaction, name, clause->toName)) + { + strcpy(RFL.RDB$FIELD_NAME, clause->toName.c_str()); + AlterDomainNode::modifyLocalFieldIndex(tdbb, transaction, name, + clause->fromName, clause->toName); + } + else + { + // msg 205: Cannot rename field %s to %s. A field with that name + // already exists in table %s. + status_exception::raise( + Arg::PrivateDyn(205) << clause->fromName << clause->toName << name); + } + END_MODIFY + } + END_FOR + + if (!found) + { + // msg 176: "column %s does not exist in table/view %s" + status_exception::raise(Arg::PrivateDyn(176) << clause->fromName << name); + } + + AutoRequest request2; + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + RCL IN RDB$RELATION_CONSTRAINTS CROSS + CHK IN RDB$CHECK_CONSTRAINTS + WITH RCL.RDB$RELATION_NAME EQ name.c_str() AND + RCL.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT AND + CHK.RDB$CONSTRAINT_NAME EQ RCL.RDB$CONSTRAINT_NAME AND + CHK.RDB$TRIGGER_NAME EQ clause->fromName.c_str() + { + MODIFY CHK + strcpy(CHK.RDB$TRIGGER_NAME, clause->toName.c_str()); + END_MODIFY + } + END_FOR + + break; + } + + case Clause::TYPE_ALTER_COL_NULL: + { + executeBeforeTrigger(); + + const AlterColNullClause* clause = + static_cast(i->getObject()); + + AutoRequest request; + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS + WITH RFL.RDB$FIELD_NAME EQ clause->name.c_str() AND + RFL.RDB$RELATION_NAME EQ name.c_str() + { + found = true; + + MODIFY RFL + { + if (!clause->notNullFlag && !RFL.RDB$GENERATOR_NAME.NULL) + { + // msg 274: Identity column @1 of table @2 cannot be changed to NULLable + status_exception::raise(Arg::PrivateDyn(274) << clause->name << name); + } + + if (clause->notNullFlag) + { + RFL.RDB$NULL_FLAG.NULL = FALSE; + RFL.RDB$NULL_FLAG = TRUE; + + MetaName dummyName; + Constraint nullConstraint(*tdbb->getDefaultPool()); + nullConstraint.type = Constraint::TYPE_NOT_NULL; + nullConstraint.columns.add(clause->name); + defineConstraint(tdbb, dsqlScratch, transaction, dummyName, + nullConstraint); + } + else + { + RFL.RDB$NULL_FLAG.NULL = TRUE; + + AutoRequest request2; + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + RCL IN RDB$RELATION_CONSTRAINTS CROSS + CHK IN RDB$CHECK_CONSTRAINTS + WITH RCL.RDB$RELATION_NAME EQ name.c_str() AND + RCL.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT AND + CHK.RDB$CONSTRAINT_NAME EQ RCL.RDB$CONSTRAINT_NAME AND + CHK.RDB$TRIGGER_NAME EQ clause->name.c_str() + { + // ASF: Record in RDB$CHECK_CONSTRAINTS is deleted by a + // system trigger. + ERASE RCL; + } + END_FOR + } + } + END_MODIFY + } + END_FOR + + if (!found) + { + // msg 176: "column %s does not exist in table/view %s" + status_exception::raise(Arg::PrivateDyn(176) << clause->name << name); + } + + break; + } + + case Clause::TYPE_ALTER_COL_POS: + { + executeBeforeTrigger(); + + const AlterColPosClause* clause = + static_cast(i->getObject()); + // CVC: Since now the parser accepts pos=1..N, let's subtract one here. + const SSHORT pos = clause->newPos - 1; + + modifyLocalFieldPosition(tdbb, transaction, name, clause->name, pos); + + break; + } + + case Clause::TYPE_DROP_COLUMN: + { + // Fix for bug 8054: + // [CASCADE | RESTRICT] syntax is available in IB4.5, but not + // required until v5.0. + // + // Option CASCADE causes an error: unsupported DSQL construct. + // Option RESTRICT is default behaviour. + + const DropColumnClause* clause = + static_cast(i->getObject()); + + if (clause->cascade) + { + // Unsupported DSQL construct + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-901) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_construct_err)); + } + + deleteLocalField(tdbb, transaction, name, clause->name, clause->silent, executeBeforeTrigger); + break; + } + + case Clause::TYPE_ADD_CONSTRAINT: + case Clause::TYPE_DROP_CONSTRAINT: + { + const bool silent = (*i)->type == Clause::TYPE_ADD_CONSTRAINT ? + static_cast(i->getObject())->createIfNotExistsOnly : + static_cast(i->getObject())->silent; + bool found = false; + + if (silent) + { + const auto& constraintName = (*i)->type == Clause::TYPE_ADD_CONSTRAINT ? + static_cast(i->getObject())->name : + static_cast(i->getObject())->name; + + AutoCacheRequest request(tdbb, drq_l_rel_con, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RC IN RDB$RELATION_CONSTRAINTS + WITH RC.RDB$CONSTRAINT_NAME EQ constraintName.c_str() AND + RC.RDB$RELATION_NAME EQ name.c_str() + { + found = true; + break; + } + END_FOR + } + + if ((*i)->type == Clause::TYPE_ADD_CONSTRAINT && !(silent && found)) + { + executeBeforeTrigger(); + makeConstraint(tdbb, dsqlScratch, transaction, + static_cast(i->getObject()), constraints); + } + else if ((*i)->type == Clause::TYPE_DROP_CONSTRAINT && !(silent && !found)) + { + executeBeforeTrigger(); + CreateDropConstraint& dropConstraint = constraints.add(); + dropConstraint.name = static_cast(i->getObject())->name; + dropConstraint.silent = static_cast(i->getObject())->silent; + } + + break; + } + + case Clause::TYPE_ALTER_SQL_SECURITY: + { + executeBeforeTrigger(); + + AutoRequest request; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS + WITH REL.RDB$RELATION_NAME EQ name.c_str() + { + MODIFY REL + { + if (ssDefiner.isAssigned()) + { + REL.RDB$SQL_SECURITY.NULL = FALSE; + REL.RDB$SQL_SECURITY = ssDefiner.asBool() ? FB_TRUE : FB_FALSE; + } + else + REL.RDB$SQL_SECURITY.NULL = TRUE; + } + END_MODIFY + } + END_FOR + + break; + } + + case Clause::TYPE_ALTER_PUBLICATION: + { + fb_assert(replicationState.isAssigned()); + + executeBeforeTrigger(); + + if (replicationState.asBool()) + { + // Add table to the publication + + try + { + RelationNode::addToPublication(tdbb, transaction, + name, DEFAULT_PUBLICATION); + } + catch (const status_exception& ex) + { + if (ex.value()[1] != isc_unique_key_violation) + throw; + + // Ignore duplicated records + fb_utils::init_status(tdbb->tdbb_status_vector); + } + } + else + { + // Drop table from the publication + + RelationNode::dropFromPublication(tdbb, transaction, + name, DEFAULT_PUBLICATION); + } + + break; + } + + default: + fb_assert(false); + break; + } + } + + for (ObjectsArray::iterator constraint(constraints.begin()); + constraint != constraints.end(); + ++constraint) + { + if (constraint->create) + { + defineConstraint(tdbb, dsqlScratch, transaction, + constraint->name, *constraint->create); + } + else + { + AutoCacheRequest request(tdbb, drq_e_rel_con, DYN_REQUESTS); + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RC IN RDB$RELATION_CONSTRAINTS + WITH RC.RDB$CONSTRAINT_NAME EQ constraint->name.c_str() AND + RC.RDB$RELATION_NAME EQ name.c_str() + { + found = true; + ERASE RC; + } + END_FOR + + if (!found) + { + // msg 130: "CONSTRAINT %s does not exist." + status_exception::raise(Arg::PrivateDyn(130) << constraint->name); + } + } + } + + MetadataCache::newVersion(tdbb, rel->getId()); + DFW_post_work(transaction, dfw_commit_relation, nullptr, rel->getId()); + + if (beforeTriggerWasExecuted) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_TABLE, name, nullptr); + + savePoint.release(); // everything is ok + } + catch (const Exception&) + { + dsqlScratch->relation = NULL; + throw; + } +} + +// Modify a field, as part of an alter table statement. +// +// If there are dependencies on the field, abort the operation +// unless the dependency is an index. In this case, rebuild the +// index once the operation has completed. +// +// If the original datatype of the field was a domain: +// if the new type is a domain, just make the change to the new domain +// if it exists +// +// if the new type is a base type, just make the change +// +// If the original datatype of the field was a base type: +// if the new type is a base type, just make the change +// +// if the new type is a domain, make the change to the field +// definition and remove the entry for RDB$FIELD_SOURCE from the original +// field. In other words ... clean up after ourselves +// +// The following conversions are not allowed: +// Blob to anything +// Array to anything +// Date to anything +// Char to any numeric +// Varchar to any numeric +// Anything to Blob +// Anything to Array +void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + jrd_tra* transaction, AlterColTypeClause* clause) +{ + Attachment* const attachment = transaction->tra_attachment; + + dsql_fld* field = clause->field; + + // Add the field to the relation being defined for parsing purposes. + bool permanent = false; + dsql_rel* relation = dsqlScratch->relation; + + if (relation) + { + if (!(relation->rel_flags & REL_new_relation)) + { + dsql_fld* permField = FB_NEW_POOL(dsqlScratch->getAttachment()->dbb_pool) + dsql_fld(dsqlScratch->getAttachment()->dbb_pool); + *permField = *field; + + field = permField; + permanent = true; + } + + field->fld_next = relation->rel_fields; + relation->rel_fields = field; + } + + try + { + bool found = false; + AutoRequest request; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFR IN RDB$RELATION_FIELDS CROSS + REL IN RDB$RELATIONS CROSS + FLD IN RDB$FIELDS + WITH REL.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME AND + FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE AND + RFR.RDB$RELATION_NAME = name.c_str() AND + RFR.RDB$FIELD_NAME = field->fld_name.c_str() + { + found = true; + + bool isView = !REL.RDB$VIEW_BLR.NULL; + + if (!isView && (!FLD.RDB$COMPUTED_BLR.NULL != (clause->computed != NULL))) + { + // Cannot add or remove COMPUTED from column @1 + status_exception::raise(Arg::PrivateDyn(249) << field->fld_name); + } + + dyn_fld origDom; + + DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, + FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); + + origDom.dyn_fld_name = field->fld_name; + origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; + origDom.dyn_dtype = FLD.RDB$FIELD_TYPE; + origDom.dyn_precision = FLD.RDB$FIELD_PRECISION; + origDom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE; + origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; + origDom.dyn_collation = FLD.RDB$COLLATION_ID; + origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0; + origDom.dyn_fld_source = RFR.RDB$FIELD_SOURCE; + + // If the original field type is an array, force its blr type to blr_blob. + const bool hasDimensions = FLD.RDB$DIMENSIONS != 0; + if (hasDimensions) + origDom.dyn_dtype = blr_blob; + + const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.c_str()) && + RFR.RDB$BASE_FIELD.NULL; + string computedSource; + BlrDebugWriter::BlrData computedValue; + + if (clause->computed) + { + field->flags |= FLD_computed; + + defineComputed(dsqlScratch, dsqlNode, field, clause->computed, computedSource, + computedValue); + } + + if (clause->defaultValue) + { + MODIFY RFR + if (!RFR.RDB$GENERATOR_NAME.NULL) + { + // msg 275: Identity column @1 of table @2 cannot have default value + status_exception::raise(Arg::PrivateDyn(275) << field->fld_name << name); + } + + if (hasDimensions) + { + // msg 225: "Default value is not allowed for array type in field %s" + status_exception::raise(Arg::PrivateDyn(225) << field->fld_name); + } + + if (clause->computed) + { + // msg 233: "Local column %s is computed, cannot set a default value" + status_exception::raise(Arg::PrivateDyn(233) << field->fld_name); + } + + string defaultSource; + BlrDebugWriter::BlrData defaultValue; + + defineDefault(tdbb, dsqlScratch, field, clause->defaultValue, + defaultSource, defaultValue); + + RFR.RDB$DEFAULT_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, + &RFR.RDB$DEFAULT_SOURCE, defaultSource); + + RFR.RDB$DEFAULT_VALUE.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, + &RFR.RDB$DEFAULT_VALUE, defaultValue); + END_MODIFY + } + else if (clause->dropDefault) + { + MODIFY RFR + if (RFR.RDB$DEFAULT_VALUE.NULL) + { + if (FLD.RDB$DEFAULT_VALUE.NULL) + { + // msg 229: "Local column %s doesn't have a default" + status_exception::raise(Arg::PrivateDyn(229) << field->fld_name); + } + else + { + // msg 230: "Local column %s default belongs to domain %s" + status_exception::raise( + Arg::PrivateDyn(230) << + field->fld_name << MetaName(FLD.RDB$FIELD_NAME)); + } + } + else + { + RFR.RDB$DEFAULT_SOURCE.NULL = TRUE; + RFR.RDB$DEFAULT_VALUE.NULL = TRUE; + } + END_MODIFY + } + else if (clause->dropIdentity) + { + if (RFR.RDB$GENERATOR_NAME.NULL) + { + // msg 285: "Column @1 is not an identity column" + status_exception::raise(Arg::PrivateDyn(285) << field->fld_name); + } + + DropSequenceNode::deleteIdentity(tdbb, transaction, RFR.RDB$GENERATOR_NAME); + + MODIFY RFR + RFR.RDB$GENERATOR_NAME.NULL = TRUE; + RFR.RDB$IDENTITY_TYPE.NULL = TRUE; + END_MODIFY + } + else if (clause->identityOptions) + { + bool found = false; + AutoRequest request2; + + FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + GEN IN RDB$GENERATORS + WITH GEN.RDB$GENERATOR_NAME EQ RFR.RDB$GENERATOR_NAME + { + const SLONG id = GEN.RDB$GENERATOR_ID; + const MetaName genName(RFR.RDB$GENERATOR_NAME); + + if (clause->identityOptions->restart) + { + const SINT64 val = + clause->identityOptions->startValue + .value_or(!GEN.RDB$INITIAL_VALUE.NULL ? GEN.RDB$INITIAL_VALUE : 0) - + clause->identityOptions->increment + .value_or(!GEN.RDB$GENERATOR_INCREMENT.NULL ? GEN.RDB$GENERATOR_INCREMENT : 1); + + transaction->getGenIdCache()->put(id, val); + } + + if (clause->identityOptions->type.has_value()) + { + MODIFY RFR + RFR.RDB$IDENTITY_TYPE = clause->identityOptions->type.value(); + END_MODIFY + } + + if (clause->identityOptions->increment.has_value()) + { + if (clause->identityOptions->increment.value() == 0) + { + status_exception::raise(Arg::Gds(isc_dyn_cant_use_zero_inc_ident) << + Arg::Str(field->fld_name) << + Arg::Str(name)); + } + + MET_update_generator_increment(tdbb, id, + clause->identityOptions->increment.value()); + } + + dsc desc; + desc.makeText((USHORT) genName.length(), ttype_metadata, + (UCHAR*) genName.c_str()); + DFW_post_work(transaction, dfw_set_generator, &desc, id); + + found = true; + } + END_FOR + + if (!found) + { + // msg 285: "Column @1 is not an identity column" + status_exception::raise(Arg::PrivateDyn(285) << field->fld_name); + } + } + else + { + // We have the type. Default and type/domain are exclusive for now. + + MetaName newDomainName; + dyn_fld newDom; + + if (field->typeOfName.hasData()) + { + // Case a1: Internal domain -> domain. + // Case a2: Domain -> domain. + + newDomainName = field->typeOfName; if (fb_utils::implicit_domain(newDomainName.c_str())) { - // msg 224: "Cannot use the internal domain %s as new type for field %s". - status_exception::raise( - Arg::PrivateDyn(224) << newDomainName << field->fld_name); + // msg 224: "Cannot use the internal domain %s as new type for field %s". + status_exception::raise( + Arg::PrivateDyn(224) << newDomainName << field->fld_name); + } + + // Get the domain information. + if (!METD_get_domain(dsqlScratch->getTransaction(), field, newDomainName)) + { + // Specified domain or source field does not exist. + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_domain_not_found) << newDomainName); + } + + DDL_resolve_intl_type(dsqlScratch, field, NULL); + + // If the original definition was a base field type, remove the + // entries from RDB$FIELDS. + if (wasInternalDomain) + { + // Case a1: Internal domain -> domain. + ERASE FLD; + } + } + else + { + // Case b1: Internal domain -> internal domain. + // Case b2: Domain -> internal domain. + + // If COMPUTED was specified but the type wasn't, we use the type of + // the computed expression. + if (clause->computed && field->dtype == dtype_unknown) + { + dsc desc; + DsqlDescMaker::fromNode(dsqlScratch, &desc, clause->computed->value); + + field->dtype = desc.dsc_dtype; + field->length = desc.dsc_length; + field->scale = desc.dsc_scale; + + if (field->dtype <= dtype_any_text) + { + field->charSetId = DSC_GET_CHARSET(&desc); + field->collationId = DSC_GET_COLLATE(&desc); + } + else + field->subType = desc.dsc_sub_type; + } + + field->resolve(dsqlScratch, true); + + if (wasInternalDomain) // Case b1: Internal domain -> internal domain. + { + MODIFY FLD + updateRdbFields(field, + FLD.RDB$FIELD_TYPE, + FLD.RDB$FIELD_LENGTH, + FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE, + FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE, + FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID, + FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH, + FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION, + FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID, + FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH); + END_MODIFY + + MODIFY RFR USING + if (field->explicitCollation) + { + RFR.RDB$COLLATION_ID.NULL = FALSE; + RFR.RDB$COLLATION_ID = field->collationId; + } + else + { + RFR.RDB$COLLATION_ID.NULL = TRUE; // CORE-2426 and Issue #7924 + } + END_MODIFY + + newDom.dyn_fld_source = origDom.dyn_fld_source; + } + else // Case b2: Domain -> internal domain. + storeGlobalField(tdbb, transaction, newDomainName, field); + } + + if (!clause->computed && !isView) + { + if (newDomainName.hasData()) + newDom.dyn_fld_source = newDomainName; + + AlterDomainNode::getDomainType(tdbb, transaction, newDom); + AlterDomainNode::checkUpdate(origDom, newDom); + + if (!RFR.RDB$GENERATOR_NAME.NULL) + { + if (!newDom.dyn_dsc.isExact() || newDom.dyn_dsc.dsc_scale != 0) + { + // Identity column @1 of table @2 must be exact numeric with zero scale. + status_exception::raise(Arg::PrivateDyn(273) << field->fld_name << name); + } } + } + + if (newDomainName.hasData()) + { + MODIFY RFR USING + RFR.RDB$FIELD_SOURCE.NULL = FALSE; + strcpy(RFR.RDB$FIELD_SOURCE, newDomainName.c_str()); + + if (clause->computed) + { + RFR.RDB$UPDATE_FLAG.NULL = FALSE; + RFR.RDB$UPDATE_FLAG = 1; + } + + RFR.RDB$COLLATION_ID.NULL = TRUE; // CORE-2426 + END_MODIFY + } + } + + if (clause->computed) + { + // We can alter FLD directly here because if we are setting a computed expression, + // it means the field already was computed. And if it was, it should be the + // "b1 case", where the field source does not change. + // This assumption may change, especially when this function starts dealing + // with views. + + MODIFY FLD + FLD.RDB$COMPUTED_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$COMPUTED_SOURCE, + computedSource); + + FLD.RDB$COMPUTED_BLR.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$COMPUTED_BLR, + computedValue); + END_MODIFY + } + + AutoRequest request2; + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + PRM IN RDB$PROCEDURE_PARAMETERS + WITH PRM.RDB$RELATION_NAME = name.c_str() AND + PRM.RDB$FIELD_NAME = field->fld_name.c_str() + { + MODIFY PRM USING + strcpy(PRM.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE); + END_MODIFY + } + END_FOR + + request2.reset(); + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + ARG IN RDB$FUNCTION_ARGUMENTS + WITH ARG.RDB$RELATION_NAME = name.c_str() AND + ARG.RDB$FIELD_NAME = field->fld_name.c_str() + { + MODIFY ARG USING + strcpy(ARG.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE); + END_MODIFY + } + END_FOR + + request2.reset(); + + FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + RFR2 IN RDB$RELATION_FIELDS CROSS + VRL IN RDB$VIEW_RELATIONS + WITH VRL.RDB$RELATION_NAME EQ name.c_str() AND + VRL.RDB$PACKAGE_NAME MISSING AND + VRL.RDB$CONTEXT_TYPE EQ VCT_TABLE AND + RFR2.RDB$RELATION_NAME EQ VRL.RDB$VIEW_NAME AND + RFR2.RDB$VIEW_CONTEXT EQ VRL.RDB$VIEW_CONTEXT AND + RFR2.RDB$BASE_FIELD = field->fld_name.c_str() + { + MODIFY RFR2 + { + strcpy(RFR2.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE); + } + END_MODIFY + } + END_FOR + } + END_FOR + + if (!found) + { + // msg 176: "column %s does not exist in table/view %s" + status_exception::raise(Arg::PrivateDyn(176) << field->fld_name << name); + } + + // Update any indices that exist. + AlterDomainNode::modifyLocalFieldIndex(tdbb, transaction, name, + field->fld_name, field->fld_name); + } + catch (const Exception&) + { + clearPermanentField(relation, permanent); + throw; + } + + clearPermanentField(relation, permanent); +} + + +//---------------------- + + +// Delete a global field if it's not used in others objects. +void DropRelationNode::deleteGlobalField(thread_db* tdbb, jrd_tra* transaction, + const MetaName& globalName) +{ + AutoCacheRequest request(tdbb, drq_e_l_gfld, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FLD IN RDB$FIELDS + WITH FLD.RDB$FIELD_NAME EQ globalName.c_str() AND + FLD.RDB$VALIDATION_SOURCE MISSING AND + FLD.RDB$NULL_FLAG MISSING AND + FLD.RDB$DEFAULT_SOURCE MISSING AND + FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX AND + (NOT ANY RFR IN RDB$RELATION_FIELDS WITH + RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND + (NOT ANY PRM IN RDB$PROCEDURE_PARAMETERS WITH + PRM.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND + (NOT ANY ARG IN RDB$FUNCTION_ARGUMENTS WITH + ARG.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) + { + DropDomainNode::deleteDimensionRecords(tdbb, transaction, globalName); + ERASE FLD; + } + END_FOR +} + +string DropRelationNode::internalPrint(NodePrinter& printer) const +{ + DdlNode::internalPrint(printer); + + NODE_PRINT(printer, name); + NODE_PRINT(printer, view); + NODE_PRINT(printer, silent); + + return "DropRelationNode"; +} + +void DropRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + dsc dscName; + dscName.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str()); + if (view) + SCL_check_view(tdbb, &dscName, SCL_drop); + else + SCL_check_relation(tdbb, &dscName, SCL_drop); +} + +void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + jrd_tra* transaction) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + + auto* rel = MetadataCache::lookup_relation(tdbb, name, CacheFlag::AUTOCREATE); + if (!rel && silent) + return; + + auto* relation = getPermanent(rel); + + // Check that DROP TABLE is dropping a table and that DROP VIEW is dropping a view. + if (view) + { + if (!relation || (relation && !(relation->isView()))) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_view_not_found) << name); + } + } + else + { + if (!relation || (relation && (relation->isView()))) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_table_not_found) << name); + } + } + + const int ddlTriggerAction = (view ? DDL_TRIGGER_DROP_VIEW : DDL_TRIGGER_DROP_TABLE); + MetaId relId = rel->getId(); - // Get the domain information. - if (!METD_get_domain(dsqlScratch->getTransaction(), field, newDomainName)) - { - // Specified domain or source field does not exist. - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_domain_not_found) << newDomainName); - } + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); - DDL_resolve_intl_type(dsqlScratch, field, NULL); + AutoCacheRequest request(tdbb, drq_l_relation, DYN_REQUESTS); + bool found = false; - // If the original definition was a base field type, remove the - // entries from RDB$FIELDS. - if (wasInternalDomain) - { - // Case a1: Internal domain -> domain. - ERASE FLD; - } - } - else - { - // Case b1: Internal domain -> internal domain. - // Case b2: Domain -> internal domain. + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + R IN RDB$RELATIONS + WITH R.RDB$RELATION_NAME EQ name.c_str() + { + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, NULL); + found = true; + } + END_FOR - // If COMPUTED was specified but the type wasn't, we use the type of - // the computed expression. - if (clause->computed && field->dtype == dtype_unknown) - { - dsc desc; - DsqlDescMaker::fromNode(dsqlScratch, &desc, clause->computed->value); + if (!found) + { + // msg 61: "Relation not found" + status_exception::raise(Arg::PrivateDyn(61)); + } - field->dtype = desc.dsc_dtype; - field->length = desc.dsc_length; - field->scale = desc.dsc_scale; + request.reset(tdbb, drq_e_rel_con2, DYN_REQUESTS); - if (field->dtype <= dtype_any_text) - { - field->charSetId = DSC_GET_CHARSET(&desc); - field->collationId = DSC_GET_COLLATE(&desc); - } - else - field->subType = desc.dsc_sub_type; - } + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + CRT IN RDB$RELATION_CONSTRAINTS + WITH CRT.RDB$RELATION_NAME EQ name.c_str() AND + (CRT.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY OR + CRT.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT OR + CRT.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) + SORTED BY ASCENDING CRT.RDB$CONSTRAINT_TYPE + { + ERASE CRT; + } + END_FOR - field->resolve(dsqlScratch, true); + request.reset(tdbb, drq_e_rel_idxs, DYN_REQUESTS); - if (wasInternalDomain) // Case b1: Internal domain -> internal domain. - { - MODIFY FLD - updateRdbFields(field, - FLD.RDB$FIELD_TYPE, - FLD.RDB$FIELD_LENGTH, - FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE, - FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE, - FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID, - FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH, - FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION, - FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID, - FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH); - END_MODIFY + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES + WITH IDX.RDB$RELATION_NAME EQ name.c_str() + { + DropIndexNode::deleteSegmentRecords(tdbb, transaction, IDX.RDB$INDEX_NAME); + ERASE IDX; + } + END_FOR - MODIFY RFR USING - if (field->explicitCollation) - { - RFR.RDB$COLLATION_ID.NULL = FALSE; - RFR.RDB$COLLATION_ID = field->collationId; - } - else - { - RFR.RDB$COLLATION_ID.NULL = TRUE; // CORE-2426 and Issue #7924 - } - END_MODIFY + request.reset(tdbb, drq_e_trg_msgs2, DYN_REQUESTS); - newDom.dyn_fld_source = origDom.dyn_fld_source; - } - else // Case b2: Domain -> internal domain. - storeGlobalField(tdbb, transaction, newDomainName, field); - } + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + TM IN RDB$TRIGGER_MESSAGES + CROSS T IN RDB$TRIGGERS + WITH T.RDB$RELATION_NAME EQ name.c_str() AND + TM.RDB$TRIGGER_NAME EQ T.RDB$TRIGGER_NAME + { + ERASE TM; + } + END_FOR - if (!clause->computed && !isView) - { - if (newDomainName.hasData()) - newDom.dyn_fld_source = newDomainName; + // CVC: Moved this block here to avoid SF Bug #1111570. + request.reset(tdbb, drq_e_rel_con3, DYN_REQUESTS); - AlterDomainNode::getDomainType(tdbb, transaction, newDom); - AlterDomainNode::checkUpdate(origDom, newDom); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + CRT IN RDB$RELATION_CONSTRAINTS + WITH CRT.RDB$RELATION_NAME EQ name.c_str() AND + (CRT.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT OR + CRT.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT) + { + ERASE CRT; + } + END_FOR - if (!RFR.RDB$GENERATOR_NAME.NULL) - { - if (!newDom.dyn_dsc.isExact() || newDom.dyn_dsc.dsc_scale != 0) - { - // Identity column @1 of table @2 must be exact numeric with zero scale. - status_exception::raise(Arg::PrivateDyn(273) << field->fld_name << name); - } - } - } + request.reset(tdbb, drq_e_rel_flds, DYN_REQUESTS); - if (newDomainName.hasData()) - { - MODIFY RFR USING - RFR.RDB$FIELD_SOURCE.NULL = FALSE; - strcpy(RFR.RDB$FIELD_SOURCE, newDomainName.c_str()); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFR IN RDB$RELATION_FIELDS + WITH RFR.RDB$RELATION_NAME EQ name.c_str() + { + if (!RFR.RDB$GENERATOR_NAME.NULL) + DropSequenceNode::deleteIdentity(tdbb, transaction, RFR.RDB$GENERATOR_NAME); - if (clause->computed) - { - RFR.RDB$UPDATE_FLAG.NULL = FALSE; - RFR.RDB$UPDATE_FLAG = 1; - } + ERASE RFR; + + if (!RFR.RDB$SECURITY_CLASS.NULL && + !strncmp(RFR.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) + { + deleteSecurityClass(tdbb, transaction, RFR.RDB$SECURITY_CLASS); + } + + deleteGlobalField(tdbb, transaction, RFR.RDB$FIELD_SOURCE); + } + END_FOR + + request.reset(tdbb, drq_e_view_rels, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + VR IN RDB$VIEW_RELATIONS + WITH VR.RDB$VIEW_NAME EQ name.c_str() + { + ERASE VR; + } + END_FOR + + request.reset(tdbb, drq_e_relation, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + R IN RDB$RELATIONS + WITH R.RDB$RELATION_NAME EQ name.c_str() + { + ERASE R; + + if (!R.RDB$SECURITY_CLASS.NULL && + !strncmp(R.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) + { + deleteSecurityClass(tdbb, transaction, R.RDB$SECURITY_CLASS); + } + } + END_FOR + + // Triggers must be deleted after check constraints + + MetaName triggerName; + + request.reset(tdbb, drq_e_trigger2, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + X IN RDB$TRIGGERS + WITH X.RDB$RELATION_NAME EQ name.c_str() + { + triggerName = X.RDB$TRIGGER_NAME; + ERASE X; + + AutoCacheRequest request2(tdbb, drq_e_trg_prv, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + PRIV IN RDB$USER_PRIVILEGES + WITH PRIV.RDB$USER EQ triggerName.c_str() AND + PRIV.RDB$USER_TYPE = obj_trigger AND + PRIV.RDB$GRANTOR NOT MISSING + { + ERASE PRIV; + } + END_FOR + } + END_FOR + + deletePrivilegesByRelName(tdbb, transaction, name, obj_relation); + + request.reset(tdbb, drq_e_view_prv, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + PRIV IN RDB$USER_PRIVILEGES + WITH PRIV.RDB$USER EQ name.c_str() AND + PRIV.RDB$USER_TYPE = obj_view AND + PRIV.RDB$GRANTOR NOT MISSING + { + ERASE PRIV; + } + END_FOR + + // Drop table from all publications + + request.reset(tdbb, drq_e_pub_tab_all, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + PTAB IN RDB$PUBLICATION_TABLES + WITH PTAB.RDB$TABLE_NAME EQ name.c_str() + { + ERASE PTAB; + } + END_FOR + + bool rolledBack = false; + + try + { + // Mark relation in the cache as dropped + MetadataCache::erase(tdbb, relId); + + // The sweep and garbage collector threads have no more than + // a single record latency in responding to the flagged relation + // deletion. Nevertheless, as a defensive programming measure, + // don't wait forever if something has gone awry and the sweep + // count doesn't run down. - RFR.RDB$COLLATION_ID.NULL = TRUE; // CORE-2426 - END_MODIFY - } - } + for (int wait = 0; wait < 60; wait++) + { + fb_assert(relation->isDropped()); - if (clause->computed) - { - // We can alter FLD directly here because if we are setting a computed expression, - // it means the field already was computed. And if it was, it should be the - // "b1 case", where the field source does not change. - // This assumption may change, especially when this function starts dealing - // with views. + if (!relation->rel_gc_lock.getSweepCount()) + break; - MODIFY FLD - FLD.RDB$COMPUTED_SOURCE.NULL = FALSE; - attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$COMPUTED_SOURCE, - computedSource); + EngineCheckout cout(tdbb, FB_FUNCTION); + Thread::sleep(1 * 1000); + } - FLD.RDB$COMPUTED_BLR.NULL = FALSE; - attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$COMPUTED_BLR, - computedValue); - END_MODIFY - } + if (relation->rel_gc_lock.getSweepCount()) + DFW_raiseRelationInUseError(relation); - AutoRequest request2; + // Free any memory associated with the relation's garbage collection bitmap + if (dbb->dbb_garbage_collector) + dbb->dbb_garbage_collector->removeRelation(rel->getId()); +// ????????????????? + if (relation->isTemporary()) + { + // release pages, allocated for current GTT instance + AutoSetRestoreFlag tmpSpace(&tdbb->tdbb_flags, TDBB_use_db_page_space, false); + relation->delPages(tdbb); + } +// + RelationPages* const relPages = relation->getBasePages(); + if (relPages->rel_index_root) + IDX_mark_indices(tdbb, relation); - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - PRM IN RDB$PROCEDURE_PARAMETERS - WITH PRM.RDB$RELATION_NAME = name.c_str() AND - PRM.RDB$FIELD_NAME = field->fld_name.c_str() - { - MODIFY PRM USING - strcpy(PRM.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE); - END_MODIFY - } - END_FOR + if (relPages->rel_pages) + DPM_mark_relation(tdbb, relation); - request2.reset(); + // if this is a view (or even if we don't know), delete dependency lists - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - ARG IN RDB$FUNCTION_ARGUMENTS - WITH ARG.RDB$RELATION_NAME = name.c_str() AND - ARG.RDB$FIELD_NAME = field->fld_name.c_str() - { - MODIFY ARG USING - strcpy(ARG.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE); - END_MODIFY - } - END_FOR + if (relation->isView() || !relation->isReady(tdbb)) + MET_delete_dependencies(tdbb, name, obj_view, transaction); - request2.reset(); + // Now that the data, pointer, and index pages are gone, + // get rid of the relation itself - FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - RFR2 IN RDB$RELATION_FIELDS CROSS - VRL IN RDB$VIEW_RELATIONS - WITH VRL.RDB$RELATION_NAME EQ name.c_str() AND - VRL.RDB$PACKAGE_NAME MISSING AND - VRL.RDB$CONTEXT_TYPE EQ VCT_TABLE AND - RFR2.RDB$RELATION_NAME EQ VRL.RDB$VIEW_NAME AND - RFR2.RDB$VIEW_CONTEXT EQ VRL.RDB$VIEW_CONTEXT AND - RFR2.RDB$BASE_FIELD = field->fld_name.c_str() - { - MODIFY RFR2 - { - strcpy(RFR2.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE); - } - END_MODIFY - } - END_FOR - } - END_FOR + AutoRequest request2; + request2.reset(); - if (!found) + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) X IN RDB$FORMATS WITH + X.RDB$RELATION_ID EQ rel->getId() { - // msg 176: "column %s does not exist in table/view %s" - status_exception::raise(Arg::PrivateDyn(176) << field->fld_name << name); + ERASE X; + } + END_FOR +/* + // Release relation locks .... only when deleted !!!!!!!!!!!!!!! + if (relation->rel_existence_lock) { + LCK_release(tdbb, relation->rel_existence_lock); + } + if (relation->rel_partners_lock) { + LCK_release(tdbb, relation->rel_partners_lock); + } + if (relation->rel_rescan_lock) { + LCK_release(tdbb, relation->rel_rescan_lock); } - // Update any indices that exist. - AlterDomainNode::modifyLocalFieldIndex(tdbb, transaction, name, - field->fld_name, field->fld_name); + // Release relation triggers + relation->releaseTriggers(tdbb, true); +*/ } - catch (const Exception&) + catch(const Exception&) { - clearPermanentField(relation, permanent); - throw; + if (!relation) + return; +/* .... only when deleted !!!!!!!!!!!!!!! + if (relation->rel_existence_lock && !(relation->rel_flags & REL_deleted)) + { + LCK_convert(tdbb, relation->rel_existence_lock, LCK_SR, transaction->getLockWait()); + } + */ + if (relation->isDropped()) + relation->rollback(tdbb); } - clearPermanentField(relation, permanent); + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL); + + savePoint.release(); // everything is ok } //---------------------- -// Delete a global field if it's not used in others objects. -void DropRelationNode::deleteGlobalField(thread_db* tdbb, jrd_tra* transaction, - const MetaName& globalName) +string CreateAlterViewNode::internalPrint(NodePrinter& printer) const { - AutoCacheRequest request(tdbb, drq_e_l_gfld, DYN_REQUESTS); + RelationNode::internalPrint(printer); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - FLD IN RDB$FIELDS - WITH FLD.RDB$FIELD_NAME EQ globalName.c_str() AND - FLD.RDB$VALIDATION_SOURCE MISSING AND - FLD.RDB$NULL_FLAG MISSING AND - FLD.RDB$DEFAULT_SOURCE MISSING AND - FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX AND - (NOT ANY RFR IN RDB$RELATION_FIELDS WITH - RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND - (NOT ANY PRM IN RDB$PROCEDURE_PARAMETERS WITH - PRM.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND - (NOT ANY ARG IN RDB$FUNCTION_ARGUMENTS WITH - ARG.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) - { - DropDomainNode::deleteDimensionRecords(tdbb, transaction, globalName); - ERASE FLD; - } - END_FOR + NODE_PRINT(printer, create); + NODE_PRINT(printer, alter); + NODE_PRINT(printer, viewFields); + NODE_PRINT(printer, selectExpr); + NODE_PRINT(printer, source); + NODE_PRINT(printer, withCheckOption); + + return "CreateAlterViewNode"; } -string DropRelationNode::internalPrint(NodePrinter& printer) const +DdlNode* CreateAlterViewNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DdlNode::internalPrint(printer); - - NODE_PRINT(printer, name); - NODE_PRINT(printer, view); - NODE_PRINT(printer, silent); - - return "DropRelationNode"; + source.ltrim("\n\r\t "); + return DdlNode::dsqlPass(dsqlScratch); } -void DropRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { dsc dscName; dscName.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str()); - if (view) - SCL_check_view(tdbb, &dscName, SCL_drop); - else - SCL_check_relation(tdbb, &dscName, SCL_drop); + if (alter) + { + if (SCL_check_view(tdbb, &dscName, SCL_alter) || !create) + return; + } + + SCL_check_create_access(tdbb, obj_views); } -void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, +void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - jrd_rel* rel_drop = MET_lookup_relation(tdbb, name); - if (rel_drop) - MET_scan_relation(tdbb, rel_drop); + if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation)) + return; - const dsql_rel* relation = METD_get_relation(transaction, dsqlScratch, name); + Attachment* const attachment = transaction->tra_attachment; + const MetaString& ownerName = attachment->getEffectiveUserName(); - if (!relation && silent) - return; + const dsql_rel* modifyingView = NULL; - // Check that DROP TABLE is dropping a table and that DROP VIEW is dropping a view. - if (view) + if (alter) + { + modifyingView = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name); + + if (!modifyingView && !create) + status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name); + } + + saveRelation(tdbb, dsqlScratch, name, true, modifyingView == NULL); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + // METD_get_relation() should feel the cache - but let's be on a safe side + if (modifyingView) + MetadataCache::oldVersion(tdbb, modifyingView->rel_id); + + const int ddlTriggerAction = (modifyingView ? DDL_TRIGGER_ALTER_VIEW : DDL_TRIGGER_CREATE_VIEW); + + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, NULL); + + if (!modifyingView) + DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation); + + // Compile the SELECT statement into a record selection expression, making sure to bump the + // context number since view contexts start at 1 (except for computed fields) -- note that + // calling PASS1_rse directly rather than PASS1_statement saves the context stack. + + dsqlScratch->resetContextStack(); + ++dsqlScratch->contextNumber; + RseNode* rse = PASS1_rse(dsqlScratch, selectExpr); + + dsqlScratch->getBlrData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + + GEN_expr(dsqlScratch, rse); + dsqlScratch->appendUChar(blr_eoc); + + // Store the blr and source string for the view definition. + + if (modifyingView) { - if (!relation || (relation && !(relation->rel_flags & REL_view))) + AutoCacheRequest request(tdbb, drq_m_view, DYN_REQUESTS); + bool found = false; + + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS + WITH REL.RDB$RELATION_NAME EQ name.c_str() AND + REL.RDB$VIEW_BLR NOT MISSING { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_view_not_found) << name); + found = true; + + MODIFY REL + attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source); + attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, + dsqlScratch->getBlrData()); + END_MODIFY + } + END_FOR + + if (!found) + status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name); + + AutoRequest request2; + + FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + VR IN RDB$VIEW_RELATIONS + WITH VR.RDB$VIEW_NAME EQ name.c_str() + { + ERASE VR; + } + END_FOR + + request2.reset(); + + FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$RELATION_NAME EQ name.c_str() AND + TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_view_check + { + ERASE TRG; } + END_FOR } else { - if (!relation || (relation && (relation->rel_flags & REL_view))) + AutoCacheRequest request(tdbb, drq_s_rels, DYN_REQUESTS); + + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_table_not_found) << name); + strcpy(REL.RDB$RELATION_NAME, name.c_str()); + REL.RDB$SYSTEM_FLAG = 0; + REL.RDB$FLAGS = REL_sql; + REL.RDB$RELATION_TYPE = SSHORT(rel_view); + + attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source); + attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, dsqlScratch->getBlrData()); } - } + END_STORE - const int ddlTriggerAction = (view ? DDL_TRIGGER_DROP_VIEW : DDL_TRIGGER_DROP_TABLE); + storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES); + } - // run all statements under savepoint control - AutoSavePoint savePoint(tdbb, transaction); + // Define the view source relations from the statement contexts and union contexts. - AutoCacheRequest request(tdbb, drq_l_relation, DYN_REQUESTS); - bool found = false; + while (dsqlScratch->derivedContext.hasData()) + dsqlScratch->context->push(dsqlScratch->derivedContext.pop()); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - R IN RDB$RELATIONS - WITH R.RDB$RELATION_NAME EQ name.c_str() - { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, NULL); - found = true; - } - END_FOR + while (dsqlScratch->unionContext.hasData()) + dsqlScratch->context->push(dsqlScratch->unionContext.pop()); - request.reset(tdbb, drq_e_rel_con2, DYN_REQUESTS); + AutoCacheRequest request(tdbb, drq_s_view_rels, DYN_REQUESTS); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - CRT IN RDB$RELATION_CONSTRAINTS - WITH CRT.RDB$RELATION_NAME EQ name.c_str() AND - (CRT.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY OR - CRT.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT OR - CRT.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) - SORTED BY ASCENDING CRT.RDB$CONSTRAINT_TYPE + for (DsqlContextStack::iterator temp(*dsqlScratch->context); temp.hasData(); ++temp) { - ERASE CRT; - } - END_FOR + const dsql_ctx* context = temp.object(); + const dsql_rel* relation = context->ctx_relation; + const dsql_prc* procedure = context->ctx_procedure; - request.reset(tdbb, drq_e_rel_idxs, DYN_REQUESTS); + if (relation || procedure) + { + const MetaName& refName = relation ? relation->rel_name : procedure->prc_name.identifier; + const char* contextName = context->ctx_alias.hasData() ? + context->ctx_alias.c_str() : refName.c_str(); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - IDX IN RDB$INDICES - WITH IDX.RDB$RELATION_NAME EQ name.c_str() - { - DropIndexNode::deleteSegmentRecords(tdbb, transaction, IDX.RDB$INDEX_NAME); - ERASE IDX; - } - END_FOR + ViewContextType ctxType; + if (relation) + { + if (!(relation->rel_flags & REL_view)) + ctxType = VCT_TABLE; + else + ctxType = VCT_VIEW; + } + else //if (procedure) + ctxType = VCT_PROCEDURE; - request.reset(tdbb, drq_e_trg_msgs2, DYN_REQUESTS); + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + VRL IN RDB$VIEW_RELATIONS + { + strcpy(VRL.RDB$VIEW_NAME, name.c_str()); + strcpy(VRL.RDB$RELATION_NAME, refName.c_str()); + VRL.RDB$CONTEXT_TYPE = SSHORT(ctxType); + VRL.RDB$VIEW_CONTEXT = context->ctx_context; + strcpy(VRL.RDB$CONTEXT_NAME, contextName); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - TM IN RDB$TRIGGER_MESSAGES - CROSS T IN RDB$TRIGGERS - WITH T.RDB$RELATION_NAME EQ name.c_str() AND - TM.RDB$TRIGGER_NAME EQ T.RDB$TRIGGER_NAME - { - ERASE TM; + if (procedure && procedure->prc_name.package.hasData()) + { + VRL.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(VRL.RDB$PACKAGE_NAME, procedure->prc_name.package.c_str()); + } + else + VRL.RDB$PACKAGE_NAME.NULL = TRUE; + } + END_STORE + } } - END_FOR - - // CVC: Moved this block here to avoid SF Bug #1111570. - request.reset(tdbb, drq_e_rel_con3, DYN_REQUESTS); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - CRT IN RDB$RELATION_CONSTRAINTS - WITH CRT.RDB$RELATION_NAME EQ name.c_str() AND - (CRT.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT OR - CRT.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT) - { - ERASE CRT; - } - END_FOR + // Check privileges on base tables and views. - request.reset(tdbb, drq_e_rel_flds, DYN_REQUESTS); + request.reset(tdbb, drq_l_view_rels, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RFR IN RDB$RELATION_FIELDS - WITH RFR.RDB$RELATION_NAME EQ name.c_str() + VRL IN RDB$VIEW_RELATIONS CROSS + PREL IN RDB$RELATIONS OVER RDB$RELATION_NAME + WITH VRL.RDB$PACKAGE_NAME MISSING AND + VRL.RDB$VIEW_NAME EQ name.c_str() { - if (!RFR.RDB$GENERATOR_NAME.NULL) - DropSequenceNode::deleteIdentity(tdbb, transaction, RFR.RDB$GENERATOR_NAME); - - ERASE RFR; + // CVC: This never matches so it causes unnecessary calls to verify, + // so I included a call to strip trailing blanks. + fb_utils::exact_name_limit(PREL.RDB$OWNER_NAME, sizeof(PREL.RDB$OWNER_NAME)); - if (!RFR.RDB$SECURITY_CLASS.NULL && - !strncmp(RFR.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) + if (ownerName != PREL.RDB$OWNER_NAME) { - deleteSecurityClass(tdbb, transaction, RFR.RDB$SECURITY_CLASS); - } + SecurityClass::flags_t priv; - deleteGlobalField(tdbb, transaction, RFR.RDB$FIELD_SOURCE); + // I think this should be the responsability of DFW or the user will find ways to + // circumvent DYN. + priv = SCL_get_mask(tdbb, PREL.RDB$RELATION_NAME, ""); + + if (!(priv & SCL_select)) + { + // msg 32: no permission for %s access to %s %s + status_exception::raise( + Arg::Gds(isc_no_priv) << Arg::Str("SELECT") << // Non-Translatable + // Remember, a view may be based on a view. + "TABLE/VIEW" << // Non-Translatable + // We want to print the name of the base table or view. + MetaName(PREL.RDB$RELATION_NAME)); + } + } } END_FOR - request.reset(tdbb, drq_e_view_rels, DYN_REQUESTS); + // If there are field names defined for the view, match them in order with the items from the + // SELECT. Otherwise use all the fields from the rse node that was created from the select + // expression. - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - VR IN RDB$VIEW_RELATIONS - WITH VR.RDB$VIEW_NAME EQ name.c_str() + const NestConst* ptr = NULL; + const NestConst* end = NULL; + + if (viewFields) { - ERASE VR; + ptr = viewFields->items.begin(); + end = viewFields->items.end(); } - END_FOR - request.reset(tdbb, drq_e_relation, DYN_REQUESTS); + // Go through the fields list, defining or modifying the local fields; + // If an expression is specified rather than a field, define a global + // field for the computed value as well. - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - R IN RDB$RELATIONS - WITH R.RDB$RELATION_NAME EQ name.c_str() + ValueListNode* items = rse->dsqlSelectList; + NestConst* itemsPtr = items->items.begin(); + SortedArray modifiedFields; + bool updatable = true; + SSHORT position = 0; + + for (NestConst* itemsEnd = items->items.end(); + itemsPtr < itemsEnd; ++itemsPtr, ++position) { - ERASE R; + ValueExprNode* fieldNode = *itemsPtr; - if (!R.RDB$SECURITY_CLASS.NULL && - !strncmp(R.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) + // Determine the proper field name, replacing the default if necessary. + + ValueExprNode* nameNode = fieldNode; + const char* aliasName = NULL; + + while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode)) { - deleteSecurityClass(tdbb, transaction, R.RDB$SECURITY_CLASS); + DsqlAliasNode* aliasNode; + DsqlMapNode* mapNode; + DerivedFieldNode* derivedField; + + if ((aliasNode = nodeAs(nameNode))) + { + if (!aliasName) + aliasName = aliasNode->name.c_str(); + nameNode = aliasNode->value; + } + else if ((mapNode = nodeAs(nameNode))) + nameNode = mapNode->map->map_node; + else if ((derivedField = nodeAs(nameNode))) + { + if (!aliasName) + aliasName = derivedField->name.c_str(); + nameNode = derivedField->value; + } } - } - END_FOR - if (!found) - { - // msg 61: "Relation not found" - status_exception::raise(Arg::PrivateDyn(61)); - } + const dsql_fld* nameField = NULL; + const FieldNode* fieldNameNode = nodeAs(nameNode); - // Triggers must be deleted after check constraints + if (fieldNameNode) + nameField = fieldNameNode->dsqlField; - MetaName triggerName; + const TEXT* fieldStr = NULL; - request.reset(tdbb, drq_e_trigger2, DYN_REQUESTS); + if (aliasName) + fieldStr = aliasName; + else if (nameField) + fieldStr = nameField->fld_name.c_str(); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - X IN RDB$TRIGGERS - WITH X.RDB$RELATION_NAME EQ name.c_str() - { - triggerName = X.RDB$TRIGGER_NAME; - ERASE X; + // Check if this is a field or an expression. - AutoCacheRequest request2(tdbb, drq_e_trg_prv, DYN_REQUESTS); + DsqlAliasNode* aliasNode = nodeAs(fieldNode); - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - PRIV IN RDB$USER_PRIVILEGES - WITH PRIV.RDB$USER EQ triggerName.c_str() AND - PRIV.RDB$USER_TYPE = obj_trigger AND - PRIV.RDB$GRANTOR NOT MISSING + if (aliasNode) + fieldNode = aliasNode->value; + + dsql_fld* field = NULL; + const dsql_ctx* context = NULL; + + fieldNameNode = nodeAs(fieldNode); + + if (fieldNameNode) { - ERASE PRIV; + field = fieldNameNode->dsqlField; + context = fieldNameNode->dsqlContext; } - END_FOR - } - END_FOR + else + updatable = false; - deletePrivilegesByRelName(tdbb, transaction, name, obj_relation); + // If this is an expression, check to make sure there is a name specified. - request.reset(tdbb, drq_e_view_prv, DYN_REQUESTS); + if (!ptr && !fieldStr) + { + // must specify field name for view select expression + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_specify_field_err)); + } - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - PRIV IN RDB$USER_PRIVILEGES - WITH PRIV.RDB$USER EQ name.c_str() AND - PRIV.RDB$USER_TYPE = obj_view AND - PRIV.RDB$GRANTOR NOT MISSING - { - ERASE PRIV; - } - END_FOR + // CVC: Small modification here to catch any mismatch between number of + // explicit field names in a view and number of fields in the select expression, + // see comment below. This closes Firebird Bug #223059. + if (ptr) + { + if (ptr < end) + fieldStr = nodeAs(*ptr)->dsqlName.c_str(); + else + { + // Generate an error when going out of this loop. + ++ptr; + break; + } - // Drop table from all publications + ++ptr; + } - request.reset(tdbb, drq_e_pub_tab_all, DYN_REQUESTS); + // If not an expression, point to the proper base relation field, + // else make up an SQL field with generated global field for calculations. - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - PTAB IN RDB$PUBLICATION_TABLES - WITH PTAB.RDB$TABLE_NAME EQ name.c_str() - { - ERASE PTAB; - } - END_FOR + dsql_fld* relField = NULL; - if (found) - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL); - else - { - // msg 61: "Relation not found" - status_exception::raise(Arg::PrivateDyn(61)); - } + if (modifyingView) // if we're modifying a view + { + for (relField = modifyingView->rel_fields; relField; relField = relField->fld_next) + { + if (relField->fld_name == fieldStr) + { + if (modifiedFields.exist(relField)) + { + // column @1 appears more than once in ALTER VIEW + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_col_more_than_once_view) << Arg::Str(fieldStr)); + } - savePoint.release(); // everything is ok + modifiedFields.add(relField); + break; + } + } + } - METD_drop_relation(transaction, name.c_str()); - MET_dsql_cache_release(tdbb, SYM_relation, name); -} + FieldDefinition fieldDefinition(*tdbb->getDefaultPool()); + fieldDefinition.relationName = name; + fieldDefinition.name = fieldStr; + fieldDefinition.position = position; + // CVC: Not sure if something should be done now that isc_dyn_view_context is used here, + // but if alter view is going to work, maybe we need here the context type and package, too. + if (field) + { + field->resolve(dsqlScratch); -//---------------------- + fieldDefinition.viewContext = context->ctx_context; + fieldDefinition.baseField = field->fld_name; + if (field->dtype <= dtype_any_text) + fieldDefinition.collationId = field->collationId; -string CreateAlterViewNode::internalPrint(NodePrinter& printer) const -{ - RelationNode::internalPrint(printer); + if (relField) // modifying a view + { + // We're now modifying a field and it will be based on another one. So if the old + // field was an expression, delete it now. - NODE_PRINT(printer, create); - NODE_PRINT(printer, alter); - NODE_PRINT(printer, viewFields); - NODE_PRINT(printer, selectExpr); - NODE_PRINT(printer, source); - NODE_PRINT(printer, withCheckOption); + AutoRequest request2; - return "CreateAlterViewNode"; -} + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS CROSS + FLD IN RDB$FIELDS + WITH RFL.RDB$FIELD_NAME EQ fieldStr AND + RFL.RDB$RELATION_NAME EQ name.c_str() AND + RFL.RDB$BASE_FIELD MISSING AND + FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE + { + bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME); + fb_assert(wasInternalDomain); -DdlNode* CreateAlterViewNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) -{ - source.ltrim("\n\r\t "); - return DdlNode::dsqlPass(dsqlScratch); -} + if (wasInternalDomain) + ERASE FLD; + } + END_FOR -void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) -{ - dsc dscName; - dscName.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str()); - if (alter) - { - if (SCL_check_view(tdbb, &dscName, SCL_alter) || !create) - return; - } + fieldDefinition.modify(tdbb, transaction); + } + else + fieldDefinition.store(tdbb, transaction); + } + else + { + dsqlScratch->getBlrData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + GEN_expr(dsqlScratch, fieldNode); + dsqlScratch->appendUChar(blr_eoc); - SCL_check_create_access(tdbb, obj_views); -} + // Get the type of the expression. + dsc desc; + DsqlDescMaker::fromNode(dsqlScratch, &desc, fieldNode); -void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction) -{ - if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation)) - return; + dsql_fld newField(*tdbb->getDefaultPool()); + newField.dtype = desc.dsc_dtype; + newField.length = desc.dsc_length; + newField.scale = desc.dsc_scale; - Attachment* const attachment = transaction->tra_attachment; - const MetaString& ownerName = attachment->getEffectiveUserName(); + if (desc.isText() || (desc.isBlob() && desc.getBlobSubType() == isc_blob_text)) + { + newField.charSetId = desc.getCharSet(); + newField.collationId = desc.getCollation(); + } - const dsql_rel* modifyingView = NULL; + if (desc.isText()) + { + const USHORT adjust = + (desc.dsc_dtype == dtype_varying) ? sizeof(USHORT) : 0; + const USHORT bpc = + METD_get_charset_bpc(dsqlScratch->getTransaction(), newField.charSetId.value_or(CS_NONE)); - if (alter) - { - modifyingView = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name); + newField.charLength = (newField.length - adjust) / bpc; + } + else + newField.subType = desc.dsc_sub_type; - if (!modifyingView && !create) - status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name); - } + newField.setExactPrecision(); - saveRelation(tdbb, dsqlScratch, name, true, modifyingView == NULL); + if (relField) // modifying a view + { + AutoRequest request2; - // run all statements under savepoint control - AutoSavePoint savePoint(tdbb, transaction); + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS CROSS + FLD IN RDB$FIELDS + WITH RFL.RDB$FIELD_NAME EQ fieldStr AND + RFL.RDB$RELATION_NAME EQ name.c_str() AND + RFL.RDB$BASE_FIELD MISSING AND + FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE + { + bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME); + fb_assert(wasInternalDomain); - const int ddlTriggerAction = (modifyingView ? DDL_TRIGGER_ALTER_VIEW : DDL_TRIGGER_CREATE_VIEW); + if (wasInternalDomain) + { + fieldDefinition.fieldSource = FLD.RDB$FIELD_NAME; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, NULL); + MODIFY FLD + updateRdbFields(&newField, + FLD.RDB$FIELD_TYPE, + FLD.RDB$FIELD_LENGTH, + FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE, + FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE, + FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID, + FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH, + FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION, + FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID, + FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH); - if (!modifyingView) - DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation); + FLD.RDB$COMPUTED_BLR.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$COMPUTED_BLR, + dsqlScratch->getBlrData()); + END_MODIFY + } + } + END_FOR - // Compile the SELECT statement into a record selection expression, making sure to bump the - // context number since view contexts start at 1 (except for computed fields) -- note that - // calling PASS1_rse directly rather than PASS1_statement saves the context stack. + if (fieldDefinition.fieldSource.isEmpty()) + { + storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField, + "", dsqlScratch->getBlrData()); + } - dsqlScratch->resetContextStack(); - ++dsqlScratch->contextNumber; - RseNode* rse = PASS1_rse(dsqlScratch, selectExpr); + fieldDefinition.modify(tdbb, transaction); + } + else + { + storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField, + "", dsqlScratch->getBlrData()); - dsqlScratch->getBlrData().clear(); - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + fieldDefinition.store(tdbb, transaction); + } + } - GEN_expr(dsqlScratch, rse); - dsqlScratch->appendUChar(blr_eoc); + if (fieldStr) + saveField(tdbb, dsqlScratch, fieldStr); + } - // Store the blr and source string for the view definition. + // CVC: This message was not catching the case when + // #fields < items in select list, see comment above. - if (modifyingView) + if (ptr != end) { - AutoCacheRequest request(tdbb, drq_m_view, DYN_REQUESTS); - bool found = false; + // number of fields does not match select list + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_num_field_err)); + } - FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - REL IN RDB$RELATIONS - WITH REL.RDB$RELATION_NAME EQ name.c_str() AND - REL.RDB$VIEW_BLR NOT MISSING + if (modifyingView) // modifying a view + { + // Delete the old fields not present in the new definition. + for (dsql_fld* relField = modifyingView->rel_fields; relField; relField = relField->fld_next) { - found = true; - - MODIFY REL - attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source); - attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, - dsqlScratch->getBlrData()); - END_MODIFY + if (!modifiedFields.exist(relField)) + deleteLocalField(tdbb, transaction, name, relField->fld_name, false); } - END_FOR - - if (!found) - status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name); + } - AutoRequest request2; + // Setup to define triggers for WITH CHECK OPTION. - FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - VR IN RDB$VIEW_RELATIONS - WITH VR.RDB$VIEW_NAME EQ name.c_str() + if (withCheckOption) + { + if (!updatable) { - ERASE VR; + // Only simple column names permitted for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_col_name_err)); } - END_FOR - request2.reset(); + RseNode* querySpec = nodeAs(selectExpr->querySpec); + fb_assert(querySpec); - FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - TRG IN RDB$TRIGGERS - WITH TRG.RDB$RELATION_NAME EQ name.c_str() AND - TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_view_check + if (querySpec->dsqlFrom->items.getCount() != 1 || + !nodeIs(querySpec->dsqlFrom->items[0])) { - ERASE TRG; + // Only one table allowed for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_table_view_err)); } - END_FOR - } - else - { - AutoCacheRequest request(tdbb, drq_s_rels, DYN_REQUESTS); - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - REL IN RDB$RELATIONS + if (!querySpec->dsqlWhere) { - strcpy(REL.RDB$RELATION_NAME, name.c_str()); - REL.RDB$SYSTEM_FLAG = 0; - REL.RDB$FLAGS = REL_sql; - REL.RDB$RELATION_TYPE = SSHORT(rel_view); + // No where clause for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_where_err)); + } - attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source); - attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, dsqlScratch->getBlrData()); + if (querySpec->dsqlDistinct || querySpec->dsqlGroup || querySpec->dsqlHaving) + { + // DISTINCT, GROUP or HAVING not permitted for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_distinct_err)); } - END_STORE - storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES); + dsqlScratch->flags |= DsqlCompilerScratch::FLAG_VIEW_WITH_CHECK; + + createCheckTrigger(tdbb, dsqlScratch, items, PRE_MODIFY_TRIGGER); + createCheckTrigger(tdbb, dsqlScratch, items, PRE_STORE_TRIGGER); } - // Define the view source relations from the statement contexts and union contexts. + dsqlScratch->resetContextStack(); - while (dsqlScratch->derivedContext.hasData()) - dsqlScratch->context->push(dsqlScratch->derivedContext.pop()); + auto rel_id = modifyingView ? modifyingView->rel_id : generateIdDbKey(tdbb, transaction); + MetadataCache::newVersion(tdbb, rel_id); - while (dsqlScratch->unionContext.hasData()) - dsqlScratch->context->push(dsqlScratch->unionContext.pop()); + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL); - AutoCacheRequest request(tdbb, drq_s_view_rels, DYN_REQUESTS); + savePoint.release(); // everything is ok +} - for (DsqlContextStack::iterator temp(*dsqlScratch->context); temp.hasData(); ++temp) - { - const dsql_ctx* context = temp.object(); - const dsql_rel* relation = context->ctx_relation; - const dsql_prc* procedure = context->ctx_procedure; +// Generate a trigger to implement the WITH CHECK OPTION clause for a VIEW. +void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + ValueListNode* items, TriggerType triggerType) +{ + MemoryPool& pool = *tdbb->getDefaultPool(); - if (relation || procedure) - { - const MetaName& refName = relation ? relation->rel_name : procedure->prc_name.identifier; - const char* contextName = context->ctx_alias.hasData() ? - context->ctx_alias.c_str() : refName.c_str(); + // Specify that the trigger should abort if the condition is not met. + ExceptionNode* exceptionNode = FB_NEW_POOL(pool) ExceptionNode(pool, CHECK_CONSTRAINT_EXCEPTION); + exceptionNode->exception->type = ExceptionItem::GDS_CODE; - ViewContextType ctxType; - if (relation) - { - if (!(relation->rel_flags & REL_view)) - ctxType = VCT_TABLE; - else - ctxType = VCT_VIEW; - } - else //if (procedure) - ctxType = VCT_PROCEDURE; + AutoSetRestore autoCheckConstraintTrigger(&dsqlScratch->checkConstraintTrigger, true); - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - VRL IN RDB$VIEW_RELATIONS - { - strcpy(VRL.RDB$VIEW_NAME, name.c_str()); - strcpy(VRL.RDB$RELATION_NAME, refName.c_str()); - VRL.RDB$CONTEXT_TYPE = SSHORT(ctxType); - VRL.RDB$VIEW_CONTEXT = context->ctx_context; - strcpy(VRL.RDB$CONTEXT_NAME, contextName); + RelationSourceNode* relationNode = dsqlNode; - if (procedure && procedure->prc_name.package.hasData()) - { - VRL.RDB$PACKAGE_NAME.NULL = FALSE; - strcpy(VRL.RDB$PACKAGE_NAME, procedure->prc_name.package.c_str()); - } - else - VRL.RDB$PACKAGE_NAME.NULL = TRUE; - } - END_STORE - } - } + // Generate the trigger blr. - // Check privileges on base tables and views. + dsqlScratch->getBlrData().clear(); + dsqlScratch->getDebugData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - request.reset(tdbb, drq_l_view_rels, DYN_REQUESTS); + dsqlScratch->appendUChar(blr_begin); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - VRL IN RDB$VIEW_RELATIONS CROSS - PREL IN RDB$RELATIONS OVER RDB$RELATION_NAME - WITH VRL.RDB$PACKAGE_NAME MISSING AND - VRL.RDB$VIEW_NAME EQ name.c_str() - { - // CVC: This never matches so it causes unnecessary calls to verify, - // so I included a call to strip trailing blanks. - fb_utils::exact_name_limit(PREL.RDB$OWNER_NAME, sizeof(PREL.RDB$OWNER_NAME)); + dsqlScratch->resetContextStack(); - if (ownerName != PREL.RDB$OWNER_NAME) - { - SecurityClass::flags_t priv; + RseNode* querySpec = nodeAs(selectExpr->querySpec); + fb_assert(querySpec); - // I think this should be the responsability of DFW or the user will find ways to - // circumvent DYN. - priv = SCL_get_mask(tdbb, PREL.RDB$RELATION_NAME, ""); + ProcedureSourceNode* sourceNode = nodeAs(querySpec->dsqlFrom->items[0]); - if (!(priv & SCL_select)) - { - // msg 32: no permission for %s access to %s %s - status_exception::raise( - Arg::Gds(isc_no_priv) << Arg::Str("SELECT") << // Non-Translatable - // Remember, a view may be based on a view. - "TABLE/VIEW" << // Non-Translatable - // We want to print the name of the base table or view. - MetaName(PREL.RDB$RELATION_NAME)); - } - } - } - END_FOR + if (triggerType == PRE_MODIFY_TRIGGER) + { + dsqlScratch->contextNumber = 2; - // If there are field names defined for the view, match them in order with the items from the - // SELECT. Otherwise use all the fields from the rse node that was created from the select - // expression. + RelationSourceNode* baseRelation = FB_NEW_POOL(pool) RelationSourceNode(pool, + sourceNode->dsqlName.identifier); + baseRelation->alias = sourceNode->alias; - const NestConst* ptr = NULL; - const NestConst* end = NULL; + dsqlScratch->appendUChar(blr_for); - if (viewFields) - { - ptr = viewFields->items.begin(); - end = viewFields->items.end(); - } + RseNode* rse = FB_NEW_POOL(pool) RseNode(pool); + rse->dsqlStreams = FB_NEW_POOL(pool) RecSourceListNode(pool, 1); - // Go through the fields list, defining or modifying the local fields; - // If an expression is specified rather than a field, define a global - // field for the computed value as well. + rse->dsqlStreams->items[0] = baseRelation; + rse->dsqlStreams->items[0] = doDsqlPass(dsqlScratch, rse->dsqlStreams->items[0]); + rse->dsqlWhere = doDsqlPass(dsqlScratch, querySpec->dsqlWhere); - ValueListNode* items = rse->dsqlSelectList; - NestConst* itemsPtr = items->items.begin(); - SortedArray modifiedFields; - bool updatable = true; - SSHORT position = 0; + dsqlScratch->contextNumber = OLD_CONTEXT_VALUE; - for (NestConst* itemsEnd = items->items.end(); - itemsPtr < itemsEnd; ++itemsPtr, ++position) - { - ValueExprNode* fieldNode = *itemsPtr; + dsql_ctx* oldContext; - // Determine the proper field name, replacing the default if necessary. + { /// scope + AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias); + relationNode->alias = OLD_CONTEXT_NAME; - ValueExprNode* nameNode = fieldNode; - const char* aliasName = NULL; + oldContext = PASS1_make_context(dsqlScratch, relationNode); + oldContext->ctx_flags |= CTX_system; + } - while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode)) + // Get the list of values and fields to compare to -- if there is no list of fields, get all + // fields in the base relation that are not computed. + + ValueListNode* valuesNode = viewFields; + ValueListNode* fieldsNode = querySpec->dsqlSelectList; + + if (!fieldsNode) { - DsqlAliasNode* aliasNode; - DsqlMapNode* mapNode; - DerivedFieldNode* derivedField; + const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), + dsqlScratch, name); + fieldsNode = FB_NEW_POOL(pool) ValueListNode(pool, 0u); - if ((aliasNode = nodeAs(nameNode))) - { - if (!aliasName) - aliasName = aliasNode->name.c_str(); - nameNode = aliasNode->value; - } - else if ((mapNode = nodeAs(nameNode))) - nameNode = mapNode->map->map_node; - else if ((derivedField = nodeAs(nameNode))) - { - if (!aliasName) - aliasName = derivedField->name.c_str(); - nameNode = derivedField->value; + for (const dsql_fld* field = relation->rel_fields; field; field = field->fld_next) + { + if (!(field->flags & FLD_computed)) + fieldsNode->add(MAKE_field_name(field->fld_name.c_str())); } } - const dsql_fld* nameField = NULL; - const FieldNode* fieldNameNode = nodeAs(nameNode); + if (!valuesNode) + valuesNode = fieldsNode; - if (fieldNameNode) - nameField = fieldNameNode->dsqlField; + // Generate the list of assignments to fields in the base relation. - const TEXT* fieldStr = NULL; + NestConst* ptr = fieldsNode->items.begin(); + const NestConst* const end = fieldsNode->items.end(); + NestConst* ptr2 = valuesNode->items.begin(); + const NestConst* const end2 = valuesNode->items.end(); + int andArg = 0; - if (aliasName) - fieldStr = aliasName; - else if (nameField) - fieldStr = nameField->fld_name.c_str(); + BinaryBoolNode* andNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and); - // Check if this is a field or an expression. + for (; ptr != end && ptr2 != end2; ++ptr, ++ptr2) + { + NestConst fieldNod = *ptr; + NestConst valueNod = *ptr2; + DsqlAliasNode* aliasNode; - DsqlAliasNode* aliasNode = nodeAs(fieldNode); + if ((aliasNode = nodeAs(fieldNod))) + fieldNod = aliasNode->value; - if (aliasNode) - fieldNode = aliasNode->value; + if ((aliasNode = nodeAs(valueNod))) + valueNod = aliasNode->value; - dsql_fld* field = NULL; - const dsql_ctx* context = NULL; + FieldNode* fieldNode = nodeAs(fieldNod); + FieldNode* valueNode = nodeAs(valueNod); - fieldNameNode = nodeAs(fieldNode); + // Generate the actual comparisons. - if (fieldNameNode) - { - field = fieldNameNode->dsqlField; - context = fieldNameNode->dsqlContext; - } - else - updatable = false; + if (fieldNode && valueNode) + { + FieldNode* oldValueNode = FB_NEW_POOL(pool) FieldNode(pool); + oldValueNode->dsqlName = (aliasNode ? aliasNode->name : valueNode->dsqlName); + oldValueNode->dsqlQualifier = OLD_CONTEXT_NAME; - // If this is an expression, check to make sure there is a name specified. + valueNod = oldValueNode->dsqlPass(dsqlScratch); + fieldNod = fieldNode->dsqlPass(dsqlScratch); - if (!ptr && !fieldStr) - { - // must specify field name for view select expression - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_specify_field_err)); - } + BoolExprNode* equivNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_equiv, + valueNod, fieldNod); - // CVC: Small modification here to catch any mismatch between number of - // explicit field names in a view and number of fields in the select expression, - // see comment below. This closes Firebird Bug #223059. - if (ptr) - { - if (ptr < end) - fieldStr = nodeAs(*ptr)->dsqlName.c_str(); - else - { - // Generate an error when going out of this loop. - ++ptr; - break; + rse->dsqlWhere = PASS1_compose(rse->dsqlWhere, equivNode, blr_and); } - - ++ptr; } - // If not an expression, point to the proper base relation field, - // else make up an SQL field with generated global field for calculations. + GEN_expr(dsqlScratch, rse); + } - dsql_fld* relField = NULL; + // ASF: We'll now map the view's base table into the trigger's NEW context. - if (modifyingView) // if we're modifying a view - { - for (relField = modifyingView->rel_fields; relField; relField = relField->fld_next) - { - if (relField->fld_name == fieldStr) - { - if (modifiedFields.exist(relField)) - { - // column @1 appears more than once in ALTER VIEW - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-104) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_col_more_than_once_view) << Arg::Str(fieldStr)); - } + ++dsqlScratch->scopeLevel; + dsqlScratch->contextNumber = NEW_CONTEXT_VALUE; - modifiedFields.add(relField); - break; - } - } - } + dsql_ctx* newContext; - FieldDefinition fieldDefinition(*tdbb->getDefaultPool()); - fieldDefinition.relationName = name; - fieldDefinition.name = fieldStr; - fieldDefinition.position = position; + { /// scope + AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias); - // CVC: Not sure if something should be done now that isc_dyn_view_context is used here, - // but if alter view is going to work, maybe we need here the context type and package, too. - if (field) - { - field->resolve(dsqlScratch); + if (relationNode->alias.isEmpty()) + relationNode->alias = sourceNode->dsqlName.identifier.c_str(); - fieldDefinition.viewContext = context->ctx_context; - fieldDefinition.baseField = field->fld_name; + newContext = PASS1_make_context(dsqlScratch, relationNode); + newContext->ctx_flags |= CTX_system; - if (field->dtype <= dtype_any_text) - fieldDefinition.collationId = field->collationId; + if (triggerType == PRE_STORE_TRIGGER) + newContext->ctx_flags |= CTX_view_with_check_store; + else + newContext->ctx_flags |= CTX_view_with_check_modify; + } - if (relField) // modifying a view - { - // We're now modifying a field and it will be based on another one. So if the old - // field was an expression, delete it now. + // Replace the view's field names by the base table field names. Save the original names + // to restore after the condition processing. - AutoRequest request2; + dsql_fld* field = newContext->ctx_relation->rel_fields; + ObjectsArray savedNames; - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - RFL IN RDB$RELATION_FIELDS CROSS - FLD IN RDB$FIELDS - WITH RFL.RDB$FIELD_NAME EQ fieldStr AND - RFL.RDB$RELATION_NAME EQ name.c_str() AND - RFL.RDB$BASE_FIELD MISSING AND - FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE - { - bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME); - fb_assert(wasInternalDomain); + // ASF: rel_fields entries are in reverse order. + for (NestConst* ptr = items->items.end(); + ptr-- != items->items.begin(); + field = field->fld_next) + { + ValueExprNode* valueNode = *ptr; + DsqlAliasNode* aliasNode; - if (wasInternalDomain) - ERASE FLD; - } - END_FOR + if ((aliasNode = nodeAs(valueNode))) + valueNode = aliasNode->value; - fieldDefinition.modify(tdbb, transaction); - } - else - fieldDefinition.store(tdbb, transaction); - } - else - { - dsqlScratch->getBlrData().clear(); - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - GEN_expr(dsqlScratch, fieldNode); - dsqlScratch->appendUChar(blr_eoc); + FieldNode* fieldNode = nodeAs(valueNode); + fb_assert(fieldNode); - // Get the type of the expression. - dsc desc; - DsqlDescMaker::fromNode(dsqlScratch, &desc, fieldNode); + savedNames.add(field->fld_name); - dsql_fld newField(*tdbb->getDefaultPool()); - newField.dtype = desc.dsc_dtype; - newField.length = desc.dsc_length; - newField.scale = desc.dsc_scale; + dsql_fld* queryField = fieldNode->dsqlField; - if (desc.isText() || (desc.isBlob() && desc.getBlobSubType() == isc_blob_text)) - { - newField.charSetId = desc.getCharSet(); - newField.collationId = desc.getCollation(); - } + field->fld_name = queryField->fld_name; + field->dtype = queryField->dtype; + field->scale = queryField->scale; + field->subType = queryField->subType; + field->length = queryField->length; + field->flags = queryField->flags; + field->charSetId = queryField->charSetId; + field->collationId = queryField->collationId; + } - if (desc.isText()) - { - const USHORT adjust = - (desc.dsc_dtype == dtype_varying) ? sizeof(USHORT) : 0; - const USHORT bpc = - METD_get_charset_bpc(dsqlScratch->getTransaction(), newField.charSetId.value_or(CS_NONE)); + dsqlScratch->appendUChar(blr_if); - newField.charLength = (newField.length - adjust) / bpc; - } - else - newField.subType = desc.dsc_sub_type; + // Process the condition for firing the trigger. + NestConst condition = doDsqlPass(dsqlScratch, querySpec->dsqlWhere); - newField.setExactPrecision(); + // Restore the field names. This must happen before the condition's BLR generation. - if (relField) // modifying a view - { - AutoRequest request2; + field = newContext->ctx_relation->rel_fields; - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - RFL IN RDB$RELATION_FIELDS CROSS - FLD IN RDB$FIELDS - WITH RFL.RDB$FIELD_NAME EQ fieldStr AND - RFL.RDB$RELATION_NAME EQ name.c_str() AND - RFL.RDB$BASE_FIELD MISSING AND - FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE - { - bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME); - fb_assert(wasInternalDomain); + for (ObjectsArray::iterator i = savedNames.begin(); i != savedNames.end(); ++i) + { + field->fld_name = *i; + field = field->fld_next; + } - if (wasInternalDomain) - { - fieldDefinition.fieldSource = FLD.RDB$FIELD_NAME; + GEN_expr(dsqlScratch, condition); + dsqlScratch->appendUChar(blr_begin); + dsqlScratch->appendUChar(blr_end); + + // Generate the action statement for the trigger. + exceptionNode->dsqlPass(dsqlScratch)->genBlr(dsqlScratch); + + dsqlScratch->appendUChar(blr_end); // of begin + dsqlScratch->appendUChar(blr_eoc); + + dsqlScratch->resetContextStack(); + + TriggerDefinition trigger(pool); + trigger.systemFlag = fb_sysflag_view_check; + trigger.relationName = name; + trigger.type = triggerType; + trigger.blrData = dsqlScratch->getBlrData(); + trigger.store(tdbb, dsqlScratch, dsqlScratch->getTransaction()); +} - MODIFY FLD - updateRdbFields(&newField, - FLD.RDB$FIELD_TYPE, - FLD.RDB$FIELD_LENGTH, - FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE, - FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE, - FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID, - FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH, - FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION, - FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID, - FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH); - FLD.RDB$COMPUTED_BLR.NULL = FALSE; - attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$COMPUTED_BLR, - dsqlScratch->getBlrData()); - END_MODIFY - } - } - END_FOR +//---------------------- - if (fieldDefinition.fieldSource.isEmpty()) - { - storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField, - "", dsqlScratch->getBlrData()); - } +ModifyIndexNode::ModifyValue ModifyIndexNode::modify(thread_db* tdbb, jrd_tra* transaction) +{ +/************************************** + * + * m o d i f y _ i n d e x + * + ************************************** + * + * Functional description + * Create\drop an index or change the state of an index between active/inactive. + * If index owns by global temporary table with on commit preserve rows scope + * change index instance for this temporary table too. For "create index" work + * item create base index instance before temp index instance. For index + * deletion delete temp index instance first. + **************************************/ - fieldDefinition.modify(tdbb, transaction); - } - else - { - storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField, - "", dsqlScratch->getBlrData()); + SET_TDBB(tdbb); + Attachment* attachment = transaction->getAttachment(); + MetaId rc; - fieldDefinition.store(tdbb, transaction); - } + if (!relName.hasData()) + { + AutoCacheRequest request; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES + WITH IDX.RDB$INDEX_NAME EQ indexName.c_str() + { + relName = IDX.RDB$RELATION_NAME; } + END_FOR - if (fieldStr) - saveField(tdbb, dsqlScratch, fieldStr); + if (!relName.hasData()) + fatal_exception::raiseFmt("Relation for index %s not known", indexName.c_str()); } - // CVC: This message was not catching the case when - // #fields < items in select list, see comment above. + Cached::Relation* relation = MetadataCache::lookupRelation(tdbb, relName, CacheFlag::AUTOCREATE); + fb_assert(relation); + if (!relation) + { + fatal_exception::raiseFmt("Relation %s for creation of index %s not found", + relName.c_str(), indexName.c_str()); + } - if (ptr != end) + if (create) + rc = exec(tdbb, relation, transaction); + + if (relation->rel_flags & REL_temp_conn) { - // number of fields does not match select list - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_num_field_err)); + AutoSetRestoreFlag preserveFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, false); + + if (relation->getPages(tdbb)) + exec(tdbb, relation, transaction); } - if (modifyingView) // modifying a view + if (!create) + rc = exec(tdbb, relation, transaction); + + return ModifyValue(create, rc); +} + +string ModifyIndexNode::print(Jrd::NodePrinter& printer) const +{ + NODE_PRINT(printer, indexName); + NODE_PRINT(printer, relName); + NODE_PRINT(printer, create); + + return "ModifyIndexNode"; +} + + +//---------------------- + + + +void ModifyIndexList::exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) +{ + fb_assert(rel); + if (!rel) + return; + + for (auto* node : nodes) { - // Delete the old fields not present in the new definition. - for (dsql_fld* relField = modifyingView->rel_fields; relField; relField = relField->fld_next) + if (node) { - if (!modifiedFields.exist(relField)) - deleteLocalField(tdbb, transaction, name, relField->fld_name, false); + auto newIndex = node->modify(tdbb, transaction); + rel->newIndexVersion(tdbb, newIndex.id); } } +} - // Setup to define triggers for WITH CHECK OPTION. - if (withCheckOption) +//---------------------- + + +MetaId StoreIndexNode::exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) +{ + return expressionIndex ? createExpression(tdbb, rel, transaction) : create(tdbb, rel, transaction); +} + + +MetaId StoreIndexNode::create(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) +{ +/************************************** + * + * c r e a t e _ i n d e x + * + ************************************** + * + * Functional description + * Create a new index or change the state of an index to active. + * + **************************************/ + AutoCacheRequest request; + jrd_rel* relation = rel->getObject(tdbb, CacheFlag::AUTOCREATE); + if (!relation) { - if (!updatable) - { - // Only simple column names permitted for VIEW WITH CHECK OPTION - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_col_name_err)); - } + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_create_err) << Arg::Str(indexName)); + // Msg308: can't create index %s + } - RseNode* querySpec = nodeAs(selectExpr->querySpec); - fb_assert(querySpec); + index_desc idx; + idx.idx_count = 0; + int key_count = 0; - if (querySpec->dsqlFrom->items.getCount() != 1 || - !nodeIs(querySpec->dsqlFrom->items[0])) - { - // Only one table allowed for VIEW WITH CHECK OPTION - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_table_view_err)); - } + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaId idxId = dbb->dbb_max_idx; - if (!querySpec->dsqlWhere) + idx.idx_flags = 0; + + // Fetch the information necessary to create the index. On the first + // time thru, check to see if the index already exists. If so, delete + // it. If the index inactive flag is set, don't create the index + + request.reset(tdbb, irq_c_index, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES CROSS + REL IN RDB$RELATIONS OVER RDB$RELATION_NAME + WITH IDX.RDB$INDEX_NAME EQ indexName.c_str() + { + // Reject here request to 'alter index inactive' + fb_assert(IDX.RDB$INDEX_ID.NULL); + fb_assert(IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE); + + idx.idx_count = IDX.RDB$SEGMENT_COUNT; + if (!idx.idx_count || idx.idx_count > MAX_INDEX_SEGMENTS) { - // No where clause for VIEW WITH CHECK OPTION - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_where_err)); + if (!idx.idx_count) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_seg_err) << Arg::Str(indexName)); + // Msg304: segment count of 0 defined for index %s + } + else + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_key_err) << Arg::Str(indexName)); + // Msg311: too many keys defined for index %s + } } - if (querySpec->dsqlDistinct || querySpec->dsqlGroup || querySpec->dsqlHaving) + if (IDX.RDB$UNIQUE_FLAG) + idx.idx_flags |= idx_unique; + if (IDX.RDB$INDEX_TYPE == 1) + idx.idx_flags |= idx_descending; + if (!IDX.RDB$FOREIGN_KEY.NULL) + idx.idx_flags |= idx_foreign; + + AutoCacheRequest rc_request(tdbb, irq_c_index_rc, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE rc_request TRANSACTION_HANDLE transaction) + RC IN RDB$RELATION_CONSTRAINTS WITH + RC.RDB$INDEX_NAME EQ indexName.c_str() AND + RC.RDB$CONSTRAINT_TYPE = PRIMARY_KEY { - // DISTINCT, GROUP or HAVING not permitted for VIEW WITH CHECK OPTION - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_distinct_err)); + idx.idx_flags |= idx_primary; } + END_FOR - dsqlScratch->flags |= DsqlCompilerScratch::FLAG_VIEW_WITH_CHECK; + idx.idx_condition_node = nullptr; + idx.idx_condition_statement = nullptr; - createCheckTrigger(tdbb, dsqlScratch, items, PRE_MODIFY_TRIGGER); - createCheckTrigger(tdbb, dsqlScratch, items, PRE_STORE_TRIGGER); - } + if (!IDX.RDB$CONDITION_BLR.NULL) + { + // Allocate a new pool to contain the expression tree + // for index condition + const auto new_pool = dbb->createPool(ALLOC_ARGS0); + CompilerScratch* csb = nullptr; - dsqlScratch->resetContextStack(); + try + { + Jrd::ContextPoolHolder context(tdbb, new_pool); - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL); + MET_get_dependencies(tdbb, rel, nullptr, 0, nullptr, &IDX.RDB$CONDITION_BLR, + nullptr, &csb, indexName, obj_index_condition, 0, + transaction); - savePoint.release(); // everything is ok + idx.idx_condition_statement = Statement::makeBoolExpression(tdbb, + idx.idx_condition_node, csb, false); - // Update DSQL cache - METD_drop_relation(transaction, name); - MET_dsql_cache_release(tdbb, SYM_relation, name); -} + idx.idx_flags |= idx_condition; + } + catch (const Exception&) + { + dbb->deletePool(new_pool); + throw; + } -// Generate a trigger to implement the WITH CHECK OPTION clause for a VIEW. -void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - ValueListNode* items, TriggerType triggerType) -{ - MemoryPool& pool = *tdbb->getDefaultPool(); + delete csb; + } - // Specify that the trigger should abort if the condition is not met. - ExceptionNode* exceptionNode = FB_NEW_POOL(pool) ExceptionNode(pool, CHECK_CONSTRAINT_EXCEPTION); - exceptionNode->exception->type = ExceptionItem::GDS_CODE; + // Here we need dirty reads from database (first of all from + // RDB$RELATION_FIELDS and RDB$FIELDS - tables not directly related + // with index to be created and it's dfw_name). Missing it breaks gbak, + // and appears can break other applications. - AutoSetRestore autoCheckConstraintTrigger(&dsqlScratch->checkConstraintTrigger, true); + AutoCacheRequest seg_request(tdbb, irq_c_index_seg, IRQ_REQUESTS); - RelationSourceNode* relationNode = dsqlNode; + FOR(REQUEST_HANDLE seg_request) + SEG IN RDB$INDEX_SEGMENTS CROSS + RFR IN RDB$RELATION_FIELDS CROSS + FLD IN RDB$FIELDS + WITH SEG.RDB$INDEX_NAME EQ indexName.c_str() + AND RFR.RDB$RELATION_NAME EQ rel->c_name() + AND RFR.RDB$FIELD_NAME EQ SEG.RDB$FIELD_NAME + AND FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE + { + if (++key_count > idx.idx_count || SEG.RDB$FIELD_POSITION > idx.idx_count || + FLD.RDB$FIELD_TYPE == blr_blob || !FLD.RDB$DIMENSIONS.NULL) + { + if (key_count > idx.idx_count) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_key_err) << Arg::Str(indexName)); + // Msg311: too many keys defined for index %s + } + else if (SEG.RDB$FIELD_POSITION > idx.idx_count) + { + fb_utils::exact_name(RFR.RDB$FIELD_NAME); + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_inval_key_posn) << + // Msg358: invalid key position + Arg::Gds(isc_field_name) << Arg::Str(RFR.RDB$FIELD_NAME) << + Arg::Gds(isc_index_name) << Arg::Str(indexName)); + } + else if (FLD.RDB$FIELD_TYPE == blr_blob) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_blob_idx_err) << Arg::Str(indexName)); + // Msg350: attempt to index blob column in index %s + } + else + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_array_idx_err) << Arg::Str(indexName)); + // Msg351: attempt to index array column in index %s + } + } - // Generate the trigger blr. + idx.idx_rpt[SEG.RDB$FIELD_POSITION].idx_field = RFR.RDB$FIELD_ID; - dsqlScratch->getBlrData().clear(); - dsqlScratch->getDebugData().clear(); - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + if (FLD.RDB$CHARACTER_SET_ID.NULL) + FLD.RDB$CHARACTER_SET_ID = CS_NONE; - dsqlScratch->appendUChar(blr_begin); + CollId collate; + if (!RFR.RDB$COLLATION_ID.NULL) + collate = CollId(RFR.RDB$COLLATION_ID); + else if (!FLD.RDB$COLLATION_ID.NULL) + collate = CollId(FLD.RDB$COLLATION_ID); + else + collate = COLLATE_NONE; - dsqlScratch->resetContextStack(); + const TTypeId text_type(CSetId(FLD.RDB$CHARACTER_SET_ID), collate); + idx.idx_rpt[SEG.RDB$FIELD_POSITION].idx_itype = + DFW_assign_index_type(tdbb, indexName, gds_cvt_blr_dtype[FLD.RDB$FIELD_TYPE], text_type); - RseNode* querySpec = nodeAs(selectExpr->querySpec); - fb_assert(querySpec); + // Initialize selectivity to zero. Otherwise random rubbish makes its way into database + idx.idx_rpt[SEG.RDB$FIELD_POSITION].idx_selectivity = 0; + } + END_FOR + } + END_FOR - ProcedureSourceNode* sourceNode = nodeAs(querySpec->dsqlFrom->items[0]); + if (!idx.idx_count) + fatal_exception::raiseFmt("The record for %s was not found in RDB$INDICES", indexName.c_str()); - if (triggerType == PRE_MODIFY_TRIGGER) + if (key_count != idx.idx_count) { - dsqlScratch->contextNumber = 2; - - RelationSourceNode* baseRelation = FB_NEW_POOL(pool) RelationSourceNode(pool, - sourceNode->dsqlName.identifier); - baseRelation->alias = sourceNode->alias; - - dsqlScratch->appendUChar(blr_for); + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_key_field_err) << Arg::Str(indexName)); + // Msg352: too few key columns found for index %s (incorrect column name?) + } - RseNode* rse = FB_NEW_POOL(pool) RseNode(pool); - rse->dsqlStreams = FB_NEW_POOL(pool) RecSourceListNode(pool, 1); + if (rel->isView()) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_create_err) << Arg::Str(indexName)); + // Msg308: can't create index %s + } - rse->dsqlStreams->items[0] = baseRelation; - rse->dsqlStreams->items[0] = doDsqlPass(dsqlScratch, rse->dsqlStreams->items[0]); - rse->dsqlWhere = doDsqlPass(dsqlScratch, querySpec->dsqlWhere); + // Actually create the index - dsqlScratch->contextNumber = OLD_CONTEXT_VALUE; + // Protect relation from modification to create consistent index + ProtectRelations protectRelations(tdbb, transaction); + protectRelations.addRelation(rel); - dsql_ctx* oldContext; + if (idx.idx_flags & idx_foreign) + { + Cached::Relation* partner_relation = nullptr; + idx.idx_id = idx_invalid; - { /// scope - AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias); - relationNode->alias = OLD_CONTEXT_NAME; + if (MET_lookup_partner(tdbb, rel, &idx, indexName.c_str())) + { + partner_relation = MetadataCache::lookupRelation(tdbb, idx.idx_primary_relation, CacheFlag::AUTOCREATE); + } - oldContext = PASS1_make_context(dsqlScratch, relationNode); - oldContext->ctx_flags |= CTX_system; + if (!partner_relation) + { + MetaName constraint_name; + MET_lookup_cnstrt_for_index(tdbb, constraint_name, indexName); + ERR_post(Arg::Gds(isc_partner_idx_not_found) << Arg::Str(constraint_name)); } - // Get the list of values and fields to compare to -- if there is no list of fields, get all - // fields in the base relation that are not computed. + // Get an protected_read lock on the both relations if the index being + // defined enforces a foreign key constraint. This will prevent + // the constraint from being violated during index construction. - ValueListNode* valuesNode = viewFields; - ValueListNode* fieldsNode = querySpec->dsqlSelectList; + protectRelations.addRelation(partner_relation); - if (!fieldsNode) + int bad_segment; + if (!IDX_check_master_types(tdbb, idx, partner_relation, bad_segment)) { - const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), - dsqlScratch, name); - fieldsNode = FB_NEW_POOL(pool) ValueListNode(pool, 0u); - - for (const dsql_fld* field = relation->rel_fields; field; field = field->fld_next) - { - if (!(field->flags & FLD_computed)) - fieldsNode->add(MAKE_field_name(field->fld_name.c_str())); - } + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_partner_idx_incompat_type) << Arg::Num(bad_segment + 1)); } - if (!valuesNode) - valuesNode = fieldsNode; - - // Generate the list of assignments to fields in the base relation. + /*** hvlad: this code was never called but i preserve it for Claudio review and decision - NestConst* ptr = fieldsNode->items.begin(); - const NestConst* const end = fieldsNode->items.end(); - NestConst* ptr2 = valuesNode->items.begin(); - const NestConst* const end2 = valuesNode->items.end(); - int andArg = 0; + // CVC: Currently, the server doesn't enforce FK creation more than at DYN level. + // If DYN is bypassed, then FK creation succeeds and operation will fail at run-time. + // The aim is to check REFERENCES at DDL time instead of DML time and behave accordingly + // to ANSI SQL rules for REFERENCES rights. + // For testing purposes, I'm calling SCL_check_index, although most of the DFW ops are + // carried using internal metadata structures that are refreshed from system tables. - BinaryBoolNode* andNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and); + // Don't bother if the master's owner is the same than the detail's owner. + // If both tables aren't defined in the same session, partner_relation->rel_owner_name + // won't be loaded hence, we need to be careful about null pointers. - for (; ptr != end && ptr2 != end2; ++ptr, ++ptr2) + if (rel->rel_owner_name.length() == 0 || + partner_relation->rel_owner_name.length() == 0 || + rel->rel_owner_name != partner_relation->rel_owner_name) { - NestConst fieldNod = *ptr; - NestConst valueNod = *ptr2; - DsqlAliasNode* aliasNode; + SCL_check_index(tdbb, partner_relation->getName(), + idx.idx_id + 1, SCL_references); + } + ***/ + } - if ((aliasNode = nodeAs(fieldNod))) - fieldNod = aliasNode->value; + protectRelations.lock(); - if ((aliasNode = nodeAs(valueNod))) - valueNod = aliasNode->value; + fb_assert(idxId <= dbb->dbb_max_idx); + idx.idx_id = idxId; + SelectivityList selectivity(*tdbb->getDefaultPool()); + IDX_create_index(tdbb, IdxCreate::ForRollback, relation, &idx, indexName.c_str(), + &idxId, transaction, selectivity); + fb_assert(idxId == idx.idx_id); + DFW_update_index(indexName.c_str(), idxId, selectivity, transaction); - FieldNode* fieldNode = nodeAs(fieldNod); - FieldNode* valueNode = nodeAs(valueNod); + if (idx.idx_condition_statement) + idx.idx_condition_statement->release(tdbb); - // Generate the actual comparisons. +/* if (partner_relation) to be done !!!!!!!!!!!!!!! + { + // signal to other processes about new constraint + relation->rel_flags |= REL_check_partners; + LCK_lock(tdbb, relation->rel_partners_lock, LCK_EX, LCK_WAIT); + LCK_release(tdbb, relation->rel_partners_lock); - if (fieldNode && valueNode) + if (relation != partner_relation) { - FieldNode* oldValueNode = FB_NEW_POOL(pool) FieldNode(pool); - oldValueNode->dsqlName = (aliasNode ? aliasNode->name : valueNode->dsqlName); - oldValueNode->dsqlQualifier = OLD_CONTEXT_NAME; + partner_relation->rel_flags |= REL_check_partners; + LCK_lock(tdbb, partner_relation->rel_partners_lock, LCK_EX, LCK_WAIT); + LCK_release(tdbb, partner_relation->rel_partners_lock); + } + } +*/ - valueNod = oldValueNode->dsqlPass(dsqlScratch); - fieldNod = fieldNode->dsqlPass(dsqlScratch); + return idxId; +} - BoolExprNode* equivNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_equiv, - valueNod, fieldNod); - rse->dsqlWhere = PASS1_compose(rse->dsqlWhere, equivNode, blr_and); - } - } +MetaId StoreIndexNode::createExpression(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) +{ +/************************************** + * + * c r e a t e _ e x p r e s s i o n _ i n d e x + * + ************************************** + * + * Functional description + * Create a new expression index. + * + **************************************/ - GEN_expr(dsqlScratch, rse); + SET_TDBB(tdbb); + + jrd_rel* relation = rel->getObject(tdbb, CacheFlag::AUTOCREATE); + if (!relation) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_create_err) << Arg::Str(indexName)); + // Msg308: can't create index %s } - // ASF: We'll now map the view's base table into the trigger's NEW context. + index_desc idx; + MOVE_CLEAR(&idx, sizeof(index_desc)); + idx.idx_flags = 0; + idx.idx_count = 0; + int key_count = 0; + CompilerScratch* csb = nullptr; - ++dsqlScratch->scopeLevel; - dsqlScratch->contextNumber = NEW_CONTEXT_VALUE; + const auto dbb = tdbb->getDatabase(); + const auto attachment = tdbb->getAttachment(); + MetaId idxId = dbb->dbb_max_idx; - dsql_ctx* newContext; + AutoCacheRequest request(tdbb, irq_c_exp_index, IRQ_REQUESTS); - { /// scope - AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias); + FOR(REQUEST_HANDLE request) + IDX IN RDB$INDICES CROSS + REL IN RDB$RELATIONS OVER RDB$RELATION_NAME WITH + IDX.RDB$EXPRESSION_BLR NOT MISSING AND + IDX.RDB$INDEX_NAME EQ indexName.c_str() + { + // Reject here request to 'alter index inactive' + fb_assert(IDX.RDB$INDEX_ID.NULL); + fb_assert(IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE); - if (relationNode->alias.isEmpty()) - relationNode->alias = sourceNode->dsqlName.identifier.c_str(); + if (IDX.RDB$SEGMENT_COUNT) + { + // Msg359: segments not allowed in expression index %s + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_no_segments_err) << Arg::Str(indexName)); + } - newContext = PASS1_make_context(dsqlScratch, relationNode); - newContext->ctx_flags |= CTX_system; + if (IDX.RDB$UNIQUE_FLAG) + idx.idx_flags |= idx_unique; + if (IDX.RDB$INDEX_TYPE == 1) + idx.idx_flags |= idx_descending; - if (triggerType == PRE_STORE_TRIGGER) - newContext->ctx_flags |= CTX_view_with_check_store; - else - newContext->ctx_flags |= CTX_view_with_check_modify; - } + // Allocate a new pool to contain the expression tree + // for index expression + const auto new_pool = dbb->createPool(ALLOC_ARGS0); - // Replace the view's field names by the base table field names. Save the original names - // to restore after the condition processing. + try + { + Jrd::ContextPoolHolder context(tdbb, new_pool); - dsql_fld* field = newContext->ctx_relation->rel_fields; - ObjectsArray savedNames; + MET_get_dependencies(tdbb, rel, nullptr, 0, nullptr, &IDX.RDB$EXPRESSION_BLR, + nullptr, &csb, indexName, obj_index_expression, 0, + transaction); - // ASF: rel_fields entries are in reverse order. - for (NestConst* ptr = items->items.end(); - ptr-- != items->items.begin(); - field = field->fld_next) - { - ValueExprNode* valueNode = *ptr; - DsqlAliasNode* aliasNode; + idx.idx_expression_statement = Statement::makeValueExpression(tdbb, + idx.idx_expression_node, idx.idx_expression_desc, csb, false); - if ((aliasNode = nodeAs(valueNode))) - valueNode = aliasNode->value; + // fake a description of the index - FieldNode* fieldNode = nodeAs(valueNode); - fb_assert(fieldNode); + idx.idx_count = 1; + idx.idx_flags |= idx_expression; + idx.idx_rpt[0].idx_itype = + DFW_assign_index_type(tdbb, indexName, + idx.idx_expression_desc.dsc_dtype, + idx.idx_expression_desc.getTextType()); + idx.idx_rpt[0].idx_selectivity = 0; + } + catch (const Exception&) + { + dbb->deletePool(new_pool); + throw; + } - savedNames.add(field->fld_name); + if (!IDX.RDB$CONDITION_BLR.NULL) + { + // Allocate a new pool to contain the expression tree + // for index condition + const auto new_pool = dbb->createPool(ALLOC_ARGS0); - dsql_fld* queryField = fieldNode->dsqlField; + try + { + Jrd::ContextPoolHolder context(tdbb, new_pool); - field->fld_name = queryField->fld_name; - field->dtype = queryField->dtype; - field->scale = queryField->scale; - field->subType = queryField->subType; - field->length = queryField->length; - field->flags = queryField->flags; - field->charSetId = queryField->charSetId; - field->collationId = queryField->collationId; + MET_get_dependencies(tdbb, rel, nullptr, 0, nullptr, &IDX.RDB$CONDITION_BLR, + nullptr, &csb, indexName, obj_index_condition, 0, + transaction); + + idx.idx_condition_statement = Statement::makeBoolExpression(tdbb, + idx.idx_condition_node, csb, false); + + idx.idx_flags |= idx_condition; + } + catch (const Exception&) + { + dbb->deletePool(new_pool); + throw; + } + } } + END_FOR - dsqlScratch->appendUChar(blr_if); + if (!relation) + { + // Msg308: can't create index %s + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_create_err) << Arg::Str(indexName)); + } - // Process the condition for firing the trigger. - NestConst condition = doDsqlPass(dsqlScratch, querySpec->dsqlWhere); + delete csb; - // Restore the field names. This must happen before the condition's BLR generation. + // Actually create the index - field = newContext->ctx_relation->rel_fields; + // Protect relation from modification to create consistent index + ProtectRelations protectRelation(tdbb, transaction); + protectRelation.addRelation(rel); - for (ObjectsArray::iterator i = savedNames.begin(); i != savedNames.end(); ++i) + SelectivityList selectivity(*tdbb->getDefaultPool()); + + jrd_tra* const current_transaction = tdbb->getTransaction(); + Request* const current_request = tdbb->getRequest(); + + try { - field->fld_name = *i; - field = field->fld_next; + fb_assert(idxId <= dbb->dbb_max_idx); + idx.idx_id = idxId; + IDX_create_index(tdbb, IdxCreate::ForRollback, relation, &idx, indexName.c_str(), &idxId, + transaction, selectivity); + + fb_assert(idxId == idx.idx_id); } + catch (const Exception&) + { + tdbb->setTransaction(current_transaction); + tdbb->setRequest(current_request); - GEN_expr(dsqlScratch, condition); - dsqlScratch->appendUChar(blr_begin); - dsqlScratch->appendUChar(blr_end); + // Get rid of the expression/condition statements + idx.idx_expression_statement->release(tdbb); + if (idx.idx_condition_statement) + idx.idx_condition_statement->release(tdbb); - // Generate the action statement for the trigger. - exceptionNode->dsqlPass(dsqlScratch)->genBlr(dsqlScratch); + throw; + } - dsqlScratch->appendUChar(blr_end); // of begin - dsqlScratch->appendUChar(blr_eoc); + tdbb->setTransaction(current_transaction); + tdbb->setRequest(current_request); - dsqlScratch->resetContextStack(); + DFW_update_index(indexName.c_str(), idx.idx_id, selectivity, transaction); - TriggerDefinition trigger(pool); - trigger.systemFlag = fb_sysflag_view_check; - trigger.relationName = name; - trigger.type = triggerType; - trigger.blrData = dsqlScratch->getBlrData(); - trigger.store(tdbb, dsqlScratch, dsqlScratch->getTransaction()); + // Get rid of the expression/condition statements + idx.idx_expression_statement->release(tdbb); + if (idx.idx_condition_statement) + idx.idx_condition_statement->release(tdbb); + + return idxId; } @@ -9619,18 +11438,17 @@ void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratc // Store an index. -void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& name, - const Definition& definition, MetaName* referredIndexName) +ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* transaction, + MetaName& idxName, Definition& definition, MetaName* referredIndexName) { - if (name.isEmpty()) - DYN_UTIL_generate_index_name(tdbb, transaction, name, definition.type); - - DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_index); - - AutoCacheRequest request(tdbb, drq_s_indices, DYN_REQUESTS); + if (idxName.isEmpty()) + DYN_UTIL_generate_index_name(tdbb, transaction, idxName, definition.type); + DYN_UTIL_check_unique_name(tdbb, transaction, idxName, obj_index); + definition.index = idxName; ULONG keyLength = 0; + AutoCacheRequest request(tdbb, drq_s_indices, DYN_REQUESTS); STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES { @@ -9640,7 +11458,7 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam IDX.RDB$FOREIGN_KEY.NULL = TRUE; IDX.RDB$EXPRESSION_SOURCE.NULL = TRUE; IDX.RDB$EXPRESSION_BLR.NULL = TRUE; - strcpy(IDX.RDB$INDEX_NAME, name.c_str()); + strcpy(IDX.RDB$INDEX_NAME, idxName.c_str()); strcpy(IDX.RDB$RELATION_NAME, definition.relation.c_str()); IDX.RDB$RELATION_NAME.NULL = FALSE; IDX.RDB$SYSTEM_FLAG = 0; @@ -9728,15 +11546,15 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam if (!F.RDB$COLLATION_ID.NULL) { length = INTL_key_length(tdbb, - INTL_TEXT_TO_INDEX(INTL_CS_COLL_TO_TTYPE( - GF.RDB$CHARACTER_SET_ID, F.RDB$COLLATION_ID)), + INTL_TEXT_TO_INDEX(TTypeId(CSetId(GF.RDB$CHARACTER_SET_ID), + CollId(F.RDB$COLLATION_ID))), GF.RDB$FIELD_LENGTH); } else if (!GF.RDB$COLLATION_ID.NULL) { length = INTL_key_length(tdbb, - INTL_TEXT_TO_INDEX(INTL_CS_COLL_TO_TTYPE( - GF.RDB$CHARACTER_SET_ID, GF.RDB$COLLATION_ID)), + INTL_TEXT_TO_INDEX(TTypeId(CSetId(GF.RDB$CHARACTER_SET_ID), + CollId(GF.RDB$COLLATION_ID))), GF.RDB$FIELD_LENGTH); } else @@ -9825,7 +11643,7 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam request2.reset(tdbb, drq_l_unq_idx, DYN_REQUESTS); - MetaName indexName; + MetaName partnerIdxName; int listIndex = -1; bool found = false; @@ -9840,14 +11658,14 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam SORTED BY IND.RDB$INDEX_NAME, DESCENDING ISEG.RDB$FIELD_POSITION { - if (indexName != IND.RDB$INDEX_NAME) + if (partnerIdxName != IND.RDB$INDEX_NAME) { if (listIndex >= 0) found = false; if (found) break; listIndex = definition.refColumns.getCount() - 1; - indexName = IND.RDB$INDEX_NAME; + partnerIdxName = IND.RDB$INDEX_NAME; found = true; } @@ -9871,10 +11689,10 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam if (found) { IDX.RDB$FOREIGN_KEY.NULL = FALSE; - strcpy(IDX.RDB$FOREIGN_KEY, indexName.c_str()); + strcpy(IDX.RDB$FOREIGN_KEY, partnerIdxName.c_str()); if (referredIndexName) - *referredIndexName = indexName; + *referredIndexName = partnerIdxName; } else { @@ -9957,7 +11775,7 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES - WITH IDX.RDB$INDEX_NAME EQ name.c_str() + WITH IDX.RDB$INDEX_NAME EQ idxName.c_str() { MODIFY IDX if (!definition.conditionBlr.isEmpty()) @@ -9975,6 +11793,8 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam } END_FOR } + + return FB_NEW_POOL(p) StoreIndexNode(definition.relation, definition.index, definition.expressionBlr.hasData()); } @@ -10014,7 +11834,7 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_INDEX, name, NULL); - CreateIndexNode::Definition definition; + Definition definition; definition.type = isc_dyn_def_idx; definition.relation = relation->dsqlName; definition.unique = unique; @@ -10059,12 +11879,15 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, &definition.conditionBlr, partialValue); } - store(tdbb, transaction, name, definition); - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_INDEX, name, NULL); savePoint.release(); // everything is ok + + ModifyIndexList oneItemList(*MemoryPool::getContextPool()); + oneItemList.push(store(tdbb, oneItemList.getPool(), transaction, name, definition)); + oneItemList.exec(tdbb, MetadataCache::lookupRelation(tdbb, definition.relation, CacheFlag::AUTOCREATE), + transaction); } @@ -10074,9 +11897,9 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, string AlterIndexNode::internalPrint(NodePrinter& printer) const { DdlNode::internalPrint(printer); + ModifyIndexNode::print(printer); - NODE_PRINT(printer, name); - NODE_PRINT(printer, active); + NODE_PRINT(printer, idxId); return "AlterIndexNode"; } @@ -10084,7 +11907,7 @@ string AlterIndexNode::internalPrint(NodePrinter& printer) const void AlterIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { bool systemIndex; - MetaName relationName = getIndexRelationName(tdbb, transaction, name, systemIndex); + MetaName relationName = getIndexRelationName(tdbb, transaction, indexName, systemIndex); dsc dscName; dscName.makeText(relationName.length(), CS_METADATA, (UCHAR*) relationName.c_str()); @@ -10097,37 +11920,78 @@ void AlterIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); + Cached::Relation* relation = nullptr; + bool expressionIndex = false; + AutoCacheRequest request(tdbb, drq_m_index, DYN_REQUESTS); bool found = false; + bool active = create; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES - WITH IDX.RDB$INDEX_NAME EQ name.c_str() + WITH IDX.RDB$INDEX_NAME EQ indexName.c_str() { found = true; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, - name, NULL); + active = IDX.RDB$INDEX_INACTIVE.NULL ? true : !IDX.RDB$INDEX_INACTIVE; - MODIFY IDX - IDX.RDB$INDEX_INACTIVE.NULL = FALSE; - IDX.RDB$INDEX_INACTIVE = active ? FALSE : TRUE; - END_MODIFY + if (active != create) + { + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, + indexName, NULL); + + relName = IDX.RDB$RELATION_NAME; + relation = MetadataCache::lookupRelation(tdbb, relName, CacheFlag::AUTOCREATE); + fb_assert(relation); + + expressionIndex = !IDX.RDB$EXPRESSION_BLR.NULL; + if (!IDX.RDB$INDEX_ID.NULL) + { + fb_assert(IDX.RDB$INDEX_ID); + idxId = IDX.RDB$INDEX_ID - 1; + } + if (relation && idxId.has_value()) + relation->oldIndexVersion(tdbb, idxId.value()); + + MODIFY IDX + IDX.RDB$INDEX_INACTIVE.NULL = FALSE; + IDX.RDB$INDEX_INACTIVE = create ? FALSE : TRUE; + END_MODIFY + } } END_FOR - if (found) - { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, - name, NULL); - } - else + if (!found) { // msg 48: "Index not found" status_exception::raise(Arg::PrivateDyn(48)); } - savePoint.release(); // everything is ok + if (active == create) + return; + + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, + indexName, NULL); + + savePoint.release(); // system table modified OK + + MemoryPool& pool = *MemoryPool::getContextPool(); + ModifyIndexList oneItemList(pool); + if (!idxId.has_value()) + oneItemList.push(FB_NEW_POOL(pool) StoreIndexNode(relName, indexName, expressionIndex)); + else + oneItemList.push(this); + oneItemList.exec(tdbb, relation, transaction); +} + +MetaId AlterIndexNode::exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) +{ + if (create) + IDX_activate_index(tdbb, rel, idxId.value()); + else + IDX_mark_index(tdbb, rel, idxId.value()); + + return idxId.value(); } @@ -10197,6 +12061,13 @@ void SetStatisticsNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc //---------------------- +DropIndexNode::DropIndexNode(MemoryPool& p, const MetaName& name) + : ModifyIndexNode(name, false), + DdlNode(p), + idxId(MAX_META_ID) +{ } + + // Delete the records in RDB$INDEX_SEGMENTS pertaining to an index. bool DropIndexNode::deleteSegmentRecords(thread_db* tdbb, jrd_tra* transaction, const MetaName& name) @@ -10218,8 +12089,9 @@ bool DropIndexNode::deleteSegmentRecords(thread_db* tdbb, jrd_tra* transaction, string DropIndexNode::internalPrint(NodePrinter& printer) const { DdlNode::internalPrint(printer); + ModifyIndexNode::print(printer); - NODE_PRINT(printer, name); + NODE_PRINT(printer, indexName); return "DropIndexNode"; } @@ -10227,7 +12099,7 @@ string DropIndexNode::internalPrint(NodePrinter& printer) const void DropIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { bool systemIndex; - MetaName relationName = getIndexRelationName(tdbb, transaction, name, systemIndex, silent); + MetaName relationName = getIndexRelationName(tdbb, transaction, indexName, systemIndex, silent); if (relationName.hasData()) { @@ -10244,31 +12116,45 @@ void DropIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j AutoSavePoint savePoint(tdbb, transaction); AutoCacheRequest request(tdbb, drq_e_indices, DYN_REQUESTS); - bool found = false; + Cached::Relation* rel = nullptr; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES - WITH IDX.RDB$INDEX_NAME EQ name.c_str() + WITH IDX.RDB$INDEX_NAME EQ indexName.c_str() { + fb_assert(IDX.RDB$INDEX_ID); + idxId = IDX.RDB$INDEX_ID - 1; + relName = IDX.RDB$RELATION_NAME; + + rel = MetadataCache::lookupRelation(tdbb, relName, CacheFlag::AUTOCREATE); + fb_assert(rel); + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, - DDL_TRIGGER_DROP_INDEX, name, NULL); + DDL_TRIGGER_DROP_INDEX, indexName, NULL); - ERASE IDX; + if (!IDX.RDB$FOREIGN_KEY.NULL) + partnerRelName = IDX.RDB$FOREIGN_KEY; + + if (rel) + rel->oldIndexVersion(tdbb, idxId); - if (IDX.RDB$EXPRESSION_BLR.NULL && !deleteSegmentRecords(tdbb, transaction, name)) + MODIFY IDX + IDX.RDB$INDEX_INACTIVE.NULL = FALSE; + IDX.RDB$INDEX_INACTIVE = MET_index_deferred_drop; + END_MODIFY + + if (IDX.RDB$EXPRESSION_BLR.NULL && !deleteSegmentRecords(tdbb, transaction, indexName)) { // msg 50: "No segments found for index" status_exception::raise(Arg::PrivateDyn(50)); } - - found = true; } END_FOR - if (found) + if (rel) { executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_INDEX, - name, NULL); + indexName, NULL); } else if (!silent) { @@ -10277,6 +12163,120 @@ void DropIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j } savePoint.release(); // everything is ok + + ModifyIndexList oneItemList(*MemoryPool::getContextPool()); + oneItemList.push(this); + oneItemList.exec(tdbb, rel, transaction); +} + + +MetaId DropIndexNode::exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) +{ +/************************************** + * + * d e l e t e _ i n d e x + * + ************************************** + * + * Functional description + * + **************************************/ + SET_TDBB(tdbb); + Database* dbb = tdbb->getDatabase(); + + // Look up the relation. If we can't find the relation, + // don't worry about the index. + if (!rel) + return idxId; + + fb_assert(idxId >= 0 && idxId < dbb->dbb_max_idx); + // Maybe a permanent check? + //if (idxId == idx_invalid) + // ERR_post(...); + + RelationPages* relPages = rel->getPages(tdbb, MAX_TRA_NUMBER, false); + if (!relPages) + return MAX_META_ID; + + // we need to special handle temp tables with ON PRESERVE ROWS only + const bool isTempIndex = (rel->rel_flags & REL_temp_conn) && + (relPages->rel_instance_id != 0); + + IDX_mark_index(tdbb, rel, idxId); + +/* ??????????????????? back to DFW? + if ((!isTempIndex) && partnerRelName.hasData()) + { + auto* partnerRel = MetadataCache::lookupRelation(tdbb, partnerRelName, CacheFlag::AUTOCREATE); + fb_assert(partnerRel); + if (partnerRel) + { + auto relId = rel->getId(); + DFW_check_partners(tdbb, relId); + + auto partnerRelId = partnerRel->getId(); + if (relId != partnerRelId) + DFW_check_partners(tdbb, partnerRelId); + } + else { + // partner relation was not found + // we must check partners of all relations in database + MetadataCache::update_partners(tdbb); + } + } +*/ + return idxId; +} + + +void DropIndexNode::clearId(thread_db* tdbb, MetaId relId, MetaId indexId) +{ + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + jrd_tra* transaction = attachment->getSysTransaction(); // cleanup NOT related to current transaction + MetaName indexName; + + AutoCacheRequest handle(tdbb, irq_index_id_erase, IRQ_REQUESTS); + FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) + IND IN RDB$INDICES + CROSS REL IN RDB$RELATIONS + OVER RDB$RELATION_NAME + WITH IND.RDB$INDEX_ID EQ indexId + 1 + AND REL.RDB$RELATION_ID EQ relId + { + indexName = IND.RDB$INDEX_NAME; + + if (MetadataCache::getIndexStatus(IND.RDB$INDEX_INACTIVE.NULL, IND.RDB$INDEX_INACTIVE) == MET_index_deferred_drop) + { + AutoSetRestore2 autoRequest(tdbb, + &thread_db::getTransaction, &thread_db::setTransaction, transaction); + + ERASE IND; + + auto rel = MetadataCache::lookupRelation(tdbb, relId, 0); + if (rel) + { + auto idx = rel->eraseIndex(tdbb, indexId); + if (idx) + idx->commit(tdbb); + } + } + else + { + MODIFY IND + IND.RDB$INDEX_ID = 0; + IND.RDB$INDEX_ID.NULL = TRUE; + END_MODIFY + } + } + END_FOR + + if (indexName.hasData()) + { + MET_delete_dependencies(tdbb, indexName, obj_index_expression, transaction); + MET_delete_dependencies(tdbb, indexName, obj_index_condition, transaction); + } } @@ -13072,4 +15072,48 @@ void AlterDatabaseNode::defineDifference(thread_db* tdbb, jrd_tra* transaction, } +static ISC_STATUS getErrorCodeByObjectType(int obj_type) +{ + ISC_STATUS err_code = 0; + + switch (obj_type) + { + case obj_relation: + err_code = isc_table_name; + break; + case obj_view: + err_code = isc_view_name; + break; + case obj_procedure: + err_code = isc_proc_name; + break; + case obj_collation: + err_code = isc_collation_name; + break; + case obj_exception: + err_code = isc_exception_name; + break; + case obj_field: + err_code = isc_domain_name; + break; + case obj_generator: + err_code = isc_generator_name; + break; + case obj_udf: + err_code = isc_udf_name; + break; + case obj_index: + err_code = isc_index_name; + break; + case obj_package_header: + case obj_package_body: + err_code = isc_package_name; + break; + default: + fb_assert(false); + } + + return err_code; +} + } // namespace Jrd diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 7f3f1b15dfa..cfbfe37170f 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -862,8 +862,8 @@ class CreateCollationNode : public DdlNode private: USHORT attributesOn; USHORT attributesOff; - USHORT forCharSetId; - USHORT fromCollationId; + CSetId forCharSetId; + CollId fromCollationId; }; @@ -1167,6 +1167,40 @@ typedef RecreateNode nodes; +}; + + class RelationNode : public DdlNode { public: @@ -1192,7 +1226,7 @@ class RelationNode : public DdlNode MetaName fieldSource; MetaName identitySequence; std::optional identityType; - std::optional collationId; + std::optional collationId; Firebird::TriState notNullFlag; // true = NOT NULL / false = NULL std::optional position; Firebird::string defaultSource; @@ -1486,6 +1520,8 @@ class RelationNode : public DdlNode RelationNode(MemoryPool& p, RelationSourceNode* aDsqlNode); + MetaId generateIdDbKey(thread_db* tdbb, jrd_tra* transaction); + static bool deleteLocalField(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName, const MetaName& fieldName, bool silent, std::function preChangeHandler = {}); @@ -1533,12 +1569,30 @@ class RelationNode : public DdlNode void stuffMatchingBlr(Constraint& constraint, BlrDebugWriter& blrWriter); void stuffTriggerFiringCondition(const Constraint& constraint, BlrDebugWriter& blrWriter); +public: + static void makeVersion(thread_db* tdbb, jrd_tra* transaction, MetaName relName); + +private: + static blb* setupTriggers(thread_db* tdbb, jrd_rel* relation, bool null_view, + TrigArray* triggers, blb* blob); + static void setupTriggerDetails(thread_db* tdbb, jrd_rel* relation, blb* blob, TrigArray* triggers, + const TEXT* trigger_name, bool null_view); + static void putSummaryRecord(thread_db* tdbb, blb* blob, rsr_t type, const UCHAR* data, ULONG length); + static void putSummaryBlob(thread_db* tdbb, blb* blob, rsr_t type, bid* blob_id, jrd_tra* transaction); + static bool validateTextType(thread_db* tdbb, const TemporaryField* tfb); + static void setupArray(thread_db* tdbb, blb* blob, const TEXT* field_name, USHORT n, TemporaryField* tfb); + static void getArrayDesc(thread_db* tdbb, const TEXT* field_name, Ods::InternalArrayDesc* desc); + static Format* makeFormat(thread_db* tdbb, jrd_tra* transaction, Cached::Relation* relation, + USHORT* version, TemporaryField* stack); + static void raiseTooManyVersionsError(const int obj_type, const MetaName& obj_name); + public: NestConst dsqlNode; MetaName name; Firebird::Array > clauses; Firebird::TriState ssDefiner; Firebird::TriState replicationState; + ModifyIndexList indexList; }; @@ -1696,6 +1750,53 @@ class RecreateViewNode : }; +// Performs 2-pass index create/drop + +class ModifyIndexNode +{ +public: + struct ModifyValue + { + ModifyValue(bool create, MetaId id) + : create(create), id(id) + { + } + + bool create; + MetaId id; + }; + + ModifyIndexNode(MetaName relName, MetaName indexName, bool create) + : indexName(indexName), + relName(relName), + create(create) + { + } + + ModifyIndexNode(MetaName indexName, bool create) + : indexName(indexName), + create(create) + { + } + + virtual ~ModifyIndexNode() + { + } + + ModifyValue modify(thread_db* tdbb, jrd_tra* transaction); + + virtual MetaId exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) = 0; + +protected: + Firebird::string print(NodePrinter& printer) const; + +public: + MetaName indexName; + MetaName relName; + bool create; +}; + + class CreateIndexNode : public DdlNode { public: @@ -1710,6 +1811,7 @@ class CreateIndexNode : public DdlNode conditionSource.clear(); } + MetaName index; MetaName relation; Firebird::ObjectsArray columns; Firebird::TriState unique; @@ -1732,8 +1834,8 @@ class CreateIndexNode : public DdlNode } public: - static void store(thread_db* tdbb, jrd_tra* transaction, MetaName& name, - const Definition& definition, MetaName* referredIndexName = nullptr); + static ModifyIndexNode* store(thread_db* tdbb, MemoryPool& p, jrd_tra* transaction, MetaName& idxName, + Definition& definition, MetaName* referredIndexName = nullptr); public: virtual Firebird::string internalPrint(NodePrinter& printer) const; @@ -1759,13 +1861,32 @@ class CreateIndexNode : public DdlNode }; -class AlterIndexNode : public DdlNode +class StoreIndexNode : public ModifyIndexNode { public: - AlterIndexNode(MemoryPool& p, const MetaName& aName, bool aActive) - : DdlNode(p), - name(p, aName), - active(aActive) + StoreIndexNode(MetaName relName, MetaName indexName, bool expressionIndex) + : ModifyIndexNode(relName, indexName, true), + expressionIndex(expressionIndex) + { } + +public: + MetaId exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) override; + +private: + MetaId create(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction); + MetaId createExpression(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction); + + bool expressionIndex = false; +}; + + +class AlterIndexNode : public ModifyIndexNode, public DdlNode +{ +public: + // never alter FK index + AlterIndexNode(MemoryPool& p, const MetaName& name, bool active) + : ModifyIndexNode(name, active), + DdlNode(p) { } @@ -1774,15 +1895,16 @@ class AlterIndexNode : public DdlNode virtual void checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); + MetaId exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) override; + protected: virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) { - statusVector << Firebird::Arg::Gds(isc_dsql_alter_index_failed) << name; + statusVector << Firebird::Arg::Gds(isc_dsql_alter_index_failed) << indexName; } public: - MetaName name; - bool active; + std::optional idxId; }; @@ -1812,31 +1934,34 @@ class SetStatisticsNode : public DdlNode }; -class DropIndexNode : public DdlNode +class DropIndexNode : public ModifyIndexNode, public DdlNode { public: - DropIndexNode(MemoryPool& p, const MetaName& aName) - : DdlNode(p), - name(p, aName) - { - } + DropIndexNode(MemoryPool& p, const MetaName& name); static bool deleteSegmentRecords(thread_db* tdbb, jrd_tra* transaction, const MetaName& name); + static void clearId(thread_db* tdbb, MetaId relId, MetaId indexId); + public: virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual void checkPermission(thread_db* tdbb, jrd_tra* transaction); virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); + MetaId exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) override; + protected: virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) { - statusVector << Firebird::Arg::Gds(isc_dsql_drop_index_failed) << name; + statusVector << Firebird::Arg::Gds(isc_dsql_drop_index_failed) << indexName; } +private: + MetaId idxId; + MetaName partnerRelName; + public: - MetaName name; bool silent = false; }; diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp index 586dfdb9eec..9cfd8f1f210 100644 --- a/src/dsql/DsqlCompilerScratch.cpp +++ b/src/dsql/DsqlCompilerScratch.cpp @@ -1048,3 +1048,20 @@ BoolExprNode* DsqlCompilerScratch::pass1JoinIsRecursive(RecordSourceNode*& input return NULL; } + +// hvlad: each member of recursive CTE can refer to CTE itself (only once) via +// CTE name or via alias. We need to substitute this aliases when processing CTE +// member to resolve field names. Therefore we store all aliases in order of +// occurrence and later use it in backward order (since our parser is right-to-left). +// Also we put CTE name after all such aliases to distinguish aliases for +// different CTE's. +// We also need to repeat this process if main select expression contains union with +// recursive CTE + +void DsqlCompilerScratch::addCTEAlias(const string& alias) +{ + thread_db* tdbb = JRD_get_thread_data(); + fb_assert(currCteAlias == NULL); + cteAliases.add(FB_NEW_POOL(*tdbb->getDefaultPool()) string(*tdbb->getDefaultPool(), alias)); +} + diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index 8755b7cb2d0..f2638935208 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -99,7 +99,10 @@ class DsqlCompilerScratch : public BlrDebugWriter ctes(p), cteAliases(p), subFunctions(p), - subProcedures(p) + subProcedures(p), + rels(p), + procedures(p), + functions(p) { } @@ -193,21 +196,7 @@ class DsqlCompilerScratch : public BlrDebugWriter SelectExprNode* findCTE(const MetaName& name); void clearCTEs(); void checkUnusedCTEs(); - - // hvlad: each member of recursive CTE can refer to CTE itself (only once) via - // CTE name or via alias. We need to substitute this aliases when processing CTE - // member to resolve field names. Therefore we store all aliases in order of - // occurrence and later use it in backward order (since our parser is right-to-left). - // Also we put CTE name after all such aliases to distinguish aliases for - // different CTE's. - // We also need to repeat this process if main select expression contains union with - // recursive CTE - void addCTEAlias(const Firebird::string& alias) - { - thread_db* tdbb = JRD_get_thread_data(); - fb_assert(currCteAlias == NULL); - cteAliases.add(FB_NEW_POOL(*tdbb->getDefaultPool()) Firebird::string(*tdbb->getDefaultPool(), alias)); - } + void addCTEAlias(const Firebird::string& alias); const Firebird::string* getNextCTEAlias() { @@ -325,6 +314,11 @@ class DsqlCompilerScratch : public BlrDebugWriter bool psql = false; Firebird::LeftPooledMap subFunctions; Firebird::LeftPooledMap subProcedures; + +public: + Firebird::LeftPooledMap rels; // known relations + Firebird::LeftPooledMap procedures; // known procedures + Firebird::LeftPooledMap functions; // known functions }; class PsqlChanger diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp index cb68720f7e9..0b661b1a2c1 100644 --- a/src/dsql/DsqlRequests.cpp +++ b/src/dsql/DsqlRequests.cpp @@ -284,7 +284,7 @@ USHORT DsqlRequest::parseMetadata(IMessageMetadata* meta, const Array checkD(&st); desc.dsc_sub_type = meta->getSubType(&st, index); checkD(&st); - unsigned textType = meta->getCharSet(&st, index); + auto textType = CSetId(meta->getCharSet(&st, index)); checkD(&st); desc.setTextType(textType); desc.dsc_address = (UCHAR*)(IPTR) dataOffset; diff --git a/src/dsql/DsqlStatementCache.cpp b/src/dsql/DsqlStatementCache.cpp index 2ba98386a22..31f0a060e9c 100644 --- a/src/dsql/DsqlStatementCache.cpp +++ b/src/dsql/DsqlStatementCache.cpp @@ -26,7 +26,6 @@ #include "../jrd/Attachment.h" #include "../jrd/Statement.h" #include "../jrd/lck.h" -#include "../jrd/lck_proto.h" using namespace Firebird; using namespace Jrd; diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 8320ab355be..7d9e46b29fb 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -32,6 +32,7 @@ #include "../jrd/align.h" #include "firebird/impl/blr.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "../jrd/Function.h" #include "../jrd/SysFunction.h" #include "../jrd/recsrc/RecordSource.h" @@ -120,12 +121,12 @@ namespace // Try to expand the given stream. If it's a view reference, collect its base streams // (the ones directly residing in the FROM clause) and recurse. - void expandViewStreams(CompilerScratch* csb, StreamType baseStream, SortedStreamList& streams) + void expandViewStreams(thread_db* tdbb, CompilerScratch* csb, StreamType baseStream, SortedStreamList& streams) { const auto csb_tail = &csb->csb_rpt[baseStream]; const RseNode* const rse = - csb_tail->csb_relation ? csb_tail->csb_relation->rel_view_rse : NULL; + csb_tail->csb_relation ? csb_tail->csb_relation(tdbb)->rel_view_rse : NULL; // If we have a view, collect its base streams and remap/expand them @@ -140,7 +141,7 @@ namespace for (auto stream : viewStreams) { // Remap stream and expand it recursively - expandViewStreams(csb, map[stream], streams); + expandViewStreams(tdbb, csb, map[stream], streams); } return; @@ -153,11 +154,11 @@ namespace } // Expand DBKEY for view - void expandViewNodes(CompilerScratch* csb, StreamType baseStream, + void expandViewNodes(thread_db* tdbb, CompilerScratch* csb, StreamType baseStream, ValueExprNodeStack& stack, UCHAR blrOp) { SortedStreamList viewStreams; - expandViewStreams(csb, baseStream, viewStreams); + expandViewStreams(tdbb, csb, baseStream, viewStreams); for (auto stream : viewStreams) { @@ -396,14 +397,14 @@ bool ExprNode::sameAs(const ExprNode* other, bool ignoreStreams) const return true; } -bool ExprNode::deterministic() const +bool ExprNode::deterministic(thread_db* tdbb) const { NodeRefsHolder holder; getChildren(holder, false); for (auto i : holder.refs) { - if (*i && !(*i)->deterministic()) + if (*i && !(*i)->deterministic(tdbb)) return false; } @@ -3543,8 +3544,8 @@ DmlNode* CastNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb if (csb->collectingDependencies() && itemInfo.explicitCollation) { - CompilerScratch::Dependency dependency(obj_collation); - dependency.number = INTL_TEXT_TYPE(node->castDesc); + Dependency dependency(obj_collation); + dependency.number = node->castDesc.getTextType(); csb->addDependency(dependency); } @@ -3705,14 +3706,11 @@ ValueExprNode* CastNode::pass1(thread_db* tdbb, CompilerScratch* csb) { ValueExprNode::pass1(tdbb, csb); - const USHORT ttype = INTL_TEXT_TYPE(castDesc); + const auto ttype = castDesc.getTextType(); // Are we using a collation? - if (TTYPE_TO_COLLATION(ttype) != 0) - { - CMP_post_resource(&csb->csb_resources, INTL_texttype_lookup(tdbb, ttype), - Resource::rsc_collation, ttype); - } + if (CollId(ttype) != CollId(0)) + INTL_texttype_lookup(tdbb, ttype); return this; } @@ -4073,15 +4071,10 @@ void CollateNode::assignFieldDtypeFromDsc(dsql_fld* field, const dsc* desc) field->subType = desc->dsc_sub_type; field->length = desc->dsc_length; - if (desc->dsc_dtype <= dtype_any_text) - { - field->collationId = DSC_GET_COLLATE(desc); - field->charSetId = DSC_GET_CHARSET(desc); - } - else if (desc->dsc_dtype == dtype_blob) + if (desc->dsc_dtype <= dtype_any_text || desc->dsc_dtype == dtype_blob) { - field->charSetId = desc->dsc_scale; - field->collationId = desc->dsc_flags >> 8; + field->collationId = desc->getCollation(); + field->charSetId = desc->getCharSet(); } if (desc->dsc_flags & DSC_nullable) @@ -4678,14 +4671,14 @@ void CurrentRoleNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) desc->dsc_dtype = dtype_varying; desc->dsc_scale = 0; desc->dsc_flags = 0; - desc->dsc_ttype() = ttype_metadata; + desc->setTextType(ttype_metadata); desc->dsc_length = USERNAME_LENGTH + sizeof(USHORT); } void CurrentRoleNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) { desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = ttype_metadata; + desc->setTextType(ttype_metadata); desc->dsc_length = USERNAME_LENGTH; desc->dsc_scale = 0; desc->dsc_flags = 0; @@ -4765,14 +4758,14 @@ void CurrentUserNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) desc->dsc_dtype = dtype_varying; desc->dsc_scale = 0; desc->dsc_flags = 0; - desc->dsc_ttype() = ttype_metadata; + desc->setTextType(ttype_metadata); desc->dsc_length = USERNAME_LENGTH + sizeof(USHORT); } void CurrentUserNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) { desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = ttype_metadata; + desc->setTextType(ttype_metadata); desc->dsc_length = USERNAME_LENGTH; desc->dsc_scale = 0; desc->dsc_flags = 0; @@ -5091,9 +5084,9 @@ DmlNode* DefaultNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* if (csb->collectingDependencies()) { - CompilerScratch::Dependency dependency(obj_relation); - dependency.relation = MET_lookup_relation(tdbb, relationName); - dependency.subName = FB_NEW_POOL(pool) MetaName(fieldName); + Dependency dependency(obj_relation); + dependency.relation = MetadataCache::lookupRelation(tdbb, relationName, CacheFlag::AUTOCREATE); + dependency.subName = fieldName; csb->addDependency(dependency); } @@ -5101,7 +5094,7 @@ DmlNode* DefaultNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* while (true) { - jrd_rel* relation = MET_lookup_relation(tdbb, relationName); + auto relation = MetadataCache::lookup_relation(tdbb, relationName, CacheFlag::AUTOCREATE); if (relation && relation->rel_fields) { @@ -5350,7 +5343,7 @@ ValueExprNode* DerivedExprNode::pass1(thread_db* tdbb, CompilerScratch* csb) SortedStreamList newStreams; for (const auto stream : internalStreamList) - expandViewStreams(csb, stream, newStreams); + expandViewStreams(tdbb, csb, stream, newStreams); #ifdef CMP_DEBUG for (const auto i : newStreams) @@ -6002,20 +5995,7 @@ DmlNode* FieldNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs else if (blrOp == blr_field) { CompilerScratch::csb_repeat* tail = &csb->csb_rpt[stream]; - const jrd_prc* procedure = tail->csb_procedure; - - // make sure procedure has been scanned before using it - - if (procedure && !procedure->isSubRoutine() && - (!(procedure->flags & Routine::FLAG_SCANNED) || - (procedure->flags & Routine::FLAG_BEING_SCANNED) || - (procedure->flags & Routine::FLAG_BEING_ALTERED))) - { - const jrd_prc* scan_proc = MET_procedure(tdbb, procedure->getId(), false, 0); - - if (scan_proc != procedure) - procedure = NULL; - } + jrd_prc* procedure = tail->csb_procedure(tdbb); if (procedure) { @@ -6029,15 +6009,10 @@ DmlNode* FieldNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs } else { - jrd_rel* relation = tail->csb_relation; + jrd_rel* relation = tail->csb_relation(tdbb); if (!relation) PAR_error(csb, Arg::Gds(isc_ctxnotdef)); - // make sure relation has been scanned before using it - - if (!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)) - MET_scan_relation(tdbb, relation); - csb->csb_blr_reader.getMetaName(name); if ((id = MET_lookup_field(tdbb, relation, name)) < 0) @@ -6050,18 +6025,18 @@ DmlNode* FieldNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs } else { - if (relation->rel_flags & REL_system) + if (relation->isSystem()) return NullNode::instance(); if (tdbb->getAttachment()->isGbak()) { PAR_warning(Arg::Warning(isc_fldnotdef) << Arg::Str(name) << - Arg::Str(relation->rel_name)); + Arg::Str(relation->getName())); } - else if (!(relation->rel_flags & REL_deleted)) + else if (!relation->rel_perm->isDropped()) { PAR_error(csb, Arg::Gds(isc_fldnotdef) << Arg::Str(name) << - Arg::Str(relation->rel_name)); + Arg::Str(relation->getName())); } else PAR_error(csb, Arg::Gds(isc_ctxnotdef)); @@ -6084,7 +6059,7 @@ DmlNode* FieldNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs if (is_column) { - const jrd_rel* const temp_rel = csb->csb_rpt[stream].csb_relation; + const jrd_rel* const temp_rel = csb->csb_rpt[stream].csb_relation(tdbb); if (temp_rel) { @@ -6093,7 +6068,7 @@ DmlNode* FieldNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs if (!temp_rel->rel_fields || id >= (int) temp_rel->rel_fields->count() || !(*temp_rel->rel_fields)[id]) { - if (temp_rel->rel_flags & REL_system) + if (temp_rel->isSystem()) return NullNode::instance(); } } @@ -6697,9 +6672,9 @@ void FieldNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) desc->dsc_address = NULL; // Fix UNICODE_FSS wrong length used in system tables. - jrd_rel* relation = csb->csb_rpt[fieldStream].csb_relation; + jrd_rel* relation = csb->csb_rpt[fieldStream].csb_relation(tdbb); - if (relation && (relation->rel_flags & REL_system) && + if (relation && relation->isSystem() && desc->isText() && desc->getCharSet() == CS_UNICODE_FSS) { USHORT adjust = 0; @@ -6740,13 +6715,13 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) StreamType stream = fieldStream; CompilerScratch::csb_repeat* tail = &csb->csb_rpt[stream]; - jrd_rel* relation = tail->csb_relation; + Rsc::Rel relation = tail->csb_relation; jrd_fld* field; - if (!relation || !(field = MET_get_field(relation, fieldId)) || + if (!relation || !(field = MET_get_field(relation(tdbb), fieldId)) || (field->fld_flags & FLD_parse_computed)) { - if (relation && (relation->rel_flags & REL_being_scanned)) + if (relation && relation()->scanInProgress()) csb->csb_g_flags |= csb_reload; markVariant(csb, stream); @@ -6756,17 +6731,15 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) dsc desc; getDesc(tdbb, csb, &desc); - const USHORT ttype = INTL_TEXT_TYPE(desc); + const auto ttype = desc.getTextType(); // Are we using a collation? - if (TTYPE_TO_COLLATION(ttype) != 0) + if (CollId(ttype) != CollId(0)) { - Collation* collation = NULL; - try { ThreadStatusGuard local_status(tdbb); - collation = INTL_texttype_lookup(tdbb, ttype); + INTL_texttype_lookup(tdbb, ttype); } catch (Exception&) { @@ -6775,9 +6748,6 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) if (!tdbb->getAttachment()->isGbak()) throw; } - - if (collation) - CMP_post_resource(&csb->csb_resources, collation, Resource::rsc_collation, ttype); } // if this is a modify or store, check REFERENCES access to any foreign keys @@ -6809,17 +6779,17 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) } const SLONG ssRelationId = tail->csb_view ? - tail->csb_view->rel_id : (csb->csb_view ? csb->csb_view->rel_id : 0); + tail->csb_view(tdbb)->getId() : csb->csb_view ? csb->csb_view(tdbb)->getId() : 0; - CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId, - privilege, obj_relations, relation->rel_name); + CMP_post_access(tdbb, csb, relation()->getSecurityName(), ssRelationId, + privilege, obj_relations, relation()->getName()); // Field-level privilege access is posted for every operation except DELETE if (privilege != SCL_delete) { CMP_post_access(tdbb, csb, field->fld_security_name, ssRelationId, - privilege, obj_column, field->fld_name, relation->rel_name); + privilege, obj_column, field->fld_name, relation()->getName()); } } @@ -6827,7 +6797,7 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) if (!(sub = field->fld_computation) && !(sub = field->fld_source)) { - if (!relation->rel_view_rse) + if (!relation()->isView()) { markVariant(csb, stream); return ValueExprNode::pass1(tdbb, csb); @@ -6835,7 +6805,7 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) // Msg 364 "cannot access column %s in view %s" ERR_post(Arg::Gds(isc_no_field_access) << Arg::Str(field->fld_name) << - Arg::Str(relation->rel_name)); + Arg::Str(relation()->getName())); } // The previous test below is an apparent temporary fix @@ -6851,7 +6821,7 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) { // dimitr: added an extra check for views, because we don't // want their old/new contexts to be substituted - if (relation->rel_view_rse || !field->fld_computation) + if (relation()->isView() || !field->fld_computation) { markVariant(csb, stream); return ValueExprNode::pass1(tdbb, csb); @@ -6879,14 +6849,14 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) // If this is a computed field, cast the computed expression to the field type if required. // See CORE-5097. - if (field->fld_computation && !relation->rel_view_rse) + if (field->fld_computation && !relation()->isView()) { if (csb->csb_currentAssignTarget == this) { // This is an assignment to a computed column. Report the error here when we have the field name. ERR_post( Arg::Gds(isc_read_only_field) << - (string(relation->rel_name.c_str()) + "." + field->fld_name.c_str())); + (string(relation()->getName()) + "." + field->fld_name.c_str())); } FB_SIZE_T pos; @@ -6908,15 +6878,15 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) sub = cast; } - AutoSetRestore autoRelationStream(&csb->csb_parent_relation, - relation->rel_ss_definer.asBool() ? relation : NULL); + AutoSetRestore autoRelationStream(&csb->csb_parent_relation, + relation(tdbb)->rel_ss_definer.asBool() ? relation : Rsc::Rel()); - if (relation->rel_view_rse) + if (relation()->isView()) { // dimitr: if we reference view columns, we need to pass them // as belonging to a view (in order to compute the access // permissions properly). - AutoSetRestore autoView(&csb->csb_view, relation); + AutoSetRestore autoView(&csb->csb_view, relation); AutoSetRestore autoViewStream(&csb->csb_view_stream, stream); // ASF: If the view field doesn't reference any of the view streams, @@ -7099,7 +7069,7 @@ DmlNode* GenIdNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs if (csb->collectingDependencies()) { - CompilerScratch::Dependency dependency(obj_generator); + Dependency dependency(obj_generator); dependency.number = node->generator.id; csb->addDependency(dependency); } @@ -9642,8 +9612,8 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch, if (tdbb->getCharSet() != CS_NONE && tdbb->getCharSet() != CS_BINARY) { - const USHORT fromCharSet = dsqlParameter->par_desc.getCharSet(); - const USHORT toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ? + const auto fromCharSet = dsqlParameter->par_desc.getCharSet(); + const auto toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ? fromCharSet : tdbb->getCharSet(); if (dsqlParameter->par_desc.dsc_dtype <= dtype_any_text) @@ -9669,8 +9639,8 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch, const USHORT toCharSetBPC = METD_get_charset_bpc( dsqlScratch->getTransaction(), toCharSet); - dsqlParameter->par_desc.setTextType(INTL_CS_COLL_TO_TTYPE(toCharSet, - (fromCharSet == toCharSet ? INTL_GET_COLLATE(&dsqlParameter->par_desc) : 0))); + dsqlParameter->par_desc.setTextType(TTypeId(toCharSet, + (fromCharSet == toCharSet ? INTL_GET_COLLATE(&dsqlParameter->par_desc) : CollId(0)))); dsqlParameter->par_desc.dsc_length = UTLD_char_length_to_byte_length( dsqlParameter->par_desc.dsc_length / fromCharSetBPC, toCharSetBPC, diff); @@ -10193,7 +10163,7 @@ void RecordKeyNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc) desc->dsc_dtype = dtype_text; desc->dsc_length = dbKeyLength; desc->dsc_flags = DSC_nullable; - desc->dsc_ttype() = ttype_binary; + desc->setTextType(ttype_binary); } else // blr_record_version2 { @@ -10250,7 +10220,7 @@ void RecordKeyNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* case blr_record_version: desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = ttype_binary; + desc->setTextType(ttype_binary); desc->dsc_length = sizeof(SINT64); desc->dsc_scale = 0; desc->dsc_flags = 0; @@ -10311,7 +10281,7 @@ ValueExprNode* RecordKeyNode::pass1(thread_db* tdbb, CompilerScratch* csb) return this; ValueExprNodeStack stack; - expandViewNodes(csb, recStream, stack, blrOp); + expandViewNodes(tdbb, csb, recStream, stack, blrOp); #ifdef CMP_DEBUG csb->dump("expand RecordKeyNode: %d\n", recStream); @@ -10350,7 +10320,7 @@ ValueExprNode* RecordKeyNode::pass1(thread_db* tdbb, CompilerScratch* csb) LiteralNode* literal = FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); literal->litDesc.dsc_dtype = dtype_text; - literal->litDesc.dsc_ttype() = CS_BINARY; + literal->litDesc.setTextType(CS_BINARY); literal->litDesc.dsc_scale = 0; literal->litDesc.dsc_length = 8; literal->litDesc.dsc_address = reinterpret_cast( @@ -10388,7 +10358,7 @@ ValueExprNode* RecordKeyNode::pass1(thread_db* tdbb, CompilerScratch* csb) LiteralNode* literal = FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); literal->litDesc.dsc_dtype = dtype_text; - literal->litDesc.dsc_ttype() = CS_BINARY; + literal->litDesc.setTextType(CS_BINARY); literal->litDesc.dsc_scale = 0; literal->litDesc.dsc_length = 0; literal->litDesc.dsc_address = reinterpret_cast( @@ -10462,7 +10432,7 @@ dsc* RecordKeyNode::execute(thread_db* /*tdbb*/, Request* request) const // Now, put relation ID into first 16 bits of DB_KEY // We do not assign it as SLONG because of big-endian machines. - *(USHORT*) impure->vlu_misc.vlu_dbkey = relation->rel_id; + *(USHORT*) impure->vlu_misc.vlu_dbkey = relation->getId(); // Encode 40-bit record number. Before that, increment the value // because users expect the numbering to start with one. @@ -10474,7 +10444,7 @@ dsc* RecordKeyNode::execute(thread_db* /*tdbb*/, Request* request) const impure->vlu_desc.dsc_address = (UCHAR*) impure->vlu_misc.vlu_dbkey; impure->vlu_desc.dsc_dtype = dtype_dbkey; impure->vlu_desc.dsc_length = type_lengths[dtype_dbkey]; - impure->vlu_desc.dsc_ttype() = ttype_binary; + impure->vlu_desc.setTextType(ttype_binary); } else if (blrOp == blr_record_version) { @@ -10507,14 +10477,14 @@ dsc* RecordKeyNode::execute(thread_db* /*tdbb*/, Request* request) const impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_int64; impure->vlu_desc.dsc_dtype = dtype_text; impure->vlu_desc.dsc_length = sizeof(SINT64); - impure->vlu_desc.dsc_ttype() = ttype_binary; + impure->vlu_desc.setTextType(ttype_binary); } else if (blrOp == blr_record_version2) { const jrd_rel* relation = rpb->rpb_relation; // If it doesn't point to a valid record, return NULL. - if (!rpb->rpb_number.isValid() || !relation || relation->isVirtual() || relation->rel_file) + if (!rpb->rpb_number.isValid() || !relation || relation->isVirtual() || relation->getExtFile()) { request->req_flags |= req_null; return NULL; @@ -10584,12 +10554,12 @@ DmlNode* ScalarNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* c return node; } -void ScalarNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* csb, dsc* desc) +void ScalarNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { FieldNode* fieldNode = nodeAs(field); fb_assert(fieldNode); - jrd_rel* relation = csb->csb_rpt[fieldNode->fieldStream].csb_relation; + jrd_rel* relation = csb->csb_rpt[fieldNode->fieldStream].csb_relation(tdbb); const jrd_fld* field = MET_get_field(relation, fieldNode->fieldId); const ArrayField* array; @@ -10779,7 +10749,7 @@ void StrCaseNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) desc->dsc_length = static_cast(sizeof(USHORT)) + DSC_string_length(desc); desc->dsc_dtype = dtype_varying; desc->dsc_scale = 0; - desc->dsc_ttype() = ttype_ascii; + desc->setTextType(ttype_ascii); desc->dsc_flags = desc->dsc_flags & DSC_nullable; } } @@ -10792,7 +10762,7 @@ void StrCaseNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { desc->dsc_length = DSC_convert_to_text_length(desc->dsc_dtype); desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = ttype_ascii; + desc->setTextType(ttype_ascii); desc->dsc_scale = 0; desc->dsc_flags = 0; } @@ -10849,9 +10819,10 @@ dsc* StrCaseNode::execute(thread_db* tdbb, Request* request) const if (request->req_flags & req_null) return NULL; - TextType* textType = INTL_texttype_lookup(tdbb, value->getTextType()); + Collation* textType = INTL_texttype_lookup(tdbb, value->getTextType()); CharSet* charSet = textType->getCharSet(); - auto intlFunction = (blrOp == blr_lowcase ? &TextType::str_to_lower : &TextType::str_to_upper); +// auto intlFunction = (blrOp == blr_lowcase ? &TextType::str_to_lower : &TextType::str_to_upper); + auto intlFunction = (blrOp == blr_lowcase ? &Collation::str_to_lower : &Collation::str_to_upper); if (value->isBlob()) { @@ -10893,7 +10864,7 @@ dsc* StrCaseNode::execute(thread_db* tdbb, Request* request) const { UCHAR* ptr; VaryStr temp; - USHORT ttype; + TTypeId ttype; ULONG len = MOV_get_string_ptr(tdbb, value, &ttype, &ptr, &temp, sizeof(temp)); dsc desc; @@ -11088,7 +11059,7 @@ dsc* StrLenNode::execute(thread_db* tdbb, Request* request) const case blr_strlen_char: { - CharSet* charSet = INTL_charset_lookup(tdbb, value->dsc_blob_ttype()); + CharSet* charSet = INTL_charset_lookup(tdbb, value->getTextType()); if (charSet->isMultiByte()) { @@ -11123,7 +11094,7 @@ dsc* StrLenNode::execute(thread_db* tdbb, Request* request) const } VaryStr temp; - USHORT ttype; + TTypeId ttype; UCHAR* p; length = MOV_get_string_ptr(tdbb, value, &ttype, &p, &temp, sizeof(temp)); @@ -11968,7 +11939,7 @@ dsc* SubstringNode::perform(thread_db* tdbb, impure_value* impure, const dsc* va // routines because the "temp" is not enough are blob and array but at this time // they aren't accepted, so they will cause error() to be called anyway. VaryStr temp; - USHORT ttype; + TTypeId ttype; desc.dsc_length = MOV_get_string_ptr(tdbb, valueDsc, &ttype, &desc.dsc_address, &temp, sizeof(temp)); desc.setTextType(ttype); @@ -12172,21 +12143,21 @@ dsc* SubstringSimilarNode::execute(thread_db* tdbb, Request* request) const if (!exprDesc || !patternDesc || !escapeDesc) return NULL; - USHORT textType = exprDesc->getTextType(); + auto textType = exprDesc->getTextType(); Collation* collation = INTL_texttype_lookup(tdbb, textType); CharSet* charSet = collation->getCharSet(); MoveBuffer exprBuffer; UCHAR* exprStr; - int exprLen = MOV_make_string2(tdbb, exprDesc, textType, &exprStr, exprBuffer); + ULONG exprLen = MOV_make_string2(tdbb, exprDesc, textType, &exprStr, exprBuffer); MoveBuffer patternBuffer; UCHAR* patternStr; - int patternLen = MOV_make_string2(tdbb, patternDesc, textType, &patternStr, patternBuffer); + ULONG patternLen = MOV_make_string2(tdbb, patternDesc, textType, &patternStr, patternBuffer); MoveBuffer escapeBuffer; UCHAR* escapeStr; - int escapeLen = MOV_make_string2(tdbb, escapeDesc, textType, &escapeStr, escapeBuffer); + ULONG escapeLen = MOV_make_string2(tdbb, escapeDesc, textType, &escapeStr, escapeBuffer); // Verify the correctness of the escape character. if (escapeLen == 0 || charSet->length(escapeLen, escapeStr, true) != 1) @@ -12347,10 +12318,10 @@ DmlNode* SysFuncCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScrat MetaName relName; CVT2_make_metaname(&literal->litDesc, relName, tdbb->getAttachment()->att_dec_status); - const jrd_rel* const relation = MET_lookup_relation(tdbb, relName); + const auto* const relation = MetadataCache::lookupRelation(tdbb, relName, CacheFlag::AUTOCREATE); if (relation) - node->args->items[0] = MAKE_const_slong(relation->rel_id); + node->args->items[0] = MAKE_const_slong(relation->getId()); } } @@ -12404,9 +12375,9 @@ void SysFuncCallNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) function->makeFunc(&dataTypeUtil, function, desc, argsArray.getCount(), argsArray.begin()); } -bool SysFuncCallNode::deterministic() const +bool SysFuncCallNode::deterministic(thread_db* tdbb) const { - return ExprNode::deterministic() && function->deterministic; + return ExprNode::deterministic(tdbb) && function->deterministic; } void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) @@ -12645,7 +12616,7 @@ void TrimNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { desc->dsc_dtype = dtype_varying; desc->dsc_scale = 0; - desc->dsc_ttype() = ttype_ascii; + desc->setTextType(ttype_ascii); desc->dsc_length = static_cast(sizeof(USHORT)) + DSC_string_length(&desc1); desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; } @@ -12668,7 +12639,7 @@ void TrimNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) if (!DTYPE_IS_TEXT(desc->dsc_dtype)) { - desc->dsc_ttype() = ttype_ascii; + desc->setTextType(ttype_ascii); desc->dsc_scale = 0; } @@ -12733,8 +12704,8 @@ dsc* TrimNode::execute(thread_db* tdbb, Request* request) const if (request->req_flags & req_null) return NULL; - USHORT ttype = INTL_TEXT_TYPE(*valueDesc); - TextType* tt = INTL_texttype_lookup(tdbb, ttype); + auto ttype = INTL_GET_TTYPE(valueDesc); + Collation* tt = INTL_texttype_lookup(tdbb, ttype); CharSet* cs = tt->getCharSet(); const UCHAR* charactersAddress; @@ -12945,6 +12916,7 @@ UdfCallNode::UdfCallNode(MemoryPool& pool, const QualifiedName& aName, { } + DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { @@ -13006,7 +12978,11 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* } } else if (!node->function) - node->function = Function::lookup(tdbb, name, false); + { + auto* func = MetadataCache::lookupFunction(tdbb, name, CacheFlag::AUTOCREATE); + if (func) + node->function = csb->csb_resources->functions.registerResource(func); + } if (!node->function) { @@ -13041,7 +13017,7 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* predateCheck(node->function, "blr_invoke_function_type", "blr_invoke_function_args"); argCount = blrReader.getWord(); - node->args = PAR_args(tdbb, csb, argCount, MAX(argCount, node->function->fun_inputs)); + node->args = PAR_args(tdbb, csb, argCount, MAX(argCount, node->function(tdbb)->fun_inputs)); break; default: @@ -13072,7 +13048,11 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* } } else if (!node->function) - node->function = Function::lookup(tdbb, name, false); + { + auto* func = MetadataCache::lookupFunction(tdbb, name, CacheFlag::AUTOCREATE); + if (func) + node->function = csb->csb_resources->functions.registerResource(func); + } if (!node->function) { @@ -13081,7 +13061,7 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* } argCount = blrReader.getByte(); - node->args = PAR_args(tdbb, csb, argCount, MAX(argCount, node->function->fun_inputs)); + node->args = PAR_args(tdbb, csb, argCount, MAX(argCount, node->function(tdbb)->fun_inputs)); } if (argNames && argNames->getCount() > argCount) @@ -13094,7 +13074,7 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* fb_assert(node->function); - if (node->function->isImplemented() && !node->function->isDefined()) + if (node->function(tdbb)->isImplemented() && !node->function(tdbb)->isDefined()) { if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) { @@ -13110,7 +13090,7 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* } node->name = name; - node->isSubRoutine = node->function->isSubRoutine(); + node->isSubRoutine = node->function.isSubRoutine(); Arg::StatusVector mismatchStatus; @@ -13123,14 +13103,14 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* if (positionalArgCount) { - if (argCount > node->function->fun_inputs) + if (argCount > node->function(tdbb)->fun_inputs) mismatchStatus << Arg::Gds(isc_wronumarg); for (auto pos = 0u; pos < positionalArgCount; ++pos) { - if (pos < node->function->fun_inputs) + if (pos < node->function(tdbb)->fun_inputs) { - const auto& parameter = node->function->getInputFields()[pos]; + const auto& parameter = node->function(tdbb)->getInputFields()[pos]; if (parameter->prm_name.hasData() && argsByName.put(parameter->prm_name, *argIt)) mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << parameter->prm_name; @@ -13149,10 +13129,10 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* } } - node->args->items.resize(node->function->getInputFields().getCount()); + node->args->items.resize(node->function(tdbb)->getInputFields().getCount()); argIt = node->args->items.begin(); - for (auto& parameter : node->function->getInputFields()) + for (auto& parameter : node->function(tdbb)->getInputFields()) { NestConst* argValue; bool argExists = false; @@ -13214,11 +13194,11 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* status_exception::raise(Arg::Gds(isc_fun_param_mismatch) << name.toString() << mismatchStatus); // CVC: I will track ufds only if a function is not being dropped. - if (!node->function->isSubRoutine() && csb->collectingDependencies()) + if (!node->function.isSubRoutine() && csb->collectingDependencies()) { { // scope - CompilerScratch::Dependency dependency(obj_udf); - dependency.function = node->function; + Dependency dependency(obj_udf); + dependency.function = node->function(); csb->addDependency(dependency); } @@ -13226,9 +13206,9 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* { for (const auto& argName : *argNames) { - CompilerScratch::Dependency dependency(obj_udf); - dependency.function = node->function; - dependency.subName = &argName; + Dependency dependency(obj_udf); + dependency.function = node->function(); + dependency.subName = argName; csb->addDependency(dependency); } } @@ -13237,6 +13217,9 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* return node; } + + + string UdfCallNode::internalPrint(NodePrinter& printer) const { ValueExprNode::internalPrint(printer); @@ -13319,18 +13302,18 @@ void UdfCallNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc) desc->setNullable(true); if (!desc->isText()) - desc->dsc_ttype() = dsqlFunction->udf_sub_type; + desc->dsc_sub_type = dsqlFunction->udf_sub_type; if (desc->isText() || (desc->isBlob() && desc->getBlobSubType() == isc_blob_text)) desc->setTextType(dsqlFunction->udf_character_set_id); } -bool UdfCallNode::deterministic() const +bool UdfCallNode::deterministic(thread_db* tdbb) const { - return ExprNode::deterministic() && function->fun_deterministic; + return ExprNode::deterministic(tdbb) && function(tdbb)->fun_deterministic; } -void UdfCallNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) +void UdfCallNode::getDesc(thread_db* tdbb, CompilerScratch* /*csb*/, dsc* desc) { // Null value for the function indicates that the function was not // looked up during parsing the BLR. This is true if the function @@ -13340,7 +13323,7 @@ void UdfCallNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* de // For normal requests, function would never be null. We would have // created a valid block while parsing. if (function) - *desc = function->getOutputFields()[0]->prm_desc; + *desc = function(tdbb)->getOutputFields()[0]->prm_desc; else desc->clear(); } @@ -13349,7 +13332,13 @@ ValueExprNode* UdfCallNode::copy(thread_db* tdbb, NodeCopier& copier) const { UdfCallNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) UdfCallNode(*tdbb->getDefaultPool(), name); node->args = copier.copy(tdbb, args); - node->function = isSubRoutine ? function : Function::lookup(tdbb, name, false); + if (isSubRoutine) + node->function = function; + else + { + auto* func = MetadataCache::lookupFunction(tdbb, name, 0); + node->function = copier.csb->csb_resources->functions.registerResource(func); + } return node; } @@ -13378,37 +13367,35 @@ ValueExprNode* UdfCallNode::pass1(thread_db* tdbb, CompilerScratch* csb) { ValueExprNode::pass1(tdbb, csb); - if (!function->isSubRoutine()) + if (!function.isSubRoutine()) { if (!(csb->csb_g_flags & (csb_internal | csb_ignore_perm))) { - if (function->getName().package.isEmpty()) + if (function()->getName().package.isEmpty()) { - SLONG ssRelationId = csb->csb_view ? csb->csb_view->rel_id : 0; + SLONG ssRelationId = csb->csb_view ? csb->csb_view()->getId() : 0; if (!ssRelationId && csb->csb_parent_relation) { - fb_assert(csb->csb_parent_relation->rel_ss_definer.asBool()); - ssRelationId = csb->csb_parent_relation->rel_id; + fb_assert(csb->csb_parent_relation(tdbb)->rel_ss_definer.asBool()); + ssRelationId = csb->csb_parent_relation()->rel_id; } - CMP_post_access(tdbb, csb, function->getSecurityName(), ssRelationId, - SCL_execute, obj_functions, function->getName().identifier); + CMP_post_access(tdbb, csb, function()->getSecurityName(), ssRelationId, + SCL_execute, obj_functions, function()->getName().identifier); } else { - CMP_post_access(tdbb, csb, function->getSecurityName(), - (csb->csb_view ? csb->csb_view->rel_id : 0), - SCL_execute, obj_packages, function->getName().package); + CMP_post_access(tdbb, csb, function()->getSecurityName(), + (csb->csb_view ? csb->csb_view()->getId() : 0), + SCL_execute, obj_packages, function()->getName().package); } - ExternalAccess temp(ExternalAccess::exa_function, function->getId()); + ExternalAccess temp(ExternalAccess::exa_function, function()->getId()); FB_SIZE_T idx; if (!csb->csb_external.find(temp, idx)) csb->csb_external.insert(idx, temp); } - - CMP_post_resource(&csb->csb_resources, function, Resource::rsc_function, function->getId()); } return this; @@ -13416,7 +13403,8 @@ ValueExprNode* UdfCallNode::pass1(thread_db* tdbb, CompilerScratch* csb) ValueExprNode* UdfCallNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - if (function->fun_deterministic && !function->fun_inputs) + Function* f = function(tdbb); + if (f->fun_deterministic && !f->fun_inputs) { // Deterministic function without input arguments is expected to be // always returning the same result, so it can be marked as invariant @@ -13431,18 +13419,18 @@ ValueExprNode* UdfCallNode::pass2(thread_db* tdbb, CompilerScratch* csb) impureOffset = csb->allocImpure(); - if (function->isDefined() && !function->fun_entrypoint) + if (f->isDefined() && !f->fun_entrypoint) { - if (function->getInputFormat() && function->getInputFormat()->fmt_count) + if (f->getInputFormat() && f->getInputFormat()->fmt_count) { - fb_assert(function->getInputFormat()->fmt_length); - csb->allocImpure(FB_ALIGNMENT, function->getInputFormat()->fmt_length); + fb_assert(f->getInputFormat()->fmt_length); + csb->allocImpure(FB_ALIGNMENT, f->getInputFormat()->fmt_length); } - fb_assert(function->getOutputFormat()->fmt_count == 3); + fb_assert(f->getOutputFormat()->fmt_count == 3); - fb_assert(function->getOutputFormat()->fmt_length); - csb->allocImpure(FB_ALIGNMENT, function->getOutputFormat()->fmt_length); + fb_assert(f->getOutputFormat()->fmt_length); + csb->allocImpure(FB_ALIGNMENT, f->getOutputFormat()->fmt_length); } return this; @@ -13450,6 +13438,8 @@ ValueExprNode* UdfCallNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const { + const Function* func = function(request->getResources()); + UCHAR* impure = request->getImpure(impureOffset); Impure* impureArea = request->getImpure(impureOffset); impure_value* value = &impureArea->value; @@ -13472,16 +13462,16 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const } } - if (!function->isImplemented()) + if (!func->isImplemented()) { status_exception::raise( Arg::Gds(isc_func_pack_not_implemented) << - Arg::Str(function->getName().identifier) << Arg::Str(function->getName().package)); + Arg::Str(function()->getName().identifier) << Arg::Str(function()->getName().package)); } - else if (!function->isDefined()) + else if (!func->isDefined()) { status_exception::raise( - Arg::Gds(isc_funnotdef) << Arg::Str(function->getName().toString()) << + Arg::Gds(isc_funnotdef) << Arg::Str(function()->getName().toString()) << Arg::Gds(isc_modnotfound)); } @@ -13491,9 +13481,9 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const // Evaluate the function. - if (function->fun_entrypoint) + if (func->fun_entrypoint) { - const Parameter* const returnParam = function->getOutputFields()[0]; + const Parameter* const returnParam = func->getOutputFields()[0]; value->vlu_desc = returnParam->prm_desc; // If the return data type is any of the string types, allocate space to hold value. @@ -13527,22 +13517,21 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const FB_NEW_POOL(*tdbb->getDefaultPool()) Array(*tdbb->getDefaultPool()); } - FUN_evaluate(tdbb, function, args->items, value, *impureArea->temp); + FUN_evaluate(tdbb, func, args->items, value, *impureArea->temp); } else { - const_cast(function.getObject())->checkReload(tdbb); + func->checkReload(tdbb); Jrd::Attachment* attachment = tdbb->getAttachment(); - - const ULONG inMsgLength = function->getInputFormat() ? function->getInputFormat()->fmt_length : 0; - const ULONG outMsgLength = function->getOutputFormat()->fmt_length; + const ULONG inMsgLength = func->getInputFormat() ? func->getInputFormat()->fmt_length : 0; + const ULONG outMsgLength = func->getOutputFormat()->fmt_length; UCHAR* const inMsg = FB_ALIGN(impure + sizeof(impure_value), FB_ALIGNMENT); UCHAR* const outMsg = FB_ALIGN(inMsg + inMsgLength, FB_ALIGNMENT); - if (function->fun_inputs != 0) + if (func->fun_inputs != 0) { - const dsc* fmtDesc = function->getInputFormat()->fmt_desc.begin(); + const dsc* fmtDesc = func->getInputFormat()->fmt_desc.begin(); for (auto& source : args->items) { @@ -13572,7 +13561,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const const SavNumber savNumber = transaction->tra_save_point ? transaction->tra_save_point->getNumber() : 0; - Request* funcRequest = function->getStatement()->findRequest(tdbb); + Request* funcRequest = func->getStatement()->findRequest(tdbb); // trace function execution start TraceFuncExecute trace(tdbb, funcRequest, request, inMsg, inMsgLength); @@ -13607,12 +13596,14 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const EXE_unwind(tdbb, funcRequest); funcRequest->req_attachment = NULL; - funcRequest->req_flags &= ~(req_in_use | req_proc_fetch); + funcRequest->req_flags &= ~req_proc_fetch; funcRequest->invalidateTimeStamp(); + + funcRequest->setUnused(); throw; } - const dsc* fmtDesc = function->getOutputFormat()->fmt_desc.begin(); + const dsc* fmtDesc = func->getOutputFormat()->fmt_desc.begin(); const ULONG nullOffset = (IPTR) fmtDesc[1].dsc_address; SSHORT* const nullPtr = reinterpret_cast(outMsg + nullOffset); @@ -13635,8 +13626,10 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const EXE_unwind(tdbb, funcRequest); funcRequest->req_attachment = NULL; - funcRequest->req_flags &= ~(req_in_use | req_proc_fetch); + funcRequest->req_flags &= ~req_proc_fetch; funcRequest->invalidateTimeStamp(); + + funcRequest->setUnused(); } if (!(request->req_flags & req_null)) diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index fb79beccf57..b2231d086f0 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -799,7 +799,7 @@ class FieldNode final : public TypedNode dsqlDesc = desc; } - virtual bool deterministic() const override + virtual bool deterministic(thread_db*) const override { return true; } @@ -880,7 +880,7 @@ class GenIdNode final : public TypedNode virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); - virtual bool deterministic() const override + virtual bool deterministic(thread_db*) const override { return false; } @@ -1644,7 +1644,7 @@ class ParameterNode final : public TypedNode args; NestConst> dsqlArgNames; - NestConst function; + SubRoutine function; // NestConst ???????????????? private: dsql_udf* dsqlFunction = nullptr; @@ -2289,7 +2289,7 @@ class VariableNode final : public TypedNode stack; Firebird::string text; }; +#else // TRIVIAL_NODE_PRINTER + +// trivial NodePrinter class - to print only node name +class NodePrinter +{ +public: + NodePrinter(unsigned aIndent = 0) + { + } + +public: + void begin(const Firebird::string& s) + { + } + + void end() + { + } + + template + void print(const Firebird::string& s, const T& value) + { + } + + void append(const NodePrinter& subPrinter) + { + } + + unsigned getIndent() const + { + return 0; + } + + const Firebird::string getText() const + { + return ""; + } +}; + +#endif // TRIVIAL_NODE_PRINTER + } // namespace Jrd diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 4df8900d540..a4723f44304 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -659,7 +659,7 @@ class ExprNode : public DmlNode } // Check if expression returns deterministic result - virtual bool deterministic() const; + virtual bool deterministic(thread_db* tdbb) const; // Check if expression could return NULL or expression can turn NULL into a true/false. virtual bool possiblyUnknown() const; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index c13853ce09f..4520aca19ac 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -93,7 +93,7 @@ namespace if (!ARG.RDB$RELATION_NAME.NULL) parameter.relationName = ARG.RDB$RELATION_NAME; if (!ARG.RDB$COLLATION_ID.NULL) - parameter.collationId = ARG.RDB$COLLATION_ID; + parameter.collationId = CollId(ARG.RDB$COLLATION_ID); if (!ARG.RDB$NULL_FLAG.NULL) parameter.nullFlag = ARG.RDB$NULL_FLAG; @@ -112,9 +112,9 @@ namespace if (!FLD.RDB$CHARACTER_LENGTH.NULL) parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; if (!FLD.RDB$COLLATION_ID.NULL) - parameter.fieldCollationId = FLD.RDB$COLLATION_ID; + parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); if (!FLD.RDB$CHARACTER_SET_ID.NULL) - parameter.fieldCharSetId = FLD.RDB$CHARACTER_SET_ID; + parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); if (!FLD.RDB$FIELD_PRECISION.NULL) parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; @@ -158,7 +158,7 @@ namespace if (!PRM.RDB$RELATION_NAME.NULL) parameter.relationName = PRM.RDB$RELATION_NAME; if (!PRM.RDB$COLLATION_ID.NULL) - parameter.collationId = PRM.RDB$COLLATION_ID; + parameter.collationId = CollId(PRM.RDB$COLLATION_ID); if (!PRM.RDB$NULL_FLAG.NULL) parameter.nullFlag = PRM.RDB$NULL_FLAG; @@ -177,9 +177,9 @@ namespace if (!FLD.RDB$CHARACTER_LENGTH.NULL) parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; if (!FLD.RDB$COLLATION_ID.NULL) - parameter.fieldCollationId = FLD.RDB$COLLATION_ID; + parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); if (!FLD.RDB$CHARACTER_SET_ID.NULL) - parameter.fieldCharSetId = FLD.RDB$CHARACTER_SET_ID; + parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); if (!FLD.RDB$FIELD_PRECISION.NULL) parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; diff --git a/src/dsql/Parser.cpp b/src/dsql/Parser.cpp index c60bea02904..c57a416164e 100644 --- a/src/dsql/Parser.cpp +++ b/src/dsql/Parser.cpp @@ -42,7 +42,7 @@ using namespace Jrd; Parser::Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, USHORT aClientDialect, USHORT aDbDialect, bool aRequireSemicolon, - const TEXT* string, size_t length, SSHORT charSetId) + const TEXT* string, size_t length, CSetId charSetId) : PermanentStorage(pool), statementPool(aStatementPool), scratch(aScratch), @@ -1158,7 +1158,6 @@ int Parser::yylexAux() if (!have_decimal && (number <= MAX_SLONG)) { yylval.int32Val = (SLONG) number; - //printf ("parse.y %p %d\n", yylval.legacyStr, number); return TOK_NUMBER32BIT; } else diff --git a/src/dsql/Parser.h b/src/dsql/Parser.h index 51cb5195623..6e64942c13c 100644 --- a/src/dsql/Parser.h +++ b/src/dsql/Parser.h @@ -35,6 +35,7 @@ #include "../jrd/RecordSourceNodes.h" #include "../common/classes/TriState.h" #include "../common/classes/stack.h" +#include "../jrd/intl.h" #include "gen/parse.h" @@ -100,7 +101,7 @@ class Parser : public Firebird::PermanentStorage const TEXT* line_start; const TEXT* last_token_bk; const TEXT* line_start_bk; - SSHORT charSetId; + CSetId charSetId; SLONG lines, lines_bk; int prev_keyword; USHORT param_number; @@ -133,7 +134,7 @@ class Parser : public Firebird::PermanentStorage public: Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, USHORT aClientDialect, USHORT aDbDialect, bool aRequireSemicolon, - const TEXT* string, size_t length, SSHORT charSetId); + const TEXT* string, size_t length, CSetId charSetId); ~Parser(); public: diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index f8a518da49c..f26ddf94ba4 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -32,6 +32,7 @@ #include "../jrd/ids.h" #include "../jrd/ini.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "../jrd/Coercion.h" #include "../jrd/Function.h" #include "../jrd/optimizer/Optimizer.h" @@ -102,13 +103,13 @@ static void makeValidation(thread_db* tdbb, CompilerScratch* csb, StreamType str static StmtNode* pass1ExpandView(thread_db* tdbb, CompilerScratch* csb, StreamType orgStream, StreamType newStream, bool remap); static RelationSourceNode* pass1Update(thread_db* tdbb, CompilerScratch* csb, jrd_rel* relation, - const TrigVector* trigger, StreamType stream, StreamType updateStream, SecurityClass::flags_t priv, + const Triggers& triggers, StreamType stream, StreamType updateStream, SecurityClass::flags_t priv, jrd_rel* view, StreamType viewStream, StreamType viewUpdateStream); static void pass1Validations(thread_db* tdbb, CompilerScratch* csb, Array& validations); static ForNode* pass2FindForNode(StmtNode* node, StreamType stream); static void postTriggerAccess(CompilerScratch* csb, jrd_rel* ownerRelation, ExternalAccess::exa_act operation, jrd_rel* view); -static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs, +static void preModifyEraseTriggers(thread_db* tdbb, Triggers& triggers, StmtNode::WhichTrigger whichTrig, record_param* rpb, record_param* rec, TriggerAction op); static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, StreamType stream, CompoundStmtNode* compoundNode, const std::optional* insertOverride); @@ -218,7 +219,7 @@ DmlNode* AssignmentNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratc return node; } -void AssignmentNode::validateTarget(CompilerScratch* csb, const ValueExprNode* target) +void AssignmentNode::validateTarget(thread_db* tdbb, CompilerScratch* csb, const ValueExprNode* target) { const FieldNode* fieldNode; @@ -246,13 +247,13 @@ void AssignmentNode::validateTarget(CompilerScratch* csb, const ValueExprNode* t if (error) { - jrd_fld* field = MET_get_field(tail->csb_relation, fieldNode->fieldId); + jrd_fld* field = MET_get_field(tail->csb_relation(tdbb), fieldNode->fieldId); string fieldName(field ? field->fld_name.c_str() : ""); if (field && tail->csb_relation) - fieldName = string(tail->csb_relation->rel_name.c_str()) + "." + fieldName; + fieldName = string(tail->csb_relation()->rel_name.c_str()) + "." + fieldName; - ERR_post(Arg::Gds(isc_read_only_field) << fieldName.c_str()); + ERR_post(Arg::Gds(isc_read_only_field) << fieldName); } } else if (!(nodeIs(target) || nodeIs(target) || nodeIs(target))) @@ -329,7 +330,7 @@ AssignmentNode* AssignmentNode::pass1(thread_db* tdbb, CompilerScratch* csb) if ((fieldNode = nodeAs(sub))) { stream = fieldNode->fieldStream; - jrd_fld* field = MET_get_field(csb->csb_rpt[stream].csb_relation, fieldNode->fieldId); + jrd_fld* field = MET_get_field(csb->csb_rpt[stream].csb_relation(tdbb), fieldNode->fieldId); if (field) missing2 = field->fld_missing_value; @@ -341,7 +342,7 @@ AssignmentNode* AssignmentNode::pass1(thread_db* tdbb, CompilerScratch* csb) { stream = fieldNode->fieldStream; tail = &csb->csb_rpt[stream]; - jrd_fld* field = MET_get_field(tail->csb_relation, fieldNode->fieldId); + jrd_fld* field = MET_get_field(tail->csb_relation(tdbb), fieldNode->fieldId); if (field && field->fld_missing_value) missing = field->fld_missing_value; @@ -412,7 +413,7 @@ AssignmentNode* AssignmentNode::pass2(thread_db* tdbb, CompilerScratch* csb) if (pushedRse) csb->csb_current_nodes.pop(); - validateTarget(csb, asgnTo); + validateTarget(tdbb, csb, asgnTo); return this; } @@ -1525,9 +1526,10 @@ DmlNode* DeclareSubFuncNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerSc DeclareSubFuncNode* node = FB_NEW_POOL(pool) DeclareSubFuncNode(pool, name); - Function* subFunc = node->routine = FB_NEW_POOL(pool) Function(pool); - subFunc->setName(QualifiedName(name)); - subFunc->setSubRoutine(true); + Function* subFunc = FB_NEW_POOL(pool) Function(pool); + node->routine = subFunc; + getPermanent(subFunc)->setName(QualifiedName(name)); + getPermanent(subFunc)->setSubRoutine(true); subFunc->setImplemented(true); { // scope @@ -1846,8 +1848,8 @@ DmlNode* DeclareSubProcNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerSc DeclareSubProcNode* node = FB_NEW_POOL(pool) DeclareSubProcNode(pool, name); jrd_prc* subProc = node->routine = FB_NEW_POOL(pool) jrd_prc(pool); - subProc->setName(QualifiedName(name)); - subProc->setSubRoutine(true); + getPermanent(subProc)->setName(QualifiedName(name)); + getPermanent(subProc)->setSubRoutine(true); subProc->setImplemented(true); { // scope @@ -2182,8 +2184,8 @@ DmlNode* DeclareVariableNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerS if (csb->collectingDependencies() && itemInfo.explicitCollation) { - CompilerScratch::Dependency dependency(obj_collation); - dependency.number = INTL_TEXT_TYPE(node->varDesc); + Dependency dependency(obj_collation); + dependency.number = node->varDesc.getTextType(); csb->addDependency(dependency); } @@ -2494,7 +2496,7 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod CompilerScratch::csb_repeat* const tail = &csb->csb_rpt[stream]; tail->csb_flags |= csb_erase; - jrd_rel* const relation = tail->csb_relation; + jrd_rel* const relation = tail->csb_relation(tdbb); //// TODO: LocalTableSourceNode if (!relation) @@ -2504,11 +2506,11 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod Arg::Gds(isc_random) << "erase local_table"); } - view = relation->rel_view_rse ? relation : view; + view = relation->isView() ? relation : view; if (!parent) { - parent = tail->csb_view; + parent = tail->csb_view(tdbb); parentStream = tail->csb_view_stream; } @@ -2523,23 +2525,23 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod if (parent) priv |= SCL_select; - RefPtr trigger(relation->rel_pre_erase ? - relation->rel_pre_erase : relation->rel_post_erase); + Triggers& triggers(relation->rel_triggers[TRIGGER_PRE_ERASE] ? + relation->rel_triggers[TRIGGER_PRE_ERASE] : relation->rel_triggers[TRIGGER_POST_ERASE]); // If we have a view with triggers, let's expand it. - if (relation->rel_view_rse && trigger) + if (relation->isView() && triggers) { newStream = csb->nextStream(); node->stream = newStream; - CMP_csb_element(csb, newStream)->csb_relation = relation; + CMP_csb_element(csb, newStream)->csb_relation = tail->csb_relation; node->statement = pass1ExpandView(tdbb, csb, stream, newStream, false); } // Get the source relation, either a table or yet another view. - RelationSourceNode* source = pass1Update(tdbb, csb, relation, trigger, stream, newStream, + RelationSourceNode* source = pass1Update(tdbb, csb, relation, triggers, stream, newStream, priv, parent, parentStream, parentStream); if (!source) @@ -2552,7 +2554,7 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod StreamType* map = tail->csb_map; - if (trigger) + if (triggers) { // ASF: This code is responsible to make view's WITH CHECK OPTION to work as constraints. // I don't see how it could run for delete statements under normal conditions. @@ -2587,21 +2589,21 @@ EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb) doPass2(tdbb, csb, returningStatement.getAddress(), this); doPass2(tdbb, csb, subStatement.getAddress(), this); - const jrd_rel* const relation = csb->csb_rpt[stream].csb_relation; + const auto* const relation = csb->csb_rpt[stream].csb_relation(); if (relation) { // Deletion from MON$ tables uses the attachment ID and the system flag // under the hood, so these field should be added as implicitly referenced - if (relation->rel_id == rel_mon_attachments) + if (relation->getId() == rel_mon_attachments) { SBM_SET(tdbb->getDefaultPool(), &csb->csb_rpt[stream].csb_fields, f_mon_att_id); // MON$ATTACHMENT_ID SBM_SET(tdbb->getDefaultPool(), &csb->csb_rpt[stream].csb_fields, f_mon_att_sys_flag); // MON$SYSTEM_FLAG } - else if (relation->rel_id == rel_mon_statements) + else if (relation->getId() == rel_mon_statements) { SBM_SET(tdbb->getDefaultPool(), &csb->csb_rpt[stream].csb_fields, f_mon_stmt_att_id); // MON$ATTACHMENT_ID @@ -2679,7 +2681,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger if (!statement) break; - const Format* format = MET_current(tdbb, rpb->rpb_relation); + const Format* format = rpb->rpb_relation->currentFormat(); Record* record = VIO_record(tdbb, rpb, format, tdbb->getDefaultPool()); rpb->rpb_address = record->getData(); @@ -2703,12 +2705,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger } request->req_operation = Request::req_return; - RLCK_reserve_relation(tdbb, transaction, relation, true); + RLCK_reserve_relation(tdbb, transaction, relation->rel_perm, true); if (rpb->rpb_runtime_flags & RPB_just_deleted) return parentStmt; - if (rpb->rpb_number.isBof() || (!relation->rel_view_rse && !rpb->rpb_number.isValid())) + if (rpb->rpb_number.isBof() || (!relation->isView() && !rpb->rpb_number.isValid())) ERR_post(Arg::Gds(isc_no_cur_rec)); if (forNode && forNode->isWriteLockMode(request)) @@ -2738,16 +2740,16 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger // transaction and delete should be skipped. const bool skipLocked = rpb->rpb_stream_flags & RPB_s_skipLocked; CondSavepointAndMarker spPreTriggers(tdbb, transaction, - skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_pre_erase); + skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_triggers[TRIGGER_PRE_ERASE]); // Handle pre-operation trigger. - preModifyEraseTriggers(tdbb, &relation->rel_pre_erase, whichTrig, rpb, NULL, TRIGGER_DELETE); + preModifyEraseTriggers(tdbb, relation->rel_triggers[TRIGGER_PRE_ERASE], whichTrig, rpb, NULL, TRIGGER_DELETE); - if (relation->rel_file) - EXT_erase(rpb, transaction); + if (auto* extFile = relation->getExtFile()) + extFile->erase(rpb, transaction); else if (relation->isVirtual()) VirtualTable::erase(tdbb, rpb); - else if (!relation->rel_view_rse) + else if (!relation->isView()) { // VIO_erase returns false if: // a) there is an update conflict in Read Consistency transaction. @@ -2781,9 +2783,9 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger spPreTriggers.release(); // Handle post operation trigger. - if ((relation->rel_post_erase || relation->isSystem()) && whichTrig != PRE_TRIG) + if ((relation->rel_triggers[TRIGGER_POST_ERASE] || relation->isSystem()) && whichTrig != PRE_TRIG) { - EXE_execute_triggers(tdbb, &relation->rel_post_erase, rpb, NULL, TRIGGER_DELETE, POST_TRIG); + EXE_execute_triggers(tdbb, relation->rel_triggers[TRIGGER_POST_ERASE], rpb, NULL, TRIGGER_DELETE, POST_TRIG); } if (forNode && (marks & StmtNode::MARK_MERGE)) @@ -2793,16 +2795,16 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger // This is required for cascading referential integrity, which can be implemented as // post_erase triggers. - if (!relation->rel_view_rse) + if (!relation->isView()) { - if (!relation->rel_file && !relation->isVirtual()) + if (!relation->getExtFile() && !relation->isVirtual()) IDX_erase(tdbb, rpb, transaction); // Mark this rpb as already deleted to skip the subsequent attempts rpb->rpb_runtime_flags |= RPB_just_deleted; } - if (!relation->rel_view_rse || (whichTrig == ALL_TRIGS || whichTrig == POST_TRIG)) + if (!relation->isView() || (whichTrig == ALL_TRIGS || whichTrig == POST_TRIG)) { if (!(marks & MARK_AVOID_COUNTERS)) { @@ -2868,7 +2870,7 @@ DmlNode* ErrorHandlerNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScra if (csb->collectingDependencies()) { - CompilerScratch::Dependency dependency(obj_exception); + Dependency dependency(obj_exception); dependency.number = item.code; csb->addDependency(dependency); } @@ -3063,7 +3065,11 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr } } else if (!node->procedure) - node->procedure = MET_lookup_procedure(tdbb, name, false); + { + auto* proc = MetadataCache::lookup_procedure(tdbb, name, CacheFlag::AUTOCREATE); + if (proc) + node->procedure = csb->csb_resources->procedures.registerResource(getPermanent(proc)); + } break; } @@ -3093,7 +3099,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_args"); inArgCount = blrReader.getWord(); node->inputSources = PAR_args(tdbb, csb, inArgCount, - MAX(inArgCount, node->procedure->getInputFields().getCount())); + MAX(inArgCount, node->procedure(tdbb)->getInputFields().getCount())); break; case blr_invsel_procedure_out_arg_names: @@ -3164,7 +3170,10 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr case blr_exec_pid: { const USHORT pid = blrReader.getWord(); - if (!(node->procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0))) + auto* proc = MetadataCache::lookup_procedure_id(tdbb, pid, CacheFlag::AUTOCREATE); + if (proc) + node->procedure = csb->csb_resources->procedures.registerResource(getPermanent(proc)); + else name.identifier.printf("id %d", pid); break; } @@ -3184,7 +3193,11 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr } } else - node->procedure = MET_lookup_procedure(tdbb, name, false); + { + auto* proc = MetadataCache::lookup_procedure(tdbb, name, CacheFlag::AUTOCREATE); + if (proc) + node->procedure = csb->csb_resources->procedures.registerResource(getPermanent(proc)); + } break; } @@ -3205,7 +3218,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr { node->outputTargets = FB_NEW_POOL(pool) ValueListNode(pool); - const auto prcInCount = node->procedure->getInputFields().getCount(); + const auto prcInCount = node->procedure(tdbb)->getInputFields().getCount(); const auto positionalArgCount = inOutArgs->items.getCount() - (inOutArgNames ? inOutArgNames->getCount() : 0); @@ -3216,7 +3229,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr outArgNames = FB_NEW_POOL(pool) ObjectsArray(pool); SortedObjectsArray outFields; - for (const auto field : node->procedure->getOutputFields()) + for (const auto field : node->procedure(tdbb)->getOutputFields()) outFields.add(field->prm_name); unsigned pos = 0; @@ -3260,8 +3273,8 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr inArgCount = node->inputSources->items.getCount(); outArgCount = node->outputTargets->items.getCount(); - node->inputSources->ensureCapacity(node->procedure->getInputFields().getCount()); - node->outputTargets->ensureCapacity(node->procedure->getOutputFields().getCount()); + node->inputSources->ensureCapacity(node->procedure(tdbb)->getInputFields().getCount()); + node->outputTargets->ensureCapacity(node->procedure(tdbb)->getOutputFields().getCount()); } if (inArgNames && inArgNames->getCount() > node->inputSources->items.getCount()) @@ -3295,7 +3308,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr "blr_invsel_procedure_out_arg_names count cannot be greater than blr_invsel_procedure_out_args"); } - if (node->procedure->isImplemented() && !node->procedure->isDefined()) + if (node->procedure(tdbb)->isImplemented() && !node->procedure(tdbb)->isDefined()) { if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) { @@ -3312,14 +3325,14 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr } } - node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount()); + node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure(tdbb)->getInputFields().getCount()); Arg::StatusVector mismatchStatus; CMP_procedure_arguments( tdbb, csb, - node->procedure, + node->procedure(tdbb), true, inArgCount, inArgNames, @@ -3331,7 +3344,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr CMP_procedure_arguments( tdbb, csb, - node->procedure, + node->procedure(tdbb), false, outArgCount, outArgNames, @@ -3343,14 +3356,14 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr if (mismatchStatus.hasData()) { status_exception::raise(Arg::Gds(isc_prcmismat) << - node->procedure->getName().toString() << mismatchStatus); + node->procedure()->getName().toString() << mismatchStatus); } - if (csb->collectingDependencies() && !node->procedure->isSubRoutine()) + if (csb->collectingDependencies() && !node->procedure.isSubRoutine()) { { // scope - CompilerScratch::Dependency dependency(obj_procedure); - dependency.procedure = node->procedure; + Dependency dependency(obj_procedure); + dependency.procedure = node->procedure(); csb->addDependency(dependency); } @@ -3358,9 +3371,9 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr { for (const auto& argName : *inArgNames) { - CompilerScratch::Dependency dependency(obj_procedure); - dependency.procedure = node->procedure; - dependency.subName = &argName; + Dependency dependency(obj_procedure); + dependency.procedure = node->procedure(); + dependency.subName = argName; csb->addDependency(dependency); } } @@ -3369,9 +3382,9 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr { for (const auto& argName : *outArgNames) { - CompilerScratch::Dependency dependency(obj_procedure); - dependency.procedure = node->procedure; - dependency.subName = &argName; + Dependency dependency(obj_procedure); + dependency.procedure = node->procedure(); + dependency.subName = argName; csb->addDependency(dependency); } } @@ -3830,11 +3843,10 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch) ExecProcedureNode* ExecProcedureNode::pass1(thread_db* tdbb, CompilerScratch* csb) { - if (!procedure->isSubRoutine()) + if (!procedure.isSubRoutine()) { // Post access to procedure. - CMP_post_procedure_access(tdbb, csb, procedure); - CMP_post_resource(&csb->csb_resources, procedure, Resource::rsc_procedure, procedure->getId()); + CMP_post_procedure_access(tdbb, csb, procedure()); } doPass1(tdbb, csb, inputSources.getAddress()); @@ -3859,7 +3871,7 @@ ExecProcedureNode* ExecProcedureNode::pass2(thread_db* tdbb, CompilerScratch* cs if (outputTargets) { for (const auto target : outputTargets->items) - AssignmentNode::validateTarget(csb, target); + AssignmentNode::validateTarget(tdbb, csb, target); } return this; @@ -3880,22 +3892,24 @@ const StmtNode* ExecProcedureNode::execute(thread_db* tdbb, Request* request, Ex // End by assigning the output parameters. void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) const { - if (!procedure->isImplemented()) + const jrd_prc* proc = procedure(request->getResources()); + + if (!proc->isImplemented()) { status_exception::raise( Arg::Gds(isc_proc_pack_not_implemented) << - Arg::Str(procedure->getName().identifier) << Arg::Str(procedure->getName().package)); + Arg::Str(proc->getName().identifier) << Arg::Str(proc->getName().package)); } - else if (!procedure->isDefined()) + else if (!proc->isDefined()) { status_exception::raise( - Arg::Gds(isc_prcnotdef) << Arg::Str(procedure->getName().toString()) << + Arg::Gds(isc_prcnotdef) << Arg::Str(proc->getName().toString()) << Arg::Gds(isc_modnotfound)); } - const_cast(procedure.getObject())->checkReload(tdbb); + proc->checkReload(tdbb); - UserId* invoker = procedure->invoker ? procedure->invoker : tdbb->getAttachment()->att_ss_user; + UserId* invoker = proc->invoker ? proc->invoker : tdbb->getAttachment()->att_ss_user; AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); ULONG inMsgLength = 0; @@ -3920,7 +3934,7 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) cons } else { - format = procedure->getOutputFormat(); + format = proc->getOutputFormat(); outMsgLength = format->fmt_length; outMsg = tempBuffer.getBuffer(outMsgLength + FB_DOUBLE_ALIGN - 1); outMsg = FB_ALIGN(outMsg, FB_DOUBLE_ALIGN); @@ -3941,7 +3955,7 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) cons const SavNumber savNumber = transaction->tra_save_point ? transaction->tra_save_point->getNumber() : 0; - Request* procRequest = procedure->getStatement()->findRequest(tdbb); + Request* procRequest = proc->getStatement()->findRequest(tdbb); // trace procedure execution start TraceProcExecute trace(tdbb, procRequest, request, inputTargets); @@ -3984,7 +3998,9 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) cons EXE_unwind(tdbb, procRequest); procRequest->req_attachment = NULL; - procRequest->req_flags &= ~(req_in_use | req_proc_fetch); + procRequest->req_flags &= ~req_proc_fetch; + + procRequest->setUnused(); throw; } @@ -3992,8 +4008,7 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) cons trace.finish(false, ITracePlugin::RESULT_SUCCESS); EXE_unwind(tdbb, procRequest); - procRequest->req_attachment = NULL; - procRequest->req_flags &= ~(req_in_use | req_proc_fetch); + procRequest->req_flags &= ~req_proc_fetch; if (outputSources) { @@ -4004,6 +4019,8 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) cons for (; sourcePtr != sourceEnd; ++sourcePtr, ++targetPtr) EXE_assignment(tdbb, *sourcePtr, *targetPtr); } + + procRequest->setUnused(); } @@ -4411,7 +4428,7 @@ ExecStatementNode* ExecStatementNode::pass2(thread_db* tdbb, CompilerScratch* cs i != outputs->items.end(); ++i) { - AssignmentNode::validateTarget(csb, *i); + AssignmentNode::validateTarget(tdbb, csb, *i); } } @@ -4513,7 +4530,7 @@ void ExecStatementNode::getString(thread_db* tdbb, Request* request, const Value if (dsc && !(request->req_flags & req_null)) { const Jrd::Attachment* att = tdbb->getAttachment(); - len = MOV_make_string2(tdbb, dsc, (useAttCS ? att->att_charset : dsc->getTextType()), + len = MOV_make_string2(tdbb, dsc, (useAttCS ? TTypeId(att->att_charset) : dsc->getTextType()), &p, buffer, false); } @@ -5217,7 +5234,7 @@ DmlNode* ExceptionNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch if (csb->collectingDependencies()) { - CompilerScratch::Dependency dependency(obj_exception); + Dependency dependency(obj_exception); dependency.number = item->code; csb->addDependency(dependency); } @@ -5875,8 +5892,8 @@ void ForNode::setWriteLockMode(Request* request) const void ForNode::checkRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const { - jrd_rel* relation = rpb->rpb_relation; - if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->rel_file || relation->rel_view_rse) + auto* relation = rpb->rpb_relation->rel_perm; + if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->isView() || relation->getExtFile()) return; ImpureMerge* impure = request->getImpure(impureOffset); @@ -5890,8 +5907,8 @@ void ForNode::checkRecordUpdated(thread_db* tdbb, Request* request, record_param void ForNode::setRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const { - jrd_rel* relation = rpb->rpb_relation; - if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->rel_file || relation->rel_view_rse) + auto* relation = rpb->rpb_relation->rel_perm; + if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->isView() || relation->getExtFile()) return; ImpureMerge* impure = request->getImpure(impureOffset); @@ -7563,7 +7580,7 @@ void ModifyNode::pass1Modify(thread_db* tdbb, CompilerScratch* csb, ModifyNode* CompilerScratch::csb_repeat* const new_tail = &csb->csb_rpt[newStream]; new_tail->csb_flags |= csb_modify; - jrd_rel* const relation = tail->csb_relation; + jrd_rel* const relation = tail->csb_relation(tdbb); //// TODO: LocalTableSourceNode if (!relation) @@ -7573,12 +7590,12 @@ void ModifyNode::pass1Modify(thread_db* tdbb, CompilerScratch* csb, ModifyNode* Arg::Gds(isc_random) << "modify local_table"); } - view = relation->rel_view_rse ? relation : view; + view = relation->isView() ? relation : view; if (!parent) { fb_assert(tail->csb_view == new_tail->csb_view); - parent = new_tail->csb_view; + parent = new_tail->csb_view(tdbb); parentStream = tail->csb_view_stream; parentNewStream = new_tail->csb_view_stream; } @@ -7594,23 +7611,23 @@ void ModifyNode::pass1Modify(thread_db* tdbb, CompilerScratch* csb, ModifyNode* if (parent) priv |= SCL_select; - RefPtr trigger(relation->rel_pre_modify ? - relation->rel_pre_modify : relation->rel_post_modify); + Triggers& triggers = relation->rel_triggers[TRIGGER_PRE_MODIFY] ? + relation->rel_triggers[TRIGGER_PRE_MODIFY] : relation->rel_triggers[TRIGGER_POST_MODIFY]; // If we have a view with triggers, let's expand it. - if (relation->rel_view_rse && trigger) + if (relation->isView() && triggers) node->mapView = pass1ExpandView(tdbb, csb, stream, newStream, false); // Get the source relation, either a table or yet another view. - RelationSourceNode* source = pass1Update(tdbb, csb, relation, trigger, stream, newStream, + RelationSourceNode* source = pass1Update(tdbb, csb, relation, triggers, stream, newStream, priv, parent, parentStream, parentNewStream); if (!source) { // No source means we're done. - if (!relation->rel_view_rse) + if (!relation->isView()) { // Apply validation constraints. makeValidation(tdbb, csb, newStream, node->validations); @@ -7636,7 +7653,7 @@ void ModifyNode::pass1Modify(thread_db* tdbb, CompilerScratch* csb, ModifyNode* NodeCopier copier(csb->csb_pool, csb, map); source = source->copy(tdbb, copier); - if (trigger) + if (triggers) { // ASF: This code is responsible to make view's WITH CHECK OPTION to work as constraints. @@ -7808,19 +7825,19 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg // transaction and update should be skipped. const bool skipLocked = orgRpb->rpb_stream_flags & RPB_s_skipLocked; CondSavepointAndMarker spPreTriggers(tdbb, transaction, - skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_pre_modify); + skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_triggers[TRIGGER_PRE_MODIFY]); - preModifyEraseTriggers(tdbb, &relation->rel_pre_modify, whichTrig, orgRpb, newRpb, + preModifyEraseTriggers(tdbb, relation->rel_triggers[TRIGGER_PRE_MODIFY], whichTrig, orgRpb, newRpb, TRIGGER_UPDATE); if (validations.hasData()) validateExpressions(tdbb, validations); - if (relation->rel_file) - EXT_modify(orgRpb, newRpb, transaction); + if (auto* extFile = relation->getExtFile()) + extFile->modify(orgRpb, newRpb, transaction); else if (relation->isVirtual()) VirtualTable::modify(tdbb, orgRpb, newRpb); - else if (!relation->rel_view_rse) + else if (!relation->isView()) { // VIO_modify returns false if: // a) there is an update conflict in Read Consistency transaction. @@ -7851,9 +7868,9 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg newRpb->rpb_number = orgRpb->rpb_number; newRpb->rpb_number.setValid(true); - if ((relation->rel_post_modify || relation->isSystem()) && whichTrig != PRE_TRIG) + if ((relation->rel_triggers[TRIGGER_POST_MODIFY] || relation->isSystem()) && whichTrig != PRE_TRIG) { - EXE_execute_triggers(tdbb, &relation->rel_post_modify, orgRpb, newRpb, + EXE_execute_triggers(tdbb, relation->rel_triggers[TRIGGER_POST_MODIFY], orgRpb, newRpb, TRIGGER_UPDATE, POST_TRIG); } @@ -7864,10 +7881,10 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg // have fired. This is required for cascading referential integrity, // which can be implemented as post_erase triggers. - if (!relation->rel_file && !relation->rel_view_rse && !relation->isVirtual()) + if (!relation->getExtFile() && !relation->isView() && !relation->isVirtual()) IDX_modify_check_constraints(tdbb, orgRpb, newRpb, transaction); - if (!relation->rel_view_rse || + if (!relation->isView() || (!subMod && (whichTrig == ALL_TRIGS || whichTrig == POST_TRIG))) { if (!(marks & MARK_AVOID_COUNTERS)) @@ -7897,7 +7914,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg } impure->sta_state = 0; - RLCK_reserve_relation(tdbb, transaction, relation, true); + RLCK_reserve_relation(tdbb, transaction, relation->rel_perm, true); if (orgRpb->rpb_runtime_flags & RPB_just_deleted) { @@ -7905,7 +7922,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg return parentStmt; } - if (orgRpb->rpb_number.isBof() || (!relation->rel_view_rse && !orgRpb->rpb_number.isValid())) + if (orgRpb->rpb_number.isBof() || (!relation->isView() && !orgRpb->rpb_number.isValid())) ERR_post(Arg::Gds(isc_no_cur_rec)); if (forNode && (marks & StmtNode::MARK_MERGE)) @@ -7932,7 +7949,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg // exists for the stream and is big enough, and copying fields from the // original record to the new record. - const Format* const newFormat = MET_current(tdbb, newRpb->rpb_relation); + const Format* const newFormat = newRpb->rpb_relation->currentFormat(); Record* newRecord = VIO_record(tdbb, newRpb, newFormat, tdbb->getDefaultPool()); newRpb->rpb_address = newRecord->getData(); newRpb->rpb_length = newFormat->fmt_length; @@ -8608,19 +8625,29 @@ bool StoreNode::pass1Store(thread_db* tdbb, CompilerScratch* csb, StoreNode* nod CompilerScratch::csb_repeat* const tail = &csb->csb_rpt[stream]; tail->csb_flags |= csb_store; - jrd_rel* const relation = tail->csb_relation; - view = relation->rel_view_rse ? relation : view; + jrd_rel* const relation = tail->csb_relation(tdbb); + if (!relation) + { + MetaName relName; + if (tail->csb_relation) + relName = tail->csb_relation()->c_name(); + else + relName = "*** unknown ***"; + + ERR_post(Arg::Gds(isc_relnotdef) << relName); + } + view = relation->isView() ? relation : view; if (!parent) { - parent = tail->csb_view; + parent = tail->csb_view(tdbb); parentStream = tail->csb_view_stream; } postTriggerAccess(csb, relation, ExternalAccess::exa_insert, view); - RefPtr trigger(relation->rel_pre_store ? - relation->rel_pre_store : relation->rel_post_store); + Triggers& triggers = relation->rel_triggers[TRIGGER_PRE_STORE] ? + relation->rel_triggers[TRIGGER_PRE_STORE] : relation->rel_triggers[TRIGGER_POST_STORE]; // Check out insert. If this is an insert thru a view, verify the view by checking for read // access on the base table. If field-level select privileges are implemented, this needs @@ -8633,14 +8660,12 @@ bool StoreNode::pass1Store(thread_db* tdbb, CompilerScratch* csb, StoreNode* nod // Get the source relation, either a table or yet another view. - relSource = pass1Update(tdbb, csb, relation, trigger, stream, stream, + relSource = pass1Update(tdbb, csb, relation, triggers, stream, stream, priv, parent, parentStream, parentStream); if (!relSource) { - CMP_post_resource(&csb->csb_resources, relation, Resource::rsc_relation, relation->rel_id); - - if (!relation->rel_view_rse) + if (!relation->isView()) { // Apply validation constraints. makeValidation(tdbb, csb, stream, node->validations); @@ -8655,12 +8680,10 @@ bool StoreNode::pass1Store(thread_db* tdbb, CompilerScratch* csb, StoreNode* nod StreamType* map = CMP_alloc_map(tdbb, csb, stream); NodeCopier copier(csb->csb_pool, csb, map); - if (trigger) + if (triggers) { // ASF: This code is responsible to make view's WITH CHECK OPTION to work as constraints. - CMP_post_resource(&csb->csb_resources, relation, Resource::rsc_relation, relation->rel_id); - // Set up the new target stream. relSource = relSource->copy(tdbb, copier); @@ -8690,7 +8713,7 @@ bool StoreNode::pass1Store(thread_db* tdbb, CompilerScratch* csb, StoreNode* nod void StoreNode::makeDefaults(thread_db* tdbb, CompilerScratch* csb) { const StreamType stream = target->getStream(); - jrd_rel* relation = csb->csb_rpt[stream].csb_relation; + jrd_rel* relation = csb->csb_rpt[stream].csb_relation(tdbb); vec* vector = relation->rel_fields; if (!vector) @@ -8866,7 +8889,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger impure->sta_state = 0; if (relation) - RLCK_reserve_relation(tdbb, transaction, relation, true); + RLCK_reserve_relation(tdbb, transaction, relation->rel_perm, true); break; case Request::req_return: @@ -8874,9 +8897,9 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger { SavepointChangeMarker scMarker(transaction); - if (relation && (relation->rel_pre_store || relation->isSystem()) && whichTrig != POST_TRIG) + if (relation && (relation->rel_triggers[TRIGGER_PRE_STORE] || relation->isSystem()) && whichTrig != POST_TRIG) { - EXE_execute_triggers(tdbb, &relation->rel_pre_store, NULL, rpb, + EXE_execute_triggers(tdbb, relation->rel_triggers[TRIGGER_PRE_STORE], NULL, rpb, TRIGGER_INSERT, PRE_TRIG); } @@ -8894,11 +8917,11 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger if (localTableSource) localTableImpure->recordBuffer->store(rpb->rpb_record); - else if (relation->rel_file) - EXT_store(tdbb, rpb); + else if (auto* extFile = relation->getExtFile()) + extFile->store(tdbb, rpb); else if (relation->isVirtual()) VirtualTable::store(tdbb, rpb); - else if (!relation->rel_view_rse) + else if (!relation->isView()) { VIO_store(tdbb, rpb, transaction); IDX_store(tdbb, rpb, transaction); @@ -8907,14 +8930,15 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger rpb->rpb_number.setValid(true); - if (relation && (relation->rel_post_store || relation->isSystem()) && whichTrig != PRE_TRIG) + if (relation && (relation->rel_triggers[TRIGGER_POST_STORE] || relation->isSystem()) && + whichTrig != PRE_TRIG) { - EXE_execute_triggers(tdbb, &relation->rel_post_store, NULL, rpb, + EXE_execute_triggers(tdbb, relation->rel_triggers[TRIGGER_POST_STORE], NULL, rpb, TRIGGER_INSERT, POST_TRIG); } if (!relation || - !relation->rel_view_rse || + !relation->isView() || (!subStore && (whichTrig == ALL_TRIGS || whichTrig == POST_TRIG))) { if (!(marks & MARK_AVOID_COUNTERS)) @@ -8944,7 +8968,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger const Format* format = localTableSource ? request->getStatement()->localTables[localTableSource->tableNumber]->format : - MET_current(tdbb, relation); + relation->currentFormat(); Record* record; @@ -9060,7 +9084,7 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) parameter->par_context = context; parameter->par_desc.dsc_dtype = dtype_text; - parameter->par_desc.dsc_ttype() = ttype_binary; + parameter->par_desc.setTextType(ttype_binary); parameter->par_desc.dsc_length = relation->rel_dbkey_length; // Set up record version. @@ -9069,7 +9093,7 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) parameter->par_context = context; parameter->par_desc.dsc_dtype = dtype_text; - parameter->par_desc.dsc_ttype() = ttype_binary; + parameter->par_desc.setTextType(ttype_binary); parameter->par_desc.dsc_length = sizeof(SINT64); } } @@ -11404,7 +11428,7 @@ static void makeValidation(thread_db* tdbb, CompilerScratch* csb, StreamType str DEV_BLKCHK(csb, type_csb); - jrd_rel* relation = csb->csb_rpt[stream].csb_relation; + jrd_rel* relation = csb->csb_rpt[stream].csb_relation(tdbb); if (!relation) //// TODO: LocalTableSourceNode return; @@ -11470,7 +11494,7 @@ static StmtNode* pass1ExpandView(thread_db* tdbb, CompilerScratch* csb, StreamTy DEV_BLKCHK(csb, type_csb); StmtNodeStack stack; - jrd_rel* relation = csb->csb_rpt[orgStream].csb_relation; + jrd_rel* relation = csb->csb_rpt[orgStream].csb_relation(tdbb); vec* fields = relation->rel_fields; dsc desc; @@ -11513,7 +11537,7 @@ static StmtNode* pass1ExpandView(thread_db* tdbb, CompilerScratch* csb, StreamTy // If it's a view update, make sure the view is updatable, and return the view source for redirection. // If it's a simple relation, return NULL. static RelationSourceNode* pass1Update(thread_db* tdbb, CompilerScratch* csb, jrd_rel* relation, - const TrigVector* trigger, StreamType stream, StreamType updateStream, SecurityClass::flags_t priv, + const Triggers& triggers, StreamType stream, StreamType updateStream, SecurityClass::flags_t priv, jrd_rel* view, StreamType viewStream, StreamType viewUpdateStream) { SET_TDBB(tdbb); @@ -11524,21 +11548,24 @@ static RelationSourceNode* pass1Update(thread_db* tdbb, CompilerScratch* csb, jr // unless this is an internal request, check access permission - CMP_post_access(tdbb, csb, relation->rel_security_name, (view ? view->rel_id : 0), - priv, obj_relations, relation->rel_name); + CMP_post_access(tdbb, csb, relation->getSecurityName(), (view ? view->getId() : 0), + priv, obj_relations, relation->getName()); // ensure that the view is set for the input streams, // so that access to views can be checked at the field level - fb_assert(viewStream <= MAX_STREAMS); - CMP_csb_element(csb, stream)->csb_view = view; - CMP_csb_element(csb, stream)->csb_view_stream = viewStream; - - if (stream != updateStream) + if (view) { - fb_assert(viewUpdateStream <= MAX_STREAMS); - CMP_csb_element(csb, updateStream)->csb_view = view; - CMP_csb_element(csb, updateStream)->csb_view_stream = viewUpdateStream; + fb_assert(viewStream <= MAX_STREAMS); + CMP_csb_element(csb, stream)->csb_view = csb->csb_resources->relations.registerResource(view->rel_perm); + CMP_csb_element(csb, stream)->csb_view_stream = viewStream; + + if (stream != updateStream) + { + fb_assert(viewUpdateStream <= MAX_STREAMS); + CMP_csb_element(csb, updateStream)->csb_view = csb->csb_resources->relations.registerResource(view->rel_perm); + CMP_csb_element(csb, updateStream)->csb_view_stream = viewUpdateStream; + } } // if we're not a view, everything's cool @@ -11549,24 +11576,10 @@ static RelationSourceNode* pass1Update(thread_db* tdbb, CompilerScratch* csb, jr // a view with triggers is always updatable - if (trigger) + if (triggers) { - bool userTriggers = false; - - for (FB_SIZE_T i = 0; i < trigger->getCount(); i++) - { - if (!(*trigger)[i].sysTrigger) - { - userTriggers = true; - break; - } - } - - if (userTriggers) - { - csb->csb_rpt[updateStream].csb_flags |= csb_view_update; - return NULL; - } + csb->csb_rpt[updateStream].csb_flags |= csb_view_update; + return NULL; } // we've got a view without triggers, let's check whether it's updateable @@ -11574,7 +11587,7 @@ static RelationSourceNode* pass1Update(thread_db* tdbb, CompilerScratch* csb, jr if (rse->rse_relations.getCount() != 1 || rse->rse_projection || rse->rse_sorted || rse->rse_relations[0]->getType() != RelationSourceNode::TYPE) { - ERR_post(Arg::Gds(isc_read_only_view) << Arg::Str(relation->rel_name)); + ERR_post(Arg::Gds(isc_read_only_view) << Arg::Str(relation->getName())); } // for an updateable view, return the view source @@ -11649,7 +11662,7 @@ static void postTriggerAccess(CompilerScratch* csb, jrd_rel* ownerRelation, return; // Post trigger access - ExternalAccess temp(operation, ownerRelation->rel_id, view ? view->rel_id : 0); + ExternalAccess temp(operation, ownerRelation->getId(), view ? view->getId() : 0); FB_SIZE_T i; if (!csb->csb_external.find(temp, i)) @@ -11657,7 +11670,7 @@ static void postTriggerAccess(CompilerScratch* csb, jrd_rel* ownerRelation, } // Perform operation's pre-triggers, storing active rpb in chain. -static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs, +static void preModifyEraseTriggers(thread_db* tdbb, Triggers& triggers, StmtNode::WhichTrigger whichTrig, record_param* rpb, record_param* rec, TriggerAction op) { if (!tdbb->getTransaction()->tra_rpblist) @@ -11669,11 +11682,11 @@ static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs, const auto relation = rpb->rpb_relation; const int rpblevel = tdbb->getTransaction()->tra_rpblist->PushRpb(rpb); - if ((*trigs || relation->isSystem()) && whichTrig != StmtNode::POST_TRIG) + if ((triggers || relation->isSystem()) && whichTrig != StmtNode::POST_TRIG) { try { - EXE_execute_triggers(tdbb, trigs, rpb, rec, op, StmtNode::PRE_TRIG); + EXE_execute_triggers(tdbb, triggers, rpb, rec, op, StmtNode::PRE_TRIG); } catch (const Exception&) { @@ -11693,7 +11706,7 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, if (!compoundNode) return; - jrd_rel* relation = csb->csb_rpt[stream].csb_relation; + jrd_rel* relation = csb->csb_rpt[stream].csb_relation(tdbb); //// TODO: LocalTableSourceNode if (!relation) @@ -11738,9 +11751,9 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, if (nodeIs(assignFrom)) compoundNode->statements.remove(i); } - else if (relation->rel_view_rse && fld->fld_source_rel_field.first.hasData()) + else if (relation->isView() && fld->fld_source_rel_field.first.hasData()) { - relation = MET_lookup_relation(tdbb, fld->fld_source_rel_field.first); + relation = MetadataCache::lookup_relation(tdbb, fld->fld_source_rel_field.first, CacheFlag::AUTOCREATE); fb_assert(relation); if (!relation) @@ -11762,15 +11775,15 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, if (insertOverride->has_value()) { if (!identityType.has_value()) - ERR_post(Arg::Gds(isc_overriding_without_identity) << relation->rel_name); + ERR_post(Arg::Gds(isc_overriding_without_identity) << relation->getName()); if (identityType == IDENT_TYPE_BY_DEFAULT && *insertOverride == OverrideClause::SYSTEM_VALUE) - ERR_post(Arg::Gds(isc_overriding_system_invalid) << relation->rel_name); + ERR_post(Arg::Gds(isc_overriding_system_invalid) << relation->getName()); } else { if (identityType == IDENT_TYPE_ALWAYS) - ERR_post(Arg::Gds(isc_overriding_missing) << relation->rel_name); + ERR_post(Arg::Gds(isc_overriding_missing) << relation->getName()); } } @@ -11827,8 +11840,8 @@ static void validateExpressions(thread_db* tdbb, const Array& vali if (vector && fieldNode->fieldId < vector->count() && (field = (*vector)[fieldNode->fieldId])) { - if (!relation->rel_name.isEmpty()) - name.printf("\"%s\".\"%s\"", relation->rel_name.c_str(), field->fld_name.c_str()); + if (!relation->getName().isEmpty()) + name.printf("\"%s\".\"%s\"", relation->c_name(), field->fld_name.c_str()); else name.printf("\"%s\"", field->fld_name.c_str()); } diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index c6cede470d9..512b4a15aa1 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -136,7 +136,7 @@ class AssignmentNode final : public TypedNode outputSources; NestConst outputTargets; NestConst outputMessage; - NestConst procedure; + SubRoutine procedure; // NestConst ???????????????????? NestConst> dsqlInputArgNames; NestConst> dsqlOutputArgNames; bool dsqlCallSyntax = false; diff --git a/src/dsql/ddl.cpp b/src/dsql/ddl.cpp index 761a13913b3..c54e6fb2aee 100644 --- a/src/dsql/ddl.cpp +++ b/src/dsql/ddl.cpp @@ -322,7 +322,7 @@ void DDL_resolve_intl_type(DsqlCompilerScratch* dsqlScratch, dsql_fld* field, defaultCharSet = METD_get_default_charset(dsqlScratch->getTransaction()); else { - USHORT charSet = dsqlScratch->getAttachment()->dbb_attachment->att_charset; + auto charSet = dsqlScratch->getAttachment()->dbb_attachment->att_charset; if (charSet != CS_NONE) defaultCharSet = METD_get_charset_name(dsqlScratch->getTransaction(), charSet); } @@ -334,7 +334,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; diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 00c3e20792f..20285a2213e 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -54,6 +54,7 @@ #include "../dsql/movd_proto.h" #include "../dsql/pass1_proto.h" #include "../dsql/metd_proto.h" +#include "../jrd/Database.h" #include "../jrd/DataTypeUtil.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" @@ -74,6 +75,7 @@ #include "../common/StatusArg.h" #include "../dsql/DsqlBatch.h" #include "../dsql/DsqlStatementCache.h" +#include "../jrd/met.h" #ifdef HAVE_CTYPE_H #include @@ -113,10 +115,7 @@ IMPLEMENT_TRACE_ROUTINE(dsql_trace, "DSQL") #endif dsql_dbb::dsql_dbb(MemoryPool& p, Attachment* attachment) - : dbb_relations(p), - dbb_procedures(p), - dbb_functions(p), - dbb_charsets(p), + : dbb_charsets(p), dbb_collations(p), dbb_charsets_by_id(p), dbb_cursors(p), @@ -131,6 +130,16 @@ dsql_dbb::~dsql_dbb() { } +MemoryPool* dsql_dbb::createPool(ALLOC_PARAMS0) +{ + return dbb_attachment->att_database->createPool(ALLOC_PASS_ARGS0); +} + +void dsql_dbb::deletePool(MemoryPool* pool) +{ + dbb_attachment->att_database->deletePool(pool); +} + // Execute a dynamic SQL statement. void DSQL_execute(thread_db* tdbb, @@ -428,7 +437,7 @@ static dsql_dbb* init(thread_db* tdbb, Jrd::Attachment* attachment) if (attachment->att_dsql_instance) return attachment->att_dsql_instance; - MemoryPool& pool = *attachment->createPool(); + MemoryPool& pool = *attachment->att_database->createPool(ALLOC_ARGS0); dsql_dbb* const database = FB_NEW_POOL(pool) dsql_dbb(pool, attachment); attachment->att_dsql_instance = database; @@ -562,12 +571,12 @@ static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* databas MemoryPool* scratchPool = nullptr; DsqlCompilerScratch* scratch = nullptr; - MemoryPool* statementPool = database->createPool(); + MemoryPool* statementPool = database->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder statementContext(tdbb, statementPool); try { - scratchPool = database->createPool(); + scratchPool = database->createPool(ALLOC_ARGS0); if (!transaction) // Useful for session management statements transaction = database->dbb_attachment->getSysTransaction(); @@ -719,7 +728,7 @@ static UCHAR* put_item( UCHAR item, // Return as UTF8 string IntlString::toUtf8(jrd_tra* transaction) const { - CHARSET_ID id = CS_dynamic; + CSetId id = CS_dynamic; if (charset.hasData()) { @@ -1311,3 +1320,126 @@ static UCHAR* var_info(const dsql_msg* message, return info; } + +dsql_rel::dsql_rel(MemoryPool& p, const jrd_rel* jrel) + : rel_fields(nullptr), + rel_name(p, jrel->getName()), + rel_owner(p, jrel->getOwnerName()), + rel_id(jrel->getId()), + rel_dbkey_length(jrel->rel_dbkey_length), + rel_flags((jrel->getExtFile() ? REL_external : 0) | (jrel->isView() ? REL_view : 0)) +{ + if (!(jrel->rel_fields)) + return; + + auto* format = jrel->currentFormat(); + fb_assert(format->fmt_count == jrel->rel_fields->count()); + + for (MetaId id = 0; id < format->fmt_count; ++id) + { + auto* jfld = (*(jrel->rel_fields))[id]; + auto* fld = FB_NEW_POOL(p) dsql_fld(p, format->fmt_desc[id], nullptr); + + fld->fld_relation = this; + fld->fld_id = id; + fld->fld_pos = jfld->fld_pos; + fld->fld_name = jfld->fld_name; + fld->length = jfld->fld_length; + fld->segLength = jfld->fld_segment_length; + fld->charLength = jfld->fld_character_length; + fld->fieldSource = jfld->fld_source_name; + fld->setExactPrecision(); + fld->flags |= (jfld->fld_computation ? FLD_computed : 0); + + if (auto* array = jfld->fld_array) + { + fld->elementDtype = array->arr_desc.iad_rpt[0].iad_desc.dsc_dtype; + fld->elementLength = array->arr_desc.iad_element_length; + fld->dimensions = array->arr_desc.iad_dimensions; + } + + auto** iter = &rel_fields; + while (*iter && (*iter)->fld_pos <= fld->fld_pos) + iter = &(*iter)->fld_next; + + fld->fld_next = *iter; + *iter = fld; + } +} + +dsql_prc::dsql_prc(MemoryPool& p, const jrd_prc* jproc) + : prc_inputs(cpFields(p, jproc->getInputFields())), + prc_outputs(cpFields(p, jproc->getOutputFields())), + prc_name(p, jproc->getName()), + prc_owner(p, jproc->getPermanent()->owner), + prc_in_count(jproc->getInputFields().getCount()), + prc_def_count(jproc->getDefaultCount()), + prc_out_count(jproc->getOutputFields().getCount()), + prc_id(jproc->getId()) +{ } + +dsql_fld* dsql_prc::cpFields(MemoryPool& p, const Array>& fields) +{ + dsql_fld* rc = nullptr; + dsql_fld** prev = &rc; + for (auto& jfld : fields) + { + auto* fld = FB_NEW_POOL(p) dsql_fld(p, jfld->prm_desc, &prev); + fld->fld_procedure = this; + + fld->fld_id = jfld->prm_number; + fld->fld_name = jfld->prm_name; + fld->segLength = jfld->prm_seg_length; + fld->fieldSource = jfld->prm_field_source; + fld->typeOfTable = jfld->prm_type_of_table; + fld->typeOfName = jfld->prm_type_of_column; + + fld->setExactPrecision(); + } + + return rc; +} + +dsql_fld::dsql_fld(MemoryPool& p, const dsc& desc, dsql_fld*** prev) + : TypeClause(p, nullptr), + fld_name(p) +{ + if (prev) + { + **prev = this; + *prev = &fld_next; + } + + dtype = desc.getType(); + scale = desc.getScale(); + subType = desc.getSubType(); + length = desc.getLength(); + collationId = desc.getCollation(); + textType = desc.getTextType(); + charSetId = desc.getCharSet(); + flags = desc.isNullable() ? FLD_nullable : 0; +} + +dsql_udf::dsql_udf(MemoryPool& p, const class Function* jfun) + : udf_name(p, jfun->getName()), + udf_arguments(p) +{ + // return value + fb_assert(jfun->getOutputFields().getCount() == 1); + const dsc& desc = jfun->getOutputFields()[0]->prm_desc; + udf_dtype = desc.getType(); + udf_scale = desc.getScale(); + udf_sub_type = desc.getSubType(); + udf_length = desc.getLength(); + udf_character_set_id = desc.getCharSet(); + + // arguments + for (auto& jfld : jfun->getInputFields()) + { + if (jfld->prm_default_value) + ++udf_def_count; + + Argument arg(jfld->prm_name, jfld->prm_desc); + udf_arguments.add(arg); + } +} diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 422e36c3961..01205c0de6b 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -116,9 +116,6 @@ namespace Jrd { class dsql_dbb : public pool_alloc { public: - Firebird::LeftPooledMap dbb_relations; // known relations in database - Firebird::LeftPooledMap dbb_procedures; // known procedures in database - Firebird::LeftPooledMap dbb_functions; // known functions in database Firebird::LeftPooledMap dbb_charsets; // known charsets in database Firebird::LeftPooledMap dbb_collations; // known collations in database Firebird::NonPooledMap dbb_charsets_by_id; // charsets sorted by charset_id @@ -132,16 +129,8 @@ class dsql_dbb : public pool_alloc dsql_dbb(MemoryPool& p, Attachment* attachment); ~dsql_dbb(); - - MemoryPool* createPool() - { - return dbb_attachment->createPool(); - } - - void deletePool(MemoryPool* pool) - { - dbb_attachment->deletePool(pool); - } + MemoryPool* createPool(ALLOC_PARAMS0); + void deletePool(MemoryPool* pool); }; //! Relation block @@ -154,8 +143,12 @@ class dsql_rel : public pool_alloc { } + dsql_rel(MemoryPool& p, const class jrd_rel* jrel); + + dsql_rel(const dsql_rel&) = delete; + dsql_rel(dsql_rel&&) = delete; + dsql_fld* rel_fields; // Field block - //dsql_rel* rel_base_relation; // base relation for an updatable view MetaName rel_name; // Name of relation MetaName rel_owner; // Owner of relation USHORT rel_id; // Relation id @@ -226,9 +219,9 @@ class TypeClause USHORT segLength = 0; // Segment length for blobs USHORT precision = 0; // Precision for exact numeric types USHORT charLength = 0; // Length of field in characters - std::optional charSetId; - SSHORT collationId = 0; - SSHORT textType = 0; + std::optional charSetId; + CollId collationId = CollId(); + TTypeId textType = TTypeId(); bool fullDomain = false; // Domain name without TYPE OF prefix bool notNull = false; // NOT NULL was explicit specified MetaName fieldSource; @@ -254,6 +247,8 @@ class dsql_fld : public TypeClause { } + dsql_fld(MemoryPool& p, const dsc& desc, dsql_fld*** prev); + public: void resolve(DsqlCompilerScratch* dsqlScratch, bool modifying = false) { @@ -261,10 +256,11 @@ class dsql_fld : public TypeClause } public: - dsql_fld* fld_next = nullptr; // Next field in relation - dsql_rel* fld_relation = nullptr; // Parent relation - dsql_prc* fld_procedure = nullptr; // Parent procedure - USHORT fld_id = 0; // Field in in database + dsql_fld* fld_next = nullptr; // Next field in relation + class dsql_rel* fld_relation = nullptr; // Parent relation + class dsql_prc* fld_procedure = nullptr; // Parent procedure + USHORT fld_id = 0; // Field ID in database + USHORT fld_pos = 0; // Field position in relation MetaName fld_name; }; @@ -295,16 +291,25 @@ class dsql_prc : public pool_alloc { } + dsql_prc(MemoryPool& p, const class jrd_prc* jproc); + + dsql_prc(const dsql_prc&) = delete; + dsql_prc(dsql_prc&&) = delete; + dsql_fld* prc_inputs = nullptr; // Input parameters dsql_fld* prc_outputs = nullptr; // Output parameters + QualifiedName prc_name; // Name of procedure MetaName prc_owner; // Owner of procedure - SSHORT prc_in_count = 0; - SSHORT prc_def_count = 0; // number of inputs with default values - SSHORT prc_out_count = 0; - USHORT prc_id = 0; // Procedure id + USHORT prc_in_count = 0; + USHORT prc_def_count = 0; // number of inputs with default values + USHORT prc_out_count = 0; + SSHORT prc_id = 0; // Procedure id USHORT prc_flags = 0; bool prc_private = false; // Packaged private procedure + +private: + dsql_fld* cpFields(MemoryPool& p, const Firebird::Array>& fields); }; // prc_flags bits @@ -322,9 +327,12 @@ class dsql_udf : public pool_alloc class Argument { public: - Argument(MemoryPool& p) - : name(p) - {} + Argument(MetaName name, dsc desc) + : name(name), desc(desc) + { } + + Argument() + { } public: MetaName name; @@ -332,20 +340,20 @@ class dsql_udf : public pool_alloc }; public: + dsql_udf(MemoryPool& p, const class Function* jfun); + explicit dsql_udf(MemoryPool& p) - : udf_name(p), - udf_arguments(p) - { - } + : udf_arguments(p) + { } USHORT udf_dtype = 0; SSHORT udf_scale = 0; SSHORT udf_sub_type = 0; USHORT udf_length = 0; - SSHORT udf_character_set_id = 0; + CSetId udf_character_set_id = CSetId(); USHORT udf_flags = 0; QualifiedName udf_name; - Firebird::ObjectsArray udf_arguments; + Firebird::Array udf_arguments; bool udf_private = false; // Packaged private function SSHORT udf_def_count = 0; // number of inputs with default values }; @@ -353,10 +361,7 @@ class dsql_udf : public pool_alloc // udf_flags bits enum udf_flags_vals { - UDF_new_udf = 1, // udf is newly declared, not committed yet - UDF_dropped = 2, // udf has been dropped - UDF_subfunc = 4, // sub function - UDF_sys_based = 8 // return value based on column from system table + UDF_subfunc = 4 // sub function }; // Variables - input, output & local @@ -404,9 +409,9 @@ class dsql_intlsym : public pool_alloc MetaName intlsym_name; USHORT intlsym_type = 0; // what type of name USHORT intlsym_flags = 0; - SSHORT intlsym_ttype = 0; // id of implementation - SSHORT intlsym_charset_id = 0; - SSHORT intlsym_collate_id = 0; + TTypeId intlsym_ttype = TTypeId(); // id of implementation + CSetId intlsym_charset_id = CSetId(); + CollId intlsym_collate_id = CollId(); USHORT intlsym_bytes_per_char = 0; }; @@ -494,12 +499,12 @@ class dsql_ctx : public pool_alloc return *this; } - Firebird::string getObjectName() const + const char* getObjectName() const { if (ctx_relation) return ctx_relation->rel_name.c_str(); if (ctx_procedure) - return ctx_procedure->prc_name.toString(); + return ctx_procedure->prc_name.c_str(); return ""; } @@ -738,7 +743,7 @@ struct SignatureParameter MetaName charSetName; MetaName collationName; MetaName subTypeName; - std::optional collationId; + std::optional collationId; std::optional nullFlag; SSHORT mechanism = 0; std::optional fieldLength; @@ -780,7 +785,7 @@ struct SignatureParameter charSetName == o.charSetName && collationName == o.collationName && subTypeName == o.subTypeName && - fieldCollationId.value_or(0) == o.fieldCollationId.value_or(0) && + fieldCollationId.value_or(CollId()) == o.fieldCollationId.value_or(CollId()) && fieldCharSetId == o.fieldCharSetId && fieldPrecision == o.fieldPrecision; } diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index ece4e4dc2d5..fdc0e0f0528 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -191,8 +191,8 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) parameter->par_parameter = (USHORT) i; - const USHORT fromCharSet = parameter->par_desc.getCharSet(); - const USHORT toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ? + const auto fromCharSet = parameter->par_desc.getCharSet(); + const auto toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ? fromCharSet : tdbb->getCharSet(); if (parameter->par_desc.dsc_dtype <= dtype_any_text && @@ -209,8 +209,8 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) const USHORT fromCharSetBPC = METD_get_charset_bpc(dsqlScratch->getTransaction(), fromCharSet); const USHORT toCharSetBPC = METD_get_charset_bpc(dsqlScratch->getTransaction(), toCharSet); - parameter->par_desc.setTextType(INTL_CS_COLL_TO_TTYPE(toCharSet, - (fromCharSet == toCharSet ? INTL_GET_COLLATE(¶meter->par_desc) : 0))); + parameter->par_desc.setTextType(TTypeId(toCharSet, + (fromCharSet == toCharSet ? INTL_GET_COLLATE(¶meter->par_desc) : CollId(0)))); parameter->par_desc.dsc_length = UTLD_char_length_to_byte_length( parameter->par_desc.dsc_length / fromCharSetBPC, toCharSetBPC, adjust); @@ -332,10 +332,10 @@ void GEN_descriptor( DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool tex switch (desc->dsc_dtype) { case dtype_text: - if (texttype || desc->dsc_ttype() == ttype_binary || desc->dsc_ttype() == ttype_none) + if (texttype || desc->getTextType() == ttype_binary || desc->getTextType() == ttype_none) { dsqlScratch->appendUChar(blr_text2); - dsqlScratch->appendUShort(desc->dsc_ttype()); + dsqlScratch->appendUShort(desc->getTextType()); } else { @@ -347,10 +347,10 @@ void GEN_descriptor( DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool tex break; case dtype_varying: - if (texttype || desc->dsc_ttype() == ttype_binary || desc->dsc_ttype() == ttype_none) + if (texttype || desc->getTextType() == ttype_binary || desc->getTextType() == ttype_none) { dsqlScratch->appendUChar(blr_varying2); - dsqlScratch->appendUShort(desc->dsc_ttype()); + dsqlScratch->appendUShort(desc->getTextType()); } else { diff --git a/src/dsql/make.cpp b/src/dsql/make.cpp index 0ee75a0428b..90feb992f93 100644 --- a/src/dsql/make.cpp +++ b/src/dsql/make.cpp @@ -128,8 +128,8 @@ void DsqlDescMaker::composeDesc(dsc* desc, SSHORT scale, SSHORT subType, FLD_LENGTH length, - SSHORT charsetId, - SSHORT collationId, + CSetId charsetId, + CollId collationId, bool nullable) { desc->clear(); @@ -139,8 +139,7 @@ void DsqlDescMaker::composeDesc(dsc* desc, desc->dsc_length = length; desc->dsc_flags = nullable ? DSC_nullable : 0; - if (desc->isText() || desc->isBlob()) - desc->setTextType(INTL_CS_COLL_TO_TTYPE(charsetId, collationId)); + desc->setTextType(TTypeId(charsetId, collationId)); } @@ -256,7 +255,7 @@ ValueExprNode* MAKE_constant(const char* str, dsql_constant_type numeric_flag, S tmp.dsc_dtype = dtype_text; tmp.dsc_scale = 0; tmp.dsc_flags = 0; - tmp.dsc_ttype() = ttype_ascii; + tmp.setTextType(ttype_ascii); tmp.dsc_length = static_cast(strlen(str)); tmp.dsc_address = (UCHAR*) str; @@ -342,7 +341,7 @@ ValueExprNode* MAKE_constant(const char* str, dsql_constant_type numeric_flag, S @param character_set **/ -LiteralNode* MAKE_str_constant(const IntlString* constant, SSHORT character_set) +LiteralNode* MAKE_str_constant(const IntlString* constant, CSetId character_set) { thread_db* tdbb = JRD_get_thread_data(); @@ -354,7 +353,7 @@ LiteralNode* MAKE_str_constant(const IntlString* constant, SSHORT character_set) literal->litDesc.dsc_scale = 0; literal->litDesc.dsc_length = static_cast(str.length()); literal->litDesc.dsc_address = (UCHAR*) str.c_str(); - literal->litDesc.dsc_ttype() = character_set; + literal->litDesc.setTextType(character_set); literal->dsqlStr = constant; diff --git a/src/dsql/make_proto.h b/src/dsql/make_proto.h index d68df999fb4..5f10c266198 100644 --- a/src/dsql/make_proto.h +++ b/src/dsql/make_proto.h @@ -27,6 +27,7 @@ #define DSQL_MAKE_PROTO_H #include "../dsql/sym.h" +#include "../jrd/intl.h" namespace Jrd { class dsql_ctx; @@ -72,8 +73,8 @@ namespace Jrd { SSHORT scale, SSHORT subType, FLD_LENGTH length, - const SSHORT charsetId, - SSHORT collationId, + const CSetId charsetId, + CollId collationId, bool nullable); }; } @@ -82,7 +83,7 @@ namespace Jrd { Jrd::LiteralNode* MAKE_const_slong(SLONG); Jrd::LiteralNode* MAKE_const_sint64(SINT64 value, SCHAR scale); Jrd::ValueExprNode* MAKE_constant(const char*, Jrd::dsql_constant_type, SSHORT = 0); -Jrd::LiteralNode* MAKE_str_constant(const Jrd::IntlString*, SSHORT); +Jrd::LiteralNode* MAKE_str_constant(const Jrd::IntlString*, CSetId); Jrd::FieldNode* MAKE_field(Jrd::dsql_ctx*, Jrd::dsql_fld*, Jrd::ValueListNode*); Jrd::FieldNode* MAKE_field_name(const char*); Jrd::dsql_par* MAKE_parameter(Jrd::dsql_msg*, bool, bool, USHORT, const Jrd::ValueExprNode*); diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index 81e380c043a..68b336ce603 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -40,6 +40,7 @@ #include "../jrd/intl.h" #include "../jrd/irq.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "../dsql/ExprNodes.h" #include "../dsql/ddl_proto.h" #include "../dsql/metd_proto.h" @@ -64,7 +65,7 @@ using namespace Firebird; DATABASE DB = STATIC "yachts.lnk"; static void convert_dtype(TypeClause*, SSHORT); -static void free_relation(dsql_rel*); +//static void free_relation(dsql_rel*); ???????????????/// namespace { @@ -75,40 +76,6 @@ namespace ERR_post(Arg::Gds(isc_bad_trans_handle)); } } - - bool isSystemRelation(thread_db* tdbb, jrd_tra* transaction, const char* relName) - { - bool rc = false; - - AutoCacheRequest handle(tdbb, irq_system_relation, IRQ_REQUESTS); - FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) - R IN RDB$RELATIONS WITH - R.RDB$RELATION_NAME EQ relName AND - R.RDB$SYSTEM_FLAG EQ 1 - { - rc = true; - } - END_FOR - - return rc; - } - - bool isSystemDomain(thread_db* tdbb, jrd_tra* transaction, const char* fldName) - { - bool rc = false; - - AutoCacheRequest handle(tdbb, irq_system_domain, IRQ_REQUESTS); - FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) - R IN RDB$FIELDS WITH - R.RDB$FIELD_NAME EQ fldName AND - R.RDB$SYSTEM_FLAG EQ 1 - { - rc = true; - } - END_FOR - - return rc; - } } @@ -134,7 +101,7 @@ void METD_drop_charset(jrd_tra* transaction, const MetaName& metaName) if (dbb->dbb_charsets.get(metaName, charSet)) { - MET_dsql_cache_use(tdbb, SYM_intlsym_charset, metaName); + MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_charset, metaName); charSet->intlsym_flags |= INTLSYM_dropped; dbb->dbb_charsets.remove(metaName); dbb->dbb_charsets_by_id.remove(charSet->intlsym_charset_id); @@ -167,111 +134,14 @@ void METD_drop_collation(jrd_tra* transaction, const MetaName& name) if (dbb->dbb_collations.get(name, collation)) { - MET_dsql_cache_use(tdbb, SYM_intlsym_collation, name); + MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_collation, name); collation->intlsym_flags |= INTLSYM_dropped; dbb->dbb_collations.remove(name); } } -void METD_drop_function(jrd_tra* transaction, const QualifiedName& name) -{ -/************************************** - * - * M E T D _ d r o p _ f u n c t i o n - * - ************************************** - * - * Functional description - * Drop a user defined function from our metadata, and - * the next caller who wants it will - * look up the new version. - * - * Dropping will be achieved by marking the function - * as dropped. Anyone with current access can continue - * accessing it. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - dsql_dbb* dbb = transaction->getDsqlAttachment(); - - dsql_udf* function; - - if (dbb->dbb_functions.get(name, function)) - { - MET_dsql_cache_use(tdbb, SYM_udf, name.identifier, name.package); - function->udf_flags |= UDF_dropped; - dbb->dbb_functions.remove(name); - } - -} - - -void METD_drop_procedure(jrd_tra* transaction, const QualifiedName& name) -{ -/************************************** - * - * M E T D _ d r o p _ p r o c e d u r e - * - ************************************** - * - * Functional description - * Drop a procedure from our metadata, and - * the next caller who wants it will - * look up the new version. - * - * Dropping will be achieved by marking the procedure - * as dropped. Anyone with current access can continue - * accessing it. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - dsql_dbb* dbb = transaction->getDsqlAttachment(); - - dsql_prc* procedure; - - if (dbb->dbb_procedures.get(name, procedure)) - { - MET_dsql_cache_use(tdbb, SYM_procedure, name.identifier, name.package); - procedure->prc_flags |= PRC_dropped; - dbb->dbb_procedures.remove(name); - } -} - - -void METD_drop_relation(jrd_tra* transaction, const MetaName& name) -{ -/************************************** - * - * M E T D _ d r o p _ r e l a t i o n - * - ************************************** - * - * Functional description - * Drop a relation from our metadata, and - * rely on the next guy who wants it to - * look up the new version. - * - * Dropping will be achieved by marking the relation - * as dropped. Anyone with current access can continue - * accessing it. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - dsql_dbb* dbb = transaction->getDsqlAttachment(); - - dsql_rel* relation; - - if (dbb->dbb_relations.get(name, relation)) - { - MET_dsql_cache_use(tdbb, SYM_relation, name); - relation->rel_flags |= REL_dropped; - dbb->dbb_relations.remove(name); - } -} - - -dsql_intlsym* METD_get_collation(jrd_tra* transaction, const MetaName& name, USHORT charset_id) +dsql_intlsym* METD_get_collation(jrd_tra* transaction, const MetaName& name, CSetId charset_id) { /************************************** * @@ -296,7 +166,7 @@ dsql_intlsym* METD_get_collation(jrd_tra* transaction, const MetaName& name, USH if (dbb->dbb_collations.get(name, symbol) && !(symbol->intlsym_flags & INTLSYM_dropped) && symbol->intlsym_charset_id == charset_id) { - if (MET_dsql_cache_use(tdbb, SYM_intlsym_collation, name)) + if (MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_collation, name)) symbol->intlsym_flags |= INTLSYM_dropped; else return symbol; @@ -312,15 +182,15 @@ dsql_intlsym* METD_get_collation(jrd_tra* transaction, const MetaName& name, USH X IN RDB$COLLATIONS CROSS Y IN RDB$CHARACTER_SETS OVER RDB$CHARACTER_SET_ID WITH X.RDB$COLLATION_NAME EQ name.c_str() AND - X.RDB$CHARACTER_SET_ID EQ charset_id; + X.RDB$CHARACTER_SET_ID EQ charset_id { symbol = FB_NEW_POOL(dbb->dbb_pool) dsql_intlsym(dbb->dbb_pool); symbol->intlsym_name = name; symbol->intlsym_flags = 0; - symbol->intlsym_charset_id = X.RDB$CHARACTER_SET_ID; - symbol->intlsym_collate_id = X.RDB$COLLATION_ID; + symbol->intlsym_charset_id = CSetId(X.RDB$CHARACTER_SET_ID); + symbol->intlsym_collate_id = CollId(X.RDB$COLLATION_ID); symbol->intlsym_ttype = - INTL_CS_COLL_TO_TTYPE(symbol->intlsym_charset_id, symbol->intlsym_collate_id); + TTypeId(symbol->intlsym_charset_id, symbol->intlsym_collate_id); symbol->intlsym_bytes_per_char = (Y.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (Y.RDB$BYTES_PER_CHARACTER); } @@ -330,7 +200,7 @@ dsql_intlsym* METD_get_collation(jrd_tra* transaction, const MetaName& name, USH return NULL; dbb->dbb_collations.put(name, symbol); - MET_dsql_cache_use(tdbb, SYM_intlsym_collation, name); + MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_collation, name); return symbol; } @@ -361,7 +231,7 @@ dsql_intlsym* METD_get_charset(jrd_tra* transaction, USHORT length, const char* dsql_intlsym* symbol; if (dbb->dbb_charsets.get(metaName, symbol) && !(symbol->intlsym_flags & INTLSYM_dropped)) { - if (MET_dsql_cache_use(tdbb, SYM_intlsym_charset, metaName)) + if (MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_charset, metaName)) symbol->intlsym_flags |= INTLSYM_dropped; else return symbol; @@ -385,10 +255,10 @@ dsql_intlsym* METD_get_charset(jrd_tra* transaction, USHORT length, const char* symbol = FB_NEW_POOL(dbb->dbb_pool) dsql_intlsym(dbb->dbb_pool); symbol->intlsym_name = metaName; symbol->intlsym_flags = 0; - symbol->intlsym_charset_id = X.RDB$CHARACTER_SET_ID; - symbol->intlsym_collate_id = X.RDB$COLLATION_ID; + symbol->intlsym_charset_id = CSetId(X.RDB$CHARACTER_SET_ID); + symbol->intlsym_collate_id = CollId(X.RDB$COLLATION_ID); symbol->intlsym_ttype = - INTL_CS_COLL_TO_TTYPE(symbol->intlsym_charset_id, symbol->intlsym_collate_id); + TTypeId(CSetId(symbol->intlsym_charset_id), CollId(symbol->intlsym_collate_id)); symbol->intlsym_bytes_per_char = (Y.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (Y.RDB$BYTES_PER_CHARACTER); } @@ -399,13 +269,13 @@ dsql_intlsym* METD_get_charset(jrd_tra* transaction, USHORT length, const char* dbb->dbb_charsets.put(metaName, symbol); dbb->dbb_charsets_by_id.put(symbol->intlsym_charset_id, symbol); - MET_dsql_cache_use(tdbb, SYM_intlsym_charset, metaName); + MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_charset, metaName); return symbol; } -USHORT METD_get_charset_bpc(jrd_tra* transaction, SSHORT charset_id) +USHORT METD_get_charset_bpc(jrd_tra* transaction, CSetId charset_id) { /************************************** * @@ -441,7 +311,7 @@ USHORT METD_get_charset_bpc(jrd_tra* transaction, SSHORT charset_id) } -MetaName METD_get_charset_name(jrd_tra* transaction, SSHORT charset_id) +MetaName METD_get_charset_name(jrd_tra* transaction, CSetId charset_id) { /************************************** * @@ -561,12 +431,13 @@ bool METD_get_domain(jrd_tra* transaction, TypeClause* field, const MetaName& na field->subType = FLX.RDB$FIELD_SUB_TYPE; field->dimensions = FLX.RDB$DIMENSIONS.NULL ? 0 : FLX.RDB$DIMENSIONS; + //field->charSetId = Nullable::empty(); ???????????????????? field->charSetId = std::nullopt; if (!FLX.RDB$CHARACTER_SET_ID.NULL) - field->charSetId = FLX.RDB$CHARACTER_SET_ID; - field->collationId = 0; + field->charSetId = CSetId(FLX.RDB$CHARACTER_SET_ID); + field->collationId = CollId(0); if (!FLX.RDB$COLLATION_ID.NULL) - field->collationId = FLX.RDB$COLLATION_ID; + field->collationId = CollId(FLX.RDB$COLLATION_ID); field->charLength = 0; if (!FLX.RDB$CHARACTER_LENGTH.NULL) field->charLength = FLX.RDB$CHARACTER_LENGTH; @@ -607,10 +478,10 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat * **************************************/ thread_db* tdbb = JRD_get_thread_data(); - validateTransaction(transaction); - dsql_dbb* dbb = transaction->getDsqlAttachment(); + dsql_udf* userFunc = NULL; + QualifiedName metaName(name); bool maybeUnqualified = dsqlScratch->package.hasData() && metaName.package.isEmpty(); @@ -619,8 +490,7 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat // Start by seeing if symbol is already defined - dsql_udf* userFunc = NULL; - if (dbb->dbb_functions.get(metaName, userFunc)) + if (dsqlScratch->functions.get(metaName, userFunc)) { if (userFunc->udf_private && metaName.package != dsqlScratch->package) { @@ -628,283 +498,40 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat Arg::Str(metaName.identifier) << Arg::Str(metaName.package)); } - if (MET_dsql_cache_use(tdbb, SYM_udf, metaName.identifier, metaName.package)) - userFunc->udf_flags |= UDF_dropped; - } - - if (userFunc && (userFunc->udf_flags & UDF_dropped)) - userFunc = nullptr; - - if (userFunc) return userFunc; + } - // Now see if it is in the database + // now see if it is in the metadata cache USHORT return_arg = 0; - while (!userFunc) + for (;;) { - AutoCacheRequest handle1(tdbb, irq_function, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction) - X IN RDB$FUNCTIONS WITH - X.RDB$FUNCTION_NAME EQ metaName.identifier.c_str() AND - X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '') + auto* jfunc = MetadataCache::lookup_function(tdbb, metaName, CacheFlag::AUTOCREATE); + if (jfunc) { - userFunc = FB_NEW_POOL(dbb->dbb_pool) dsql_udf(dbb->dbb_pool); - userFunc->udf_name = metaName; - userFunc->udf_private = !X.RDB$PRIVATE_FLAG.NULL && X.RDB$PRIVATE_FLAG != 0; - - return_arg = X.RDB$RETURN_ARGUMENT; - } - END_FOR + userFunc = FB_NEW_POOL(dsqlScratch->getPool()) dsql_udf(dsqlScratch->getPool(), jfunc); - if (!userFunc) - { - if (maybeUnqualified) + if (userFunc->udf_private && metaName.package != dsqlScratch->package) { - maybeUnqualified = false; - metaName.package = ""; + status_exception::raise(Arg::Gds(isc_private_procedure) << + Arg::Str(metaName.identifier) << Arg::Str(metaName.package)); } - else - return NULL; - } - } - - SSHORT defaults = 0; - AutoCacheRequest handle2(tdbb, irq_func_return, IRQ_REQUESTS); + dsqlScratch->functions.put(userFunc->udf_name, userFunc); + return userFunc; + } - FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) - X IN RDB$FUNCTION_ARGUMENTS WITH - X.RDB$FUNCTION_NAME EQ metaName.identifier.c_str() AND - X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '') - SORTED BY X.RDB$ARGUMENT_POSITION - { - if (!X.RDB$FIELD_SOURCE.NULL) + if (maybeUnqualified) { - AutoCacheRequest handle3(tdbb, irq_func_ret_fld, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE transaction) - F IN RDB$FIELDS WITH - F.RDB$FIELD_NAME EQ X.RDB$FIELD_SOURCE - { - if (X.RDB$ARGUMENT_POSITION == return_arg) - { - userFunc->udf_dtype = (F.RDB$FIELD_TYPE != blr_blob) ? - gds_cvt_blr_dtype[F.RDB$FIELD_TYPE] : dtype_blob; - userFunc->udf_scale = F.RDB$FIELD_SCALE; - - if (!F.RDB$FIELD_SUB_TYPE.NULL) { - userFunc->udf_sub_type = F.RDB$FIELD_SUB_TYPE; - } - else { - userFunc->udf_sub_type = 0; - } - // CVC: We are overcoming a bug in ddl.cpp:put_field() - // when any field is defined: the length is not given for blobs. - if (F.RDB$FIELD_TYPE == blr_blob) - userFunc->udf_length = sizeof(ISC_QUAD); - else - userFunc->udf_length = F.RDB$FIELD_LENGTH; - - if (!F.RDB$CHARACTER_SET_ID.NULL) { - userFunc->udf_character_set_id = F.RDB$CHARACTER_SET_ID; - } - - if (!X.RDB$ARGUMENT_MECHANISM.NULL && X.RDB$ARGUMENT_MECHANISM == prm_mech_type_of && - !X.RDB$FIELD_NAME.NULL && X.RDB$FIELD_NAME[0] && - !X.RDB$RELATION_NAME.NULL && X.RDB$RELATION_NAME[0]) - { - // type of column used in declaration - if (isSystemRelation(tdbb, transaction, X.RDB$RELATION_NAME)) - userFunc->udf_flags |= UDF_sys_based; - } - else if (!X.RDB$FIELD_SOURCE.NULL && X.RDB$FIELD_SOURCE[0]) - { - // domain used in declaration - if (isSystemDomain(tdbb, transaction, X.RDB$FIELD_SOURCE)) - userFunc->udf_flags |= UDF_sys_based; - } - } - else - { - DSC d; - - if (X.RDB$MECHANISM == FUN_scalar_array) - { - d.dsc_dtype = dtype_array; - d.dsc_scale = 0; - d.dsc_sub_type = 0; - d.dsc_length = sizeof(ISC_QUAD); - d.dsc_flags = DSC_nullable; - } - else - { - d.dsc_dtype = (F.RDB$FIELD_TYPE != blr_blob) ? - gds_cvt_blr_dtype[F.RDB$FIELD_TYPE] : dtype_blob; - // dimitr: adjust the UDF arguments for CSTRING - if (d.dsc_dtype == dtype_cstring) { - d.dsc_dtype = dtype_text; - } - d.dsc_scale = F.RDB$FIELD_SCALE; - if (!F.RDB$FIELD_SUB_TYPE.NULL) { - d.dsc_sub_type = F.RDB$FIELD_SUB_TYPE; - } - else { - d.dsc_sub_type = 0; - } - d.dsc_length = F.RDB$FIELD_LENGTH; - if (d.dsc_dtype == dtype_varying) { - d.dsc_length += sizeof(USHORT); - } - - if (!F.RDB$CHARACTER_SET_ID.NULL) - { - if (d.dsc_dtype != dtype_blob) { - d.dsc_ttype() = F.RDB$CHARACTER_SET_ID; - } - else { - d.dsc_scale = F.RDB$CHARACTER_SET_ID; - } - } - - if (X.RDB$MECHANISM != FUN_value && X.RDB$MECHANISM != FUN_reference) - { - d.dsc_flags = DSC_nullable; - } - } - - d.dsc_address = NULL; - - if (!X.RDB$DEFAULT_VALUE.NULL || - (fb_utils::implicit_domain(F.RDB$FIELD_NAME) && !F.RDB$DEFAULT_VALUE.NULL)) - { - defaults++; - } - - auto& argument = userFunc->udf_arguments.add(); - argument.name = X.RDB$ARGUMENT_NAME; - argument.desc = d; - } - } - END_FOR + maybeUnqualified = false; + metaName.package = ""; } else - { - if (X.RDB$ARGUMENT_POSITION == return_arg) - { - userFunc->udf_dtype = (X.RDB$FIELD_TYPE != blr_blob) ? - gds_cvt_blr_dtype[X.RDB$FIELD_TYPE] : dtype_blob; - userFunc->udf_scale = X.RDB$FIELD_SCALE; - - if (!X.RDB$FIELD_SUB_TYPE.NULL) { - userFunc->udf_sub_type = X.RDB$FIELD_SUB_TYPE; - } - else { - userFunc->udf_sub_type = 0; - } - // CVC: We are overcoming a bug in ddl.c:put_field() - // when any field is defined: the length is not given for blobs. - if (X.RDB$FIELD_TYPE == blr_blob) - userFunc->udf_length = sizeof(ISC_QUAD); - else - userFunc->udf_length = X.RDB$FIELD_LENGTH; - - if (!X.RDB$CHARACTER_SET_ID.NULL) { - userFunc->udf_character_set_id = X.RDB$CHARACTER_SET_ID; - } - } - else - { - DSC d; - - if (X.RDB$MECHANISM == FUN_scalar_array) - { - d.dsc_dtype = dtype_array; - d.dsc_scale = 0; - d.dsc_sub_type = 0; - d.dsc_length = sizeof(ISC_QUAD); - d.dsc_flags = DSC_nullable; - } - else - { - d.dsc_dtype = (X.RDB$FIELD_TYPE != blr_blob) ? - gds_cvt_blr_dtype[X.RDB$FIELD_TYPE] : dtype_blob; - // dimitr: adjust the UDF arguments for CSTRING - if (d.dsc_dtype == dtype_cstring) { - d.dsc_dtype = dtype_text; - } - d.dsc_scale = X.RDB$FIELD_SCALE; - if (!X.RDB$FIELD_SUB_TYPE.NULL) { - d.dsc_sub_type = X.RDB$FIELD_SUB_TYPE; - } - else { - d.dsc_sub_type = 0; - } - d.dsc_length = X.RDB$FIELD_LENGTH; - if (d.dsc_dtype == dtype_varying) { - d.dsc_length += sizeof(USHORT); - } - - if (!X.RDB$CHARACTER_SET_ID.NULL) - { - if (d.dsc_dtype != dtype_blob) { - d.dsc_ttype() = X.RDB$CHARACTER_SET_ID; - } - else { - d.dsc_scale = X.RDB$CHARACTER_SET_ID; - } - } - - if (X.RDB$MECHANISM != FUN_value && X.RDB$MECHANISM != FUN_reference) - { - d.dsc_flags = DSC_nullable; - } - } - - d.dsc_address = NULL; - - if (!X.RDB$DEFAULT_VALUE.NULL) - { - defaults++; - } - - auto& argument = userFunc->udf_arguments.add(); - argument.name = X.RDB$ARGUMENT_NAME; - argument.desc = d; - } - } + break; } - END_FOR - - userFunc->udf_def_count = defaults; - - // Adjust the return type & length of the UDF to account for - // cstring & varying. While a UDF can return CSTRING, we convert it - // to VARCHAR for manipulation as CSTRING is not a SQL type. - if (userFunc->udf_dtype == dtype_cstring) - { - userFunc->udf_dtype = dtype_varying; - userFunc->udf_length += sizeof(USHORT); - if (userFunc->udf_length > MAX_SSHORT) - userFunc->udf_length = MAX_SSHORT; - } - else if (userFunc->udf_dtype == dtype_varying) - userFunc->udf_length += sizeof(USHORT); - - dbb->dbb_functions.put(userFunc->udf_name, userFunc); - - if (userFunc->udf_private && metaName.package != dsqlScratch->package) - { - status_exception::raise(Arg::Gds(isc_private_function) << - Arg::Str(metaName.identifier) << Arg::Str(metaName.package)); - } - - MET_dsql_cache_use(tdbb, SYM_udf, userFunc->udf_name.identifier, userFunc->udf_name.package); - - return userFunc; + return nullptr; } @@ -964,45 +591,8 @@ dsql_prc* METD_get_procedure(jrd_tra* transaction, DsqlCompilerScratch* dsqlScra * **************************************/ thread_db* tdbb = JRD_get_thread_data(); - validateTransaction(transaction); - dsql_dbb* dbb = transaction->getDsqlAttachment(); - - // ASF: I've removed the code where we verify if the procedure being looked up is the one being - // defined (dsqlScratch->procedure). This code is totally incorrect, not considering - // transactions and savepoints, hence being incompatible with packages). - // Example (with autocommit off): - // - // SQL> create procedure p1 as begin end! - // SQL> create procedure p2 as begin execute procedure p1; end! - // SQL> rollback! - // SQL> execute procedure p2! - // Statement failed, SQLSTATE = 42000 - // Dynamic SQL Error - // -SQL error code = -204 - // -Procedure unknown - // -P2 - // SQL> execute procedure p1! - // Statement failed, SQLSTATE = 42000 - // invalid request BLR at offset 5 - // -procedure P1 is not defined - // - // The side effect is that this occur in more cases now: - // - // SQL> create procedure p as begin execute procedure p; execute procedure p2; end! - // Statement failed, SQLSTATE = 42000 - // Dynamic SQL Error - // -SQL error code = -204 - // -Procedure unknown - // -P2 - // SQL> execute procedure p! - // Statement failed, SQLSTATE = 42000 - // invalid request BLR at offset 4 - // -procedure P is not defined - // - // I hope for a solution, involving savepoint logic. - QualifiedName metaName(name); bool maybeUnqualified = dsqlScratch->package.hasData() && metaName.package.isEmpty(); @@ -1012,7 +602,7 @@ dsql_prc* METD_get_procedure(jrd_tra* transaction, DsqlCompilerScratch* dsqlScra // Start by seeing if symbol is already defined dsql_prc* procedure = NULL; - if (dbb->dbb_procedures.get(metaName, procedure)) + if (dsqlScratch->procedures.get(metaName, procedure)) { if (procedure->prc_private && metaName.package != dsqlScratch->package) { @@ -1020,179 +610,38 @@ dsql_prc* METD_get_procedure(jrd_tra* transaction, DsqlCompilerScratch* dsqlScra Arg::Str(metaName.identifier) << Arg::Str(metaName.package)); } - if (MET_dsql_cache_use(tdbb, SYM_procedure, metaName.identifier, metaName.package)) - procedure->prc_flags |= PRC_dropped; - } - - if (procedure && (procedure->prc_flags & PRC_dropped)) - procedure = nullptr; - - if (procedure) return procedure; - - // now see if it is in the database - - while (!procedure) - { - AutoCacheRequest handle1(tdbb, irq_procedure, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction) - X IN RDB$PROCEDURES - WITH X.RDB$PROCEDURE_NAME EQ metaName.identifier.c_str() AND - X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '') - { - fb_utils::exact_name(X.RDB$OWNER_NAME); - - procedure = FB_NEW_POOL(dbb->dbb_pool) dsql_prc(dbb->dbb_pool); - procedure->prc_id = X.RDB$PROCEDURE_ID; - procedure->prc_name = metaName; - procedure->prc_owner = X.RDB$OWNER_NAME; - procedure->prc_private = !X.RDB$PRIVATE_FLAG.NULL && X.RDB$PRIVATE_FLAG != 0; - } - END_FOR - - if (!procedure) - { - if (maybeUnqualified) - { - maybeUnqualified = false; - metaName.package = ""; - } - else - return NULL; - } } - // Lookup parameter stuff + // now see if it is in the metadata cache - for (int type = 0; type < 2; type++) + for (;;) { - dsql_fld** const ptr = type ? &procedure->prc_outputs : &procedure->prc_inputs; - - SSHORT count = 0, defaults = 0; - - AutoCacheRequest handle2(tdbb, irq_parameters, IRQ_REQUESTS); - - FOR (REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) - PR IN RDB$PROCEDURE_PARAMETERS - CROSS FLD IN RDB$FIELDS - WITH FLD.RDB$FIELD_NAME EQ PR.RDB$FIELD_SOURCE AND - PR.RDB$PROCEDURE_NAME EQ metaName.identifier.c_str() AND - PR.RDB$PARAMETER_TYPE = type AND - PR.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '') - SORTED BY DESCENDING PR.RDB$PARAMETER_NUMBER + auto* jproc = MetadataCache::lookup_procedure(tdbb, metaName, CacheFlag::AUTOCREATE); + if (jproc) { - const SSHORT pr_collation_id_null = PR.RDB$COLLATION_ID.NULL; - const SSHORT pr_collation_id = PR.RDB$COLLATION_ID; - - const SSHORT pr_default_value_null = PR.RDB$DEFAULT_VALUE.NULL; - - const SSHORT pr_null_flag_null = PR.RDB$NULL_FLAG.NULL; - const SSHORT pr_null_flag = PR.RDB$NULL_FLAG; - - const bool pr_type_of = - (!PR.RDB$PARAMETER_MECHANISM.NULL && PR.RDB$PARAMETER_MECHANISM == prm_mech_type_of); - - count++; - // allocate the field block - - fb_utils::exact_name(PR.RDB$PARAMETER_NAME); - fb_utils::exact_name(PR.RDB$FIELD_SOURCE); - - dsql_fld* parameter = FB_NEW_POOL(dbb->dbb_pool) dsql_fld(dbb->dbb_pool); - parameter->fld_next = *ptr; - *ptr = parameter; - - // get parameter information - - parameter->fld_name = PR.RDB$PARAMETER_NAME; - parameter->fieldSource = PR.RDB$FIELD_SOURCE; + procedure = FB_NEW_POOL(dsqlScratch->getPool()) dsql_prc(dsqlScratch->getPool(), jproc); - parameter->fld_id = PR.RDB$PARAMETER_NUMBER; - parameter->length = FLD.RDB$FIELD_LENGTH; - parameter->scale = FLD.RDB$FIELD_SCALE; - parameter->subType = FLD.RDB$FIELD_SUB_TYPE; - parameter->fld_procedure = procedure; - - if (!FLD.RDB$CHARACTER_SET_ID.NULL) - parameter->charSetId = FLD.RDB$CHARACTER_SET_ID; - - if (!pr_collation_id_null) - parameter->collationId = pr_collation_id; - else if (!FLD.RDB$COLLATION_ID.NULL) - parameter->collationId = FLD.RDB$COLLATION_ID; - - convert_dtype(parameter, FLD.RDB$FIELD_TYPE); - - if (!pr_null_flag_null) - { - if (!pr_null_flag) - parameter->flags |= FLD_nullable; - } - else if (!FLD.RDB$NULL_FLAG || pr_type_of) - parameter->flags |= FLD_nullable; - - if (FLD.RDB$FIELD_TYPE == blr_blob) - parameter->segLength = FLD.RDB$SEGMENT_LENGTH; - - if (!PR.RDB$FIELD_NAME.NULL) - { - fb_utils::exact_name(PR.RDB$FIELD_NAME); - parameter->typeOfName = PR.RDB$FIELD_NAME; - } - - if (!PR.RDB$RELATION_NAME.NULL) - { - fb_utils::exact_name(PR.RDB$RELATION_NAME); - parameter->typeOfTable = PR.RDB$RELATION_NAME; - } - - if (parameter->typeOfTable.hasData()) - { - if (isSystemRelation(tdbb, transaction, parameter->typeOfTable.c_str())) - parameter->flags |= FLD_system; - } - else if (parameter->typeOfName.hasData()) - { - if (isSystemDomain(tdbb, transaction, parameter->typeOfName.c_str())) - parameter->flags |= FLD_system; - } - else if (parameter->fieldSource.hasData()) + if (procedure->prc_private && metaName.package != dsqlScratch->package) { - if (isSystemDomain(tdbb, transaction, parameter->fieldSource.c_str())) - parameter->flags |= FLD_system; + status_exception::raise(Arg::Gds(isc_private_procedure) << + Arg::Str(metaName.identifier) << Arg::Str(metaName.package)); } - if (type == 0 && - (!pr_default_value_null || - (fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) && !FLD.RDB$DEFAULT_VALUE.NULL))) - { - defaults++; - } + dsqlScratch->procedures.put(procedure->prc_name, procedure); + return procedure; } - END_FOR - if (type) - procedure->prc_out_count = count; - else + if (maybeUnqualified) { - procedure->prc_in_count = count; - procedure->prc_def_count = defaults; + maybeUnqualified = false; + metaName.package = ""; } + else + break; } - dbb->dbb_procedures.put(procedure->prc_name, procedure); - - if (procedure->prc_private && metaName.package != dsqlScratch->package) - { - status_exception::raise(Arg::Gds(isc_private_procedure) << - Arg::Str(metaName.identifier) << Arg::Str(metaName.package)); - } - - MET_dsql_cache_use(tdbb, SYM_procedure, procedure->prc_name.identifier, - procedure->prc_name.package); - - return procedure; + return nullptr; } @@ -1210,185 +659,32 @@ dsql_rel* METD_get_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat * If it does, fetch field information as well. * **************************************/ + thread_db* tdbb = JRD_get_thread_data(); validateTransaction(transaction); - dsql_dbb* dbb = transaction->getDsqlAttachment(); - // See if the relation is the one currently being defined in this statement - dsql_rel* temp = dsqlScratch->relation; - if (temp != NULL && temp->rel_name == name) - return temp; - - // Start by seeing if symbol is already defined - - if (dbb->dbb_relations.get(name, temp) && !(temp->rel_flags & REL_dropped)) + auto* relation = dsqlScratch->relation; + if (relation != NULL && relation->rel_name == name) { - if (MET_dsql_cache_use(tdbb, SYM_relation, name)) - temp->rel_flags |= REL_dropped; - else - return temp; + return relation; } - // If the relation id or any of the field ids have not yet been assigned, - // and this is a type of statement which does not use ids, prepare a - // temporary relation block to provide information without caching it - - bool permanent = true; - - AutoCacheRequest handle1(tdbb, irq_rel_ids, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction) - REL IN RDB$RELATIONS - CROSS RFR IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME - WITH REL.RDB$RELATION_NAME EQ name.c_str() - AND (REL.RDB$RELATION_ID MISSING OR RFR.RDB$FIELD_ID MISSING) + if (dsqlScratch->rels.get(name, relation)) { - permanent = false; + return relation; } - END_FOR - // Now see if it is in the database + // now see if it is in the metadata cache - MemoryPool& pool = permanent ? dbb->dbb_pool : *tdbb->getDefaultPool(); + auto* jrel = MetadataCache::lookup_relation(tdbb, name, CacheFlag::AUTOCREATE); + if (!jrel) + return nullptr; - dsql_rel* relation = NULL; - - AutoCacheRequest handle2(tdbb, irq_relation, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) - X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name.c_str() - { - fb_utils::exact_name(X.RDB$OWNER_NAME); - - // Allocate from default or permanent pool as appropriate - - if (!X.RDB$RELATION_ID.NULL) - { - relation = FB_NEW_POOL(pool) dsql_rel(pool); - relation->rel_id = X.RDB$RELATION_ID; - } - else if (!DDL_ids(dsqlScratch)) - relation = FB_NEW_POOL(pool) dsql_rel(pool); - - // fill out the relation information - - if (relation) - { - relation->rel_name = name; - relation->rel_owner = X.RDB$OWNER_NAME; - if (!(relation->rel_dbkey_length = X.RDB$DBKEY_LENGTH)) - relation->rel_dbkey_length = 8; - // CVC: let's see if this is a table or a view. - if (!X.RDB$VIEW_BLR.NULL) - relation->rel_flags |= REL_view; - if (!X.RDB$EXTERNAL_FILE.NULL) - relation->rel_flags |= REL_external; - } - } - END_FOR - - if (!relation) - return NULL; - - // Lookup field stuff - - dsql_fld** ptr = &relation->rel_fields; - - AutoCacheRequest handle3(tdbb, irq_fields, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE transaction) - FLX IN RDB$FIELDS CROSS - RFR IN RDB$RELATION_FIELDS - WITH FLX.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE - AND RFR.RDB$RELATION_NAME EQ name.c_str() - SORTED BY RFR.RDB$FIELD_POSITION - { - // allocate the field block - - fb_utils::exact_name(RFR.RDB$FIELD_NAME); - fb_utils::exact_name(RFR.RDB$FIELD_SOURCE); - - // Allocate from default or permanent pool as appropriate - - dsql_fld* field = NULL; - - if (!RFR.RDB$FIELD_ID.NULL) - { - field = FB_NEW_POOL(pool) dsql_fld(pool); - field->fld_id = RFR.RDB$FIELD_ID; - } - else if (!DDL_ids(dsqlScratch)) - field = FB_NEW_POOL(pool) dsql_fld(pool); - - if (field) - { - *ptr = field; - ptr = &field->fld_next; - - // get field information - - field->fld_name = RFR.RDB$FIELD_NAME; - field->fieldSource = RFR.RDB$FIELD_SOURCE; - field->length = FLX.RDB$FIELD_LENGTH; - field->scale = FLX.RDB$FIELD_SCALE; - field->subType = FLX.RDB$FIELD_SUB_TYPE; - field->fld_relation = relation; - - if (!FLX.RDB$COMPUTED_BLR.NULL) - field->flags |= FLD_computed; - - convert_dtype(field, FLX.RDB$FIELD_TYPE); - - if (FLX.RDB$FIELD_TYPE == blr_blob) { - field->segLength = FLX.RDB$SEGMENT_LENGTH; - } - - if (!FLX.RDB$DIMENSIONS.NULL && FLX.RDB$DIMENSIONS) - { - field->elementDtype = field->dtype; - field->elementLength = field->length; - field->dtype = dtype_array; - field->length = sizeof(ISC_QUAD); - field->dimensions = FLX.RDB$DIMENSIONS; - } - - if (!FLX.RDB$CHARACTER_SET_ID.NULL) - field->charSetId = FLX.RDB$CHARACTER_SET_ID; - - if (!RFR.RDB$COLLATION_ID.NULL) - field->collationId = RFR.RDB$COLLATION_ID; - else if (!FLX.RDB$COLLATION_ID.NULL) - field->collationId = FLX.RDB$COLLATION_ID; - - if (!(RFR.RDB$NULL_FLAG || FLX.RDB$NULL_FLAG) || (relation->rel_flags & REL_view)) - { - field->flags |= FLD_nullable; - } - - if (RFR.RDB$SYSTEM_FLAG == 1 || FLX.RDB$SYSTEM_FLAG == 1) - field->flags |= FLD_system; - } - } - END_FOR - - if (dbb->dbb_relations.get(name, temp) && !(temp->rel_flags & REL_dropped)) - { - free_relation(relation); - return temp; - } - - // Add relation to the list - - if (permanent) - { - dbb->dbb_relations.put(relation->rel_name, relation); - MET_dsql_cache_use(tdbb, SYM_relation, relation->rel_name); - } - else - relation->rel_flags |= REL_new_relation; + relation = FB_NEW_POOL(dsqlScratch->getPool()) dsql_rel(dsqlScratch->getPool(), jrel); + dsqlScratch->rels.put(relation->rel_name, relation); return relation; } @@ -1682,10 +978,10 @@ static void free_procedure(dsql_prc* procedure) } #endif // NOT_USED_OR_REPLACED - +/* ?????????????????? static void free_relation(dsql_rel* relation) { -/************************************** + ************************************** * * f r e e _ r e l a t i o n * @@ -1694,7 +990,7 @@ static void free_relation(dsql_rel* relation) * Functional description * Free memory allocated for a relation block and fields * - **************************************/ + ************************************** // release the field blocks @@ -1709,3 +1005,5 @@ static void free_relation(dsql_rel* relation) delete relation; } +*/ + diff --git a/src/dsql/metd_proto.h b/src/dsql/metd_proto.h index f2130052ecb..c98c3c88897 100644 --- a/src/dsql/metd_proto.h +++ b/src/dsql/metd_proto.h @@ -47,14 +47,11 @@ namespace Jrd { void METD_drop_charset(Jrd::jrd_tra*, const Jrd::MetaName&); void METD_drop_collation(Jrd::jrd_tra*, const Jrd::MetaName&); -void METD_drop_function(Jrd::jrd_tra*, const Jrd::QualifiedName&); -void METD_drop_procedure(Jrd::jrd_tra*, const Jrd::QualifiedName&); -void METD_drop_relation(Jrd::jrd_tra*, const Jrd::MetaName&); Jrd::dsql_intlsym* METD_get_charset(Jrd::jrd_tra*, USHORT, const char* name); -USHORT METD_get_charset_bpc(Jrd::jrd_tra*, SSHORT); -Jrd::MetaName METD_get_charset_name(Jrd::jrd_tra*, SSHORT); -Jrd::dsql_intlsym* METD_get_collation(Jrd::jrd_tra*, const Jrd::MetaName&, USHORT charset_id); +USHORT METD_get_charset_bpc(Jrd::jrd_tra*, CSetId); +Jrd::MetaName METD_get_charset_name(Jrd::jrd_tra*, CSetId); +Jrd::dsql_intlsym* METD_get_collation(Jrd::jrd_tra*, const Jrd::MetaName&, CSetId charset_id); Jrd::MetaName METD_get_default_charset(Jrd::jrd_tra*); bool METD_get_domain(Jrd::jrd_tra*, class Jrd::TypeClause*, const Jrd::MetaName& name); Jrd::dsql_udf* METD_get_function(Jrd::jrd_tra*, Jrd::DsqlCompilerScratch*, diff --git a/src/gpre/cmd.cpp b/src/gpre/cmd.cpp index 30c98c00cf2..89817674cc4 100644 --- a/src/gpre/cmd.cpp +++ b/src/gpre/cmd.cpp @@ -2243,8 +2243,8 @@ static void init_field_struct( gpre_fld* field) field->fld_collate = 0; field->fld_computed = 0; field->fld_char_length = 0; - field->fld_charset_id = 0; - field->fld_collate_id = 0; + field->fld_charset_id = CS_NONE; + field->fld_collate_id = COLLATE_NONE; } diff --git a/src/gpre/cme.cpp b/src/gpre/cme.cpp index cc0575d8c85..5547ad3641b 100644 --- a/src/gpre/cme.cpp +++ b/src/gpre/cme.cpp @@ -513,9 +513,9 @@ void CME_get_dtype(const gpre_nod* node, gpre_fld* f) f->fld_length = 0; f->fld_scale = 0; f->fld_sub_type = 0; - f->fld_charset_id = 0; - f->fld_collate_id = 0; - f->fld_ttype = 0; + f->fld_charset_id = CS_NONE; + f->fld_collate_id = COLLATE_NONE; + f->fld_ttype = CS_NONE;; switch (node->nod_type) { @@ -2198,7 +2198,8 @@ static void get_dtype_of_list(const gpre_nod* node, gpre_fld* f) UCHAR max_dtype = 0; SCHAR max_scale = 0; USHORT max_length = 0, max_dtype_length = 0, maxtextlength = 0, max_significant_digits = 0; - SSHORT max_sub_type = 0, first_sub_type = -1, ttype = ttype_ascii; // default type if all nodes are nod_null. + SSHORT max_sub_type = 0, first_sub_type = -1; + TTypeId ttype = ttype_ascii; // default type if all nodes are nod_null. SSHORT max_numeric_sub_type = 0; bool firstarg = true, all_same_sub_type = true, all_equal = true; //, all_nulls = true; bool all_numeric = true, any_numeric = false, any_approx = false, any_float = false; diff --git a/src/gpre/gpre.h b/src/gpre/gpre.h index 9702f557264..be39451fe29 100644 --- a/src/gpre/gpre.h +++ b/src/gpre/gpre.h @@ -66,6 +66,7 @@ #include "dyn_consts.h" #include "ibase.h" #include "../jrd/constants.h" +#include "../jrd/intl.h" #include "../common/utils_proto.h" #ifdef GPRE_FORTRAN @@ -1015,9 +1016,9 @@ struct intlsym intlsym* intlsym_next; USHORT intlsym_type; // what type of name USHORT intlsym_flags; - SSHORT intlsym_ttype; // id of implementation - SSHORT intlsym_charset_id; - SSHORT intlsym_collate_id; + TTypeId intlsym_ttype; // id of implementation + CSetId intlsym_charset_id; + CollId intlsym_collate_id; USHORT intlsym_bytes_per_char; TEXT intlsym_name[2]; }; @@ -1060,9 +1061,9 @@ struct gpre_fld intlsym* fld_collate; // collation clause for SQL declared field cmpf* fld_computed; // computed field definition USHORT fld_char_length; // field length in CHARACTERS - SSHORT fld_charset_id; // Field character set id for text - SSHORT fld_collate_id; // Field collation id for text - SSHORT fld_ttype; // ID of text type's implementation + CSetId fld_charset_id; // Field character set id for text + CollId fld_collate_id; // Field collation id for text + TTypeId fld_ttype; // ID of text type's implementation }; const size_t FLD_LEN = sizeof(gpre_fld); @@ -1299,7 +1300,7 @@ class ref USHORT ref_offset; // offset of field in port #endif USHORT ref_flags; - SSHORT ref_ttype; // Character set type for literals + TTypeId ref_ttype; // Text type for literals inline void add_byte(const int byte) { @@ -1478,8 +1479,8 @@ struct udf { SSHORT udf_scale; // Return scale USHORT udf_length; // Return length USHORT udf_sub_type; // Return sub-type - USHORT udf_charset_id; // Return character set - USHORT udf_ttype; // Return text type + CSetId udf_charset_id; // Return character set + TTypeId udf_ttype; // Return text type USHORT udf_type; // Function type gpre_fld* udf_inputs; // List of udf input arguments TEXT udf_function[1]; // Function name diff --git a/src/gpre/sql.cpp b/src/gpre/sql.cpp index 5f73dc7eba1..41e7df690fb 100644 --- a/src/gpre/sql.cpp +++ b/src/gpre/sql.cpp @@ -379,9 +379,9 @@ void SQL_adjust_field_dtype( gpre_fld* field) field_length = (ULONG) field->fld_char_length * 1; else field_length = field->fld_length; - field->fld_collate_id = 0; - field->fld_charset_id = 0; - field->fld_ttype = 0; + field->fld_collate_id = COLLATE_NONE; + field->fld_charset_id = CS_NONE; + field->fld_ttype = CS_NONE; } if (!(field->fld_flags & FLD_meta)) diff --git a/src/gpre/std/gpre_meta.epp b/src/gpre/std/gpre_meta.epp index 52586c59990..03ab011b18e 100644 --- a/src/gpre/std/gpre_meta.epp +++ b/src/gpre/std/gpre_meta.epp @@ -357,12 +357,12 @@ bool MET_domain_lookup(gpre_req* request, gpre_fld* field, const char* string) if (!F.RDB$CHARACTER_LENGTH.NULL) field->fld_char_length = F.RDB$CHARACTER_LENGTH; if (!F.RDB$CHARACTER_SET_ID.NULL) - field->fld_charset_id = F.RDB$CHARACTER_SET_ID; + field->fld_charset_id = CSetId(F.RDB$CHARACTER_SET_ID); if (!F.RDB$COLLATION_ID.NULL) - field->fld_collate_id = F.RDB$COLLATION_ID; + field->fld_collate_id = CollId(F.RDB$COLLATION_ID); } - field->fld_ttype = INTL_CS_COLL_TO_TTYPE(field->fld_charset_id, field->fld_collate_id); + field->fld_ttype = TTypeId(field->fld_charset_id, field->fld_collate_id); END_FOR; @@ -701,15 +701,15 @@ gpre_fld* MET_field(gpre_rel* relation, const char* string) if ((field->fld_dtype <= dtype_any_text) || (field->fld_dtype == dtype_blob)) { if (!F.RDB$CHARACTER_SET_ID.NULL) - field->fld_charset_id = F.RDB$CHARACTER_SET_ID; + field->fld_charset_id = CSetId(F.RDB$CHARACTER_SET_ID); + if (!RFR.RDB$COLLATION_ID.NULL) - field->fld_collate_id = RFR.RDB$COLLATION_ID; + field->fld_collate_id = CollId(RFR.RDB$COLLATION_ID); else if (!F.RDB$COLLATION_ID.NULL) - field->fld_collate_id = F.RDB$COLLATION_ID; + field->fld_collate_id = CollId(F.RDB$COLLATION_ID); } - field->fld_ttype = - INTL_CS_COLL_TO_TTYPE(field->fld_charset_id, field->fld_collate_id); + field->fld_ttype = TTypeId(field->fld_charset_id, field->fld_collate_id); field->fld_symbol = symbol = MSC_symbol(SYM_field, name, length, (gpre_ctx*) field); HSH_insert(symbol); field->fld_global = symbol = @@ -1052,18 +1052,18 @@ gpre_prc* MET_get_procedure(gpre_dbb* database, const TEXT* string, const TEXT* if (!F.RDB$CHARACTER_LENGTH.NULL) field->fld_char_length = F.RDB$CHARACTER_LENGTH; if (!F.RDB$CHARACTER_SET_ID.NULL) - field->fld_charset_id = F.RDB$CHARACTER_SET_ID; + field->fld_charset_id = CSetId(F.RDB$CHARACTER_SET_ID); if (!F.RDB$COLLATION_ID.NULL) - field->fld_collate_id = F.RDB$COLLATION_ID; - field->fld_ttype = - INTL_CS_COLL_TO_TTYPE(field->fld_charset_id, field->fld_collate_id); + field->fld_collate_id = CollId(F.RDB$COLLATION_ID); + field->fld_ttype = TTypeId(field->fld_charset_id, field->fld_collate_id); break; case dtype_blob: field->fld_flags |= FLD_blob; field->fld_seg_length = F.RDB$SEGMENT_LENGTH; if (!F.RDB$CHARACTER_SET_ID.NULL) - field->fld_charset_id = F.RDB$CHARACTER_SET_ID; + field->fld_charset_id = CSetId(F.RDB$CHARACTER_SET_ID); + field->fld_ttype = TTypeId(field->fld_charset_id, field->fld_collate_id); break; } field->fld_symbol = MSC_symbol(SYM_field, @@ -1189,14 +1189,15 @@ udf* MET_get_udf(gpre_dbb* database, const TEXT* string) case dtype_cstring: field->fld_flags |= FLD_text; if (!UDF_ARG.RDB$CHARACTER_SET_ID.NULL) - field->fld_charset_id = UDF_ARG.RDB$CHARACTER_SET_ID; - field->fld_ttype = INTL_CS_COLL_TO_TTYPE(field->fld_charset_id, field->fld_collate_id); + field->fld_charset_id = CSetId(UDF_ARG.RDB$CHARACTER_SET_ID); + field->fld_ttype = TTypeId(field->fld_charset_id, field->fld_collate_id); break; case dtype_blob: field->fld_flags |= FLD_blob; if (!UDF_ARG.RDB$CHARACTER_SET_ID.NULL) - field->fld_charset_id = UDF_ARG.RDB$CHARACTER_SET_ID; + field->fld_charset_id = CSetId(UDF_ARG.RDB$CHARACTER_SET_ID); + field->fld_ttype = TTypeId(field->fld_charset_id, field->fld_collate_id); break; } END_FOR; @@ -1456,9 +1457,8 @@ void MET_load_hash_table(gpre_dbb* database) V4ARG.RDB$PACKAGE_NAME MISSING AND V4ARG.RDB$ARGUMENT_POSITION EQ ARG.RDB$ARGUMENT_POSITION; - an_udf->udf_charset_id = V4ARG.RDB$CHARACTER_SET_ID; - an_udf->udf_ttype = - INTL_CS_COLL_TO_TTYPE(an_udf->udf_charset_id, COLL.RDB$COLLATION_ID); + an_udf->udf_charset_id = CSetId(V4ARG.RDB$CHARACTER_SET_ID); + an_udf->udf_ttype = TTypeId(an_udf->udf_charset_id, CollId(COLL.RDB$COLLATION_ID)); END_FOR } @@ -1491,10 +1491,9 @@ void MET_load_hash_table(gpre_dbb* database) HSH_insert(symbol); iname->intlsym_type = INTLSYM_collation; iname->intlsym_flags = 0; - iname->intlsym_charset_id = COLL.RDB$CHARACTER_SET_ID; - iname->intlsym_collate_id = COLL.RDB$COLLATION_ID; - iname->intlsym_ttype = - INTL_CS_COLL_TO_TTYPE(iname->intlsym_charset_id, iname->intlsym_collate_id); + iname->intlsym_charset_id = CSetId(COLL.RDB$CHARACTER_SET_ID); + iname->intlsym_collate_id = CollId(COLL.RDB$COLLATION_ID); + iname->intlsym_ttype = TTypeId(iname->intlsym_charset_id, iname->intlsym_collate_id); iname->intlsym_bytes_per_char = (CHARSET.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (CHARSET.RDB$BYTES_PER_CHARACTER); iname->intlsym_next = gpreGlob.text_subtypes; @@ -1544,10 +1543,9 @@ void MET_load_hash_table(gpre_dbb* database) HSH_insert(symbol); iname->intlsym_type = INTLSYM_collation; iname->intlsym_flags = 0; - iname->intlsym_charset_id = COLL.RDB$CHARACTER_SET_ID; - iname->intlsym_collate_id = COLL.RDB$COLLATION_ID; - iname->intlsym_ttype = - INTL_CS_COLL_TO_TTYPE(iname->intlsym_charset_id, iname->intlsym_collate_id); + iname->intlsym_charset_id = CSetId(COLL.RDB$CHARACTER_SET_ID); + iname->intlsym_collate_id = CollId(COLL.RDB$COLLATION_ID); + iname->intlsym_ttype = TTypeId(iname->intlsym_charset_id, iname->intlsym_collate_id); iname->intlsym_bytes_per_char = (CHARSET.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (CHARSET.RDB$BYTES_PER_CHARACTER); diff --git a/src/include/fb_blk.h b/src/include/fb_blk.h index fec0ce090c8..1931092ce4e 100644 --- a/src/include/fb_blk.h +++ b/src/include/fb_blk.h @@ -34,7 +34,6 @@ enum BlockType type_att, type_sym, type_irl, - type_idl, type_sdw, type_blf, type_arr, diff --git a/src/include/fb_exception.h b/src/include/fb_exception.h index 988efafaff5..b15ac4fb610 100644 --- a/src/include/fb_exception.h +++ b/src/include/fb_exception.h @@ -74,7 +74,7 @@ class LongJump : public Exception public: virtual void stuffByException(StaticStatusVector& status_vector) const noexcept; virtual const char* what() const noexcept; - static void raise(); + static void raise [[noreturn]] (); LongJump() noexcept : Exception() { } }; @@ -85,7 +85,7 @@ class BadAlloc : public std::bad_alloc, public Exception BadAlloc() noexcept : std::bad_alloc(), Exception() { } virtual void stuffByException(StaticStatusVector& status_vector) const noexcept; virtual const char* what() const noexcept; - static void raise(); + static void raise [[noreturn]] (); }; // Main exception class in firebird @@ -102,9 +102,9 @@ class status_exception : public Exception const ISC_STATUS* value() const noexcept { return m_status_vector; } - [[noreturn]] static void raise(const ISC_STATUS* status_vector); - [[noreturn]] static void raise(const Arg::StatusVector& statusVector); - [[noreturn]] static void raise(const IStatus* status); + static void raise [[noreturn]] (const ISC_STATUS* status_vector); + static void raise [[noreturn]] (const Arg::StatusVector& statusVector); + static void raise [[noreturn]] (const IStatus* status); protected: // Create exception with undefined status vector, this constructor allows @@ -134,8 +134,8 @@ class system_error : public status_exception system_error(const char* syscall, const char* arg, int error_code); public: - static void raise(const char* syscall, int error_code); - static void raise(const char* syscall); + static void raise [[noreturn]] (const char* syscall, int error_code); + static void raise [[noreturn]] (const char* syscall); int getErrorCode() const { @@ -153,19 +153,19 @@ class system_call_failed : public system_error system_call_failed(const char* syscall, const char* arg, int error_code); public: - static void raise(const char* syscall, int error_code); - static void raise(const char* syscall); - static void raise(const char* syscall, const char* arg, int error_code); - static void raise(const char* syscall, const char* arg); + static void raise [[noreturn]] (const char* syscall, int error_code); + static void raise [[noreturn]] (const char* syscall); + static void raise [[noreturn]] (const char* syscall, const char* arg, int error_code); + static void raise [[noreturn]] (const char* syscall, const char* arg); }; class fatal_exception : public status_exception { public: explicit fatal_exception(const char* message); - static void raiseFmt(const char* format, ...); + static void raiseFmt [[noreturn]] (const char* format, ...); const char* what() const noexcept; - static void raise(const char* message); + static void raise [[noreturn]] (const char* message); }; diff --git a/src/include/fb_types.h b/src/include/fb_types.h index 50769721720..95433455d16 100644 --- a/src/include/fb_types.h +++ b/src/include/fb_types.h @@ -156,11 +156,6 @@ constexpr FB_SIZE_T FB_NELEM(const T (&)[N]) return static_cast(N); } -// Intl types -typedef SSHORT CHARSET_ID; -typedef SSHORT COLLATE_ID; -typedef USHORT TTYPE_ID; - // Stream type, had to move it from dsql/Nodes.h due to circular dependencies. typedef ULONG StreamType; @@ -171,13 +166,15 @@ constexpr T FB_ALIGN(T n, uintptr_t b) return (T) ((((uintptr_t) n) + b - 1) & ~(b - 1)); } -// Various object IDs (longer-than-32-bit) +// Various object IDs typedef FB_UINT64 AttNumber; typedef FB_UINT64 TraNumber; typedef FB_UINT64 StmtNumber; typedef FB_UINT64 CommitNumber; typedef ULONG SnapshotHandle; +typedef ULONG MdcVersion; +typedef USHORT MetaId; typedef SINT64 SavNumber; #endif /* INCLUDE_FB_TYPES_H */ diff --git a/src/include/firebird/impl/dsc_pub.h b/src/include/firebird/impl/dsc_pub.h index 39e8ff9eb1b..3d92ae11aaf 100644 --- a/src/include/firebird/impl/dsc_pub.h +++ b/src/include/firebird/impl/dsc_pub.h @@ -39,6 +39,7 @@ #define DSC_nullable 4 /* not stored. instead, is derived from metadata primarily to flag SQLDA (in DSQL) */ +#define DSC_computed 128 /* not stored, used in RelationNode::makeFormat() */ #define dtype_unknown 0 #define dtype_text 1 diff --git a/src/intl/charsets.h b/src/intl/charsets.h index 446b9220222..8b919fa3c4a 100644 --- a/src/intl/charsets.h +++ b/src/intl/charsets.h @@ -22,82 +22,84 @@ CS_737, CS_775, CS_858, CS_862, CS_864, CS_866, CS_869 #ifndef INTL_CHARSETS_H #define INTL_CHARSETS_H +#include "../jrd/intl.h" + #define DEFAULT_ATTACHMENT_CHARSET CS_NONE -#define CS_NONE 0 /* No Character Set */ -#define CS_BINARY 1 /* BINARY BYTES */ -#define CS_ASCII 2 /* ASCII */ -#define CS_UNICODE_FSS 3 /* UNICODE in FSS format */ -#define CS_UTF8 4 /* UTF-8 */ +#define CS_NONE CSetId(0) /* No Character Set */ +#define CS_BINARY CSetId(1) /* BINARY BYTES */ +#define CS_ASCII CSetId(2) /* ASCII */ +#define CS_UNICODE_FSS CSetId(3) /* UNICODE in FSS format */ +#define CS_UTF8 CSetId(4) /* UTF-8 */ -#define CS_SJIS 5 /* SJIS */ -#define CS_EUCJ 6 /* EUC-J */ +#define CS_SJIS CSetId(5) /* SJIS */ +#define CS_EUCJ CSetId(6) /* EUC-J */ -#define CS_JIS_0208 7 /* JIS 0208; 1990 */ -#define CS_UNICODE_UCS2 8 /* UNICODE v 1.10 */ +#define CS_JIS_0208 CSetId(7) /* JIS 0208; 1990 */ +#define CS_UNICODE_UCS2 CSetId(8) /* UNICODE v 1.10 */ -#define CS_DOS_737 9 -#define CS_DOS_437 10 /* DOS CP 437 */ -#define CS_DOS_850 11 /* DOS CP 850 */ -#define CS_DOS_865 12 /* DOS CP 865 */ -#define CS_DOS_860 13 /* DOS CP 860 */ -#define CS_DOS_863 14 /* DOS CP 863 */ +#define CS_DOS_737 CSetId(9) +#define CS_DOS_437 CSetId(10) /* DOS CP 437 */ +#define CS_DOS_850 CSetId(11) /* DOS CP 850 */ +#define CS_DOS_865 CSetId(12) /* DOS CP 865 */ +#define CS_DOS_860 CSetId(13) /* DOS CP 860 */ +#define CS_DOS_863 CSetId(14) /* DOS CP 863 */ -#define CS_DOS_775 15 -#define CS_DOS_858 16 -#define CS_DOS_862 17 -#define CS_DOS_864 18 +#define CS_DOS_775 CSetId(15) +#define CS_DOS_858 CSetId(16) +#define CS_DOS_862 CSetId(17) +#define CS_DOS_864 CSetId(18) -#define CS_NEXT 19 /* NeXTSTEP OS native charset */ +#define CS_NEXT CSetId(19) /* NeXTSTEP OS native charset */ -#define CS_ISO8859_1 21 /* ISO-8859.1 */ -#define CS_ISO8859_2 22 /* ISO-8859.2 */ -#define CS_ISO8859_3 23 /* ISO-8859.3 */ -#define CS_ISO8859_4 34 /* ISO-8859.4 */ -#define CS_ISO8859_5 35 /* ISO-8859.5 */ -#define CS_ISO8859_6 36 /* ISO-8859.6 */ -#define CS_ISO8859_7 37 /* ISO-8859.7 */ -#define CS_ISO8859_8 38 /* ISO-8859.8 */ -#define CS_ISO8859_9 39 /* ISO-8859.9 */ -#define CS_ISO8859_13 40 /* ISO-8859.13 */ +#define CS_ISO8859_1 CSetId(21) /* ISO-8859.1 */ +#define CS_ISO8859_2 CSetId(22) /* ISO-8859.2 */ +#define CS_ISO8859_3 CSetId(23) /* ISO-8859.3 */ +#define CS_ISO8859_4 CSetId(34) /* ISO-8859.4 */ +#define CS_ISO8859_5 CSetId(35) /* ISO-8859.5 */ +#define CS_ISO8859_6 CSetId(36) /* ISO-8859.6 */ +#define CS_ISO8859_7 CSetId(37) /* ISO-8859.7 */ +#define CS_ISO8859_8 CSetId(38) /* ISO-8859.8 */ +#define CS_ISO8859_9 CSetId(39) /* ISO-8859.9 */ +#define CS_ISO8859_13 CSetId(40) /* ISO-8859.13 */ -#define CS_KSC5601 44 /* KOREAN STANDARD 5601 */ +#define CS_KSC5601 CSetId(44) /* KOREAN STANDARD 5601 */ -#define CS_DOS_852 45 /* DOS CP 852 */ -#define CS_DOS_857 46 /* DOS CP 857 */ -#define CS_DOS_861 47 /* DOS CP 861 */ +#define CS_DOS_852 CSetId(45) /* DOS CP 852 */ +#define CS_DOS_857 CSetId(46) /* DOS CP 857 */ +#define CS_DOS_861 CSetId(47) /* DOS CP 861 */ -#define CS_DOS_866 48 -#define CS_DOS_869 49 +#define CS_DOS_866 CSetId(48) +#define CS_DOS_869 CSetId(49) -#define CS_CYRL 50 -#define CS_WIN1250 51 /* Windows cp 1250 */ -#define CS_WIN1251 52 /* Windows cp 1251 */ -#define CS_WIN1252 53 /* Windows cp 1252 */ -#define CS_WIN1253 54 /* Windows cp 1253 */ -#define CS_WIN1254 55 /* Windows cp 1254 */ +#define CS_CYRL CSetId(50) +#define CS_WIN1250 CSetId(51) /* Windows cp 1250 */ +#define CS_WIN1251 CSetId(52) /* Windows cp 1251 */ +#define CS_WIN1252 CSetId(53) /* Windows cp 1252 */ +#define CS_WIN1253 CSetId(54) /* Windows cp 1253 */ +#define CS_WIN1254 CSetId(55) /* Windows cp 1254 */ -#define CS_BIG5 56 /* Big Five unicode cs */ -#define CS_GB2312 57 /* GB 2312-80 cs */ +#define CS_BIG5 CSetId(56) /* Big Five unicode cs */ +#define CS_GB2312 CSetId(57) /* GB 2312-80 cs */ -#define CS_WIN1255 58 /* Windows cp 1255 */ -#define CS_WIN1256 59 /* Windows cp 1256 */ -#define CS_WIN1257 60 /* Windows cp 1257 */ +#define CS_WIN1255 CSetId(58) /* Windows cp 1255 */ +#define CS_WIN1256 CSetId(59) /* Windows cp 1256 */ +#define CS_WIN1257 CSetId(60) /* Windows cp 1257 */ -#define CS_UTF16 61 /* UTF-16 */ -#define CS_UTF32 62 /* UTF-32 */ +#define CS_UTF16 CSetId(61) /* UTF-16 */ +#define CS_UTF32 CSetId(62) /* UTF-32 */ -#define CS_KOI8R 63 /* Russian KOI8R */ -#define CS_KOI8U 64 /* Ukrainian KOI8U */ +#define CS_KOI8R CSetId(63) /* Russian KOI8R */ +#define CS_KOI8U CSetId(64) /* Ukrainian KOI8U */ -#define CS_WIN1258 65 /* Windows cp 1258 */ +#define CS_WIN1258 CSetId(65) /* Windows cp 1258 */ -#define CS_TIS620 66 /* TIS620 */ -#define CS_GBK 67 /* GBK */ -#define CS_CP943C 68 /* CP943C */ +#define CS_TIS620 CSetId(66) /* TIS620 */ +#define CS_GBK CSetId(67) /* GBK */ +#define CS_CP943C CSetId(68) /* CP943C */ -#define CS_GB18030 69 // GB18030 +#define CS_GB18030 CSetId(69) // GB18030 -#define CS_dynamic 127 // Pseudo number for runtime charset +#define CS_dynamic CSetId(127) // Pseudo number for runtime charset #endif /* INTL_CHARSETS_H */ diff --git a/src/intl/lc_ascii.cpp b/src/intl/lc_ascii.cpp index 182e3496a84..35d541c866f 100644 --- a/src/intl/lc_ascii.cpp +++ b/src/intl/lc_ascii.cpp @@ -490,7 +490,6 @@ TEXTTYPE_ENTRY2(WIN1258_c0_init) const USHORT LANGASCII_MAX_KEY = MAX_KEY; -const BYTE ASCII_SPACE = 32; // ASCII code for space /* * key_length (in_len) diff --git a/src/intl/lc_ksc.cpp b/src/intl/lc_ksc.cpp index e24d5a18692..1c23d1c55f7 100644 --- a/src/intl/lc_ksc.cpp +++ b/src/intl/lc_ksc.cpp @@ -130,7 +130,6 @@ const UCHAR gen_han[18][2] = }; const USHORT LANGKSC_MAX_KEY = MAX_KEY; -const BYTE ASCII_SPACE = 32; static USHORT LCKSC_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInChar, diff --git a/src/intl/lc_narrow.cpp b/src/intl/lc_narrow.cpp index 5a1bc0dbae6..18516a45066 100644 --- a/src/intl/lc_narrow.cpp +++ b/src/intl/lc_narrow.cpp @@ -44,7 +44,6 @@ static ULONG fam2_str_to_lower(texttype* obj, ULONG iLen, const BYTE* pStr, ULON const USHORT LANGFAM2_MAX_KEY = MAX_KEY; -const BYTE ASCII_SPACE = 32; const UINT16 NULL_WEIGHT = 0; const UINT16 NULL_SECONDARY = 0; const UINT16 NULL_TERTIARY = 0; diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 486aa6fa636..cd90d5fcce6 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -204,10 +204,8 @@ namespace IcuUtil }; // Return the number of characters of a string. - static unsigned charLength(SSHORT charset, unsigned len, const char* str) + static unsigned charLength(CSetId charset, unsigned len, const char* str) { - charset = TTYPE_TO_CHARSET(charset); - if (charset != CS_UNICODE_FSS && charset != CS_UTF8) return len; @@ -225,11 +223,9 @@ namespace IcuUtil } // Pads a string to a specified column width. - static void pad(char* buffer, SSHORT charset, unsigned len, const char* str, unsigned width, + static void pad(char* buffer, CSetId charset, unsigned len, const char* str, unsigned width, bool right) { - charset = TTYPE_TO_CHARSET(charset); - if (charset != CS_UNICODE_FSS && charset != CS_UTF8) { // Truncate if necessary. @@ -505,7 +501,7 @@ static int query_abort(const int, const int, void*); static bool stdin_redirected(); static void strip_quotes(const TEXT*, TEXT*); static const char* sqltype_to_string(unsigned); -static const char* charset_to_string(unsigned); +static const char* charset_to_string(CSetId); // The dialect spoken by the database, should be 0 when no database is connected. USHORT global_dialect_spoken = 0; @@ -846,7 +842,7 @@ int ISQL_main(int argc, char* argv[]) isqlGlob.major_ods = 0; isqlGlob.minor_ods = 0; isqlGlob.db_SQL_dialect = 0; - isqlGlob.att_charset = 0; + isqlGlob.att_charset = CS_NONE; // Output goes to stdout by default isqlGlob.Out = stdout; @@ -1567,16 +1563,16 @@ processing_state ISQL_fill_var(IsqlVar* var, unsigned index, UCHAR* buf) { - var->field = msg->getField(fbStatus, index); if (failed()) return ps_ERR; - var->relation = msg->getRelation(fbStatus, index); if (failed()) return ps_ERR; - var->owner = msg->getOwner(fbStatus, index); if (failed()) return ps_ERR; - var->alias = msg->getAlias(fbStatus, index); if (failed()) return ps_ERR; - var->subType = msg->getSubType(fbStatus, index); if (failed()) return ps_ERR; - var->scale = msg->getScale(fbStatus, index); if (failed()) return ps_ERR; - var->type = msg->getType(fbStatus, index); if (failed()) return ps_ERR; - var->length = msg->getLength(fbStatus, index); if (failed()) return ps_ERR; - var->charSet = msg->getCharSet(fbStatus, index); if (failed()) return ps_ERR; - var->nullable = msg->isNullable(fbStatus, index); if (failed()) return ps_ERR; + var->field = msg->getField(fbStatus, index); if (failed()) return ps_ERR; + var->relation = msg->getRelation(fbStatus, index); if (failed()) return ps_ERR; + var->owner = msg->getOwner(fbStatus, index); if (failed()) return ps_ERR; + var->alias = msg->getAlias(fbStatus, index); if (failed()) return ps_ERR; + var->subType = msg->getSubType(fbStatus, index); if (failed()) return ps_ERR; + var->scale = msg->getScale(fbStatus, index); if (failed()) return ps_ERR; + var->type = msg->getType(fbStatus, index); if (failed()) return ps_ERR; + var->length = msg->getLength(fbStatus, index); if (failed()) return ps_ERR; + var->charSet = CSetId(msg->getCharSet(fbStatus, index)); if (failed()) return ps_ERR; + var->nullable = msg->isNullable(fbStatus, index); if (failed()) return ps_ERR; if (buf) { @@ -5778,7 +5774,7 @@ void ISQL_get_version(bool call_by_create_db) break; case frb_info_att_charset: - isqlGlob.att_charset = p.getInt(); + isqlGlob.att_charset = CSetId(p.getInt()); break; default: @@ -7496,7 +7492,7 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length) case SQL_TEXT: str2 = var->value.asChar; // See if it is character set OCTETS - if (var->charSet == 1) + if (var->charSet == CS_BINARY) { const ULONG hex_len = 2 * var->length; TEXT* buff2 = (TEXT*) ISQL_ALLOC(hex_len + 1); @@ -7528,7 +7524,7 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length) vary* avary = var->value.asVary; // If CHARACTER SET OCTETS, print two hex digits per octet - if (var->charSet == 1) + if (var->charSet == CS_BINARY) { const ULONG hex_len = 2 * avary->vary_length; char* buff2 = static_cast(ISQL_ALLOC(hex_len + 1)); @@ -8069,7 +8065,7 @@ static void print_message(Firebird::IMessageMetadata* msg, const char* dir) { unsigned type = msg->getType(fbStatus, i); unsigned subtype = msg->getSubType(fbStatus, i); - unsigned cs; + CSetId cs; isqlGlob.printf("%02d: sqltype: %d %s %sscale: %d subtype: %d len: %d", i + 1, type, sqltype_to_string(type), msg->isNullable(fbStatus, i) ? "Nullable " : "", msg->getScale(fbStatus, i), subtype, msg->getLength(fbStatus, i)); @@ -8082,7 +8078,7 @@ static void print_message(Firebird::IMessageMetadata* msg, const char* dir) case SQL_TEXT: case SQL_VARYING: - cs = msg->getCharSet(fbStatus, i); + cs = CSetId(msg->getCharSet(fbStatus, i)); isqlGlob.printf(" charset: %d %s", cs, charset_to_string(cs)); break; } @@ -8308,7 +8304,7 @@ static unsigned process_message_display(Firebird::IMessageMetadata* message, uns namelength = NULL_DISP_LEN; const unsigned type = var.type; - const SSHORT charSet = TTYPE_TO_CHARSET(var.charSet); + const auto charSet = var.charSet; switch (type) { @@ -9148,7 +9144,7 @@ static const char* sqltype_to_string(unsigned sqltype) } -static const char* charset_to_string(unsigned charset) +static const char* charset_to_string(CSetId charset) { /************************************** * @@ -9162,8 +9158,6 @@ static const char* charset_to_string(unsigned charset) **************************************/ static Firebird::GlobalPtr > > > csMap; - charset = TTYPE_TO_CHARSET(charset); - string* text = csMap->get(charset); if (text) return text->c_str(); diff --git a/src/isql/isql.h b/src/isql/isql.h index 8c97ec42a8e..835d8048dde 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -35,6 +35,7 @@ #include "../jrd/flags.h" #include "../jrd/constants.h" +#include "../jrd/intl.h" #include #include #include @@ -239,7 +240,7 @@ class IsqlGlobals // from isql.epp USHORT major_ods; USHORT minor_ods; - USHORT att_charset; + CSetId att_charset; Firebird::IDecFloat16* df16; Firebird::IDecFloat34* df34; Firebird::IInt128* i128; @@ -282,7 +283,8 @@ struct IsqlVar const char* owner; const char* alias; int subType, scale; - unsigned type, length, charSet; + unsigned type, length; + CSetId charSet; bool nullable; short* nullInd; diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index a30facf742e..0e8d885a1ae 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -43,6 +43,9 @@ #include "../jrd/tpc_proto.h" #include "../jrd/extds/ExtDS.h" +#include "../jrd/met.h" +#include "../jrd/Statement.h" + #include "../jrd/ProfilerManager.h" #include "../jrd/replication/Applier.h" #include "../jrd/replication/Manager.h" @@ -89,7 +92,7 @@ CommitNumber ActiveSnapshots::getSnapshotForVersion(CommitNumber version_cn) // static method Jrd::Attachment* Jrd::Attachment::create(Database* dbb, JProvider* provider) { - MemoryPool* const pool = dbb->createPool(); + MemoryPool* const pool = dbb->createPool(ALLOC_ARGS0); try { @@ -122,19 +125,22 @@ void Jrd::Attachment::destroy(Attachment* const attachment) sAtt->manualUnlock(attachment->att_flags); } - thread_db* tdbb = JRD_get_thread_data(); - - jrd_tra* sysTransaction = attachment->getSysTransaction(); - if (sysTransaction) + Database* const dbb = attachment->att_database; { - // unwind any active system requests - while (sysTransaction->tra_requests) - EXE_unwind(tdbb, sysTransaction->tra_requests); + // context scope is needed here for correct GC of hazard pointers + ThreadContextHolder tdbb(dbb, attachment); + + jrd_tra* sysTransaction = attachment->getSysTransaction(); + if (sysTransaction) + { + // unwind any active system requests + while (sysTransaction->tra_requests) + EXE_unwind(tdbb, sysTransaction->tra_requests); - jrd_tra::destroy(NULL, sysTransaction); + jrd_tra::destroy(NULL, sysTransaction); + } } - Database* const dbb = attachment->att_database; MemoryPool* const pool = attachment->att_pool; Firebird::MemoryStats temp_stats; pool->setStatsGroup(temp_stats); @@ -145,42 +151,6 @@ void Jrd::Attachment::destroy(Attachment* const attachment) } -MemoryPool* Jrd::Attachment::createPool() -{ - MemoryPool* const pool = MemoryPool::createPool(att_pool, att_memory_stats); - auto stats = FB_NEW_POOL(*pool) MemoryStats(&att_memory_stats); - pool->setStatsGroup(*stats); - att_pools.add(pool); - return pool; -} - - -void Jrd::Attachment::deletePool(MemoryPool* pool) -{ - if (pool) - { - att_pools.findAndRemove(pool); - -#ifdef DEBUG_LCK_LIST - // hvlad: this could be slow, use only when absolutely necessary - for (Lock* lock = att_long_locks; lock; ) - { - Lock* next = lock->lck_next; - if (BtrPageGCLock::checkPool(lock, pool)) - { - gds__log("DEBUG_LCK_LIST: found not detached lock 0x%p in deleting pool 0x%p", lock, pool); - - //delete lock; - lock->setLockAttachment(NULL); - } - lock = next; - } -#endif - MemoryPool::deletePool(pool); - } -} - - bool Jrd::Attachment::backupStateWriteLock(thread_db* tdbb, SSHORT wait) { if (att_backup_state_counter++) @@ -261,25 +231,13 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb, JProvider* provider att_parallel_workers(0), att_repl_appliers(*pool), att_utility(UTIL_NONE), - att_procedures(*pool), - att_functions(*pool), - att_generators(*pool), - att_internal(*pool), - att_dyn_req(*pool), - att_internal_cached_statements(*pool), att_dec_status(DecimalStatus::DEFAULT), - att_charsets(*pool), - att_charset_ids(*pool), - att_pools(*pool), att_idle_timeout(0), att_stmt_timeout(0), att_batches(*pool), att_initial_options(*pool), att_provider(provider) -{ - att_internal.grow(irq_MAX); - att_dyn_req.grow(drq_MAX); -} +{ } Jrd::Attachment::~Attachment() @@ -289,23 +247,6 @@ Jrd::Attachment::~Attachment() delete att_trace_manager; - for (Function** iter = att_functions.begin(); iter < att_functions.end(); ++iter) - { - Function* const function = *iter; - if (function) - delete function; - } - - for (jrd_prc** iter = att_procedures.begin(); iter < att_procedures.end(); ++iter) - { - jrd_prc* const procedure = *iter; - if (procedure) - delete procedure; - } - - while (att_pools.hasData()) - deletePool(att_pools.pop()); - // For normal attachments that happens in release_attachment(), // but for special ones like GC should be done also in dtor - // they do not (and should not) call release_attachment(). @@ -371,11 +312,11 @@ MetaName Jrd::Attachment::nameToUserCharSet(thread_db* tdbb, const MetaName& nam string Jrd::Attachment::stringToMetaCharSet(thread_db* tdbb, const string& str, const char* charSet) { - USHORT charSetId = att_charset; + auto charSetId = att_charset; if (charSet) { - if (!MET_get_char_coll_subtype(tdbb, &charSetId, (const UCHAR*) charSet, + if (!MetadataCache::get_char_coll_subtype(tdbb, &charSetId, (const UCHAR*) charSet, static_cast(strlen(charSet)))) { (Arg::Gds(isc_charset_not_found) << Arg::Str(charSet)).raise(); @@ -453,63 +394,6 @@ void Jrd::Attachment::releaseBatches() delete att_batches.pop(); } -void Jrd::Attachment::releaseGTTs(thread_db* tdbb) -{ - if (!att_relations) - return; - - for (FB_SIZE_T i = 1; i < att_relations->count(); i++) - { - jrd_rel* relation = (*att_relations)[i]; - if (relation && (relation->rel_flags & REL_temp_conn) && - !(relation->rel_flags & (REL_deleted | REL_deleting))) - { - relation->delPages(tdbb); - } - } -} - -static void runDBTriggers(thread_db* tdbb, TriggerAction action) -{ - fb_assert(action == TRIGGER_CONNECT || action == TRIGGER_DISCONNECT); - - Database* dbb = tdbb->getDatabase(); - Attachment* att = tdbb->getAttachment(); - fb_assert(dbb); - fb_assert(att); - - const unsigned trgKind = (action == TRIGGER_CONNECT) ? DB_TRIGGER_CONNECT : DB_TRIGGER_DISCONNECT; - - const TrigVector* const triggers = att->att_triggers[trgKind]; - if (!triggers || triggers->isEmpty()) - return; - - ThreadStatusGuard temp_status(tdbb); - jrd_tra* transaction = NULL; - - try - { - transaction = TRA_start(tdbb, 0, NULL); - EXE_execute_db_triggers(tdbb, transaction, action); - TRA_commit(tdbb, transaction, false); - return; - } - catch (const Exception& /*ex*/) - { - if (!(dbb->dbb_flags & DBB_bugcheck) && transaction) - { - try - { - TRA_rollback(tdbb, transaction, false, false); - } - catch (const Exception& /*ex2*/) - { - } - } - throw; - } -} - void Jrd::Attachment::resetSession(thread_db* tdbb, jrd_tra** traHandle) { jrd_tra* oldTran = traHandle ? *traHandle : nullptr; @@ -543,7 +427,7 @@ void Jrd::Attachment::resetSession(thread_db* tdbb, jrd_tra** traHandle) { // Run ON DISCONNECT trigger before reset if (!(att_flags & ATT_no_db_triggers)) - runDBTriggers(tdbb, TRIGGER_DISCONNECT); + MetadataCache::get(tdbb)->runDBTriggers(tdbb, TRIGGER_DISCONNECT); // shutdown attachment on any error after this point shutAtt = true; @@ -581,11 +465,11 @@ void Jrd::Attachment::resetSession(thread_db* tdbb, jrd_tra** traHandle) SCL_release_all(att_security_classes); // reset GTT's - releaseGTTs(tdbb); + att_database->dbb_mdc->releaseGTTs(tdbb); // Run ON CONNECT trigger after reset if (!(att_flags & ATT_no_db_triggers)) - runDBTriggers(tdbb, TRIGGER_CONNECT); + att_database->dbb_mdc->runDBTriggers(tdbb, TRIGGER_CONNECT); if (oldTran) { @@ -677,49 +561,6 @@ bool Attachment::hasActiveRequests() const } -// Find an inactive incarnation of a system request. If necessary, clone it. -Request* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT which) -{ - static const int MAX_RECURSION = 100; - - // If the request hasn't been compiled or isn't active, there're nothing to do. - - //Database::CheckoutLockGuard guard(this, dbb_cmp_clone_mutex); - - fb_assert(which == IRQ_REQUESTS || which == DYN_REQUESTS || which == CACHED_REQUESTS); - - if (which == CACHED_REQUESTS && id >= att_internal_cached_statements.getCount()) - att_internal_cached_statements.grow(id + 1); - - Statement* statement = - which == IRQ_REQUESTS ? att_internal[id] : - which == DYN_REQUESTS ? att_dyn_req[id] : - att_internal_cached_statements[id]; - - if (!statement) - return NULL; - - // Look for requests until we find one that is available. - - for (int n = 0;; ++n) - { - if (n > MAX_RECURSION) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_req_depth_exceeded) << Arg::Num(MAX_RECURSION)); - // Msg363 "request depth exceeded. (Recursive definition?)" - } - - Request* clone = statement->getRequest(tdbb, n); - - if (!(clone->req_flags & (req_active | req_reserved))) - { - clone->req_flags |= req_reserved; - return clone; - } - } -} - void Jrd::Attachment::initLocks(thread_db* tdbb) { // Take out lock on attachment id @@ -763,95 +604,6 @@ void Jrd::Attachment::initLocks(thread_db* tdbb) void Jrd::Attachment::releaseLocks(thread_db* tdbb) { - // Go through relations and indices and release - // all existence locks that might have been taken. - - vec* rvector = att_relations; - - if (rvector) - { - vec::iterator ptr, end; - - for (ptr = rvector->begin(), end = rvector->end(); ptr < end; ++ptr) - { - jrd_rel* relation = *ptr; - - if (relation) - { - if (relation->rel_existence_lock) - { - LCK_release(tdbb, relation->rel_existence_lock); - relation->rel_flags |= REL_check_existence; - relation->rel_use_count = 0; - } - - if (relation->rel_partners_lock) - { - LCK_release(tdbb, relation->rel_partners_lock); - relation->rel_flags |= REL_check_partners; - } - - if (relation->rel_rescan_lock) - { - LCK_release(tdbb, relation->rel_rescan_lock); - relation->rel_flags &= ~REL_scanned; - } - - if (relation->rel_gc_lock) - { - LCK_release(tdbb, relation->rel_gc_lock); - relation->rel_flags |= REL_gc_lockneed; - } - - for (IndexLock* index = relation->rel_index_locks; index; index = index->idl_next) - { - if (index->idl_lock) - { - index->idl_count = 0; - LCK_release(tdbb, index->idl_lock); - } - } - - for (IndexBlock* index = relation->rel_index_blocks; index; index = index->idb_next) - { - if (index->idb_lock) - LCK_release(tdbb, index->idb_lock); - } - } - } - } - - // Release all procedure existence locks that might have been taken - - for (jrd_prc** iter = att_procedures.begin(); iter < att_procedures.end(); ++iter) - { - jrd_prc* const procedure = *iter; - - if (procedure) - { - if (procedure->existenceLock) - { - LCK_release(tdbb, procedure->existenceLock); - procedure->flags |= Routine::FLAG_CHECK_EXISTENCE; - procedure->useCount = 0; - } - } - } - - // Release all function existence locks that might have been taken - - for (Function** iter = att_functions.begin(); iter < att_functions.end(); ++iter) - { - Function* const function = *iter; - - if (function) - function->releaseLocks(tdbb); - } - - // Release collation existence locks - - releaseIntlObjects(tdbb); - // Release the DSQL cache locks DSqlCache::Accessor accessor(&att_dsql_cache); @@ -882,20 +634,6 @@ void Jrd::Attachment::releaseLocks(thread_db* tdbb) if (att_profiler_listener_lock) LCK_release(tdbb, att_profiler_listener_lock); - - // And release the system requests - - for (Statement** itr = att_internal.begin(); itr != att_internal.end(); ++itr) - { - if (*itr) - (*itr)->release(tdbb); - } - - for (Statement** itr = att_dyn_req.begin(); itr != att_dyn_req.end(); ++itr) - { - if (*itr) - (*itr)->release(tdbb); - } } void Jrd::Attachment::detachLocks() @@ -927,27 +665,6 @@ void Jrd::Attachment::detachLocks() att_long_locks = NULL; } -void Jrd::Attachment::releaseRelations(thread_db* tdbb) -{ - if (att_relations) - { - vec* vector = att_relations; - - for (vec::iterator ptr = vector->begin(), end = vector->end(); ptr < end; ++ptr) - { - jrd_rel* relation = *ptr; - - if (relation) - { - if (relation->rel_file) - EXT_fini(relation, false); - - delete relation; - } - } - } -} - int Jrd::Attachment::blockingAstShutdown(void* ast_object) { Jrd::Attachment* const attachment = static_cast(ast_object); @@ -1150,8 +867,16 @@ void Attachment::checkReplSetLock(thread_db* tdbb) } } +// Move to database level ???????? !!!!!!!!!!!!!!!!!!!!!!!!!!!! + void Attachment::invalidateReplSet(thread_db* tdbb, bool broadcast) { + + att_flags |= ATT_repl_reset; + + att_database->dbb_mdc->invalidateReplSet(tdbb); + +/* !!!!!!!!!!!!!!!!!!!!!!! if (broadcast) { // Signal other attachments about the changed state @@ -1175,7 +900,7 @@ void Attachment::invalidateReplSet(thread_db* tdbb, bool broadcast) } } - LCK_release(tdbb, att_repl_lock); + LCK_release(tdbb, att_repl_lock); */ } int Attachment::blockingAstReplSet(void* ast_object) diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index c5cd453876c..36e009eb869 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -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" @@ -48,10 +50,10 @@ #include "../jrd/EngineInterface.h" #include "../jrd/sbm.h" +// ?????????????? #include "../jrd/HazardPtr.h" #include -#define DEBUG_LCK_LIST namespace EDS { class Connection; @@ -62,12 +64,6 @@ namespace Replication class TableMatcher; } -namespace Firebird { - class TextType; -} - -class CharSetContainer; - namespace Jrd { class thread_db; @@ -83,8 +79,6 @@ namespace Jrd class jrd_rel; class ExternalFile; class ViewContext; - class IndexBlock; - class IndexLock; class ArrayField; struct sort_context; class vcl; @@ -97,12 +91,13 @@ namespace Jrd class jrd_rel; class jrd_prc; class Trigger; - class TrigVector; + class Triggers; class Function; class Statement; class ProfilerManager; class Validation; class Applier; + enum InternalRequest : USHORT; struct DSqlCacheItem @@ -436,55 +431,6 @@ class Attachment : public pool_alloc Firebird::RefPtr jStable; }; - class GeneratorFinder - { - public: - explicit GeneratorFinder(MemoryPool& pool) - : m_objects(pool) - {} - - void store(SLONG id, const MetaName& name) - { - fb_assert(id >= 0); - fb_assert(name.hasData()); - - if (id < (int) m_objects.getCount()) - { - fb_assert(m_objects[id].isEmpty()); - m_objects[id] = name; - } - else - { - m_objects.resize(id + 1); - m_objects[id] = name; - } - } - - bool lookup(SLONG id, MetaName& name) - { - if (id < (int) m_objects.getCount() && m_objects[id].hasData()) - { - name = m_objects[id]; - return true; - } - - return false; - } - - SLONG lookup(const MetaName& name) - { - FB_SIZE_T pos; - - if (m_objects.find(name, pos)) - return (SLONG) pos; - - return -1; - } - - private: - Firebird::Array m_objects; - }; - class InitialOptions { public: @@ -575,7 +521,8 @@ class Attachment : public pool_alloc public: Firebird::SortedArray att_statements; // Statements belonging to attachment - Firebird::SortedArray att_requests; // Requests belonging to attachment + Firebird::SortedArray att_requests; // Requests belonging to attachment + Lock* att_id_lock; // Attachment lock (if any) AttNumber att_attachment_id; // Attachment ID Lock* att_cancel_lock; // Lock to cancel the active request @@ -591,8 +538,8 @@ class Attachment : public pool_alloc RuntimeStatistics att_stats; RuntimeStatistics att_base_stats; ULONG att_flags; // Flags describing the state of the attachment - SSHORT att_client_charset; // user's charset specified in dpb - SSHORT att_charset; // current (client or external) attachment charset + CSetId att_client_charset; // user's charset specified in dpb + CSetId att_charset; // current (client or external) attachment charset // ASF: Attention: att_in_system_routine was initially added to support the profiler plugin // writing to system tables. But a modified implementation used non-system tables and @@ -655,48 +602,18 @@ class Attachment : public pool_alloc UtilType att_utility; - /// former Database members - start - - vec* att_relations; // relation vector - Firebird::Array att_procedures; // scanned procedures - TrigVector* att_triggers[DB_TRIGGER_MAX]; - TrigVector* att_ddl_triggers; - Firebird::Array att_functions; // User defined functions - GeneratorFinder att_generators; - - Firebird::Array att_internal; // internal statements - Firebird::Array att_dyn_req; // internal dyn statements - Firebird::Array att_internal_cached_statements; // internal cached statements Firebird::ICryptKeyCallback* att_crypt_callback; // callback for DB crypt Firebird::DecimalStatus att_dec_status; // error handling and rounding - Request* findSystemRequest(thread_db* tdbb, USHORT id, USHORT which); - - Firebird::Array att_charsets; // intl character set descriptions - Firebird::GenericMap > > att_charset_ids; // Character set ids - - void releaseIntlObjects(thread_db* tdbb); // defined in intl.cpp - void destroyIntlObjects(thread_db* tdbb); // defined in intl.cpp - void initLocks(thread_db* tdbb); void releaseLocks(thread_db* tdbb); void detachLocks(); - void releaseRelations(thread_db* tdbb); - static int blockingAstShutdown(void*); static int blockingAstCancel(void*); static int blockingAstMonitor(void*); static int blockingAstReplSet(void*); - Firebird::Array att_pools; // pools - - MemoryPool* createPool(); - void deletePool(MemoryPool* pool); - - /// former Database members - end - bool locksmith(thread_db* tdbb, SystemPrivilege sp) const; jrd_tra* getSysTransaction(); void setSysTransaction(jrd_tra* trans); // used only by TRA_init diff --git a/src/jrd/CacheVector.cpp b/src/jrd/CacheVector.cpp new file mode 100644 index 00000000000..3987f2d4f1e --- /dev/null +++ b/src/jrd/CacheVector.cpp @@ -0,0 +1,114 @@ +/* + * PROGRAM: Engine Code + * MODULE: CacheVector.cpp + * DESCRIPTION: Vector used in shared metadata cache. + * + * The contents of this file are subject to the Initial + * Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * 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 Alexander Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2021 Alexander Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#include "firebird.h" + +#include "../jrd/CacheVector.h" +#include "../jrd/jrd.h" +#include "../jrd/Database.h" +#include "../jrd/tra.h" +#include "../jrd/met.h" +#include "../jrd/tpc_proto.h" + +using namespace Jrd; +using namespace Firebird; + +// 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 + 1; +} + +bool TransactionNumber::isNotActive(thread_db* tdbb, TraNumber traNumber) +{ + auto state = TPC_cache_state(tdbb, traNumber); + return (state == tra_committed) || (state == tra_dead); +} + +ULONG* TransactionNumber::getFlags(thread_db* tdbb) +{ + jrd_tra* tra = tdbb->getTransaction(); + + // try to recover missing transaction - sooner of all scan() will use system transaction + static ULONG pseudoFlag = 0u; + return tra ? &tra->tra_flags : &pseudoFlag; +} + + +// class VersionSupport + +MdcVersion VersionSupport::next(thread_db* tdbb) +{ + return MetadataCache::get(tdbb)->nextVersion(); +} + + +// class ObjectBase + +void ObjectBase::lockedExcl [[noreturn]] (thread_db* tdbb) +{ + fatal_exception::raise("Unspecified object locked exclusive for deletion"); +} + + +// class CachePool + +MemoryPool& CachePool::get(thread_db* tdbb) +{ + return MetadataCache::get(tdbb)->getPool(); +} + + +// class ElementBase + +[[noreturn]] void ElementBase::busyError(thread_db* tdbb, MetaId id, const char* name, const char* family) +{ + fatal_exception::raiseFmt("%s %s%sid=%d busy in another thread - operation failed\n", + family, name ? name : "", name ? " " : "", id); +} + +void ElementBase::commitErase(thread_db* tdbb) +{ + MetadataCache::get(tdbb)->objectCleanup(TransactionNumber::current(tdbb), this); +} + +ElementBase::~ElementBase() +{ } + diff --git a/src/jrd/CacheVector.h b/src/jrd/CacheVector.h new file mode 100644 index 00000000000..b3df0b8e79b --- /dev/null +++ b/src/jrd/CacheVector.h @@ -0,0 +1,1187 @@ +/* + * PROGRAM: Engine Code + * MODULE: CacheVector.h + * DESCRIPTION: Vector used in shared metadata cache. + * + * The contents of this file are subject to the Initial + * Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * 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 Alexander Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2021 Alexander Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef JRD_CACHEVECTOR_H +#define JRD_CACHEVECTOR_H + +#include + +#include "../common/ThreadStart.h" +#include "../common/StatusArg.h" + +#include "../jrd/SharedReadVector.h" +#include "../jrd/constants.h" +#include "../jrd/tra_proto.h" + +namespace Jrd { + +class thread_db; + + +class ObjectBase +{ +public: + typedef unsigned Flag; + virtual void lockedExcl [[noreturn]] (thread_db* tdbb) /*const*/; + virtual const char* c_name() const = 0; +}; + + +class ElementBase +{ +public: + enum ResetType {Recompile, Mark, Commit, Rollback}; + + typedef SLONG ReturnedId; // enable '-1' as not found + +public: + virtual ~ElementBase(); + virtual void resetDependentObject(thread_db* tdbb, ResetType rt) = 0; + virtual void cleanup(thread_db* tdbb) = 0; + +public: + void resetDependentObjects(thread_db* tdbb, TraNumber olderThan); + void addDependentObject(thread_db* tdbb, ElementBase* dep); + void removeDependentObject(thread_db* tdbb, ElementBase* dep); + [[noreturn]] void busyError(thread_db* tdbb, MetaId id, const char* name, const char* family); + void commitErase(thread_db* tdbb); +}; + + +namespace CacheFlag +{ + static constexpr ObjectBase::Flag COMMITTED = 0x01; // version already committed + static constexpr ObjectBase::Flag ERASED = 0x02; // object erased + static constexpr ObjectBase::Flag NOSCAN = 0x04; // do not call Versioned::scan() + static constexpr ObjectBase::Flag AUTOCREATE = 0x08; // create initial version automatically + static constexpr ObjectBase::Flag NOCOMMIT = 0x10; // do not commit created version + static constexpr ObjectBase::Flag RET_ERASED = 0x20; // return erased objects + static constexpr ObjectBase::Flag RETIRED = 0x40; // object is in a process of GC + static constexpr ObjectBase::Flag UPGRADE = 0x80; // create new versions for already existing in a cache objects + + // Useful combinations + static constexpr ObjectBase::Flag TAG_FOR_UPDATE = NOCOMMIT | NOSCAN; +} + + +class VersionSupport +{ +public: + static MdcVersion next(thread_db* tdbb); +}; + + +class CachePool +{ +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 isNotActive(thread_db* tdbb, TraNumber traNumber); + + // Not related to number - but definitely related to transaction + static ULONG* getFlags(thread_db* tdbb); +}; + + +enum class ScanResult { COMPLETE, MISS, SKIP, REPEAT }; + + +template +class ListEntry : public HazardObject +{ +public: + enum State { INITIAL, RELOAD, SCANNING, READY }; + + ListEntry(OBJ* object, TraNumber traNumber, ObjectBase::Flag fl) + : object(object), traNumber(traNumber), cacheFlags(fl), state(INITIAL) + { } + + ~ListEntry() + { + fb_assert(!object); + } + + void cleanup(thread_db* tdbb, bool withObject = true) + { + if (object) // be careful with ERASED entries + { + if (withObject) + OBJ::destroy(tdbb, object); + object = nullptr; + } + + ListEntry* ptr = next.load(atomics::memory_order_relaxed); + if (ptr) + { + ptr->cleanup(tdbb, withObject); + delete ptr; + next.store(nullptr, atomics::memory_order_relaxed); + } + } + + // find appropriate object in cache + static OBJ* getObject(thread_db* tdbb, HazardPtr& listEntry, TraNumber currentTrans, ObjectBase::Flag fl) + { + auto entry = getEntry(tdbb, listEntry, currentTrans, fl); + return entry ? entry->getObject() : nullptr; + } + + // find appropriate entry in cache + static HazardPtr getEntry(thread_db* tdbb, HazardPtr& listEntry, TraNumber currentTrans, ObjectBase::Flag fl) + { + for (; listEntry; listEntry.set(listEntry->next)) + { + ObjectBase::Flag f(listEntry->getFlags()); + + if ((f & CacheFlag::COMMITTED) || + // committed (i.e. confirmed) objects are freely available + (listEntry->traNumber == currentTrans)) + // transaction that created an object can always access it + { + if (f & CacheFlag::ERASED) + { + // object does not exist + fb_assert(!listEntry->object); + + if (fl & CacheFlag::ERASED) + return listEntry; + + return HazardPtr(nullptr); // object dropped + } + + // required entry found in the list + if (listEntry->object) + { + switch (listEntry->scan(tdbb, fl)) + { + case ScanResult::COMPLETE: + case ScanResult::REPEAT: // scan complete but reload was requested - + case ScanResult::SKIP: // scan skipped due to NOSCAN flag + break; + + case ScanResult::MISS: // no object + default: + return HazardPtr(nullptr); + } + } + return listEntry; + } + } + + return HazardPtr(nullptr); // object created (not by us) and not committed yet + } + + bool isBusy(TraNumber currentTrans, TraNumber* number = nullptr) const noexcept + { + bool rc = !((getFlags() & CacheFlag::COMMITTED) || (traNumber == currentTrans)); + if (rc && number) + *number = traNumber; + return rc; + } + + ObjectBase::Flag getFlags() const noexcept + { + return cacheFlags.load(atomics::memory_order_relaxed); + } + + OBJ* getObject() + { + return object; + } + + // add new entry to the list + static bool add(thread_db* tdbb, atomics::atomic& list, ListEntry* newVal) + { + HazardPtr oldVal(list); + + do + { + while(oldVal && oldVal->isBusy(newVal->traNumber)) + { + // modified in transaction oldVal->traNumber + if (TransactionNumber::isNotActive(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; + } + + // insert newVal in the beginning of a list provided there is still oldVal at the top of the list + static bool replace(atomics::atomic& list, ListEntry* newVal, ListEntry* oldVal) noexcept + { + if (oldVal && oldVal->isBusy(newVal->traNumber)) // modified in other transaction + return false; + + newVal->next.store(oldVal, atomics::memory_order_release); + 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 gc(thread_db* tdbb, atomics::atomic* list, const TraNumber oldest) + { + TraNumber rc = 0; + for (HazardPtr entry(*list); entry; list = &entry->next, entry.set(*list)) + { + if (!(entry->getFlags() & CacheFlag::COMMITTED)) + continue; + + if (rc && entry->traNumber < oldest) + { + if (entry->cacheFlags.fetch_or(CacheFlag::RETIRED) & CacheFlag::RETIRED) + break; // someone else also performs GC + + // split remaining list off + if (entry.replace(*list, nullptr)) + { + while (entry)// && !(entry->cacheFlags.fetch_or(CacheFlag::RETIRED) & CacheFlag::RETIRED)) + { + if (entry->object) + { + OBJ::destroy(tdbb, entry->object); + entry->object = nullptr; + } + entry->retire(); + entry.set(entry->next); + if (entry && (entry->cacheFlags.fetch_or(CacheFlag::RETIRED) & CacheFlag::RETIRED)) + break; + } + } + break; + } + + // store traNumber of last not removed list element + rc = entry->traNumber; + } + + return rc; // 0 is returned in a case when list was empty + } + + // created (erased) earlier object is OK and should become visible to the world + // return true if object was erased + bool commit(thread_db* tdbb, TraNumber currentTrans, TraNumber nextTrans) + { + TraNumber oldNumber = traNumber; + + traNumber = nextTrans; + version = VersionSupport::next(tdbb); + auto flags = cacheFlags.fetch_or(CacheFlag::COMMITTED); // !!!!!!!!!!!!!!!! CMPXCHG!!! + + fb_assert((flags & CacheFlag::COMMITTED ? nextTrans : currentTrans) == oldNumber); + + return flags & CacheFlag::ERASED; + } + + // created earlier object is bad and should be destroyed + static void rollback(thread_db* tdbb, atomics::atomic& 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 entry(list); + while (entry) + { + if (entry->getFlags() & CacheFlag::COMMITTED) + break; + fb_assert(entry->traNumber == currentTran); + + if (entry.replace(list, entry->next)) + { + entry->next = nullptr; + auto* obj = entry->object; + if (obj) + OBJ::destroy(tdbb, obj); + entry->object = nullptr; + entry->retire(); + + entry = list; + } + } + } + + void assertCommitted() + { + fb_assert(getFlags() & CacheFlag::COMMITTED); + } + +private: + static ScanResult scan(thread_db* tdbb, OBJ* obj, ObjectBase::Flag fl, bool rld = false) + { + if ((fl & CacheFlag::NOSCAN) || (!obj)) + return ScanResult::SKIP; + + auto* flags = TransactionNumber::getFlags(tdbb); + Firebird::AutoSetRestoreFlag readCommitted(flags, + (*flags) & TRA_degree3 ? 0 : TRA_read_committed | TRA_rec_version, true); + + return rld ? obj->reload(tdbb, fl) : obj->scan(tdbb, fl); + } + +public: + ScanResult scan(thread_db* tdbb, ObjectBase::Flag fl) + { + // no need opening barrier twice + // explicitly done first cause will be done in 99.99% + if (state == READY) + return ScanResult::COMPLETE; + + // enable recursive no-action pass by scanning thread + // if thd is current thread state is not going to be changed - current thread holds mutex + if ((thd == Thread::getId()) && (state == SCANNING)) + return ScanResult::COMPLETE; + + std::unique_lock g(mtx); + for(;;) + { + bool reason = true; + auto savedState = state; + + switch (state) + { + case INITIAL: // Our thread becomes scanning thread + reason = false; + // fall through... + case RELOAD: // may be because object body reload required. + thd = Thread::getId(); + state = SCANNING; + + try + { + auto result = scan(tdbb, object, fl, reason); + switch (result) + { + case ScanResult::COMPLETE: + state = READY; + break; + + case ScanResult::SKIP: + state = savedState; + break; + + case ScanResult::REPEAT: // scan complete but reload was requested + state = RELOAD; + break; + + case ScanResult::MISS: // Hey, we scan existing object? What a hell... + state = savedState; + break; + + default: + fb_assert(false); + state = savedState; + break; + } + + thd = 0; + cond.notify_all(); // other threads may proceed successfully + return result; + + } + catch(...) // scan failed - give up + { + state = savedState; + thd = 0; + cond.notify_all(); // avoid deadlock in other threads + + throw; + } + + + case SCANNING: // other thread is already scanning object + cond.wait(g, [this]{ return state != SCANNING; }); + continue; // repeat check of FLG value + + case READY: + return ScanResult::COMPLETE; + } + } + } + + bool isReady() + { + return (state == READY) || ((thd == Thread::getId()) && (state == SCANNING)); + } + + bool scanInProgress() const + { + return state == READY ? false : (thd == Thread::getId()) && (state == SCANNING); + } + +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 + + std::condition_variable cond; + std::mutex mtx; + OBJ* object; + atomics::atomic next = nullptr; + 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 = 0; // version of metadata cache when object was added + ThreadId thd = 0; // thread that performs object scan() + atomics::atomic cacheFlags; + State state; // current entry state +}; + + +enum class StoreResult { DUP, DONE, MISS, SKIP }; + + +typedef class Lock* MakeLock(thread_db*, MemoryPool&); + +template +class CacheElement : public ElementBase, public P +{ +public: + typedef V Versioned; + typedef P Permanent; + + typedef atomics::atomic AtomicElementPointer; + + template + CacheElement(thread_db* tdbb, MemoryPool& p, MetaId id, MakeLock* makeLock, EXTEND extend) : + Permanent(tdbb, p, id, makeLock, extend), list(nullptr), resetAt(0), ptrToClean(nullptr) + { } + + CacheElement(MemoryPool& p) : + Permanent(p), list(nullptr), resetAt(0), ptrToClean(nullptr) + { } + + static void cleanup(thread_db* tdbb, CacheElement* element) + { + auto* ptr = element->list.load(atomics::memory_order_relaxed); + if (ptr) + { + ptr->cleanup(tdbb); + delete ptr; + } + + if (element->ptrToClean) + *element->ptrToClean = nullptr; + + if (!Permanent::destroy(tdbb, element)) + { + // destroy() returns true if it completed removal of permamnet part (delete by pool) + // if not - delete it ourself here + delete element; + } + } + + void cleanup(thread_db* tdbb) override + { + cleanup(tdbb, this); + } + + void setCleanup(AtomicElementPointer* clearPtr) + { + ptrToClean = clearPtr; + } + + void reload(thread_db* tdbb, ObjectBase::Flag fl) + { + HazardPtr> listEntry(list); + TraNumber cur = TransactionNumber::current(tdbb); + if (listEntry) + { + fl &= ~CacheFlag::AUTOCREATE; + Versioned* obj = ListEntry::getObject(tdbb, listEntry, cur, fl); + if (obj) + listEntry->scan(tdbb, fl); + } + } + + Versioned* getObject(thread_db* tdbb, ObjectBase::Flag fl) + { + return getObject(tdbb, TransactionNumber::current(tdbb), fl); + } + + bool isReady(thread_db* tdbb) + { + auto entry = getEntry(tdbb, TransactionNumber::current(tdbb), CacheFlag::NOSCAN | CacheFlag::NOCOMMIT); + return entry && entry->isReady(); + } + + enum Availability {MISSING, MODIFIED, OCCUPIED, READY}; + + Availability isAvailable(thread_db* tdbb, TraNumber* number = nullptr) + { + auto entry = list.load(atomics::memory_order_acquire); + //getEntry(tdbb, TransactionNumber::current(tdbb), CacheFlag::NOSCAN | CacheFlag::NOCOMMIT); + if (!entry) + return MISSING; + if (entry->isBusy(TransactionNumber::current(tdbb), number)) + return OCCUPIED; + return entry->isReady() ? READY : MODIFIED; + } + + ObjectBase::Flag getFlags(thread_db* tdbb) + { + auto entry = getEntry(tdbb, TransactionNumber::current(tdbb), CacheFlag::NOSCAN | CacheFlag::NOCOMMIT); + return entry ? entry->getFlags() : 0; + } + + Versioned* getObject(thread_db* tdbb, TraNumber traNum, ObjectBase::Flag fl) + { + auto entry = getEntry(tdbb, traNum, fl); + return entry ? entry->getObject() : nullptr; + } + + HazardPtr> getEntry(thread_db* tdbb, TraNumber traNum, ObjectBase::Flag fl) + { + HazardPtr> listEntry(list); + if (!listEntry) + { + if (!(fl & CacheFlag::AUTOCREATE)) + return listEntry; // nullptr + + fb_assert(tdbb); + + // create almost empty object ... + Versioned* obj = Versioned::create(tdbb, this->getPool(), this); + + // make new entry to store it in cache + ListEntry* newEntry = nullptr; + try + { + newEntry = FB_NEW_POOL(*getDefaultMemoryPool()) ListEntry(obj, traNum, fl); + } + catch (const Firebird::Exception&) + { + if (obj) + Versioned::destroy(tdbb, obj); + throw; + } + + if (ListEntry::replace(list, newEntry, nullptr)) + { + auto sr = newEntry->scan(tdbb, fl); + if (! (fl & CacheFlag::NOCOMMIT)) + newEntry->commit(tdbb, traNum, TransactionNumber::next(tdbb)); + + switch (sr) + { + case ScanResult::COMPLETE: + case ScanResult::REPEAT: + break; + + case ScanResult::MISS: + case ScanResult::SKIP: + default: + return listEntry; // nullptr + } + + return HazardPtr>(newEntry); + } + + newEntry->cleanup(tdbb); + delete newEntry; + fb_assert(list.load()); + listEntry = list; + } + return ListEntry::getEntry(tdbb, listEntry, traNum, fl); + } + + // return latest committed version or nullptr when does not exist + Versioned* getLatestObject(thread_db* tdbb) const + { + HazardPtr> listEntry(list); + if (!listEntry) + return nullptr; + + return ListEntry::getObject(tdbb, listEntry, MAX_TRA_NUMBER, 0); + } + + StoreResult storeObject(thread_db* tdbb, Versioned* obj, ObjectBase::Flag fl) + { + TraNumber oldest = TransactionNumber::oldestActive(tdbb); + TraNumber oldResetAt = resetAt.load(atomics::memory_order_acquire); + if (oldResetAt && oldResetAt < oldest) + setNewResetAt(oldResetAt, ListEntry::gc(tdbb, &list, oldest)); + + TraNumber cur = TransactionNumber::current(tdbb); + ListEntry* newEntry = FB_NEW_POOL(*getDefaultMemoryPool()) ListEntry(obj, cur, fl); + if (!ListEntry::add(tdbb, list, newEntry)) + { + newEntry->cleanup(tdbb, false); + delete newEntry; + return StoreResult::DUP; + } + setNewResetAt(oldResetAt, cur); + + auto rc = StoreResult::SKIP; + if (obj && !(fl & CacheFlag::NOSCAN)) + { + auto sr = newEntry->scan(tdbb, fl); + switch (sr) + { + case ScanResult::COMPLETE: + case ScanResult::REPEAT: + rc = StoreResult::DONE; + break; + + case ScanResult::MISS: + rc = StoreResult::MISS; + break; + } + } + + if (!(fl & CacheFlag::NOCOMMIT)) + newEntry->commit(tdbb, cur, TransactionNumber::next(tdbb)); + + return rc; + } + + Versioned* makeObject(thread_db* tdbb, ObjectBase::Flag fl) + { + auto obj = Versioned::create(tdbb, Permanent::getPool(), this); + if (!obj) + (Firebird::Arg::Gds(isc_random) << "Object create failed in makeObject()").raise(); + + switch (storeObject(tdbb, obj, fl)) + { + case StoreResult::DUP: + Versioned::destroy(tdbb, obj); + break; + + case StoreResult::SKIP: + case StoreResult::DONE: + return obj; + + case StoreResult::MISS: + break; + } + + return nullptr; + } + + void commit(thread_db* tdbb) + { + HazardPtr> current(list); + if (current) + { + if (current->commit(tdbb, TransactionNumber::current(tdbb), TransactionNumber::next(tdbb))) + commitErase(tdbb); + } + } + + void rollback(thread_db* tdbb) + { + ListEntry::rollback(tdbb, list, TransactionNumber::current(tdbb)); + } +/* + void gc() + { + list.load()->assertCommitted(); + ListEntry::gc(&list, MAX_TRA_NUMBER); + } + */ + void resetDependentObject(thread_db* tdbb, ResetType rt) override + { + switch (rt) + { + case ElementBase::ResetType::Recompile: + { + Versioned* newObj = Versioned::create(tdbb, CachePool::get(tdbb), this); + if (storeObject(tdbb, newObj, CacheFlag::NOCOMMIT) == StoreResult::DUP) + { + Versioned::destroy(tdbb, newObj); + busyError(tdbb, this->getId(), this->c_name(), V::objectFamily(this)); + } + } + break; + + case ElementBase::ResetType::Mark: + // used in AST, therefore ignore error when saving empty object + storeObject(tdbb, nullptr, 0); + break; + + case ElementBase::ResetType::Commit: + commit(tdbb); + break; + + case ElementBase::ResetType::Rollback: + rollback(tdbb); + break; + } + } + + CacheElement* erase(thread_db* tdbb) + { + HazardPtr> l(list); + fb_assert(l); + if (!l) + return nullptr; + + if (storeObject(tdbb, nullptr, CacheFlag::ERASED | CacheFlag::NOCOMMIT) == StoreResult::DUP) + { + Versioned* oldObj = getObject(tdbb, 0); + busyError(tdbb, this->getId(), this->c_name(), V::objectFamily(this)); + } + + return this; + } + + bool isDropped() const + { + auto* l = list.load(atomics::memory_order_relaxed); + return l && (l->getFlags() & CacheFlag::ERASED); + } + + bool scanInProgress() const + { + HazardPtr> listEntry(list); + if (!listEntry) + return false; + + return listEntry->scanInProgress(); + } + + static int getObjectType() + { + return Versioned::objectType(); + } + + void tagForUpdate(thread_db* tdbb) + { + TraNumber traNum; + + switch (isAvailable(tdbb, &traNum)) + { + case OCCUPIED: + Firebird::fatal_exception::raiseFmt("tagForUpdate: %s %s is used by transaction %d\n", + Versioned::objectFamily(this), this->c_name(), traNum); + + case MODIFIED: + return; + + case MISSING: + case READY: + if (scanInProgress()) + { + Firebird::fatal_exception::raiseFmt("tagForUpdate: %s %s is scanned by us\n", + Versioned::objectFamily(this), this->c_name()); + } + makeObject(tdbb, CacheFlag::TAG_FOR_UPDATE); + return; + } + } + +private: + void setNewResetAt(TraNumber oldVal, TraNumber newVal) + { + resetAt.compare_exchange_strong(oldVal, newVal, + atomics::memory_order_release, atomics::memory_order_relaxed); + } + +private: + atomics::atomic*> list; + atomics::atomic resetAt; + AtomicElementPointer* ptrToClean; +}; + + +struct NoData +{ + NoData() { } +}; + +template +class CacheVector : public Firebird::PermanentStorage +{ +public: + static const unsigned SUBARRAY_SIZE = 1 << SUBARRAY_SHIFT; + static const unsigned SUBARRAY_MASK = SUBARRAY_SIZE - 1; + + typedef typename StoredElement::Versioned Versioned; + typedef typename StoredElement::Permanent Permanent; + typedef typename StoredElement::AtomicElementPointer SubArrayData; + typedef atomics::atomic ArrayData; + typedef SharedReadVector Storage; + + explicit CacheVector(MemoryPool& pool, EXTEND extend = NoData()) + : Firebird::PermanentStorage(pool), + m_objects(), + m_extend(extend) + {} + +private: + static FB_SIZE_T getCount(const HazardPtr& 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, false); + 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->addStart()->store(sub, atomics::memory_order_release); + wa->addComplete(); + } + } + +public: + StoredElement* getDataNoChecks(MetaId id) const + { + SubArrayData* ptr = getDataPointer(id); + return ptr ? ptr->load(atomics::memory_order_relaxed) : nullptr; + } + + StoredElement* getData(thread_db* tdbb, MetaId id, ObjectBase::Flag fl) const + { + SubArrayData* ptr = getDataPointer(id); + + if (ptr) + { + StoredElement* rc = ptr->load(atomics::memory_order_relaxed); + if (rc && rc->getEntry(tdbb, TransactionNumber::current(tdbb), fl)) + return rc; + } + + return nullptr; + } + + FB_SIZE_T getCount() const + { + return getCount(m_objects.readAccessor()); + } + + Versioned* getObject(thread_db* tdbb, MetaId id, ObjectBase::Flag fl) + { + +// 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) + { + StoredElement* data = ptr->load(atomics::memory_order_acquire); + if (data) + { + if (fl & CacheFlag::UPGRADE) + { + auto val = makeObject(tdbb, id, fl); + if (val) + return val; + continue; + } + + return data->getObject(tdbb, fl); + } + } + + if (!(fl & CacheFlag::AUTOCREATE)) + return nullptr; + + auto val = makeObject(tdbb, id, fl); + if (val) + return val; + } +#ifdef DEV_BUILD + (Firebird::Arg::Gds(isc_random) << "Object suddenly disappeared").raise(); +#endif + } + + StoredElement* erase(thread_db* tdbb, MetaId id) + { + auto ptr = getDataPointer(id); + if (ptr) + { + StoredElement* data = ptr->load(atomics::memory_order_acquire); + if (data) + return data->erase(tdbb); + } + + return nullptr; + } + + Versioned* makeObject(thread_db* tdbb, MetaId id, ObjectBase::Flag fl) + { + if (id >= getCount()) + grow(id + 1); + + auto ptr = getDataPointer(id); + fb_assert(ptr); + + StoredElement* data = ptr->load(atomics::memory_order_acquire); + if (!data) + { + StoredElement* newData = FB_NEW_POOL(getPool()) + StoredElement(tdbb, getPool(), id, Versioned::makeLock, m_extend); + if (ptr->compare_exchange_strong(data, newData, + atomics::memory_order_release, atomics::memory_order_acquire)) + { + newData->setCleanup(ptr); + data = newData; + } + else + StoredElement::cleanup(tdbb, newData); + } + + return data->makeObject(tdbb, fl); + } + + void tagForUpdate(thread_db* tdbb, MetaId id) + { + fb_assert(id < getCount()); + if (id < getCount()) + { + auto ptr = getDataPointer(id); + fb_assert(ptr); + + StoredElement* data = ptr->load(atomics::memory_order_acquire); + if (data) + data->tagForUpdate(tdbb); + } + + makeObject(tdbb, id, CacheFlag::TAG_FOR_UPDATE); + } + + template + StoredElement* lookup(thread_db* tdbb, F&& cmp, ObjectBase::Flag fl) 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--;) + { + StoredElement* ptr = end->load(atomics::memory_order_relaxed); + if (ptr) + { + auto listEntry = ptr->getEntry(tdbb, TransactionNumber::current(tdbb), fl | CacheFlag::NOSCAN); + if (listEntry && cmp(ptr)) + { + // Optimize ?????????????? + if (!(fl & CacheFlag::ERASED)) + ptr->reload(tdbb, fl); + return ptr; + } + } + } + } + + return nullptr; + } + + ~CacheVector() + { + fb_assert(!m_objects.hasData()); + } + + void cleanup(thread_db* tdbb) + { + 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--;) + { + StoredElement* elem = end->load(atomics::memory_order_relaxed); + if (!elem) + continue; + + StoredElement::cleanup(tdbb, elem); + fb_assert(!end->load(atomics::memory_order_relaxed)); + } + + delete[] sub; // no need using retire() here in CacheVector's cleanup + a->value(i).store(nullptr, atomics::memory_order_relaxed); + } + + m_objects.clear(); + } + + 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; + } + + HazardPtr readAccessor() const + { + return m_objects.readAccessor(); + } + + class Iterator + { + static const FB_SIZE_T eof = ~0u; + static const FB_SIZE_T endloop = ~0u; + + public: + StoredElement* 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 || + (index == endloop && itr.index == eof) || + (itr.index == endloop && index == eof); + } + + bool operator!=(const Iterator& itr) const + { + fb_assert(data == itr.data); + return !operator==(itr); + } + + private: + void* operator new(size_t) = delete; + void* operator new[](size_t) = delete; + + public: + enum class Location {Begin, End}; + Iterator(const CacheVector* v, Location loc) + : data(v), + index(loc == Location::Begin ? locateData(0) : endloop) + { } + + StoredElement* get() + { + fb_assert(index != eof); + if (index == eof) + return nullptr; + StoredElement* ptr = data->getDataNoChecks(index); + fb_assert(ptr); + return ptr; + } + + private: + FB_SIZE_T locateData(FB_SIZE_T i) + { + while (i < data->getCount()) + { + if (data->getDataNoChecks(i)) + return i; + ++i; + } + + return eof; + } + + 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; + EXTEND m_extend; +}; + +template +auto getPermanent(T* t) -> decltype(t->getPermanent()) +{ + return t ? t->getPermanent() : nullptr; +} + +} // namespace Jrd + +#endif // JRD_CACHEVECTOR_H diff --git a/src/jrd/CharSetContainer.cpp b/src/jrd/CharSetContainer.cpp new file mode 100644 index 00000000000..b525d203d65 --- /dev/null +++ b/src/jrd/CharSetContainer.cpp @@ -0,0 +1,96 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: CharSetContainer.cpp + * 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 + */ + +#include "firebird.h" +#include "../jrd/CharSetContainer.h" +#include "../jrd/jrd.h" +#include "../jrd/obj.h" + +using namespace Jrd; + +CharSetVers* CharSetVers::create(thread_db* tdbb, MemoryPool& pool, Cached::CharSet* csp) +{ + return FB_NEW_POOL(pool) CharSetVers(csp); +} + +void CharSetVers::destroy(thread_db*, CharSetVers* csv) +{ + delete csv; +} + +MetaId CharSetContainer::getId() +{ + return cs->getId(); +} + +Lock* CharSetVers::makeLock(thread_db* tdbb, MemoryPool& p) +{ + return FB_NEW_RPT(p, 0) Lock(tdbb, sizeof(SLONG), LCK_cs_exist, nullptr, CharSetContainer::blockingAst); +} + +int CharSetContainer::blockingAst(void* ast_object) +{ + auto* const charSet = static_cast(ast_object); + + try + { + Database* const dbb = charSet->cs_lock->lck_dbb; + + AsyncContextHolder tdbb(dbb, FB_FUNCTION, charSet->cs_lock); + + LCK_release(tdbb, charSet->cs_lock); + charSet->resetDependentObject(tdbb, ElementBase::ResetType::Mark); + } + catch (const Firebird::Exception&) + {} // no-op + + return 0; +} + +void CharSetContainer::releaseLocks(thread_db* tdbb) +{ + LCK_release(tdbb, cs_lock); +} + +bool CharSetContainer::destroy(thread_db* tdbb, CharSetContainer* container) +{ + container->cs->destroy(); + return false; +} + +int CharSetVers::objectType() +{ + return obj_charset; +} + +const char* CharSetContainer::c_name() const +{ + return cs->getName(); +} + +MetaName CharSetContainer::getName() const +{ + return cs->getName(); +} diff --git a/src/jrd/CharSetContainer.h b/src/jrd/CharSetContainer.h new file mode 100644 index 00000000000..0fe3cd99952 --- /dev/null +++ b/src/jrd/CharSetContainer.h @@ -0,0 +1,154 @@ +/* + * 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 + */ + +#ifndef JRD_CHARSETCONTAINER_H +#define JRD_CHARSETCONTAINER_H + +#include "../jrd/MetaName.h" +#include "../jrd/CacheVector.h" +#include "../jrd/Collation.h" +#include "../jrd/Resources.h" +#include "../jrd/met_proto.h" +#include "../common/classes/alloc.h" + + +namespace Firebird { + +class CharSet; +class CsConvert; + +} + + +namespace Jrd { + +struct SubtypeInfo; + +class CharSetContainer : public Firebird::PermanentStorage +{ +public: + CharSetContainer(thread_db* tdbb, MemoryPool& p, MetaId cs_id, MakeLock* makeLock, NoData); + + static bool destroy(thread_db* tdbb, CharSetContainer* container); + static CharSetContainer* create(thread_db* tdbb, MetaId id); + static int blockingAst(void* ast_object); + + Firebird::CharSet* getCharSet() + { + return cs; + } + + Firebird::CsConvert lookupConverter(thread_db* tdbb, CSetId to_cs); + + static CharSetContainer* lookupCharset(thread_db* tdbb, TTypeId ttype); + + bool hasData() const + { + return cs != nullptr; + } + + const char* c_name() const; + MetaName getName() const; + MetaId getId(); + + Lock* getLock() + { + return cs_lock; + } + + void releaseLocks(thread_db* tdbb); + +private: + static bool lookupInternalCharSet(CSetId id, SubtypeInfo* info); + +public: + CharsetVariants names; + +private: + Firebird::CharSet* cs; + Lock* cs_lock; +}; + +class CharSetVers final : public ObjectBase +{ +public: + CharSetVers(Cached::CharSet* parent) + : perm(parent), charset_collations(perm->getPool()) + { } + + const char* c_name() const override + { + return perm->c_name(); + } + + static const char* objectFamily(void*) + { + return "character set"; + } + + MetaId getId() + { + return perm->getId(); + } + + MetaName getName() const + { + return perm->getName(); + } + + static void destroy(thread_db* tdbb, CharSetVers* csv); + static CharSetVers* create(thread_db* tdbb, MemoryPool& p, Cached::CharSet* perm); + static Lock* makeLock(thread_db* tdbb, MemoryPool& p); + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); + + ScanResult reload(thread_db* tdbb, ObjectBase::Flag flags) + { + return scan(tdbb, flags); + } + + Collation* getCollation(CollId id); + Collation* getCollation(MetaName name); + Cached::CharSet* getContainer() const + { + return perm; + } + + static int objectType(); + +private: + Cached::CharSet* perm; + Firebird::HalfStaticArray charset_collations; + +public: + decltype(perm) getPermanent() const + { + return perm; + } +}; + +} // namespace Jrd + +#endif // JRD_CHARSETCONTAINER_H + diff --git a/src/jrd/Collation.cpp b/src/jrd/Collation.cpp index 94fde12f1c5..43b2ea2258d 100644 --- a/src/jrd/Collation.cpp +++ b/src/jrd/Collation.cpp @@ -97,7 +97,7 @@ #include "../jrd/err_proto.h" #include "../jrd/evl_string.h" #include "../jrd/intl_classes.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/intl_classes.h" #include "../jrd/intl_proto.h" #include "../jrd/Collation.h" @@ -990,7 +990,7 @@ template < class CollationImpl : public Collation { public: - CollationImpl(TTYPE_ID a_type, texttype* a_tt, USHORT a_attributes, CharSet* a_cs) + CollationImpl(TTypeId a_type, texttype* a_tt, USHORT a_attributes, CharSet* a_cs) : Collation(a_type, a_tt, a_attributes, a_cs) { } @@ -1068,7 +1068,7 @@ class CollationImpl : public Collation }; template -Collation* newCollation(MemoryPool& pool, TTYPE_ID id, texttype* tt, USHORT attributes, CharSet* cs) +Collation* newCollation(MemoryPool& pool, TTypeId id, texttype* tt, USHORT attributes, CharSet* cs) { using namespace Firebird; @@ -1107,7 +1107,7 @@ Collation* newCollation(MemoryPool& pool, TTYPE_ID id, texttype* tt, USHORT attr namespace Jrd { -Collation* Collation::createInstance(MemoryPool& pool, TTYPE_ID id, texttype* tt, USHORT attributes, CharSet* cs) +Collation* Collation::createInstance(MemoryPool& pool, TTypeId id, texttype* tt, USHORT attributes, CharSet* cs) { switch (tt->texttype_canonical_width) { @@ -1125,55 +1125,16 @@ Collation* Collation::createInstance(MemoryPool& pool, TTYPE_ID id, texttype* tt return NULL; // compiler silencer } -void Collation::release(thread_db* tdbb) -{ - fb_assert(useCount >= 0); - - if (existenceLock) - LCK_release(tdbb, existenceLock); - - useCount = 0; -} - void Collation::destroy(thread_db* tdbb) { - fb_assert(useCount == 0); + fprintf(stderr, "Collation::destroy(%p) tt=%p\n", this, tt); if (tt->texttype_fn_destroy) tt->texttype_fn_destroy(tt); delete tt; - release(tdbb); - - delete existenceLock; - existenceLock = NULL; -} - -void Collation::incUseCount(thread_db* /*tdbb*/) -{ - fb_assert(!obsolete); - fb_assert(useCount >= 0); - - ++useCount; + delete this; } -void Collation::decUseCount(thread_db* tdbb) -{ - fb_assert(useCount >= 0); - - if (useCount > 0) - { - useCount--; - - if (!useCount) - { - fb_assert(existenceLock); - if (obsolete) - LCK_re_post(tdbb, existenceLock); - } - } -} - - } // namespace Jrd diff --git a/src/jrd/Collation.h b/src/jrd/Collation.h index cd00977b1f1..c01d8b7b08b 100644 --- a/src/jrd/Collation.h +++ b/src/jrd/Collation.h @@ -31,23 +31,31 @@ #define JRD_COLLATION_H #include "../common/TextType.h" +#include "../jrd/CacheVector.h" +#include "../jrd/Resources.h" + +namespace Firebird { + +class CharSet; + +} namespace Jrd { class Lock; class BaseSubstringSimilarMatcher; +class PatternMatcher; class Collation : public Firebird::TextType { public: - static Collation* createInstance(MemoryPool& pool, TTYPE_ID id, texttype* tt, USHORT attributes, Firebird::CharSet* cs); + static Collation* createInstance(MemoryPool& pool, TTypeId id, texttype* tt, + USHORT attributes, Firebird::CharSet* cs); protected: - Collation(TTYPE_ID id, texttype *a_tt, USHORT a_attributes, Firebird::CharSet* a_cs) - : Firebird::TextType(id, a_tt, a_attributes, a_cs), - useCount(0), - existenceLock(NULL), + Collation(TTypeId id, texttype *a_tt, USHORT a_attributes, Firebird::CharSet* a_cs) + : TextType(id, a_tt, a_attributes, a_cs), obsolete(false) { } @@ -77,14 +85,11 @@ class Collation : public Firebird::TextType virtual bool contains(MemoryPool& pool, const UCHAR* s, SLONG sl, const UCHAR* p, SLONG pl) = 0; virtual PatternMatcher* createContainsMatcher(MemoryPool& pool, const UCHAR* p, SLONG pl) = 0; - void release(thread_db* tdbb); void destroy(thread_db* tdbb); void incUseCount(thread_db* tdbb); void decUseCount(thread_db* tdbb); public: - int useCount; - Lock* existenceLock; bool obsolete; }; diff --git a/src/jrd/ConfigTable.cpp b/src/jrd/ConfigTable.cpp index 4d70f65134b..1d56e5a3694 100644 --- a/src/jrd/ConfigTable.cpp +++ b/src/jrd/ConfigTable.cpp @@ -36,16 +36,16 @@ ConfigTable::ConfigTable(MemoryPool& pool, const Config* conf) : { } -RecordBuffer* ConfigTable::getRecords(thread_db* tdbb, jrd_rel* relation) +RecordBuffer* ConfigTable::getRecords(thread_db* tdbb, RelationPermanent* 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(); @@ -97,7 +97,7 @@ void ConfigTableScan::close(thread_db* tdbb) const VirtualTableScan::close(tdbb); } -const Format* ConfigTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +const Format* ConfigTableScan::getFormat(thread_db* tdbb, RelationPermanent* relation) const { RecordBuffer* records = getRecords(tdbb, relation); return records->getFormat(); @@ -106,11 +106,11 @@ const Format* ConfigTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) con bool ConfigTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const { - RecordBuffer* records = getRecords(tdbb, relation); + RecordBuffer* records = getRecords(tdbb, getPermanent(relation)); return records->fetch(position, record); } -RecordBuffer* ConfigTableScan::getRecords(thread_db* tdbb, jrd_rel* relation) const +RecordBuffer* ConfigTableScan::getRecords(thread_db* tdbb, RelationPermanent* relation) const { Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/ConfigTable.h b/src/jrd/ConfigTable.h index 61601b7fde9..a8932f04813 100644 --- a/src/jrd/ConfigTable.h +++ b/src/jrd/ConfigTable.h @@ -38,7 +38,7 @@ class ConfigTable : public SnapshotData ConfigTable(MemoryPool& pool, const Firebird::Config* conf); // return data for RDB$CONFIG - RecordBuffer* getRecords(thread_db* tdbb, jrd_rel* relation); + RecordBuffer* getRecords(thread_db* tdbb, RelationPermanent* relation); private: const Firebird::Config* m_conf; @@ -49,7 +49,7 @@ class ConfigTableScan final : public VirtualTableScan { public: ConfigTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : VirtualTableScan(csb, alias, stream, relation) { m_impure = csb->allocImpure(); @@ -58,7 +58,7 @@ class ConfigTableScan final : public VirtualTableScan void close(thread_db* tdbb) const override; protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; + const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const override; bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; @@ -69,7 +69,7 @@ class ConfigTableScan final : public VirtualTableScan ConfigTable* table; }; - RecordBuffer* getRecords(thread_db* tdbb, jrd_rel* relation) const; + RecordBuffer* getRecords(thread_db* tdbb, RelationPermanent* relation) const; ULONG m_impure; }; diff --git a/src/jrd/CryptoManager.cpp b/src/jrd/CryptoManager.cpp index 118d0b54158..697a3b14aab 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -41,7 +41,7 @@ #include "../jrd/pag.h" #include "../jrd/nbak.h" #include "../jrd/cch_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/pag_proto.h" #include "firebird/impl/inf_pub.h" #include "../jrd/Monitoring.h" diff --git a/src/jrd/DataTypeUtil.cpp b/src/jrd/DataTypeUtil.cpp index 581ab201352..0199e78fc9d 100644 --- a/src/jrd/DataTypeUtil.cpp +++ b/src/jrd/DataTypeUtil.cpp @@ -56,13 +56,13 @@ SSHORT DataTypeUtilBase::getResultBlobSubType(const dsc* value1, const dsc* valu } -USHORT DataTypeUtilBase::getResultTextType(const dsc* value1, const dsc* value2) +TTypeId DataTypeUtilBase::getResultTextType(const dsc* value1, const dsc* value2) { - const USHORT cs1 = value1->getCharSet(); - const USHORT cs2 = value2->getCharSet(); + const auto cs1 = value1->getCharSet(); + const auto cs2 = value2->getCharSet(); - const USHORT ttype1 = value1->getTextType(); - const USHORT ttype2 = value2->getTextType(); + const auto ttype1 = value1->getTextType(); + const auto ttype2 = value2->getTextType(); if (cs1 == CS_NONE || cs2 == CS_BINARY) return ttype2; @@ -214,7 +214,7 @@ void DataTypeUtilBase::makeFromList(dsc* result, const char* expressionName, int } -ULONG DataTypeUtilBase::convertLength(ULONG len, USHORT srcCharSet, USHORT dstCharSet) +ULONG DataTypeUtilBase::convertLength(ULONG len, CSetId srcCharSet, CSetId dstCharSet) { if (dstCharSet == CS_NONE || dstCharSet == CS_BINARY) return len; @@ -307,7 +307,7 @@ void DataTypeUtilBase::makeSubstr(dsc* result, const dsc* value, const dsc* offs result->dsc_dtype = dtype_varying; } - result->setTextType(value->isText() || value->isBlob() ? value->getTextType() : CS_ASCII); + result->setTextType(value->isText() || value->isBlob() ? value->getTextType() : TTypeId(CS_ASCII)); result->setNullable(value->isNullable() || (offset && offset->isNullable()) || (length && length->isNullable())); @@ -352,7 +352,7 @@ bool DataTypeUtilBase::makeBlobOrText(dsc* result, const dsc* arg, bool force) namespace Jrd { -UCHAR DataTypeUtil::maxBytesPerChar(UCHAR charSet) +UCHAR DataTypeUtil::maxBytesPerChar(CSetId charSet) { return INTL_charset_lookup(tdbb, charSet)->maxBytesPerChar(); } @@ -363,7 +363,7 @@ USHORT DataTypeUtil::getDialect() const } // Returns false if conversion is not needed. -bool DataTypeUtil::convertToUTF8(const string& src, string& dst, CHARSET_ID charset, ErrorFunction err) +bool DataTypeUtil::convertToUTF8(const string& src, string& dst, CSetId charset, ErrorFunction err) { thread_db* tdbb = JRD_get_thread_data(); diff --git a/src/jrd/DataTypeUtil.h b/src/jrd/DataTypeUtil.h index 5232e031f45..957687d713d 100644 --- a/src/jrd/DataTypeUtil.h +++ b/src/jrd/DataTypeUtil.h @@ -30,6 +30,7 @@ #include "../intl/charsets.h" #include "../common/classes/fb_string.h" #include "../jrd/err_proto.h" +#include "../jrd/intl.h" struct dsc; @@ -40,11 +41,11 @@ class DataTypeUtilBase public: static SSHORT getResultBlobSubType(const dsc* value1, const dsc* value2); - static USHORT getResultTextType(const dsc* value1, const dsc* value2); + static TTypeId getResultTextType(const dsc* value1, const dsc* value2); public: void makeFromList(dsc* result, const char* expressionName, int argsCount, const dsc** args); - ULONG convertLength(ULONG len, USHORT srcCharSet, USHORT dstCharSet); + ULONG convertLength(ULONG len, CSetId srcCharSet, CSetId dstCharSet); ULONG convertLength(const dsc* src, const dsc* dst); ULONG fixLength(const dsc* desc, ULONG length); @@ -55,7 +56,7 @@ class DataTypeUtilBase bool makeBlobOrText(dsc* result, const dsc* arg, bool force); public: - virtual UCHAR maxBytesPerChar(UCHAR charSet) = 0; + virtual UCHAR maxBytesPerChar(CSetId charSet) = 0; virtual USHORT getDialect() const = 0; // returns client dialect in DSQL and database dialect in JRD }; @@ -73,12 +74,12 @@ class DataTypeUtil : public DataTypeUtilBase } public: - virtual UCHAR maxBytesPerChar(UCHAR charSet); + virtual UCHAR maxBytesPerChar(CSetId charSet); virtual USHORT getDialect() const; public: static bool convertToUTF8(const Firebird::string& src, Firebird::string& dst, - CHARSET_ID charset = CS_dynamic, ErrorFunction err = ERR_post); + CSetId charset = CS_dynamic, ErrorFunction err = ERR_post); private: thread_db* tdbb; diff --git a/src/jrd/Database.cpp b/src/jrd/Database.cpp index eb5f17ed040..a7d24cef42c 100644 --- a/src/jrd/Database.cpp +++ b/src/jrd/Database.cpp @@ -36,11 +36,12 @@ #include "../jrd/met_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/tpc_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/CryptoManager.h" #include "../jrd/os/pio_proto.h" #include "../common/os/os_utils.h" -//#include "../dsql/Parser.h" +#include "../jrd/met.h" +#include "../jrd/Statement.h" // Thread data block #include "../common/ThreadData.h" @@ -168,19 +169,27 @@ namespace Jrd delete[] dbb_sort_buffers.pop(); } - { // scope - SyncLockGuard guard(&dbb_pools_sync, SYNC_EXCLUSIVE, "Database::~Database"); - - fb_assert(dbb_pools[0] == dbb_permanent); - - for (FB_SIZE_T i = 1; i < dbb_pools.getCount(); ++i) - MemoryPool::deletePool(dbb_pools[i]); - } - delete dbb_tip_cache; delete dbb_monitoring_data; delete dbb_backup_manager; delete dbb_crypto_manager; + delete dbb_mdc; + + fb_assert(dbb_pools[0] == dbb_permanent); + + for (FB_SIZE_T i = 1; i < dbb_pools.getCount(); ++i) + { + MemoryPool::deletePool(dbb_pools[i]); + } + } + + MemoryPool* Database::createPool(ALLOC_PARAMS0) + { + MemoryPool* const pool = MemoryPool::createPool(ALLOC_PASS_ARGS1 dbb_permanent, dbb_memory_stats); + + Firebird::SyncLockGuard guard(&dbb_pools_sync, Firebird::SYNC_EXCLUSIVE, "Database::createPool"); + dbb_pools.add(pool); + return pool; } void Database::deletePool(MemoryPool* pool) @@ -474,6 +483,75 @@ namespace Jrd dbb_filename, dbb_config)); } + // Find an inactive incarnation of a system request. + Request* Database::findSystemRequest(thread_db* tdbb, USHORT id, InternalRequest which) + { + fb_assert(which == IRQ_REQUESTS || which == DYN_REQUESTS || which == CACHED_REQUESTS); + + //Database::CheckoutLockGuard guard(this, dbb_cmp_clone_mutex); + + auto* stPtr = &(which == IRQ_REQUESTS ? dbb_internal : dbb_dyn_req)[id]; + if (which == CACHED_REQUESTS) + { + auto readAccessor = dbb_internal_cached_statements.readAccessor(); + if (id >= readAccessor->getCount()) + return nullptr; + + stPtr = &(readAccessor->value(id)); + } + + Statement* statement = stPtr->load(std::memory_order_relaxed); + + // If the request hasn't been compiled, there're nothing to do. + if (!statement) + return nullptr; + + return statement->findRequest(tdbb); + } + + // Store newly compiled request in the cache + Request* Database::cacheRequest(InternalRequest which, USHORT id, Request* req) + { + bool ok = req->setUsed(); + fb_assert(ok); + + MutexEnsureUnlock g(dbb_internal_cached_mutex, FB_FUNCTION); + + auto* stPtr = &(which == IRQ_REQUESTS ? dbb_internal : dbb_dyn_req)[id]; + if (which == CACHED_REQUESTS) + { + g.enter(); + auto writeAccessor = dbb_internal_cached_statements.writeAccessor(); + if (id >= writeAccessor->getCount()) + { + dbb_internal_cached_statements.grow(id + 1, true); + writeAccessor = dbb_internal_cached_statements.writeAccessor(); + } + stPtr = &(writeAccessor->value(id)); + } + + Statement* existingStmt = stPtr->load(std::memory_order_acquire); + Statement* const compiledStmt = req->getStatement(); + if (!existingStmt) + { + if (stPtr->compare_exchange_strong(existingStmt, compiledStmt, + std::memory_order_release, std::memory_order_acquire)) + { + return req; + } + } + + // Someone else already stored system request in the cache + // Let's use it + fb_assert(existingStmt); + + thread_db* tdbb = JRD_get_thread_data(); + req->setUnused(); + compiledStmt->release(tdbb); + + return existingStmt->findRequest(tdbb); + } + // Methods encapsulating operations with vectors of known pages ULONG Database::getKnownPagesCount(SCHAR ptype) @@ -659,6 +737,45 @@ namespace Jrd return m_replMgr; } + Database::Database(MemoryPool* p, Firebird::IPluginConfig* pConf, bool shared) + : dbb_permanent(p), + dbb_page_manager(this, *p), + dbb_file_id(*p), + dbb_modules(*p), + dbb_internal(*p), + dbb_dyn_req(*p), + dbb_extManager(nullptr), + dbb_flags(shared ? DBB_shared : 0), + dbb_filename(*p), + dbb_database_name(*p), +#ifdef HAVE_ID_BY_NAME + dbb_id(*p), +#endif + dbb_owner(*p), + dbb_pools(*p, 4), + dbb_sort_buffers(*p), + dbb_gc_fini(*p, garbage_collector, THREAD_medium), + dbb_stats(*p), + dbb_lock_owner_id(getLockOwnerId()), + dbb_tip_cache(NULL), + dbb_creation_date(Firebird::TimeZoneUtil::getCurrentGmtTimeStamp()), + dbb_external_file_directory_list(NULL), + dbb_init_fini(FB_NEW_POOL(*getDefaultMemoryPool()) ExistenceRefMutex()), + dbb_linger_seconds(0), + dbb_linger_end(0), + dbb_plugin_config(pConf), + dbb_repl_sequence(0), + dbb_replica_mode(REPLICA_NONE), + dbb_compatibility_index(~0U), + dbb_dic(*p), + dbb_mdc(FB_NEW_POOL(*p) MetadataCache(*p)) + { + dbb_pools.add(p); + + dbb_internal.grow(irq_MAX); + dbb_dyn_req.grow(drq_MAX); + } + bool Database::GlobalObjectHolder::incTempCacheUsage(FB_SIZE_T size) { if (m_tempCacheUsage + size > m_tempCacheLimit) @@ -685,4 +802,29 @@ namespace Jrd Database::GlobalObjectHolder::g_hashTable; GlobalPtr Database::GlobalObjectHolder::g_mutex; + void Database::releaseSystemRequests(thread_db* tdbb) + { + for (auto& itr : dbb_internal) + { + auto* stmt = itr.load(std::memory_order_relaxed); + if (stmt) + stmt->release(tdbb); + } + + for (auto& itr : dbb_dyn_req) + { + auto* stmt = itr.load(std::memory_order_relaxed); + if (stmt) + stmt->release(tdbb); + } + + auto writeAccessor = dbb_internal_cached_statements.writeAccessor(); + for (unsigned int n = 0; n < writeAccessor->getCount(); ++n) + { + auto* stmt = writeAccessor->value(n).load(std::memory_order_relaxed); + if (stmt) + stmt->release(tdbb); + } + } + } // namespace diff --git a/src/jrd/Database.h b/src/jrd/Database.h index e4d2e0ee321..1915bbe4642 100644 --- a/src/jrd/Database.h +++ b/src/jrd/Database.h @@ -35,6 +35,7 @@ #include "../common/gdsassert.h" #include "../common/dsc.h" #include "../jrd/btn.h" +#include "../jrd/vec.h" #include "../jrd/jrd_proto.h" #include "../jrd/val.h" #include "../jrd/irq.h" @@ -72,6 +73,7 @@ #include "../common/classes/SyncObject.h" #include "../common/classes/Synchronize.h" #include "../jrd/replication/Manager.h" +#include "../jrd/SharedReadVector.h" #include "../dsql/Keywords.h" #include "fb_types.h" @@ -92,120 +94,16 @@ class MonitoringData; class GarbageCollector; class CryptoManager; class KeywordsMap; +class MetadataCache; +class ExtEngineManager; -// general purpose vector -template -class vec_base : protected pool_alloc +// Flags to indicate normal internal requests vs. dyn internal requests +// IRQ_REQUESTS & DYN_REQUESTS are depecated +enum InternalRequest : USHORT { -public: - typedef typename Firebird::Array::iterator iterator; - typedef typename Firebird::Array::const_iterator const_iterator; - - /* - static vec_base* newVector(MemoryPool& p, int len) - { - return FB_NEW_POOL(p) vec_base(p, len); - } - - static vec_base* newVector(MemoryPool& p, const vec_base& base) - { - return FB_NEW_POOL(p) vec_base(p, base); - } - */ - - FB_SIZE_T count() const { return v.getCount(); } - T& operator[](FB_SIZE_T index) { return v[index]; } - const T& operator[](FB_SIZE_T index) const { return v[index]; } - - iterator begin() { return v.begin(); } - iterator end() { return v.end(); } - - const_iterator begin() const { return v.begin(); } - const_iterator end() const { return v.end(); } - - void clear() { v.clear(); } - - T* memPtr() { return &v[0]; } - - void resize(FB_SIZE_T n, T val = T()) { v.resize(n, val); } - - void operator delete(void* mem) { MemoryPool::globalFree(mem); } - -protected: - vec_base(MemoryPool& p, int len) - : v(p, len) - { - v.resize(len); - } - - vec_base(MemoryPool& p, const vec_base& base) - : v(p) - { - v = base.v; - } - -private: - Firebird::Array v; + NOT_REQUEST, IRQ_REQUESTS, DYN_REQUESTS, CACHED_REQUESTS }; -template -class vec : public vec_base -{ -public: - static vec* newVector(MemoryPool& p, int len) - { - return FB_NEW_POOL(p) vec(p, len); - } - - static vec* newVector(MemoryPool& p, const vec& base) - { - return FB_NEW_POOL(p) vec(p, base); - } - - static vec* newVector(MemoryPool& p, vec* base, int len) - { - if (!base) - base = FB_NEW_POOL(p) vec(p, len); - else if (len > (int) base->count()) - base->resize(len); - return base; - } - -private: - vec(MemoryPool& p, int len) : vec_base(p, len) {} - vec(MemoryPool& p, const vec& base) : vec_base(p, base) {} -}; - -class vcl : public vec_base -{ -public: - static vcl* newVector(MemoryPool& p, int len) - { - return FB_NEW_POOL(p) vcl(p, len); - } - - static vcl* newVector(MemoryPool& p, const vcl& base) - { - return FB_NEW_POOL(p) vcl(p, base); - } - - static vcl* newVector(MemoryPool& p, vcl* base, int len) - { - if (!base) - base = FB_NEW_POOL(p) vcl(p, len); - else if (len > (int) base->count()) - base->resize(len); - return base; - } - -private: - vcl(MemoryPool& p, int len) : vec_base(p, len) {} - vcl(MemoryPool& p, const vcl& base) : vec_base(p, base) {} -}; - -typedef vec TransactionsVector; - - // // bit values for dbb_flags // @@ -228,8 +126,9 @@ const ULONG DBB_new = 0x8000L; // Database object is just created const ULONG DBB_gc_cooperative = 0x10000L; // cooperative garbage collection const ULONG DBB_gc_background = 0x20000L; // background garbage collection by gc_thread const ULONG DBB_sweep_starting = 0x40000L; // Auto-sweep is starting -const ULONG DBB_creating = 0x80000L; // Database creation is in progress +const ULONG DBB_creating = 0x80000L; // Database creation is in progress const ULONG DBB_shared = 0x100000L; // Database object is shared among connections +const ULONG DBB_rescan_pages = 0x200000L; // Rescan pages after TIP cache creation // // dbb_ast_flags @@ -395,7 +294,7 @@ class Database : public pool_alloc static Database* create(Firebird::IPluginConfig* pConf, bool shared) { Firebird::MemoryStats temp_stats; - MemoryPool* const pool = MemoryPool::createPool(NULL, temp_stats); + MemoryPool* const pool = MemoryPool::createPool(ALLOC_ARGS1 NULL, temp_stats); Database* const dbb = FB_NEW_POOL(*pool) Database(pool, pConf, shared); pool->setStatsGroup(dbb->dbb_memory_stats); return dbb; @@ -465,6 +364,12 @@ class Database : public pool_alloc Firebird::SyncObject dbb_pages_sync; // guard access to dbb_XXX_pages vectors vcl* dbb_tip_pages; // known TIP pages vcl* dbb_gen_pages; // known generator pages + + Firebird::Array> dbb_internal; // internal statements DEPRECATED + Firebird::Array> dbb_dyn_req; // internal dyn statements DEPRECATED + SharedReadVector, 8> dbb_internal_cached_statements; // dynamic internal statements + Firebird::Mutex dbb_internal_cached_mutex; + public: Firebird::AutoPtr dbb_extManager; // external engine manager @@ -546,6 +451,8 @@ class Database : public pool_alloc Dictionary dbb_dic; // metanames dictionary Firebird::InitInstance dbb_keywords; + MetadataCache* dbb_mdc; + // returns true if primary file is located on raw device bool onRawDevice() const; @@ -563,15 +470,7 @@ class Database : public pool_alloc } #endif - MemoryPool* createPool() - { - MemoryPool* const pool = MemoryPool::createPool(dbb_permanent, dbb_memory_stats); - - Firebird::SyncLockGuard guard(&dbb_pools_sync, Firebird::SYNC_EXCLUSIVE, "Database::createPool"); - dbb_pools.add(pool); - return pool; - } - + MemoryPool* createPool(ALLOC_PARAMS0); void deletePool(MemoryPool* pool); void registerModule(Module&); @@ -598,39 +497,7 @@ class Database : public pool_alloc void copyKnownPages(SCHAR ptype, ULONG count, ULONG* data); private: - Database(MemoryPool* p, Firebird::IPluginConfig* pConf, bool shared) - : dbb_permanent(p), - dbb_page_manager(this, *p), - dbb_file_id(*p), - dbb_modules(*p), - dbb_extManager(nullptr), - dbb_flags(shared ? DBB_shared : 0), - dbb_filename(*p), - dbb_database_name(*p), -#ifdef HAVE_ID_BY_NAME - dbb_id(*p), -#endif - dbb_owner(*p), - dbb_pools(*p, 4), - dbb_sort_buffers(*p), - dbb_gc_fini(*p, garbage_collector, THREAD_medium), - dbb_stats(*p), - dbb_lock_owner_id(getLockOwnerId()), - dbb_tip_cache(NULL), - dbb_creation_date(Firebird::TimeZoneUtil::getCurrentGmtTimeStamp()), - dbb_external_file_directory_list(NULL), - dbb_init_fini(FB_NEW_POOL(*getDefaultMemoryPool()) ExistenceRefMutex()), - dbb_linger_seconds(0), - dbb_linger_end(0), - dbb_plugin_config(pConf), - dbb_repl_sequence(0), - dbb_replica_mode(REPLICA_NONE), - dbb_compatibility_index(~0U), - dbb_dic(*p) - { - dbb_pools.add(p); - } - + Database(MemoryPool* p, Firebird::IPluginConfig* pConf, bool shared); ~Database(); public: @@ -708,6 +575,10 @@ class Database : public pool_alloc dbb_gblobj_holder->decTempCacheUsage(size); } + Request* findSystemRequest(thread_db* tdbb, USHORT id, InternalRequest which); + Request* cacheRequest(InternalRequest which, USHORT id, Request* req); + void releaseSystemRequests(thread_db* tdbb); + private: //static int blockingAstSharedCounter(void*); static int blocking_ast_sweep(void* ast_object); diff --git a/src/jrd/DbCreators.cpp b/src/jrd/DbCreators.cpp index d075b4ed218..0f3af994597 100644 --- a/src/jrd/DbCreators.cpp +++ b/src/jrd/DbCreators.cpp @@ -251,7 +251,7 @@ CreateGrant checkCreateDatabaseGrant(const MetaString& userName, const MetaStrin } -const Format* DbCreatorsScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +const Format* DbCreatorsScan::getFormat(thread_db* tdbb, RelationPermanent* relation) const { jrd_tra* const transaction = tdbb->getTransaction(); return transaction->getDbCreatorsList()->getList(tdbb, relation)->getFormat(); @@ -261,7 +261,7 @@ bool DbCreatorsScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const { jrd_tra* const transaction = tdbb->getTransaction(); - return transaction->getDbCreatorsList()->getList(tdbb, relation)->fetch(position, record); + return transaction->getDbCreatorsList()->getList(tdbb, getPermanent(relation))->fetch(position, record); } DbCreatorsList::DbCreatorsList(jrd_tra* tra) @@ -275,10 +275,10 @@ RecordBuffer* DbCreatorsList::makeBuffer(thread_db* tdbb) return getData(rel_sec_db_creators); } -RecordBuffer* DbCreatorsList::getList(thread_db* tdbb, jrd_rel* relation) +RecordBuffer* DbCreatorsList::getList(thread_db* tdbb, RelationPermanent* 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) diff --git a/src/jrd/DbCreators.h b/src/jrd/DbCreators.h index a3a767891af..d3a6a5ecc11 100644 --- a/src/jrd/DbCreators.h +++ b/src/jrd/DbCreators.h @@ -50,12 +50,12 @@ class DbCreatorsScan: public VirtualTableScan { public: DbCreatorsScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : VirtualTableScan(csb, alias, stream, relation) {} protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; + const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const override; bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; }; @@ -65,7 +65,7 @@ class DbCreatorsList : public SnapshotData public: explicit DbCreatorsList(jrd_tra* tra); - RecordBuffer* getList(thread_db* tdbb, jrd_rel* relation); + RecordBuffer* getList(thread_db* tdbb, RelationPermanent* relation); private: RecordBuffer* makeBuffer(thread_db* tdbb); diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 8f28a59fcc3..9ad84bf5d18 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -47,6 +47,8 @@ #include "../jrd/Function.h" #include "../jrd/TimeZone.h" #include "../jrd/SystemPackages.h" +#include "../jrd/Statement.h" +#include "../jrd/met.h" #include "../common/isc_proto.h" #include "../common/classes/auto.h" #include "../common/classes/fb_pair.h" @@ -537,7 +539,7 @@ template class ExtEngineManager::ContextManager setCharSet(tdbb, attInfo, obj); } - ContextManager(thread_db* tdbb, EngineAttachmentInfo* aAttInfo, USHORT aCharSet, + ContextManager(thread_db* tdbb, EngineAttachmentInfo* aAttInfo, CSetId aCharSet, CallerName aCallerName = CallerName()) : attInfo(aAttInfo), attachment(tdbb->getAttachment()), @@ -594,9 +596,9 @@ template class ExtEngineManager::ContextManager charSetName[MAX_SQL_IDENTIFIER_LEN] = '\0'; } - USHORT charSetId; + TTypeId charSetId; - if (!MET_get_char_coll_subtype(tdbb, &charSetId, + if (!MetadataCache::get_char_coll_subtype(tdbb, &charSetId, reinterpret_cast(charSetName), static_cast(strlen(charSetName)))) { status_exception::raise(Arg::Gds(isc_charset_not_found) << Arg::Str(charSetName)); @@ -610,7 +612,7 @@ template class ExtEngineManager::ContextManager Jrd::Attachment* attachment; jrd_tra* transaction; // These data members are to restore the original information. - const USHORT charSet; + const CSetId charSet; const bool attInUse; const bool traInUse; CallerName callerName; @@ -1756,7 +1758,7 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ mainNode->statements.add(extProcedureNode); Statement* statement = prc->getStatement(); - PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0); + PAR_preparsed_node(tdbb, nullptr, mainNode, NULL, &csb, &statement, false, 0); prc->setStatement(statement); } catch (...) @@ -1792,7 +1794,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; @@ -1857,7 +1859,7 @@ void ExtEngineManager::makeTrigger(thread_db* tdbb, CompilerScratch* csb, Jrd::T const auto extTriggerNode = FB_NEW_POOL(csbPool) ExtTriggerNode(csbPool, extTrigger); mainNode->statements.add(extTriggerNode); - PAR_preparsed_node(tdbb, trg->relation, mainNode, NULL, &csb, &trg->statement, true, 0); + PAR_preparsed_node(tdbb, trg->relation->rel_perm, mainNode, NULL, &csb, &trg->statement, true, 0); } catch (...) { @@ -2002,7 +2004,7 @@ void ExtEngineManager::setupAdminCharSet(thread_db* tdbb, IExternalEngine* engin charSetName[MAX_SQL_IDENTIFIER_LEN] = '\0'; - if (!MET_get_char_coll_subtype(tdbb, &attInfo->adminCharSet, + if (!MetadataCache::get_char_coll_subtype(tdbb, &attInfo->adminCharSet, reinterpret_cast(charSetName), static_cast(strlen(charSetName)))) { diff --git a/src/jrd/ExtEngineManager.h b/src/jrd/ExtEngineManager.h index c75db6a6fcd..db0b16adef4 100644 --- a/src/jrd/ExtEngineManager.h +++ b/src/jrd/ExtEngineManager.h @@ -32,6 +32,7 @@ #include "../common/classes/fb_string.h" #include "../common/classes/GenericMap.h" #include "../jrd/MetaName.h" +// ???????????????????? #include "../jrd/HazardPtr.h" #include "../common/classes/NestConst.h" #include "../common/classes/auto.h" #include "../common/classes/rwlock.h" @@ -210,7 +211,7 @@ class ExtEngineManager final : public Firebird::PermanentStorage Firebird::IExternalEngine* engine; Firebird::AutoPtr context; - USHORT adminCharSet; + TTypeId adminCharSet; }; public: @@ -301,7 +302,7 @@ class ExtEngineManager final : public Firebird::PermanentStorage bool firstFetch; EngineAttachmentInfo* attInfo; Firebird::IExternalResultSet* resultSet; - USHORT charSet; + CSetId charSet; }; class Trigger final : public ExtRoutine diff --git a/src/jrd/Function.epp b/src/jrd/Function.epp index e6531a10ce4..5caeced9118 100644 --- a/src/jrd/Function.epp +++ b/src/jrd/Function.epp @@ -39,13 +39,15 @@ #include "../jrd/exe_proto.h" #include "../jrd/flu_proto.h" #include "../jrd/fun_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/par_proto.h" #include "../jrd/vio_proto.h" #include "../common/utils_proto.h" #include "../jrd/DebugInterface.h" +#include "../jrd/QualifiedName.h" +#include "../jrd/Statement.h" #include "../jrd/trace/TraceJrdHelpers.h" #include "../jrd/Function.h" @@ -58,177 +60,78 @@ DATABASE DB = FILENAME "ODS.RDB"; const char* const Function::EXCEPTION_MESSAGE = "The user defined function: \t%s\n\t referencing" " entrypoint: \t%s\n\t in module: \t%s\n\tcaused the fatal exception:"; -Function* Function::lookup(thread_db* tdbb, USHORT id, bool return_deleted, bool noscan, USHORT flags) -{ - Jrd::Attachment* attachment = tdbb->getAttachment(); - Function* check_function = NULL; - - Function* function = (id < attachment->att_functions.getCount()) ? attachment->att_functions[id] : NULL; - - if (function && function->getId() == id && - !(function->flags & Routine::FLAG_CLEARED) && - !(function->flags & Routine::FLAG_BEING_SCANNED) && - ((function->flags & Routine::FLAG_SCANNED) || noscan) && - !(function->flags & Routine::FLAG_BEING_ALTERED) && - (!(function->flags & Routine::FLAG_OBSOLETE) || return_deleted)) - { - if (!(function->flags & Routine::FLAG_CHECK_EXISTENCE)) - { - return function; - } - - check_function = function; - LCK_lock(tdbb, check_function->existenceLock, LCK_SR, LCK_WAIT); - } - - // We need to look up the function in RDB$FUNCTIONS - function = NULL; +Function* Function::lookup(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) +{ + Function* function = MetadataCache::get(tdbb)->getFunction(tdbb, id, flags); + return function; +} - AutoCacheRequest request(tdbb, irq_l_fun_id, IRQ_REQUESTS); - FOR(REQUEST_HANDLE request) - X IN RDB$FUNCTIONS WITH X.RDB$FUNCTION_ID EQ id - { - function = loadMetadata(tdbb, X.RDB$FUNCTION_ID, noscan, flags); - } - END_FOR +Function* Function::create(thread_db* tdbb, MemoryPool& pool, Cached::Function* perm) +{ + return FB_NEW_POOL(perm->getPool()) Function(perm); +} - if (check_function) - { - check_function->flags &= ~Routine::FLAG_CHECK_EXISTENCE; - if (check_function != function) - { - LCK_release(tdbb, check_function->existenceLock); - check_function->flags |= Routine::FLAG_OBSOLETE; - } - } - return function; +Function* Function::lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) +{ + return MetadataCache::lookup_function(tdbb, name, flags); } -Function* Function::lookup(thread_db* tdbb, const QualifiedName& name, bool noscan) +Lock* Function::makeLock(thread_db* tdbb, MemoryPool& p) { - Jrd::Attachment* attachment = tdbb->getAttachment(); - - Function* check_function = NULL; + return FB_NEW_RPT(p, 0) Lock(tdbb, sizeof(SLONG), LCK_fun_exist, nullptr, blockingAst); +} - // See if we already know the function by name +int Function::blockingAst(void* ast_object) +{ + auto* const function = static_cast(ast_object); - for (Function** iter = attachment->att_functions.begin(); iter < attachment->att_functions.end(); ++iter) + try { - Function* const function = *iter; - - if (function && !(function->flags & Routine::FLAG_OBSOLETE) && - !(function->flags & Routine::FLAG_CLEARED) && - ((function->flags & Routine::FLAG_SCANNED) || noscan) && - !(function->flags & Routine::FLAG_BEING_SCANNED) && - !(function->flags & Routine::FLAG_BEING_ALTERED)) - { - if (function->getName() == name) - { - if (function->flags & Routine::FLAG_CHECK_EXISTENCE) - { - check_function = function; - LCK_lock(tdbb, check_function->existenceLock, LCK_SR, LCK_WAIT); - break; - } - - return function; - } - } - } - - // We need to look up the function in RDB$FUNCTIONS - - Function* function = NULL; - - AutoCacheRequest request(tdbb, irq_l_fun_name, IRQ_REQUESTS); + Database* const dbb = function->existenceLock->lck_dbb; - FOR(REQUEST_HANDLE request) - X IN RDB$FUNCTIONS - WITH X.RDB$FUNCTION_NAME EQ name.identifier.c_str() AND - X.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') - { - function = loadMetadata(tdbb, X.RDB$FUNCTION_ID, noscan, 0); - } - END_FOR + AsyncContextHolder tdbb(dbb, FB_FUNCTION, function->existenceLock); - if (check_function) - { - check_function->flags &= ~Routine::FLAG_CHECK_EXISTENCE; - if (check_function != function) - { - LCK_release(tdbb, check_function->existenceLock); - check_function->flags |= Routine::FLAG_OBSOLETE; - } + LCK_release(tdbb, function->existenceLock); + function->resetDependentObject(tdbb, ElementBase::ResetType::Mark); } + catch (const Firebird::Exception&) + {} // no-op - return function; + return 0; } -Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) +ScanResult Function::scan(thread_db* tdbb, ObjectBase::Flag) { - Jrd::Attachment* attachment = tdbb->getAttachment(); + Attachment* attachment = tdbb->getAttachment(); jrd_tra* sysTransaction = attachment->getSysTransaction(); - Database* const dbb = tdbb->getDatabase(); + Database* dbb = tdbb->getDatabase(); + MetadataCache* mdc = dbb->dbb_mdc; - if (id >= attachment->att_functions.getCount()) - attachment->att_functions.grow(id + 1); + MemoryPool& pool = getPermanent()->getPool(); + bool found = false; - Function* function = attachment->att_functions[id]; - - if (function && !(function->flags & Routine::FLAG_OBSOLETE)) - { - // Make sure Routine::FLAG_BEING_SCANNED and Routine::FLAG_SCANNED are not set at the same time - fb_assert(!(function->flags & Routine::FLAG_BEING_SCANNED) || - !(function->flags & Routine::FLAG_SCANNED)); - - if ((function->flags & Routine::FLAG_BEING_SCANNED) || - (function->flags & Routine::FLAG_SCANNED)) - { - return function; - } - } - - if (!function) - function = FB_NEW_POOL(*attachment->att_pool) Function(*attachment->att_pool); - - try - { - function->flags |= (Routine::FLAG_BEING_SCANNED | flags); - function->flags &= ~(Routine::FLAG_OBSOLETE | Routine::FLAG_CLEARED); - - function->setId(id); - attachment->att_functions[id] = function; - - if (!function->existenceLock) - { - Lock* const lock = FB_NEW_RPT(*attachment->att_pool, 0) - Lock(tdbb, sizeof(SLONG), LCK_fun_exist, function, blockingAst); - function->existenceLock = lock; - lock->setKey(function->getId()); - } - - LCK_lock(tdbb, function->existenceLock, LCK_SR, LCK_WAIT); - - if (!noscan) + //try { AutoCacheRequest request_fun(tdbb, irq_l_functions, IRQ_REQUESTS); FOR(REQUEST_HANDLE request_fun) X IN RDB$FUNCTIONS - WITH X.RDB$FUNCTION_ID EQ id + WITH X.RDB$FUNCTION_ID EQ getPermanent()->getId() { - function->setName(QualifiedName(X.RDB$FUNCTION_NAME, - (X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME))); + found = true; + + getPermanent()->setName(QualifiedName(X.RDB$FUNCTION_NAME, + (X.RDB$PACKAGE_NAME.NULL ? nullptr : X.RDB$PACKAGE_NAME))); + getPermanent()->owner = X.RDB$OWNER_NAME; - function->owner = X.RDB$OWNER_NAME; TriState ssDefiner; if (!X.RDB$SECURITY_CLASS.NULL) { - function->setSecurityName(X.RDB$SECURITY_CLASS); + getPermanent()->setSecurityName(X.RDB$SECURITY_CLASS); } else if (!X.RDB$PACKAGE_NAME.NULL) { @@ -240,7 +143,7 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT if (!PKG.RDB$SECURITY_CLASS.NULL) { - function->setSecurityName(PKG.RDB$SECURITY_CLASS); + getPermanent()->setSecurityName(PKG.RDB$SECURITY_CLASS); } // SQL SECURITY of function must be the same if it's defined in package @@ -259,40 +162,40 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT } if (ssDefiner.asBool()) - function->invoker = attachment->getUserId(function->owner); + invoker = attachment->getUserId(getPermanent()->owner); // !!!!!!!!!!!!!!!!!!!!!!!! size_t count = 0; ULONG length = 0; - function->fun_inputs = 0; - function->setDefaultCount(0); + fun_inputs = 0; + setDefaultCount(0); - function->getInputFields().clear(); - function->getOutputFields().clear(); + getInputFields().clear(); + getOutputFields().clear(); AutoCacheRequest request_arg(tdbb, irq_l_args, IRQ_REQUESTS); FOR(REQUEST_HANDLE request_arg) Y IN RDB$FUNCTION_ARGUMENTS - WITH Y.RDB$FUNCTION_NAME EQ function->getName().identifier.c_str() AND - Y.RDB$PACKAGE_NAME EQUIV NULLIF(function->getName().package.c_str(), '') + WITH Y.RDB$FUNCTION_NAME EQ getName().identifier.c_str() AND + Y.RDB$PACKAGE_NAME EQUIV NULLIF(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) { - function->fun_inputs++; - int newCount = Y.RDB$ARGUMENT_POSITION - function->getOutputFields().getCount(); + fun_inputs++; + int newCount = Y.RDB$ARGUMENT_POSITION - getOutputFields().getCount(); fb_assert(newCount >= 0); - function->getInputFields().resize(newCount + 1); - function->getInputFields()[newCount] = parameter; + getInputFields().resize(newCount + 1); + getInputFields()[newCount] = parameter; } else { - fb_assert(function->getOutputFields().isEmpty()); - function->getOutputFields().add(parameter); + fb_assert(getOutputFields().isEmpty()); + getOutputFields().add(parameter); } parameter->prm_fun_mechanism = (FUN_T) Y.RDB$MECHANISM; @@ -303,7 +206,7 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT prm_mech_normal : (prm_mech_t) Y.RDB$ARGUMENT_MECHANISM; const SSHORT collation_id_null = Y.RDB$COLLATION_ID.NULL; - const SSHORT collation_id = Y.RDB$COLLATION_ID; + const CollId collation_id(Y.RDB$COLLATION_ID); SSHORT default_value_null = Y.RDB$DEFAULT_VALUE.NULL; bid default_value = Y.RDB$DEFAULT_VALUE; @@ -320,8 +223,8 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT { DSC_make_descriptor(¶meter->prm_desc, F.RDB$FIELD_TYPE, F.RDB$FIELD_SCALE, F.RDB$FIELD_LENGTH, - F.RDB$FIELD_SUB_TYPE, F.RDB$CHARACTER_SET_ID, - (collation_id_null ? F.RDB$COLLATION_ID : collation_id)); + F.RDB$FIELD_SUB_TYPE, CSetId(F.RDB$CHARACTER_SET_ID), + (collation_id_null ? CollId(F.RDB$COLLATION_ID) : collation_id)); if (default_value_null && fb_utils::implicit_domain(F.RDB$FIELD_NAME)) { @@ -335,8 +238,8 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT { DSC_make_descriptor(¶meter->prm_desc, Y.RDB$FIELD_TYPE, Y.RDB$FIELD_SCALE, Y.RDB$FIELD_LENGTH, - Y.RDB$FIELD_SUB_TYPE, Y.RDB$CHARACTER_SET_ID, - (collation_id_null ? 0 : collation_id)); + Y.RDB$FIELD_SUB_TYPE, CSetId(Y.RDB$CHARACTER_SET_ID), + (collation_id_null ? CollId(0) : collation_id)); } if (parameter->prm_desc.isText() && parameter->prm_desc.getTextType() != CS_NONE) @@ -356,19 +259,19 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT if (Y.RDB$ARGUMENT_POSITION != X.RDB$RETURN_ARGUMENT && !default_value_null) { - function->setDefaultCount(function->getDefaultCount() + 1); + setDefaultCount(getDefaultCount() + 1); - MemoryPool* const csb_pool = attachment->createPool(); + MemoryPool* const csb_pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); try { parameter->prm_default_value = static_cast(MET_parse_blob( - tdbb, NULL, &default_value, NULL, NULL, false, false)); + tdbb, nullptr, &default_value, nullptr, nullptr, false, false)); } catch (const Exception&) { - attachment->deletePool(csb_pool); + dbb->deletePool(csb_pool); throw; // an explicit error message would be better } } @@ -383,50 +286,50 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT } END_FOR - for (int i = (int) function->getInputFields().getCount() - 1; i >= 0; --i) + for (int i = (int) getInputFields().getCount() - 1; i >= 0; --i) { - if (!function->getInputFields()[i]) - function->getInputFields().remove(i); + if (!getInputFields()[i]) + getInputFields().remove(i); } - function->fun_return_arg = X.RDB$RETURN_ARGUMENT; - function->fun_temp_length = length; + fun_return_arg = X.RDB$RETURN_ARGUMENT; + fun_temp_length = length; // Prepare the exception message to be used in case this function ever // causes an exception. This is done at this time to save us from preparing // (thus allocating) this message every time the function is called. - function->fun_exception_message.printf(EXCEPTION_MESSAGE, - function->getName().toString().c_str(), X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME); + fun_exception_message.printf(EXCEPTION_MESSAGE, + getName().toString().c_str(), X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME); if (!X.RDB$DETERMINISTIC_FLAG.NULL) - function->fun_deterministic = (X.RDB$DETERMINISTIC_FLAG != 0); + fun_deterministic = (X.RDB$DETERMINISTIC_FLAG != 0); - function->setImplemented(true); - function->setDefined(true); + setImplemented(true); + setDefined(true); - function->fun_entrypoint = NULL; - function->fun_external = NULL; - function->setStatement(NULL); + fun_entrypoint = nullptr; + fun_external = nullptr; + setStatement(nullptr); if (!X.RDB$MODULE_NAME.NULL && !X.RDB$ENTRYPOINT.NULL) { - function->fun_entrypoint = + fun_entrypoint = Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb); // Could not find a function with given MODULE, ENTRYPOINT. // Try the list of internally implemented functions. - if (!function->fun_entrypoint) + if (!fun_entrypoint) { - function->fun_entrypoint = + fun_entrypoint = BUILTIN_entrypoint(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT); } - if (!function->fun_entrypoint) - function->setDefined(false); + if (!fun_entrypoint) + setDefined(false); } else if (!X.RDB$ENGINE_NAME.NULL || !X.RDB$FUNCTION_BLR.NULL) { - MemoryPool* const csb_pool = attachment->createPool(); + MemoryPool* const csb_pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); try @@ -447,24 +350,24 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT else body.getBuffer(1)[0] = 0; - dbb->dbb_extManager->makeFunction(tdbb, csb, function, X.RDB$ENGINE_NAME, + dbb->dbb_extManager->makeFunction(tdbb, csb, this, X.RDB$ENGINE_NAME, (X.RDB$ENTRYPOINT.NULL ? "" : X.RDB$ENTRYPOINT), (char*) body.begin()); - if (!function->fun_external) - function->setDefined(false); + if (!fun_external) + setDefined(false); } else if (!X.RDB$FUNCTION_BLR.NULL) { - const string name = function->getName().toString(); + const string name = getName().toString(); try { TraceFuncCompile trace(tdbb, name); - function->parseBlr(tdbb, csb, &X.RDB$FUNCTION_BLR, - X.RDB$DEBUG_INFO.NULL ? NULL : &X.RDB$DEBUG_INFO); + parseBlr(tdbb, csb, &X.RDB$FUNCTION_BLR, + X.RDB$DEBUG_INFO.NULL ? nullptr : &X.RDB$DEBUG_INFO); - trace.finish(function->getStatement(), ITracePlugin::RESULT_SUCCESS); + trace.finish(getStatement(), ITracePlugin::RESULT_SUCCESS); } catch (const Exception& ex) { @@ -477,25 +380,23 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT } catch (const Exception&) { - attachment->deletePool(csb_pool); + dbb->deletePool(csb_pool); throw; } - fb_assert(!function->isDefined() || function->getStatement()->function == function); + fb_assert(!isDefined() || this == getStatement()->function); } else { - RefPtr inputMetadata(REF_NO_INCR, createMetadata(function->getInputFields(), false)); - function->setInputFormat(createFormat(function->getPool(), inputMetadata, false)); + RefPtr inputMetadata(REF_NO_INCR, createMetadata(getInputFields(), false)); + setInputFormat(createFormat(pool, inputMetadata, false)); - RefPtr outputMetadata(REF_NO_INCR, createMetadata(function->getOutputFields(), false)); - function->setOutputFormat(createFormat(function->getPool(), outputMetadata, true)); + RefPtr outputMetadata(REF_NO_INCR, createMetadata(getOutputFields(), false)); + setOutputFormat(createFormat(pool, outputMetadata, true)); - function->setImplemented(false); + setImplemented(false); } - function->flags |= Routine::FLAG_SCANNED; - if (!dbb->readOnly() && !X.RDB$FUNCTION_BLR.NULL && !X.RDB$VALID_BLR.NULL && X.RDB$VALID_BLR == FALSE) @@ -512,75 +413,17 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT END_FOR } - // Make sure that it is really being scanned - fb_assert(function->flags & Routine::FLAG_BEING_SCANNED); - - function->flags &= ~Routine::FLAG_BEING_SCANNED; - - } // try - catch (const Exception&) - { - function->flags &= ~(Routine::FLAG_BEING_SCANNED | Routine::FLAG_SCANNED); - - if (function->existenceLock) - { - LCK_release(tdbb, function->existenceLock); - delete function->existenceLock; - function->existenceLock = NULL; - } - - throw; - } - - return function; -} - -int Function::blockingAst(void* ast_object) -{ - Function* const function = static_cast(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; -} - -void Function::releaseLocks(thread_db* tdbb) -{ - if (existenceLock) - { - LCK_release(tdbb, existenceLock); - flags |= Routine::FLAG_CHECK_EXISTENCE; - useCount = 0; - } + return found ? (this->flReload ? ScanResult::REPEAT : ScanResult::COMPLETE) : ScanResult::MISS; } -bool Function::checkCache(thread_db* tdbb) const +ScanResult Function::reload(thread_db* tdbb, ObjectBase::Flag /*unused*/) { - return tdbb->getAttachment()->att_functions[getId()] == this; -} - -void Function::clearCache(thread_db* tdbb) -{ - tdbb->getAttachment()->att_functions[getId()] = NULL; -} - -bool Function::reload(thread_db* tdbb) -{ - fb_assert(this->flags & Routine::FLAG_RELOAD); - Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); AutoCacheRequest request(tdbb, irq_l_funct_blr, IRQ_REQUESTS); + bool found = false; + FOR(REQUEST_HANDLE request) X IN RDB$FUNCTIONS WITH X.RDB$FUNCTION_ID EQ this->getId() @@ -588,7 +431,9 @@ bool Function::reload(thread_db* tdbb) if (X.RDB$FUNCTION_BLR.NULL) continue; - MemoryPool* const csb_pool = attachment->createPool(); + found = true; + + MemoryPool* const csb_pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); try @@ -598,10 +443,7 @@ bool Function::reload(thread_db* tdbb) try { this->parseBlr(tdbb, csb, &X.RDB$FUNCTION_BLR, - X.RDB$DEBUG_INFO.NULL ? NULL : &X.RDB$DEBUG_INFO); - - // parseBlr() above could set FLAG_RELOAD again - return !(this->flags & Routine::FLAG_RELOAD); + X.RDB$DEBUG_INFO.NULL ? nullptr : &X.RDB$DEBUG_INFO); } catch (const Exception& ex) { @@ -615,11 +457,23 @@ bool Function::reload(thread_db* tdbb) } catch (const Exception&) { - attachment->deletePool(csb_pool); + dbb->deletePool(csb_pool); throw; } } END_FOR - return false; + return found ? ScanResult::COMPLETE : ScanResult::MISS; } + +int Function::objectType() +{ + return obj_udf; +} + +void Function::checkReload(thread_db* tdbb) const +{ + if (flReload) + getPermanent()->reload(tdbb, 0); +} + diff --git a/src/jrd/Function.h b/src/jrd/Function.h index 289a28fc9b0..066eedd4e59 100644 --- a/src/jrd/Function.h +++ b/src/jrd/Function.h @@ -26,24 +26,44 @@ #include "../common/classes/NestConst.h" #include "../jrd/val.h" #include "../dsql/Nodes.h" +#include "../jrd/CacheVector.h" +#include "../jrd/lck.h" namespace Jrd { class ValueListNode; class QualifiedName; + class Function; class Function final : public Routine { static const char* const EXCEPTION_MESSAGE; public: - static Function* lookup(thread_db* tdbb, USHORT id, bool return_deleted, bool noscan, USHORT flags); - static Function* lookup(thread_db* tdbb, const QualifiedName& name, bool noscan); + static Lock* makeLock(thread_db* tdbb, MemoryPool& p); + static int blockingAst(void* ast_object); - void releaseLocks(thread_db* tdbb); + static Function* lookup(thread_db* tdbb, MetaId id, ObjectBase::Flag flags); + static Function* lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags); - explicit Function(MemoryPool& p) + private: + explicit Function(Cached::Function* perm) + : Routine(perm->getPool()), + cachedFunction(perm), + fun_entrypoint(NULL), + fun_inputs(0), + fun_return_arg(0), + fun_temp_length(0), + fun_exception_message(perm->getPool()), + fun_deterministic(false), + fun_external(NULL) + { + } + + public: + Function(MemoryPool& p) : Routine(p), + cachedFunction(FB_NEW_POOL(p) Cached::Function(p)), fun_entrypoint(NULL), fun_inputs(0), fun_return_arg(0), @@ -54,13 +74,19 @@ namespace Jrd { } - static Function* loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT flags); - static int blockingAst(void*); + static Function* create(thread_db* tdbb, MemoryPool& pool, Cached::Function* perm); + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); + void checkReload(thread_db* tdbb) const override; + + static const char* objectFamily(void*) + { + return "function"; + } public: int getObjectType() const override { - return obj_udf; + return objectType(); } SLONG getSclType() const override @@ -68,14 +94,15 @@ namespace Jrd return obj_functions; } - bool checkCache(thread_db* tdbb) const override; - void clearCache(thread_db* tdbb) override; + static int objectType(); + private: ~Function() override { delete fun_external; } + public: void releaseExternal() override { delete fun_external; @@ -83,6 +110,7 @@ namespace Jrd } public: + Cached::Function* cachedFunction; // entry in the cache int (*fun_entrypoint)(); // function entrypoint USHORT fun_inputs; // input arguments USHORT fun_return_arg; // return argument @@ -93,8 +121,12 @@ namespace Jrd bool fun_deterministic; const ExtEngineManager::Function* fun_external; - protected: - bool reload(thread_db* tdbb) override; + Cached::Function* getPermanent() const override + { + return cachedFunction; + } + + ScanResult reload(thread_db* tdbb, ObjectBase::Flag fl); }; } diff --git a/src/jrd/GlobalRWLock.cpp b/src/jrd/GlobalRWLock.cpp index 983dee92592..57b6dbeeeeb 100644 --- a/src/jrd/GlobalRWLock.cpp +++ b/src/jrd/GlobalRWLock.cpp @@ -33,7 +33,7 @@ #include "../lock/lock_proto.h" #include "../common/isc_proto.h" #include "jrd.h" -#include "lck_proto.h" +#include "lck.h" #include "err_proto.h" #include "Attachment.h" #include "../common/classes/rwlock.h" diff --git a/src/jrd/GlobalRWLock.h b/src/jrd/GlobalRWLock.h index 0db2ddb938e..3f24938291c 100644 --- a/src/jrd/GlobalRWLock.h +++ b/src/jrd/GlobalRWLock.h @@ -33,7 +33,7 @@ #include "../common/classes/alloc.h" #include "../jrd/jrd.h" #include "../jrd/lck.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "fb_types.h" #include "os/pio.h" #include "../common/classes/condition.h" diff --git a/src/jrd/HazardPtr.h b/src/jrd/HazardPtr.h new file mode 100644 index 00000000000..f53cc44c8b8 --- /dev/null +++ b/src/jrd/HazardPtr.h @@ -0,0 +1,236 @@ +/* + * PROGRAM: Engine Code + * MODULE: HazardPtr.h + * DESCRIPTION: Generic hazard pointers based on DHP::Guard from CDS. + * + * The contents of this file are subject to the Initial + * Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * 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 Alexander Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2021 Alexander Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef JRD_HAZARDPTR_H +#define JRD_HAZARDPTR_H + +#include "../common/gdsassert.h" + +#include +#include + +#include + +namespace Jrd { + +class HazardObject +{ +protected: + void retire() + { + struct Disposer + { + void operator()(HazardObject* hazardObject) + { + fb_assert(hazardObject); + delete hazardObject; + } + }; + + cds::gc::DHP::retire(this); + } + + virtual ~HazardObject() + { } +}; + +template +class HazardPtr : private cds::gc::DHP::Guard +{ + typedef cds::gc::DHP::Guard inherited; + static_assert(std::is_base_of::value, "class derived from HazardObject should be used"); + +public: + HazardPtr() = default; + + HazardPtr(const atomics::atomic& from) + { + protect(from); + } + + HazardPtr(const HazardPtr& copyFrom) + { + copy(copyFrom); + } + + HazardPtr(HazardPtr&& moveFrom) = default; + + template + HazardPtr(const HazardPtr& copyFrom) + { + checkAssign(); + copy(copyFrom); + } + + template + HazardPtr(HazardPtr&& moveFrom) + : inherited(std::move(moveFrom)) + { + checkAssign(); + } + + ~HazardPtr() + { } + + T* getPointer() const + { + return get(); + } + + T* releasePointer() + { + T* rc = get(); + clear(); + return rc; + } + + void set(const atomics::atomic& from) + { + protect(from); + } + + // atomically replaces 'where' with 'newVal', using *this as old value for comparison + // sets *this to actual data from 'where' if replace failed + bool replace(atomics::atomic& where, T* newVal) + { + T* val = get(); + bool rc = where.compare_exchange_strong(val, newVal, + std::memory_order_release, std::memory_order_acquire); + if (!rc) + assign(val); + return rc; + } + + void clear() + { + inherited::clear(); + } + + T* operator->() + { + return get(); + } + + const T* operator->() const + { + return get(); + } +/* + template + R& operator->*(R T::*mem) + { + return (this->hazardPointer)->*mem; + } + */ + bool operator!() const + { + return !hasData(); + } + + bool hasData() const + { + return get_native() != nullptr; + } + + bool operator==(const T* v) const + { + return get() == v; + } + + bool operator!=(const T* v) const + { + return get() != v; + } + + operator bool() const + { + return hasData(); + } + + HazardPtr& operator=(const HazardPtr& copyAssign) + { + copy(copyAssign); + return *this; + } + + HazardPtr& operator=(HazardPtr&& moveAssign) + { + inherited::operator=(std::move(moveAssign)); + return *this; + } + + template + HazardPtr& operator=(const HazardPtr& copyAssign) + { + checkAssign(); + copy(copyAssign); + return *this; + } + + template + HazardPtr& operator=(HazardPtr&& moveAssign) + { + checkAssign(); + inherited::operator=(std::move(moveAssign)); + return *this; + } + + void safePointer(T* ptr) + { + assign(ptr); + } + +private: + template + struct checkAssign + { + static_assert(std::is_trivially_assignable::value, "Invalid type of pointer assigned"); + }; +}; + +template +bool operator==(const T* v1, const HazardPtr v2) +{ + return v2 == v1; +} + +template +bool operator==(const T* v1, const HazardPtr v2) +{ + return v1 == v2.getPointer(); +} + +template +bool operator!=(const T* v1, const HazardPtr v2) +{ + return v2 != v1; +} + +} // namespace Jrd + +#endif // JRD_HAZARDPTR_H diff --git a/src/jrd/InitCDSLib.cpp b/src/jrd/InitCDSLib.cpp index 5195a2fe0b6..2d1f3400b25 100644 --- a/src/jrd/InitCDSLib.cpp +++ b/src/jrd/InitCDSLib.cpp @@ -49,7 +49,7 @@ static GlobalPtr initCDS; InitCDS::InitCDS(MemoryPool&) { - m_pool = MemoryPool::createPool(nullptr, m_stats); + m_pool = MemoryPool::createPool(ALLOC_ARGS1 nullptr, m_stats); m_pools = FB_NEW_POOL(*m_pool) Array(*m_pool); cds::Initialize(); @@ -124,7 +124,7 @@ static InitInstance mutex; // guard InitCDS::m_pools MemoryPool* InitCDS::createPool() { - MemoryPool* pool = MemoryPool::createPool(nullptr, m_stats); + MemoryPool* pool = MemoryPool::createPool(ALLOC_ARGS1 nullptr, m_stats); MemoryStats* newStats = FB_NEW_POOL(*pool) MemoryStats; pool->setStatsGroup(*newStats); diff --git a/src/jrd/InitCDSLib.h b/src/jrd/InitCDSLib.h index 07f7e8fce3e..b2dc2ee53ae 100644 --- a/src/jrd/InitCDSLib.h +++ b/src/jrd/InitCDSLib.h @@ -65,6 +65,46 @@ class InitCDS static Firebird::MemoryStats m_stats; }; +class InitPool +{ +public: + explicit InitPool(MemoryPool&) + : m_pool(InitCDS::createPool()), + m_stats(m_pool->getStatsGroup()) + { } + + ~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: + Firebird::MemoryPool* m_pool; + Firebird::MemoryStats& m_stats; +}; + } // namespace Jrd #endif // FB_INIT_CDSLIB_H diff --git a/src/jrd/IntlManager.cpp b/src/jrd/IntlManager.cpp index 765038bcc37..a8a88a2c02e 100644 --- a/src/jrd/IntlManager.cpp +++ b/src/jrd/IntlManager.cpp @@ -159,7 +159,7 @@ const IntlManager::CharSetDefinition IntlManager::defaultCharSets[] = {"GBK", CS_GBK, 2}, {"CP943C", CS_CP943C, 2}, {"GB18030", CS_GB18030, 4}, - {NULL, 0, 0} + {NULL, CS_NONE, 0} }; const IntlManager::CharSetAliasDefinition IntlManager::defaultCharSetAliases[] = @@ -235,182 +235,182 @@ const IntlManager::CharSetAliasDefinition IntlManager::defaultCharSetAliases[] = {"GB2312", CS_GB2312}, {"DOS_936", CS_GB2312}, {"WIN_936", CS_GB2312}, - {NULL, 0} + {NULL, CS_NONE} }; const IntlManager::CollationDefinition IntlManager::defaultCollations[] = { - {CS_NONE, 0, "NONE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_BINARY, 0, "OCTETS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ASCII, 0, "ASCII", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UNICODE_FSS, 0, "UNICODE_FSS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UTF8, 0, "UTF8", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UTF8, 1, "UCS_BASIC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UTF8, 2, "UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UTF8, 3, "UNICODE_CI", "UNICODE", + {CS_NONE, CollId(0), "NONE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_BINARY, CollId(0), "OCTETS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ASCII, CollId(0), "ASCII", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UNICODE_FSS, CollId(0), "UNICODE_FSS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF8, CollId(0), "UTF8", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF8, CollId(1), "UCS_BASIC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF8, CollId(2), "UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF8, CollId(3), "UNICODE_CI", "UNICODE", TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE, NULL}, - {CS_UTF8, 4, "UNICODE_CI_AI", "UNICODE", + {CS_UTF8, CollId(4), "UNICODE_CI_AI", "UNICODE", TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE | TEXTTYPE_ATTR_ACCENT_INSENSITIVE, NULL}, #ifdef FB_NEW_INTL_ALLOW_NOT_READY - {CS_UTF16, 0, "UTF16", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UTF16, 1, "UCS_BASIC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UTF32, 0, "UTF32", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_UTF32, 1, "UCS_BASIC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF16, CollId(0), "UTF16", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF16, CollId(1), "UCS_BASIC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF32, CollId(0), "UTF32", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_UTF32, CollId(1), "UCS_BASIC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, #endif // FB_NEW_INTL_NOT_READY - {CS_SJIS, 0, "SJIS_0208", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_EUCJ, 0, "EUCJ_0208", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 0, "DOS437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 1, "PDOX_ASCII", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 2, "PDOX_INTL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 3, "PDOX_SWEDFIN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 4, "DB_DEU437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 5, "DB_ESP437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 6, "DB_FIN437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 7, "DB_FRA437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 8, "DB_ITA437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 9, "DB_NLD437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 10, "DB_SVE437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 11, "DB_UK437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_437, 12, "DB_US437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 0, "DOS850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 1, "DB_FRC850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 2, "DB_DEU850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 3, "DB_ESP850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 4, "DB_FRA850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 5, "DB_ITA850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 6, "DB_NLD850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 7, "DB_PTB850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 8, "DB_SVE850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 9, "DB_UK850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_850, 10, "DB_US850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_865, 0, "DOS865", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_865, 1, "PDOX_NORDAN4", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_865, 2, "DB_DAN865", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_865, 3, "DB_NOR865", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 0, "ISO8859_1", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 1, "DA_DA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 2, "DU_NL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 3, "FI_FI", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 4, "FR_FR", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 5, "FR_CA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 6, "DE_DE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 7, "IS_IS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 8, "IT_IT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 9, "NO_NO", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 10, "ES_ES", NULL, TEXTTYPE_ATTR_PAD_SPACE, "DISABLE-COMPRESSIONS=1;SPECIALS-FIRST=1"}, - {CS_ISO8859_1, 11, "SV_SV", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 12, "EN_UK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 14, "EN_US", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 15, "PT_PT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_1, 16, "PT_BR", NULL, + {CS_SJIS, CollId(0), "SJIS_0208", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_EUCJ, CollId(0), "EUCJ_0208", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(0), "DOS437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(1), "PDOX_ASCII", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(2), "PDOX_INTL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(3), "PDOX_SWEDFIN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(4), "DB_DEU437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(5), "DB_ESP437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(6), "DB_FIN437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(7), "DB_FRA437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(8), "DB_ITA437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(9), "DB_NLD437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(10), "DB_SVE437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(11), "DB_UK437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_437, CollId(12), "DB_US437", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(0), "DOS850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(1), "DB_FRC850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(2), "DB_DEU850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(3), "DB_ESP850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(4), "DB_FRA850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(5), "DB_ITA850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(6), "DB_NLD850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(7), "DB_PTB850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(8), "DB_SVE850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(9), "DB_UK850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_850, CollId(10), "DB_US850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_865, CollId(0), "DOS865", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_865, CollId(1), "PDOX_NORDAN4", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_865, CollId(2), "DB_DAN865", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_865, CollId(3), "DB_NOR865", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(0), "ISO8859_1", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(1), "DA_DA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(2), "DU_NL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(3), "FI_FI", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(4), "FR_FR", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(5), "FR_CA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(6), "DE_DE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(7), "IS_IS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(8), "IT_IT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(9), "NO_NO", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(10), "ES_ES", NULL, TEXTTYPE_ATTR_PAD_SPACE, "DISABLE-COMPRESSIONS=1;SPECIALS-FIRST=1"}, + {CS_ISO8859_1, CollId(11), "SV_SV", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(12), "EN_UK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(14), "EN_US", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(15), "PT_PT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_1, CollId(16), "PT_BR", NULL, TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE | TEXTTYPE_ATTR_ACCENT_INSENSITIVE, NULL}, - {CS_ISO8859_1, 17, "ES_ES_CI_AI", NULL, + {CS_ISO8859_1, CollId(17), "ES_ES_CI_AI", NULL, TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE | TEXTTYPE_ATTR_ACCENT_INSENSITIVE, "DISABLE-COMPRESSIONS=1;SPECIALS-FIRST=1"}, - {CS_ISO8859_1, 18, "FR_FR_CI_AI", "FR_FR", + {CS_ISO8859_1, CollId(18), "FR_FR_CI_AI", "FR_FR", TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE | TEXTTYPE_ATTR_ACCENT_INSENSITIVE, "SPECIALS-FIRST=1"}, - {CS_ISO8859_1, 19, "FR_CA_CI_AI", "FR_CA", + {CS_ISO8859_1, CollId(19), "FR_CA_CI_AI", "FR_CA", TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE | TEXTTYPE_ATTR_ACCENT_INSENSITIVE, "SPECIALS-FIRST=1"}, - {CS_ISO8859_2, 0, "ISO8859_2", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_2, 1, "CS_CZ", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_2, 2, "ISO_HUN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_2, 3, "ISO_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_3, 0, "ISO8859_3", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_4, 0, "ISO8859_4", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_5, 0, "ISO8859_5", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_6, 0, "ISO8859_6", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_7, 0, "ISO8859_7", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_8, 0, "ISO8859_8", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_9, 0, "ISO8859_9", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_13, 0, "ISO8859_13", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_ISO8859_13, 1, "LT_LT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 0, "DOS852", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 1, "DB_CSY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 2, "DB_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 4, "DB_SLO", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 5, "PDOX_CSY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 6, "PDOX_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 7, "PDOX_HUN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_852, 8, "PDOX_SLO", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_857, 0, "DOS857", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_857, 1, "DB_TRK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_860, 0, "DOS860", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_860, 1, "DB_PTG860", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_861, 0, "DOS861", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_861, 1, "PDOX_ISL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_863, 0, "DOS863", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_863, 1, "DB_FRC863", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_CYRL, 0, "CYRL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_CYRL, 1, "DB_RUS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_CYRL, 2, "PDOX_CYRL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_737, 0, "DOS737", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_775, 0, "DOS775", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_858, 0, "DOS858", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_862, 0, "DOS862", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_864, 0, "DOS864", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_866, 0, "DOS866", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_DOS_869, 0, "DOS869", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 0, "WIN1250", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 1, "PXW_CSY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 2, "PXW_HUNDC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 3, "PXW_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 4, "PXW_SLOV", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 5, "PXW_HUN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 6, "BS_BA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1250, 7, "WIN_CZ", NULL, TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE, NULL}, - {CS_WIN1250, 8, "WIN_CZ_CI_AI", NULL, + {CS_ISO8859_2, CollId(0), "ISO8859_2", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_2, CollId(1), "CS_CZ", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_2, CollId(2), "ISO_HUN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_2, CollId(3), "ISO_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_3, CollId(0), "ISO8859_3", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_4, CollId(0), "ISO8859_4", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_5, CollId(0), "ISO8859_5", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_6, CollId(0), "ISO8859_6", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_7, CollId(0), "ISO8859_7", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_8, CollId(0), "ISO8859_8", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_9, CollId(0), "ISO8859_9", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_13, CollId(0), "ISO8859_13", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_ISO8859_13, CollId(1), "LT_LT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(0), "DOS852", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(1), "DB_CSY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(2), "DB_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(4), "DB_SLO", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(5), "PDOX_CSY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(6), "PDOX_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(7), "PDOX_HUN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_852, CollId(8), "PDOX_SLO", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_857, CollId(0), "DOS857", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_857, CollId(1), "DB_TRK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_860, CollId(0), "DOS860", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_860, CollId(1), "DB_PTG860", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_861, CollId(0), "DOS861", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_861, CollId(1), "PDOX_ISL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_863, CollId(0), "DOS863", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_863, CollId(1), "DB_FRC863", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_CYRL, CollId(0), "CYRL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_CYRL, CollId(1), "DB_RUS", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_CYRL, CollId(2), "PDOX_CYRL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_737, CollId(0), "DOS737", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_775, CollId(0), "DOS775", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_858, CollId(0), "DOS858", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_862, CollId(0), "DOS862", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_864, CollId(0), "DOS864", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_866, CollId(0), "DOS866", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_DOS_869, CollId(0), "DOS869", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(0), "WIN1250", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(1), "PXW_CSY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(2), "PXW_HUNDC", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(3), "PXW_PLK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(4), "PXW_SLOV", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(5), "PXW_HUN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(6), "BS_BA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1250, CollId(7), "WIN_CZ", NULL, TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE, NULL}, + {CS_WIN1250, CollId(8), "WIN_CZ_CI_AI", NULL, TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE | TEXTTYPE_ATTR_ACCENT_INSENSITIVE, NULL}, - {CS_WIN1251, 0, "WIN1251", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1251, 1, "PXW_CYRL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1251, 2, "WIN1251_UA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1252, 0, "WIN1252", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1252, 1, "PXW_INTL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1252, 2, "PXW_INTL850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1252, 3, "PXW_NORDAN4", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1252, 4, "PXW_SPAN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1252, 5, "PXW_SWEDFIN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1252, 6, "WIN_PTBR", NULL, + {CS_WIN1251, CollId(0), "WIN1251", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1251, CollId(1), "PXW_CYRL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1251, CollId(2), "WIN1251_UA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1252, CollId(0), "WIN1252", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1252, CollId(1), "PXW_INTL", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1252, CollId(2), "PXW_INTL850", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1252, CollId(3), "PXW_NORDAN4", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1252, CollId(4), "PXW_SPAN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1252, CollId(5), "PXW_SWEDFIN", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1252, CollId(6), "WIN_PTBR", NULL, TEXTTYPE_ATTR_PAD_SPACE | TEXTTYPE_ATTR_CASE_INSENSITIVE | TEXTTYPE_ATTR_ACCENT_INSENSITIVE, NULL}, - {CS_WIN1253, 0, "WIN1253", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1253, 1, "PXW_GREEK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1254, 0, "WIN1254", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1254, 1, "PXW_TURK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_NEXT, 0, "NEXT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_NEXT, 1, "NXT_US", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_NEXT, 2, "NXT_DEU", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_NEXT, 3, "NXT_FRA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_NEXT, 4, "NXT_ITA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_NEXT, 5, "NXT_ESP", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1255, 0, "WIN1255", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1256, 0, "WIN1256", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1257, 0, "WIN1257", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1257, 1, "WIN1257_EE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1257, 2, "WIN1257_LT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1257, 3, "WIN1257_LV", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_KSC5601, 0, "KSC_5601", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_KSC5601, 1, "KSC_DICTIONARY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_BIG5, 0, "BIG_5", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_GB2312, 0, "GB_2312", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_KOI8R, 0, "KOI8R", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_KOI8R, 1, "KOI8R_RU", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_KOI8U, 0, "KOI8U", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_KOI8U, 1, "KOI8U_UA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_WIN1258, 0, "WIN1258", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_TIS620, 0, "TIS620", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_TIS620, 1, "TIS620_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_GBK, 0, "GBK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_GBK, 1, "GBK_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_CP943C, 0, "CP943C", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_CP943C, 1, "CP943C_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_GB18030, 0, "GB18030", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {CS_GB18030, 1, "GB18030_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, - {0, 0, NULL, NULL, 0, NULL} + {CS_WIN1253, CollId(0), "WIN1253", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1253, CollId(1), "PXW_GREEK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1254, CollId(0), "WIN1254", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1254, CollId(1), "PXW_TURK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_NEXT, CollId(0), "NEXT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_NEXT, CollId(1), "NXT_US", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_NEXT, CollId(2), "NXT_DEU", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_NEXT, CollId(3), "NXT_FRA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_NEXT, CollId(4), "NXT_ITA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_NEXT, CollId(5), "NXT_ESP", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1255, CollId(0), "WIN1255", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1256, CollId(0), "WIN1256", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1257, CollId(0), "WIN1257", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1257, CollId(1), "WIN1257_EE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1257, CollId(2), "WIN1257_LT", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1257, CollId(3), "WIN1257_LV", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_KSC5601, CollId(0), "KSC_5601", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_KSC5601, CollId(1), "KSC_DICTIONARY", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_BIG5, CollId(0), "BIG_5", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_GB2312, CollId(0), "GB_2312", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_KOI8R, CollId(0), "KOI8R", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_KOI8R, CollId(1), "KOI8R_RU", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_KOI8U, CollId(0), "KOI8U", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_KOI8U, CollId(1), "KOI8U_UA", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_WIN1258, CollId(0), "WIN1258", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_TIS620, CollId(0), "TIS620", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_TIS620, CollId(1), "TIS620_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_GBK, CollId(0), "GBK", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_GBK, CollId(1), "GBK_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_CP943C, CollId(0), "CP943C", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_CP943C, CollId(1), "CP943C_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_GB18030, CollId(0), "GB18030", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_GB18030, CollId(1), "GB18030_UNICODE", NULL, TEXTTYPE_ATTR_PAD_SPACE, NULL}, + {CS_NONE, CollId(0), NULL, NULL, 0, NULL} }; @@ -640,63 +640,77 @@ bool IntlManager::lookupCharSet(const string& charSetName, charset* cs) void IntlManager::lookupCollation(const string& collationName, - const string& charSetName, + const Jrd::CharsetVariants& charsetVariants, USHORT attributes, const UCHAR* specificAttributes, ULONG specificAttributesLen, bool ignoreAttributes, texttype* tt) { ExternalInfo charSetExternalInfo; ExternalInfo collationExternalInfo; - char statusBuffer[BUFFER_LARGE] = ""; + char statusBufferBig[BUFFER_LARGE] = ""; + char statusBufferTiny[BUFFER_TINY]; + char *statusBuffer = statusBufferBig; + ULONG bufferLength = sizeof(statusBufferBig); - if (charSetCollations->get(charSetName + ":" + charSetName, charSetExternalInfo) && - charSetCollations->get(charSetName + ":" + collationName, collationExternalInfo)) + for(const auto& charSetMetaName : charsetVariants) { - ModuleLoader::Module* module = nullptr; + string charSetName(charSetMetaName); + if (charSetCollations->get(charSetName + ":" + charSetName, charSetExternalInfo) && + charSetCollations->get(charSetName + ":" + collationName, collationExternalInfo)) + { + ModuleLoader::Module* module = nullptr; - if (collationExternalInfo.moduleName.hasData()) - modules->get(collationExternalInfo.moduleName, module); + if (collationExternalInfo.moduleName.hasData()) + modules->get(collationExternalInfo.moduleName, module); - pfn_INTL_lookup_texttype_with_status lookupStatusFunction = nullptr; + pfn_INTL_lookup_texttype_with_status lookupStatusFunction = nullptr; - if (collationExternalInfo.moduleName.isEmpty()) - lookupStatusFunction = INTL_builtin_lookup_texttype_status; - else if (module) - module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_WITH_STATUS_ENTRYPOINT), lookupStatusFunction); + if (collationExternalInfo.moduleName.isEmpty()) + lookupStatusFunction = INTL_builtin_lookup_texttype_status; + else if (module) + module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_WITH_STATUS_ENTRYPOINT), lookupStatusFunction); - if (lookupStatusFunction) - { - if ((*lookupStatusFunction)(statusBuffer, sizeof(statusBuffer), - tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), - attributes, specificAttributes, specificAttributesLen, ignoreAttributes, - collationExternalInfo.configInfo.c_str())) + if (lookupStatusFunction) { - return; - } - } - else if (module) - { - pfn_INTL_lookup_texttype lookupFunction = nullptr; - module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_ENTRYPOINT), lookupFunction); + if ((*lookupStatusFunction)(statusBuffer, bufferLength, + tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), + attributes, specificAttributes, specificAttributesLen, ignoreAttributes, + collationExternalInfo.configInfo.c_str())) + { + return; + } - if (lookupFunction && - (*lookupFunction)(tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), - attributes, specificAttributes, specificAttributesLen, ignoreAttributes, - collationExternalInfo.configInfo.c_str())) + if (statusBuffer[0]) + { + statusBuffer = statusBufferTiny; + bufferLength = sizeof(statusBufferTiny); + statusBuffer[0] = '\0'; + } + } + else if (module) { - return; + pfn_INTL_lookup_texttype lookupFunction = nullptr; + module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_ENTRYPOINT), lookupFunction); + + if (lookupFunction && + (*lookupFunction)(tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), + attributes, specificAttributes, specificAttributesLen, ignoreAttributes, + collationExternalInfo.configInfo.c_str())) + { + return; + } } } } - if (statusBuffer[0]) + if (statusBufferBig[0]) { - (Arg::Gds(isc_collation_not_installed) << collationName << charSetName << - Arg::Gds(isc_random) << statusBuffer + (Arg::Gds(isc_collation_not_installed) << collationName << charsetVariants[0] << + Arg::Gds(isc_random) << statusBufferBig ).raise(); } else - (Arg::Gds(isc_collation_not_installed) << collationName << charSetName).raise(); + (Arg::Gds(isc_collation_not_installed) << collationName << charsetVariants[0]).raise(); } diff --git a/src/jrd/IntlManager.h b/src/jrd/IntlManager.h index c35ecf9705b..0e8d18663b9 100644 --- a/src/jrd/IntlManager.h +++ b/src/jrd/IntlManager.h @@ -29,6 +29,8 @@ #include "../common/classes/fb_string.h" #include "../common/config/config_file.h" +#include "../jrd/intl.h" +#include "../jrd/met_proto.h" struct charset; struct texttype; @@ -48,7 +50,7 @@ class IntlManager static bool lookupCharSet(const Firebird::string& charSetName, charset* cs); static void lookupCollation(const Firebird::string& collationName, - const Firebird::string& charSetName, + const Jrd::CharsetVariants& charsetVariants, USHORT attributes, const UCHAR* specificAttributes, ULONG specificAttributesLen, bool ignoreAttributes, texttype* tt); @@ -61,20 +63,20 @@ class IntlManager struct CharSetDefinition { const char* name; - UCHAR id; + CSetId id; USHORT maxBytes; }; struct CharSetAliasDefinition { const char* name; - UCHAR charSetId; + CSetId charSetId; }; struct CollationDefinition { - UCHAR charSetId; - UCHAR collationId; + CSetId charSetId; + CollId collationId; const char* name; const char* baseName; USHORT attributes; diff --git a/src/jrd/KeywordsTable.cpp b/src/jrd/KeywordsTable.cpp index 9214cde02ce..e231ed4f466 100644 --- a/src/jrd/KeywordsTable.cpp +++ b/src/jrd/KeywordsTable.cpp @@ -29,16 +29,16 @@ using namespace Jrd; using namespace Firebird; -RecordBuffer* KeywordsTable::getRecords(thread_db* tdbb, jrd_rel* relation) +RecordBuffer* KeywordsTable::getRecords(thread_db* tdbb, RelationPermanent* 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(); @@ -78,7 +78,7 @@ void KeywordsTableScan::close(thread_db* tdbb) const VirtualTableScan::close(tdbb); } -const Format* KeywordsTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +const Format* KeywordsTableScan::getFormat(thread_db* tdbb, RelationPermanent* relation) const { const auto records = getRecords(tdbb, relation); return records->getFormat(); @@ -87,11 +87,11 @@ const Format* KeywordsTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) c bool KeywordsTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const { - const auto records = getRecords(tdbb, relation); + const auto records = getRecords(tdbb, relation->rel_perm); return records->fetch(position, record); } -RecordBuffer* KeywordsTableScan::getRecords(thread_db* tdbb, jrd_rel* relation) const +RecordBuffer* KeywordsTableScan::getRecords(thread_db* tdbb, RelationPermanent* relation) const { const auto request = tdbb->getRequest(); const auto impure = request->getImpure(impureOffset); diff --git a/src/jrd/KeywordsTable.h b/src/jrd/KeywordsTable.h index 0aeb424a135..6f771d0aefd 100644 --- a/src/jrd/KeywordsTable.h +++ b/src/jrd/KeywordsTable.h @@ -41,7 +41,7 @@ class KeywordsTable : public SnapshotData } public: - RecordBuffer* getRecords(thread_db* tdbb, jrd_rel* relation); + RecordBuffer* getRecords(thread_db* tdbb, RelationPermanent* relation); }; @@ -49,7 +49,7 @@ class KeywordsTableScan final : public VirtualTableScan { public: KeywordsTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : VirtualTableScan(csb, alias, stream, relation) { impureOffset = csb->allocImpure(); @@ -58,7 +58,7 @@ class KeywordsTableScan final : public VirtualTableScan void close(thread_db* tdbb) const override; protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; + const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const override; bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; @@ -69,7 +69,7 @@ class KeywordsTableScan final : public VirtualTableScan KeywordsTable* table; }; - RecordBuffer* getRecords(thread_db* tdbb, jrd_rel* relation) const; + RecordBuffer* getRecords(thread_db* tdbb, RelationPermanent* relation) const; ULONG impureOffset; }; diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index dba426c3bd4..05ce5a535f2 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -1659,7 +1659,7 @@ void Mapping::clearCache(const char* dbName, USHORT index) } -const Format* GlobalMappingScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +const Format* GlobalMappingScan::getFormat(thread_db* tdbb, RelationPermanent* relation) const { jrd_tra* const transaction = tdbb->getTransaction(); return transaction->getMappingList()->getList(tdbb, relation)->getFormat(); @@ -1669,7 +1669,7 @@ bool GlobalMappingScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const { jrd_tra* const transaction = tdbb->getTransaction(); - return transaction->getMappingList()->getList(tdbb, relation)->fetch(position, record); + return transaction->getMappingList()->getList(tdbb, relation->rel_perm)->fetch(position, record); } MappingList::MappingList(jrd_tra* tra) @@ -1683,10 +1683,10 @@ RecordBuffer* MappingList::makeBuffer(thread_db* tdbb) return getData(rel_global_auth_mapping); } -RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) +RecordBuffer* MappingList::getList(thread_db* tdbb, RelationPermanent* 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) diff --git a/src/jrd/Mapping.h b/src/jrd/Mapping.h index 5ff436676ee..c4be5595a78 100644 --- a/src/jrd/Mapping.h +++ b/src/jrd/Mapping.h @@ -173,12 +173,12 @@ class GlobalMappingScan: public VirtualTableScan { public: GlobalMappingScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : VirtualTableScan(csb, alias, stream, relation) {} protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; + const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const override; bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; }; @@ -188,7 +188,7 @@ class MappingList : public SnapshotData public: explicit MappingList(jrd_tra* tra); - RecordBuffer* getList(thread_db* tdbb, jrd_rel* relation); + RecordBuffer* getList(thread_db* tdbb, RelationPermanent* relation); private: RecordBuffer* makeBuffer(thread_db* tdbb); diff --git a/src/jrd/MetaName.h b/src/jrd/MetaName.h index c8450e624f6..58a8c7025d5 100644 --- a/src/jrd/MetaName.h +++ b/src/jrd/MetaName.h @@ -171,17 +171,12 @@ class MetaName : word(get(s, l)) { } - MetaName(const MetaName& m) - : word(m.word) - { - test(); - } + MetaName(const MetaName& m) = default; MetaName(const Firebird::AbstractString& s) : word(get(s.c_str(), s.length())) { } - explicit MetaName(MemoryPool&) : word(nullptr) { } @@ -229,12 +224,7 @@ class MetaName return *this; } - MetaName& operator=(const MetaName& m) - { - word = m.word; - test(); - return *this; - } + MetaName& operator=(const MetaName& m) = default; MetaName& operator=(const Firebird::MetaString& s); @@ -357,6 +347,11 @@ class MetaName static void adjustLength(const char* const s, FB_SIZE_T& l); }; +inline bool operator==(const char* s, const MetaName& m) +{ + return m.compare(s) == 0; +} + typedef Firebird::Pair > MetaNamePair; } // namespace Jrd diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index 704c1168654..caf6967d0f4 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -36,7 +36,7 @@ #include "../common/isc_f_proto.h" #include "../common/isc_s_proto.h" #include "../common/db_alias.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" @@ -46,6 +46,8 @@ #include "../jrd/RecordBuffer.h" #include "../jrd/Monitoring.h" #include "../jrd/Function.h" +#include "../jrd/met.h" +#include "../jrd/Statement.h" #include "../jrd/optimizer/Optimizer.h" #ifdef WIN_NT @@ -106,7 +108,7 @@ namespace } // namespace -const Format* MonitoringTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +const Format* MonitoringTableScan::getFormat(thread_db* tdbb, RelationPermanent* relation) const { MonitoringSnapshot* const snapshot = MonitoringSnapshot::create(tdbb); return snapshot->getData(relation)->getFormat(); @@ -117,12 +119,12 @@ bool MonitoringTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const { MonitoringSnapshot* const snapshot = MonitoringSnapshot::create(tdbb); - if (!snapshot->getData(relation)->fetch(position, record)) + if (!snapshot->getData(getPermanent(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; @@ -134,7 +136,7 @@ bool MonitoringTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, ISC_TIMESTAMP_TZ* ts = reinterpret_cast (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); @@ -631,11 +633,11 @@ void SnapshotData::clearSnapshot() } -RecordBuffer* SnapshotData::getData(const jrd_rel* relation) const +RecordBuffer* SnapshotData::getData(const RelationPermanent* relation) const { fb_assert(relation); - return getData(relation->rel_id); + return getData(relation->getId()); } @@ -653,16 +655,15 @@ RecordBuffer* SnapshotData::getData(int id) const RecordBuffer* SnapshotData::allocBuffer(thread_db* tdbb, MemoryPool& pool, int rel_id) { - jrd_rel* const relation = MET_lookup_relation_id(tdbb, rel_id, false); + jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, rel_id, 0); fb_assert(relation); - MET_scan_relation(tdbb, relation); fb_assert(relation->isVirtual()); - const Format* const format = MET_current(tdbb, relation); + const Format* const format = relation->currentFormat(); 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; @@ -713,11 +714,11 @@ void SnapshotData::putField(thread_db* tdbb, Record* record, const DumpField& fi SLONG rel_id; memcpy(&rel_id, field.data, field.length); - const jrd_rel* const relation = MET_lookup_relation_id(tdbb, rel_id, false); - if (!relation || relation->rel_name.isEmpty()) + RelationPermanent* relation = MetadataCache::lookupRelation(tdbb, rel_id, 0); + 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); diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index dccc2a4de46..0d5569d8056 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -226,7 +226,7 @@ class SnapshotData void putField(thread_db*, Record*, const DumpField&); RecordBuffer* allocBuffer(thread_db*, MemoryPool&, int); - RecordBuffer* getData(const jrd_rel*) const; + RecordBuffer* getData(const RelationPermanent*) const; RecordBuffer* getData(int) const; void clearSnapshot(); @@ -359,12 +359,12 @@ class MonitoringTableScan: public VirtualTableScan { public: MonitoringTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : VirtualTableScan(csb, alias, stream, relation) {} protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; + const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const override; bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; }; diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index ce6e4140e4b..f25345133c9 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -54,13 +54,13 @@ namespace { case dtype_text: item.type = SQL_TEXT; - item.charSet = desc->dsc_ttype(); + item.charSet = desc->getTextType(); item.length = desc->dsc_length; break; case dtype_varying: item.type = SQL_VARYING; - item.charSet = desc->dsc_ttype(); + item.charSet = desc->getTextType(); fb_assert(desc->dsc_length >= sizeof(USHORT)); item.length = desc->dsc_length - sizeof(USHORT); break; @@ -416,7 +416,7 @@ PreparedStatement::~PreparedStatement() void PreparedStatement::init(thread_db* tdbb, Attachment* attachment, jrd_tra* transaction, const Firebird::string& text, bool isInternalRequest) { - AutoSetRestore autoAttCharset(&attachment->att_charset, + AutoSetRestore autoAttCharset(&attachment->att_charset, (isInternalRequest ? CS_METADATA : attachment->att_charset)); dsqlRequest = NULL; diff --git a/src/jrd/ProfilerManager.cpp b/src/jrd/ProfilerManager.cpp index 95fb61d779d..0a039891fff 100644 --- a/src/jrd/ProfilerManager.cpp +++ b/src/jrd/ProfilerManager.cpp @@ -29,10 +29,11 @@ #include "../jrd/recsrc/Cursor.h" #include "../dsql/BoolNodes.h" #include "../jrd/dpm_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/tra_proto.h" +#include "../jrd/Statement.h" #include diff --git a/src/jrd/ProfilerManager.h b/src/jrd/ProfilerManager.h index 0b9570be6ed..5a44b95ca3a 100644 --- a/src/jrd/ProfilerManager.h +++ b/src/jrd/ProfilerManager.h @@ -34,6 +34,7 @@ #include "../jrd/recsrc/RecordSource.h" #include "../jrd/req.h" #include "../jrd/SystemPackages.h" +#include "../jrd/Statement.h" namespace Jrd { diff --git a/src/jrd/ProtectRelations.h b/src/jrd/ProtectRelations.h new file mode 100644 index 00000000000..5d51c5c7958 --- /dev/null +++ b/src/jrd/ProtectRelations.h @@ -0,0 +1,131 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: ProtectRelations.h + * DESCRIPTION: Relation lock holder + * + * 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): ______________________________________. + * + * + */ + +#include "firebird.h" + +#include "../jrd/jrd.h" +#include "../jrd/Relation.h" +#include "../common/classes/array.h" + + +#if (!defined(FB_JRD_PROTECT_RELATIONS)) + +#define FB_JRD_PROTECT_RELATIONS + +using namespace Firebird; + +namespace Jrd { +// Lock relation with protected_read level or raise existing relation lock +// to this level to ensure nobody can write to this relation. +// Used when new index is built. +// releaseLock set to true if there was no existing lock before +class ProtectRelations +{ +public: + ProtectRelations(thread_db* tdbb, jrd_tra* transaction) : + m_tdbb(tdbb), + m_transaction(transaction), + m_locks() + { + } + + ProtectRelations(thread_db* tdbb, jrd_tra* transaction, Cached::Relation* relation) : + m_tdbb(tdbb), + m_transaction(transaction), + m_locks() + { + addRelation(relation); + lock(); + } + + ~ProtectRelations() + { + unlock(); + } + + void addRelation(Cached::Relation* relation) + { + FB_SIZE_T pos; + if (!m_locks.find(relation->getId(), pos)) + m_locks.insert(pos, relLock(relation)); + } + + bool exists(USHORT rel_id) const + { + FB_SIZE_T pos; + return m_locks.find(rel_id, pos); + } + + void lock() + { + for (auto& item : m_locks) + item.takeLock(m_tdbb, m_transaction); + } + + void unlock() + { + for (auto& item : m_locks) + item.releaseLock(m_tdbb, m_transaction); + } + +private: + struct relLock + { + relLock(Cached::Relation* relation = nullptr) : + m_relation(relation), + m_lock(NULL), + m_release(false) + { + } + + relLock(MemoryPool&, const Jrd::ProtectRelations::relLock& l) : + m_relation(l.m_relation), + m_lock(l.m_lock), + m_release(l.m_release) + { + fb_assert(!m_lock); + } + + void takeLock(thread_db* tdbb, jrd_tra* transaction); + void releaseLock(thread_db* tdbb, jrd_tra* transaction); + + static const USHORT generate(const relLock& item) + { + return item.m_relation->getId(); + } + + Cached::Relation* m_relation; + Lock* m_lock; + bool m_release; + }; + + thread_db* m_tdbb; + jrd_tra* m_transaction; + SortedArray, USHORT, relLock> m_locks; +}; + +} // namespace Jrd + +#endif // FB_JRD_PROTECT_RELATIONS diff --git a/src/jrd/QualifiedName.h b/src/jrd/QualifiedName.h index 3457a5df8fe..be7d9d4d656 100644 --- a/src/jrd/QualifiedName.h +++ b/src/jrd/QualifiedName.h @@ -37,7 +37,8 @@ class QualifiedName public: QualifiedName(MemoryPool& p, const MetaName& aIdentifier, const MetaName& aPackage) : identifier(p, aIdentifier), - package(p, aPackage) + package(p, aPackage), + tmp(p) { } @@ -49,7 +50,8 @@ class QualifiedName QualifiedName(MemoryPool& p, const MetaName& aIdentifier) : identifier(p, aIdentifier), - package(p) + package(p), + tmp(p) { } @@ -60,7 +62,8 @@ class QualifiedName explicit QualifiedName(MemoryPool& p) : identifier(p), - package(p) + package(p), + tmp(p) { } @@ -70,7 +73,8 @@ class QualifiedName QualifiedName(MemoryPool& p, const QualifiedName& src) : identifier(p, src.identifier), - package(p, src.package) + package(p, src.package), + tmp(p) { } @@ -90,22 +94,37 @@ class QualifiedName return !(identifier == m.identifier && package == m.package); } + bool hasData() const + { + return identifier.hasData(); + } + public: - Firebird::string toString() const + Firebird::string& toString() const { - Firebird::string s; + if (tmp.hasData()) + return tmp; + if (package.hasData()) { - s = package.c_str(); - s.append("."); + tmp = package.c_str(); + tmp += '.'; } - s.append(identifier.c_str()); - return s; + tmp += identifier.c_str(); + + return tmp; + } + + const char* c_str() const + { + return toString().c_str(); } public: MetaName identifier; MetaName package; + + mutable Firebird::string tmp; }; } // namespace Jrd diff --git a/src/jrd/RecordNumber.h b/src/jrd/RecordNumber.h index 61ea4d8a883..68861c054f3 100644 --- a/src/jrd/RecordNumber.h +++ b/src/jrd/RecordNumber.h @@ -109,18 +109,13 @@ class RecordNumber inline RecordNumber() : value(0), valid(false) {} // Copy constructor - inline RecordNumber(const RecordNumber& from) : value(from.value), valid(from.valid) {} + inline RecordNumber(const RecordNumber& from) = default; // Explicit constructor from 64-bit record number value inline explicit RecordNumber(SINT64 number) : value(number), valid(true) {} // Assignment operator - inline RecordNumber& operator =(const RecordNumber& from) - { - value = from.value; - valid = from.valid; - return *this; - } + inline RecordNumber& operator =(const RecordNumber& from) = default; inline bool operator ==(const RecordNumber& other) const { @@ -252,6 +247,11 @@ struct bid return bid_quad.bid_quad_high == 0 && bid_quad.bid_quad_low == 0; } + bool hasData() const + { + return !isEmpty(); + } + void clear() { bid_quad.bid_quad_high = 0; diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index f355b6bf1c1..832d52ea0c2 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -36,6 +36,7 @@ #include "../dsql/gen_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/pass1_proto.h" +#include "../jrd/met.h" #include "../jrd/optimizer/Optimizer.h" using namespace Firebird; @@ -560,7 +561,7 @@ LocalTableSourceNode* LocalTableSourceNode::copy(thread_db* tdbb, NodeCopier& co void LocalTableSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* /*rse*/, BoolExprNode** /*boolean*/, RecordSourceNodeStack& stack) { - fb_assert(!csb->csb_view); // local tables cannot be inside a view + fb_assert(!csb->csb_view.isSet()); // local tables cannot be inside a view stack.push(this); // Assume that the source will be used. Push it on the final stream stack. @@ -604,6 +605,7 @@ RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* // Find relation either by id or by name AutoPtr aliasString; MetaName name; + Cached::Relation* rel = nullptr; switch (blrOp) { @@ -618,9 +620,10 @@ RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* csb->csb_blr_reader.getString(*aliasString); } - if (!(node->relation = MET_lookup_relation_id(tdbb, id, false))) + rel = MetadataCache::lookupRelation(tdbb, id, + CacheFlag::AUTOCREATE | (csb->csb_g_flags & csb_internal ? CacheFlag::NOSCAN : 0)); + if (!rel) name.printf("id %d", id); - break; } @@ -635,7 +638,7 @@ RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* csb->csb_blr_reader.getString(*aliasString); } - node->relation = MET_lookup_relation(tdbb, name); + rel = MetadataCache::lookupRelation(tdbb, name, CacheFlag::AUTOCREATE); break; } @@ -643,23 +646,18 @@ RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* fb_assert(false); } - if (!node->relation) + if (!rel) PAR_error(csb, Arg::Gds(isc_relnotdef) << Arg::Str(name), false); + // Store relation in CSB resources and after it - in the node + + node->relation = csb->csb_resources->relations.registerResource(rel); + // if an alias was passed, store with the relation if (aliasString) node->alias = *aliasString; - // 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)) && - !(csb->csb_g_flags & csb_internal)) - { - MET_scan_relation(tdbb, node->relation); - } - // generate a stream for the relation reference, assuming it is a real reference if (parseContext) @@ -683,8 +681,8 @@ string RelationSourceNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, dsqlName); NODE_PRINT(printer, alias); NODE_PRINT(printer, context); - if (relation) - printer.print("rel_name", relation->rel_name); + if (relation.isSet()) + printer.print("rel_name", relation(JRD_get_thread_data())->getName()); return "RelationSourceNode"; } @@ -759,12 +757,16 @@ RecordSourceNode* RelationSourceNode::pass1(thread_db* tdbb, CompilerScratch* cs const auto tail = &csb->csb_rpt[stream]; const auto relation = tail->csb_relation; - if (relation && !csb->csb_implicit_cursor) + if (relation.isSet() && !csb->csb_implicit_cursor) { - const SLONG ssRelationId = tail->csb_view ? tail->csb_view->rel_id : - view ? view->rel_id : csb->csb_view ? csb->csb_view->rel_id : 0; - CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId, - SCL_select, obj_relations, relation->rel_name); + const SLONG ssRelationId = tail->csb_view.isSet() ? + tail->csb_view()->getId() : view.isSet() ? + view()->getId() : csb->csb_view.isSet() ? + csb->csb_view()->getId() : 0; + + const RelationPermanent* r = relation(); + CMP_post_access(tdbb, csb, r->rel_security_name, ssRelationId, + SCL_select, obj_relations, r->getName()); } return this; @@ -781,11 +783,10 @@ 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. - jrd_rel* const parentView = csb->csb_view; + Rsc::Rel const parentView = csb->csb_view; const StreamType viewStream = csb->csb_view_stream; - jrd_rel* relationView = relation; - CMP_post_resource(&csb->csb_resources, relationView, Resource::rsc_relation, relationView->rel_id); + Rsc::Rel relationView = relation; view = parentView; CompilerScratch::csb_repeat* const element = CMP_csb_element(csb, stream); @@ -794,9 +795,9 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN // in the case where there is a parent view, find the context name - if (parentView) + if (parentView.isSet()) { - const ViewContexts& ctx = parentView->rel_view_contexts; + const ViewContexts& ctx = parentView(tdbb)->rel_view_contexts; const USHORT key = context; FB_SIZE_T pos; @@ -809,7 +810,11 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN // check for a view - if not, nothing more to do - RseNode* viewRse = relationView->rel_view_rse; + auto* jrdRel = relationView(tdbb); + if (!jrdRel) + fatal_exception::raiseFmt("Relation '%s' unavailable", relationView() ? relationView()->c_name() : ""); // !!!!!!!!!!! + + RseNode* viewRse = relationView(tdbb)->rel_view_rse; if (!viewRse) return; @@ -820,7 +825,7 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN AutoSetRestore autoRemapVariable(&csb->csb_remap_variable, (csb->csb_variables ? csb->csb_variables->count() : 0) + 1); - AutoSetRestore autoView(&csb->csb_view, relationView); + AutoSetRestore autoView(&csb->csb_view, relationView); AutoSetRestore autoViewStream(&csb->csb_view_stream, stream); // We don't expand the view in two cases: @@ -944,6 +949,8 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch ObjectsArray* inArgNames = nullptr; USHORT inArgCount = 0; QualifiedName name; + Cached::Procedure* proc = nullptr; + SubRoutine nodeProc; const auto node = FB_NEW_POOL(pool) ProcedureSourceNode(pool); @@ -988,7 +995,7 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch } } else if (!node->procedure) - node->procedure = MET_lookup_procedure(tdbb, name, false); + proc = MetadataCache::lookupProcedure(tdbb, name, CacheFlag::AUTOCREATE); break; } @@ -1018,7 +1025,7 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch inArgCount = blrReader.getWord(); node->inputSources = PAR_args(tdbb, csb, inArgCount, - MAX(inArgCount, node->procedure->getInputFields().getCount())); + MAX(inArgCount, node->procedure(tdbb)->getInputFields().getCount())); break; case blr_invsel_procedure_context: @@ -1059,14 +1066,13 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch case blr_pid: case blr_pid2: { - const SSHORT pid = blrReader.getWord(); + const SSHORT procId = blrReader.getWord(); if (blrOp == blr_pid2) blrReader.getString(node->alias); - if (!(node->procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0))) - name.identifier.printf("id %d", pid); - + proc = MetadataCache::lookupProcedure(tdbb, procId, CacheFlag::AUTOCREATE); + name.identifier.printf("id %d", procId); break; } @@ -1092,7 +1098,7 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch } } else - node->procedure = MET_lookup_procedure(tdbb, name, false); + proc = MetadataCache::lookupProcedure(tdbb, name, CacheFlag::AUTOCREATE); break; @@ -1108,24 +1114,32 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch "blr_invsel_procedure_in_arg_names count cannot be greater than blr_invsel_procedure_in_args"); } - if (!node->procedure) + if (proc) + { + fb_assert(!node->procedure); + node->procedure = csb->csb_resources->procedures.registerResource(proc); + } + + fb_assert(node->procedure); + jrd_prc* procedure = node->procedure(tdbb); + if (!procedure) { blrReader.setPos(blrStartPos); - PAR_error(csb, Arg::Gds(isc_prcnotdef) << name.toString()); + PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); } - if (node->procedure->prc_type == prc_executable) + if (procedure->prc_type == prc_executable) { if (tdbb->getAttachment()->isGbak()) - PAR_warning(Arg::Warning(isc_illegal_prc_type) << node->procedure->getName().toString()); + PAR_warning(Arg::Warning(isc_illegal_prc_type) << node->procedure()->getName().toString()); else - PAR_error(csb, Arg::Gds(isc_illegal_prc_type) << node->procedure->getName().toString()); + PAR_error(csb, Arg::Gds(isc_illegal_prc_type) << node->procedure()->getName().toString()); } - node->isSubRoutine = node->procedure->isSubRoutine(); - node->procedureId = node->isSubRoutine ? 0 : node->procedure->getId(); + // node->isSubRoutine = node->procedure.isSubRoutine(); + node->procedureId = node->procedure()->getId(); - if (node->procedure->isImplemented() && !node->procedure->isDefined()) + if (procedure->isImplemented() && !procedure->isDefined()) { if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) { @@ -1159,14 +1173,14 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch if (!node->inputSources) node->inputSources = FB_NEW_POOL(pool) ValueListNode(pool); - node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount()); + node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, procedure->getInputFields().getCount()); Arg::StatusVector mismatchStatus; if (!CMP_procedure_arguments( tdbb, csb, - node->procedure, + procedure, true, inArgCount, inArgNames, @@ -1176,14 +1190,14 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch mismatchStatus)) { status_exception::raise(Arg::Gds(isc_prcmismat) << - node->procedure->getName().toString() << mismatchStatus); + node->procedure()->getName().toString() << mismatchStatus); } - if (csb->collectingDependencies() && !node->procedure->isSubRoutine()) + if (csb->collectingDependencies() && !node->procedure.isSubRoutine()) { { // scope - CompilerScratch::Dependency dependency(obj_procedure); - dependency.procedure = node->procedure; + Dependency dependency(obj_procedure); + dependency.procedure = node->procedure(); csb->addDependency(dependency); } @@ -1191,9 +1205,9 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch { for (const auto& argName : *inArgNames) { - CompilerScratch::Dependency dependency(obj_procedure); - dependency.procedure = node->procedure; - dependency.subName = &argName; + Dependency dependency(obj_procedure); + dependency.procedure = node->procedure(); + dependency.subName = argName; csb->addDependency(dependency); } } @@ -1402,21 +1416,22 @@ 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) + if (procedure.isSubRoutine()) newSource->procedure = procedure; else { - newSource->procedure = MET_lookup_procedure_id(tdbb, procedureId, false, false, 0); - if (!newSource->procedure) + auto proc = MetadataCache::lookupProcedure(tdbb, procedureId, CacheFlag::AUTOCREATE); + 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). @@ -1433,7 +1448,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; @@ -1466,13 +1480,10 @@ void ProcedureSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, Rse pass1(tdbb, csb); - if (!isSubRoutine) - { - CMP_post_procedure_access(tdbb, csb, procedure); - CMP_post_resource(&csb->csb_resources, procedure, Resource::rsc_procedure, procedureId); - } + if (!procedure.isSubRoutine()) + CMP_post_procedure_access(tdbb, csb, procedure()); - jrd_rel* const parentView = csb->csb_view; + Rsc::Rel const parentView = csb->csb_view; const StreamType viewStream = csb->csb_view_stream; view = parentView; @@ -1484,7 +1495,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; @@ -1516,7 +1527,7 @@ RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool const auto csb = opt->getCompilerScratch(); const string alias = opt->makeAlias(stream); - return FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureScan(csb, alias, stream, procedure, + return FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureScan(tdbb, csb, alias, stream, procedure, inputSources, inputTargets, inputMessage); } @@ -1808,7 +1819,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); @@ -2112,7 +2123,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); @@ -2512,7 +2523,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::iterator window = windows.begin(); @@ -2659,14 +2670,14 @@ string RseNode::internalPrint(NodePrinter& printer) const bool RseNode::dsqlAggregateFinder(AggregateFinder& visitor) { AutoSetRestore 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 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) @@ -2683,7 +2694,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) @@ -3252,8 +3263,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(); @@ -3325,7 +3336,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 @@ -3341,26 +3352,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(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) @@ -3373,35 +3385,35 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan) string planAlias; - jrd_rel* planRelation = nullptr; + RelationPermanent* planRelation = nullptr; if (const auto relationNode = nodeAs(plan->recordSourceNode)) { - planRelation = relationNode->relation; + planRelation = relationNode->relation(); planAlias = relationNode->alias; } - jrd_prc* planProcedure = nullptr; + RoutinePermanent* planProcedure = nullptr; if (const auto procedureNode = nodeAs(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) @@ -3426,15 +3438,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())) { @@ -3469,11 +3481,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())) { @@ -3485,7 +3497,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; @@ -3503,8 +3515,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 @@ -3551,9 +3563,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)); @@ -3829,8 +3841,8 @@ static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Form *desc = desc2; else if (max == dtype_blob) { - USHORT subtype = DataTypeUtil::getResultBlobSubType(desc, &desc2); - USHORT ttype = DataTypeUtil::getResultTextType(desc, &desc2); + auto subtype = DataTypeUtil::getResultBlobSubType(desc, &desc2); + auto ttype = DataTypeUtil::getResultTextType(desc, &desc2); desc->makeBlob(subtype, ttype); } else if (min <= dtype_any_text) @@ -3844,7 +3856,7 @@ static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Form // pick the max text type, so any transparent casts from ints are // not left in ASCII format, but converted to the richer text format - desc->setTextType(MAX(INTL_TEXT_TYPE(*desc), INTL_TEXT_TYPE(desc2))); + desc->setTextType(MAX(desc->getTextType(), desc2.getTextType())); desc->dsc_scale = 0; desc->dsc_flags = 0; } @@ -3852,7 +3864,7 @@ static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Form { desc->dsc_dtype = dtype_varying; desc->dsc_length = DSC_convert_to_text_length(max) + sizeof(USHORT); - desc->dsc_ttype() = ttype_ascii; + desc->setTextType(ttype_ascii); desc->dsc_scale = 0; desc->dsc_flags = 0; } diff --git a/src/jrd/RecordSourceNodes.h b/src/jrd/RecordSourceNodes.h index ce4f7c0d51d..b9294a78cb0 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -358,8 +358,6 @@ class RelationSourceNode final : public TypedNode(pool), dsqlName(pool, aDsqlName), alias(pool), - relation(NULL), - view(NULL), context(0) { } @@ -418,10 +416,10 @@ class RelationSourceNode final : public TypedNode procedure; NestConst inputSources; NestConst inputTargets; NestConst> dsqlInputArgNames; private: NestConst inputMessage; - - jrd_rel* view = nullptr; + Rsc::Rel view; USHORT procedureId = 0; SSHORT context = 0; - bool isSubRoutine = false; }; class AggregateSourceNode final : public TypedNode @@ -855,8 +851,8 @@ class RseNode final : public TypedNode dsqlFirst; diff --git a/src/jrd/Relation.cpp b/src/jrd/Relation.cpp index f379ba4c1ff..5ffd1740d6f 100644 --- a/src/jrd/Relation.cpp +++ b/src/jrd/Relation.cpp @@ -28,16 +28,193 @@ #include "../jrd/btr_proto.h" #include "../jrd/dpm_proto.h" #include "../jrd/idx_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/vio_debug.h" +#include "../jrd/ext_proto.h" +#include "../jrd/dfw_proto.h" +#include "../jrd/Statement.h" +#include "../common/StatusArg.h" + +// Pick up relation ids +#include "../jrd/ini.h" using namespace Jrd; +using namespace Firebird; + + +TrigArray::TrigArray(MemoryPool& p) + : preErase(p), postErase(p), preModify(p), + postModify(p), preStore(p), postStore(p) +{ } + +Triggers& TrigArray::operator[](int t) +{ + switch(t) + { + case TRIGGER_PRE_STORE: + return preStore; + + case TRIGGER_POST_STORE: + return postStore; + + case TRIGGER_PRE_MODIFY: + return preModify; + + case TRIGGER_POST_MODIFY: + return postModify; -/// jrd_rel + case TRIGGER_PRE_ERASE: + return preErase; -bool jrd_rel::isReplicating(thread_db* tdbb) + case TRIGGER_POST_ERASE: + return postErase; + } + + fb_assert(false); + fatal_exception::raise("Invalid trigger type"); +} + +const char* DbTriggersHeader::c_name() const +{ + switch(type) + { + case TRIGGER_CONNECT: + return "database connect"; + + case TRIGGER_DISCONNECT: + return "database disconnect"; + + case TRIGGER_TRANS_START: + return "transaction start"; + + case TRIGGER_TRANS_COMMIT: + return "transaction commit"; + + case TRIGGER_TRANS_ROLLBACK: + return "transaction rollback"; + } + + return "DDL"; +} + +const Triggers& TrigArray::operator[](int t) const +{ + switch(t) + { + case TRIGGER_PRE_STORE: + return preStore; + + case TRIGGER_POST_STORE: + return postStore; + + case TRIGGER_PRE_MODIFY: + return preModify; + + case TRIGGER_POST_MODIFY: + return postModify; + + case TRIGGER_PRE_ERASE: + return preErase; + + case TRIGGER_POST_ERASE: + return postErase; + } + + fb_assert(false); + fatal_exception::raise("Invalid trigger type"); +} + +jrd_rel::jrd_rel(MemoryPool& p, Cached::Relation* r) + : rel_pool(&p), + rel_perm(r), + rel_current_fmt(0), + rel_current_format(nullptr), + rel_fields(nullptr), + rel_view_rse(nullptr), + rel_view_contexts(p), + rel_triggers(p), + rel_ss_definer(false) +{ } + +RelationPermanent::RelationPermanent(thread_db* tdbb, MemoryPool& p, MetaId id, MakeLock* /*makeLock*/, NoData) + : PermanentStorage(p), + rel_existence_lock(nullptr), + rel_partners_lock(nullptr), + rel_rescan_lock(nullptr), + rel_gc_lock(this), + rel_gc_records(p), + rel_scan_count(0), + rel_formats(nullptr), + rel_indices(p, this), + rel_name(p), + rel_id(id), + rel_flags(0u), + rel_pages_inst(nullptr), + rel_pages_base(p), + rel_pages_free(nullptr), + rel_file(nullptr) +{ + rel_partners_lock = FB_NEW_RPT(getPool(), 0) + Lock(tdbb, sizeof(SLONG), LCK_rel_partners, this, partners_ast_relation); + rel_partners_lock->setKey(rel_id); + + rel_rescan_lock = FB_NEW_RPT(getPool(), 0) + Lock(tdbb, sizeof(SLONG), LCK_rel_rescan, this, rescan_ast_relation); + rel_rescan_lock->setKey(rel_id); + + if (rel_id >= rel_MAX) + { + rel_existence_lock = FB_NEW_RPT(getPool(), 0) + Lock(tdbb, sizeof(SLONG), LCK_rel_exist, this, blocking_ast_relation); + rel_existence_lock->setKey(rel_id); + } + +} + +RelationPermanent::~RelationPermanent() +{ + fb_assert(!(rel_existence_lock || rel_partners_lock || rel_rescan_lock)); +} + +bool RelationPermanent::destroy(thread_db* tdbb, RelationPermanent* rel) +{ + if (rel->rel_existence_lock) + { + LCK_release(tdbb, rel->rel_existence_lock); + rel->rel_existence_lock = nullptr; + } + + if (rel->rel_partners_lock) + { + LCK_release(tdbb, rel->rel_partners_lock); + rel->rel_partners_lock = nullptr; + } + + if (rel->rel_rescan_lock) + { + LCK_release(tdbb, rel->rel_rescan_lock); + rel->rel_rescan_lock = nullptr; + } + + if (rel->rel_file) + { + rel->rel_file->release(); + delete rel->rel_file; + } + + rel->rel_indices.cleanup(tdbb); +/* + // delete by pool ???????????????? + auto& pool = rel->getPool(); + tdbb->getDatabase()->deletePool(&pool); + + return true;*/ + return false; +} + +bool RelationPermanent::isReplicating(thread_db* tdbb) { Database* const dbb = tdbb->getDatabase(); if (!dbb->isReplicating(tdbb)) @@ -47,12 +224,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.asBool(); } -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; @@ -76,8 +253,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)) @@ -87,7 +266,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 { @@ -108,7 +287,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, @@ -120,30 +299,34 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a const bool poolCreated = !pool; if (poolCreated) - pool = dbb->createPool(); + pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, pool); jrd_tra* idxTran = tdbb->getTransaction(); if (!idxTran) idxTran = attachment->getSysTransaction(); + jrd_rel* rel = MetadataCache::lookup_relation_id(tdbb, getId(), CacheFlag::AUTOCREATE); + fb_assert(rel); + IndexDescList indices; - // read indices from "base" index root page - BTR_all(tdbb, this, indices, &rel_pages_base); + BTR_all(tdbb, getPermanent(rel), indices, &rel_pages_base); for (auto& idx : indices) { + auto* idp = this->lookupIndex(tdbb, idx.idx_id, CacheFlag::AUTOCREATE); MetaName idx_name; - MET_lookup_index(tdbb, idx_name, this->rel_name, idx.idx_id + 1); + if (idp) + idx_name = idp->getName(); idx.idx_root = 0; SelectivityList selectivity(*pool); - IDX_create_index(tdbb, this, &idx, idx_name.c_str(), NULL, idxTran, selectivity); + IDX_create_index(tdbb, IdxCreate::AtOnce, rel, &idx, idx_name.c_str(), NULL, idxTran, selectivity); #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, @@ -163,7 +346,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) @@ -179,7 +362,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, @@ -205,7 +388,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); @@ -228,9 +411,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); @@ -238,19 +421,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) { @@ -287,7 +470,8 @@ 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; @@ -306,75 +490,144 @@ void jrd_rel::RelPagesSnapshot::clear() inherited::clear(); } -bool jrd_rel::hasTriggers() const + +IndexVersion* RelationPermanent::lookup_index(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) { - typedef const TrigVector* ctv; - ctv trigs[6] = // non-const array, don't want optimization tricks by the compiler. + return rel_indices.getObject(tdbb, id, flags); +} + + +Cached::Index* RelationPermanent::lookupIndex(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) +{ + auto* idp = rel_indices.getDataNoChecks(id); + if (idp) + return idp; + + if (flags & CacheFlag::AUTOCREATE) { - rel_pre_erase, - rel_post_erase, - rel_pre_modify, - rel_post_modify, - rel_pre_store, - rel_post_store - }; + auto* idv = lookup_index(tdbb, id, flags); + if (idv) + return getPermanent(idv); + } - for (int i = 0; i < 6; ++i) + return nullptr; +} + + +PageNumber RelationPermanent::getIndexRootPage(thread_db* tdbb) +{ +/************************************** + * + * g e t _ r o o t _ p a g e + * + ************************************** + * + * Functional description + * Find the root page for a relation. + * + **************************************/ + SET_TDBB(tdbb); + + RelationPages* relPages = getPages(tdbb); + SLONG page = relPages->rel_index_root; + if (!page) { - if (trigs[i] && trigs[i]->getCount()) - return true; + DPM_scan_pages(tdbb); + page = relPages->rel_index_root; } - return false; + + return PageNumber(relPages->rel_pg_space_id, page); } -void jrd_rel::releaseTriggers(thread_db* tdbb, bool destroy) + +void RelationPermanent::tagForUpdate(thread_db* tdbb, const MetaName name) { - 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); + auto* relation = MetadataCache::lookupRelation(tdbb, name, + CacheFlag::AUTOCREATE | CacheFlag::NOCOMMIT | CacheFlag::NOSCAN); + fb_assert(relation); + + if (relation && relation->getId()) + { + relation->tagForUpdate(tdbb); + relation->rel_flags |= REL_format; + DFW_post_work(tdbb->getTransaction(), dfw_commit_relation, nullptr, relation->getId()); + } } -void jrd_rel::replaceTriggers(thread_db* tdbb, TrigVector** triggers) + +const char* IndexPermanent::c_name() const { - TrigVector* tmp_vector; + // Here we use MetaName feature - pointers in it are DBB-lifetime stable + return idp_name.c_str(); +} - tmp_vector = rel_pre_store; - rel_pre_store = triggers[TRIGGER_PRE_STORE]; - MET_release_triggers(tdbb, &tmp_vector, true); +void IndexPermanent::createLock(thread_db* tdbb, MetaId relId, MetaId indId) +{ +/* if (!idp_lock) + { + idp_lock = FB_NEW_RPT(idp_relation->getPool(), 0) + Lock(tdbb, sizeof(SLONG), LCK_expression, this, indexReload); + idp_lock->setKey((FB_UINT64(relId) << REL_ID_KEY_OFFSET) + indId); + } */ +} + +IndexVersion::IndexVersion(MemoryPool& p, Cached::Index* idp) + : perm(idp) +{ } + +void IndexVersion::destroy(thread_db* tdbb, IndexVersion* idv) +{ + if (idv->idv_expression_statement) + idv->idv_expression_statement->release(tdbb); - tmp_vector = rel_post_store; - rel_post_store = triggers[TRIGGER_POST_STORE]; - MET_release_triggers(tdbb, &tmp_vector, true); + if (idv->idv_condition_statement) + idv->idv_condition_statement->release(tdbb); - tmp_vector = rel_pre_erase; - rel_pre_erase = triggers[TRIGGER_PRE_ERASE]; - MET_release_triggers(tdbb, &tmp_vector, true); + delete idv; +} - tmp_vector = rel_post_erase; - rel_post_erase = triggers[TRIGGER_POST_ERASE]; - MET_release_triggers(tdbb, &tmp_vector, true); - tmp_vector = rel_pre_modify; - rel_pre_modify = triggers[TRIGGER_PRE_MODIFY]; - MET_release_triggers(tdbb, &tmp_vector, true); +void jrd_rel::releaseTriggers(thread_db* tdbb, bool destroy) +{ + for (int n = 1; n < TRIGGER_MAX; ++n) + { + rel_triggers[n].release(tdbb, destroy); + } +} - tmp_vector = rel_post_modify; - rel_post_modify = triggers[TRIGGER_POST_MODIFY]; - MET_release_triggers(tdbb, &tmp_vector, true); +void Triggers::release(thread_db* tdbb, bool destroy) +{ +/*********************************************** + * + * 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. + * + **************************************/ + if (destroy) + { + Triggers::destroy(tdbb, this); + return; + } } -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; + return createLock(tdbb, getPool(), lckType, noAst); +} - const USHORT relLockLen = relation->getRelLockKeyLength(); +Lock* RelationPermanent::createLock(thread_db* tdbb, MemoryPool& pool, lck_t lckType, bool noAst) +{ + const USHORT relLockLen = getRelLockKeyLength(); - Lock* lock = FB_NEW_RPT(*pool, relLockLen) Lock(tdbb, relLockLen, lckType, relation); - relation->getRelLockKey(tdbb, lock->getKeyPtr()); + Lock* lock = FB_NEW_RPT(pool, relLockLen) + Lock(tdbb, relLockLen, lckType, lckType == LCK_relation ? (void*)this : (void*)&rel_gc_lock); + getRelLockKey(tdbb, lock->getKeyPtr()); lock->lck_type = lckType; switch (lckType) @@ -383,7 +636,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: @@ -393,191 +646,237 @@ Lock* jrd_rel::createLock(thread_db* tdbb, MemoryPool* pool, jrd_rel* relation, return lock; } -bool jrd_rel::acquireGCLock(thread_db* tdbb, int wait) -{ - fb_assert(rel_flags & REL_gc_lockneed); - if (!(rel_flags & REL_gc_lockneed)) - { - fb_assert(rel_gc_lock->lck_id); - fb_assert(rel_gc_lock->lck_physical == (rel_flags & REL_gc_disabled ? LCK_SR : LCK_SW)); - return true; - } - if (!rel_gc_lock) - rel_gc_lock = createLock(tdbb, NULL, this, LCK_rel_gc, false); +void GCLock::blockingAst() +{ + /**** + SR - gc forbidden, awaiting moment to re-establish SW lock + SW - gc allowed, usual state + PW - gc allowed to the one connection only + ****/ - fb_assert(!rel_gc_lock->lck_id); - fb_assert(!(rel_flags & REL_gc_blocking)); + Database* dbb = lck->lck_dbb; - ThreadStatusGuard temp_status(tdbb); + AsyncContextHolder tdbb(dbb, FB_FUNCTION, lck); - 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)) + unsigned oldFlags = flags.load(std::memory_order_acquire); + do { - rel_flags |= REL_gc_disabled; - ret = LCK_lock(tdbb, rel_gc_lock, LCK_SR, wait); - if (!ret) - rel_flags &= ~REL_gc_disabled; - } + 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 (ret) - rel_flags &= ~REL_gc_lockneed; - - return ret; -} + if (oldFlags & GC_counterMask) + return; -void jrd_rel::downgradeGCLock(thread_db* tdbb) -{ - if (!rel_sweep_count && (rel_flags & REL_gc_blocking)) + if (oldFlags & GC_disabled) { - fb_assert(!(rel_flags & REL_gc_lockneed)); - fb_assert(rel_gc_lock->lck_id); - fb_assert(rel_gc_lock->lck_physical == LCK_SW); + // someone acquired EX lock - rel_flags &= ~REL_gc_blocking; - rel_flags |= REL_gc_disabled; + fb_assert(lck->lck_id); + fb_assert(lck->lck_physical == LCK_SR); - LCK_downgrade(tdbb, rel_gc_lock); + LCK_release(tdbb, lck); + flags.fetch_and(~(GC_disabled | GC_blocking | GC_locked)); + } + else + { + // someone acquired PW lock - if (rel_gc_lock->lck_physical != LCK_SR) - { - rel_flags &= ~REL_gc_disabled; - if (rel_gc_lock->lck_physical < LCK_SR) - rel_flags |= REL_gc_lockneed; - } + fb_assert(lck->lck_id); + fb_assert(lck->lck_physical == LCK_SW); + + flags.fetch_or(GC_disabled); + downgrade(tdbb); } } -int jrd_rel::blocking_ast_gcLock(void* ast_object) +[[noreturn]] void GCLock::incrementError() { - /**** - 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(ast_object); + fatal_exception::raise("Overflow when changing GC lock atomic counter (guard bit set)"); +} - try +bool GCLock::acquire(thread_db* tdbb, int wait) +{ + unsigned oldFlags = flags.load(std::memory_order_acquire); + for(;;) { - Lock* lock = relation->rel_gc_lock; - Database* dbb = lock->lck_dbb; + if (oldFlags & (GC_blocking | GC_disabled)) // lock should not be obtained + return false; - AsyncContextHolder tdbb(dbb, FB_FUNCTION, lock); + const unsigned newFlags = oldFlags + 1; + if (newFlags & GC_guardBit) + incrementError(); - fb_assert(!(relation->rel_flags & REL_gc_lockneed)); - if (relation->rel_flags & REL_gc_lockneed) // work already done synchronously ? - return 0; + if (!flags.compare_exchange_weak(oldFlags, newFlags, std::memory_order_release, std::memory_order_acquire)) + continue; - relation->rel_flags |= REL_gc_blocking; - if (relation->rel_sweep_count) - return 0; + if (oldFlags & GC_locked) // lock was already taken when we checked flags + return true; - if (relation->rel_flags & REL_gc_disabled) - { - // someone acquired EX lock + if (!(oldFlags & GC_counterMask)) // we must take lock + break; + + // unstable state - someone else it getting a lock right now: + --flags; // decrement counter, + Thread::yield(); // wait a bit + oldFlags = flags.fetch_sub(1, std::memory_order_acquire); // and retry + } + + // We incremented counter from 0 to 1 - take care about lck + if (!lck) + lck = relPerm->createLock(tdbb, LCK_rel_gc, false); - fb_assert(lock->lck_id); - fb_assert(lock->lck_physical == LCK_SR); + fb_assert(!lck->lck_id); - LCK_release(tdbb, lock); - relation->rel_flags &= ~(REL_gc_disabled | REL_gc_blocking); - relation->rel_flags |= REL_gc_lockneed; + ThreadStatusGuard temp_status(tdbb); + + bool ret; + if (oldFlags & GC_disabled) + ret = LCK_lock(tdbb, lck, LCK_SR, wait); + else + { + ret = LCK_lock(tdbb, lck, LCK_SW, wait); + if (ret) + { + 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; } +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(); -/// jrd_rel::GCExclusive + 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); -jrd_rel::GCExclusive::GCExclusive(thread_db* tdbb, jrd_rel* relation) : - m_tdbb(tdbb), - m_relation(relation), - m_lock(NULL) -{ + 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() +// violates rules of atomic counters - ok ONLY for ASSERT +unsigned GCLock::getSweepCount() const { - release(); - delete m_lock; + return flags.load(std::memory_order_relaxed) & GC_counterMask; } -bool jrd_rel::GCExclusive::acquire(int wait) +bool GCLock::disable(thread_db* tdbb, int wait, Lock*& tempLock) { - // if validation is already running - go out - if (m_relation->rel_flags & REL_gc_disabled) - return false; - - ThreadStatusGuard temp_status(m_tdbb); + ThreadStatusGuard temp_status(tdbb); - m_relation->rel_flags |= REL_gc_disabled; + // if validation is already running - go out + 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) - return; + 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; + } - fb_assert(m_relation->rel_flags & REL_gc_disabled); + // exactly one who cleared GC_locked bit releases a lock + LCK_release(tdbb, lck); + } - if (!(m_relation->rel_flags & REL_gc_lockneed)) - { - m_relation->rel_flags |= REL_gc_lockneed; - LCK_release(m_tdbb, m_relation->rel_gc_lock); + return; } +} + +void GCLock::forcedRelease(thread_db* tdbb) +{ + flags.fetch_and(~GC_locked); + if (lck) + LCK_release(tdbb, lck); +} - LCK_convert(m_tdbb, m_lock, LCK_EX, LCK_WAIT); - m_relation->rel_flags &= ~REL_gc_disabled; +void GCLock::enable(thread_db* tdbb, Lock* tempLock) +{ + if (!(tempLock && tempLock->lck_id)) + return; + + fb_assert(flags.load() & GC_disabled); + + ensureReleased(tdbb); + + LCK_convert(tdbb, tempLock, LCK_EX, LCK_WAIT); + flags.fetch_and(~GC_disabled); - LCK_release(m_tdbb, m_lock); + LCK_release(tdbb, tempLock); } @@ -599,3 +898,158 @@ void RelationPages::free(RelationPages*& nextFree) dpMap.clear(); dpMapMark = 0; } + +void IndexPermanent::unlock(thread_db* tdbb) +{ +// LCK_release(tdbb, idp_lock); +} + +[[noreturn]] void IndexPermanent::errIndexGone() +{ + fatal_exception::raise("Index is gone unexpectedly"); +} + +void jrd_rel::destroy(thread_db* tdbb, jrd_rel* rel) +{ + rel->releaseTriggers(tdbb, true); + + // A lot more things to do !!!!!!!!!!!!!!!! + + delete rel; +} + +jrd_rel* jrd_rel::create(thread_db* tdbb, MemoryPool& pool, Cached::Relation* rlp) +{ + return FB_NEW_POOL(pool) jrd_rel(pool, rlp); +} + +const char* jrd_rel::objectFamily(RelationPermanent* perm) +{ + return perm->isView() ? "view" : "table"; +} + +int jrd_rel::objectType() +{ + return obj_relation; +} + +const Format* jrd_rel::currentFormat() const +{ +/************************************** + * + * M E T _ c u r r e n t + * + ************************************** + * + * Functional description + * Get the current format for a relation. The current format is the + * format in which new records are to be stored. + * + **************************************/ + + // dimitr: rel_current_format may sometimes get out of sync, + // e.g. after DFW error raised during ALTER TABLE command. + // Thus it makes sense to validate it before usage and + // fetch the proper one if something is suspicious. + + // AP: no reasons for rel_current_format to be wrong with versioned cache + // but better check it carefully + + fb_assert(rel_current_format && (rel_current_format->fmt_version == rel_current_fmt)); +/* ??????????????????????????????? + if (rel_current_format && (rel_current_format->fmt_version == rel_current_fmt)) + { + return rel_current_format; + } + + thread_db* tdbb = JRD_get_thread_data(); + + // Usually, format numbers start with one and they are present in RDB$FORMATS. + // However, system tables have zero as their initial format and they don't have + // any related records in RDB$FORMATS, instead their rel_formats[0] is initialized + // directly (see ini.epp). Every other case of zero format number found for an already + // scanned table must be catched here and investigated. + fb_assert(rel_current_fmt || isSystem()); + + rel_current_format = MET_format(tdbb, getPermanent(), rel_current_fmt); +*/ + return rel_current_format; +} + + + +void Triggers::destroy(thread_db* tdbb, Triggers* trigs) +{ + for (auto t : trigs->triggers) + { + t->free(tdbb, true); + delete t; + } + trigs->triggers.clear(); +} + +void Trigger::free(thread_db* tdbb, bool force) +{ + extTrigger.reset(); + + if (!statement /* ????????????????? || statement->isActive() */|| releaseInProgress) + return; + + AutoSetRestore autoProgressFlag(&releaseInProgress, true); + + statement->release(tdbb); + statement = nullptr; +} + + +// class DbTriggers + +DbTriggersHeader::DbTriggersHeader(thread_db* tdbb, MemoryPool& p, MetaId& t, MakeLock* makeLock, NoData) + : Firebird::PermanentStorage(p), + type(t), + lock(nullptr) +{ + lock = makeLock(tdbb, p); + lock->setKey(type); + lock->lck_object = this; +} + +bool DbTriggersHeader::destroy(thread_db* tdbb, DbTriggersHeader* trigs) +{ + if (trigs->lock) + { + LCK_release(tdbb, trigs->lock); + trigs->lock = nullptr; + } + + return false; +} + +int DbTriggersHeader::blockingAst(void* ast_object) +{ + auto* const trigs = static_cast(ast_object); + + try + { + Database* const dbb = trigs->lock->lck_dbb; + + AsyncContextHolder tdbb(dbb, FB_FUNCTION, trigs->lock); + + LCK_release(tdbb, trigs->lock); + trigs->resetDependentObject(tdbb, ElementBase::ResetType::Mark); + } + catch (const Firebird::Exception&) + {} // no-op + + return 0; +} + +Lock* DbTriggers::makeLock(thread_db* tdbb, MemoryPool& p) +{ + return FB_NEW_RPT(p, 0) Lock(tdbb, sizeof(SLONG), LCK_dbwide_triggers, nullptr, DbTriggersHeader::blockingAst); +} + +int DbTriggers::objectType() +{ + return obj_relation; +} diff --git a/src/jrd/Relation.h b/src/jrd/Relation.h index f2784c2bf64..9ae93766c45 100644 --- a/src/jrd/Relation.h +++ b/src/jrd/Relation.h @@ -22,21 +22,250 @@ #ifndef JRD_RELATION_H #define JRD_RELATION_H +#include "../jrd/vec.h" #include -#include "../jrd/jrd.h" +// ???????????????????? #include "../jrd/jrd.h" #include "../jrd/btr.h" #include "../jrd/lck.h" #include "../jrd/pag.h" #include "../jrd/val.h" #include "../jrd/Attachment.h" +#include "../jrd/CacheVector.h" +#include "../jrd/ExtEngineManager.h" +#include "../jrd/met_proto.h" +#include "../jrd/Resources.h" #include "../common/classes/TriState.h" namespace Jrd { +template class vec; class BoolExprNode; class RseNode; class StmtNode; +class jrd_fld; +class ExternalFile; +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: + Firebird::HalfStaticArray blr; // BLR code + Firebird::HalfStaticArray debugInfo; // Debug info + Statement* statement = nullptr; // Compiled statement + bool releaseInProgress = false; + FB_UINT64 type = 0; // Trigger type + USHORT flags = 0; // Flags as they are in RDB$TRIGGERS table + jrd_rel* relation = nullptr; // Trigger parent relation + MetaName name; // Trigger name + MetaName engine; // External engine name + MetaName owner; // Owner for SQL SECURITY + Firebird::string entryPoint; // External trigger entrypoint + Firebird::string extBody; // External trigger body + Firebird::TriState ssDefiner; // SQL SECURITY + std::unique_ptr extTrigger; // External trigger + + MemoryPool& getPool(); + + bool isActive() const; + + void compile(thread_db*); // Ensure that trigger is compiled + void free(thread_db*, bool force); // Try to free trigger request + + explicit Trigger(MemoryPool& p) + : blr(p), debugInfo(p), entryPoint(p), extBody(p) + {} +}; + +// Set of triggers (use separate arrays for triggers of different types) +class Triggers +{ +public: + explicit Triggers(MemoryPool& p) + : triggers(p) + { } + + bool hasActive() const; + void decompile(thread_db* tdbb); + + void addTrigger(thread_db*, Trigger* trigger) + { + triggers.add(trigger); + } + + Trigger* const* begin() const + { + return triggers.begin(); + } + + Trigger* const* end() const + { + return triggers.end(); + } + + bool operator!() const + { + return !hasData(); + } + + operator bool() const + { + return hasData(); + } + + bool hasData() const + { + return triggers.hasData(); + } + + void release(thread_db* tdbb, bool destroy); + + static void destroy(thread_db* tdbb, Triggers* trigs); + +private: + Firebird::HalfStaticArray triggers; +}; + +class DbTriggersHeader : public Firebird::PermanentStorage +{ +public: + DbTriggersHeader(thread_db*, MemoryPool& p, MetaId& t, MakeLock* makeLock, NoData = NoData()); + + MetaId getId() + { + return type; + } + + static int blockingAst(void* ast_object); + static bool destroy(thread_db* tdbb, DbTriggersHeader* trigs); + + const char* c_name() const; + +private: + MetaId type; + Lock* lock; +}; + +class DbTriggers final : public Triggers, public ObjectBase +{ +public: + DbTriggers(DbTriggersHeader* hdr) + : Triggers(hdr->getPool()), + ObjectBase(), + perm(hdr) + { } + + static DbTriggers* create(thread_db*, MemoryPool&, DbTriggersHeader* hdr) + { + return FB_NEW_POOL(hdr->getPool()) DbTriggers(hdr); + } + + static void destroy(thread_db* tdbb, DbTriggers* trigs) + { + Triggers::destroy(tdbb, trigs); + delete trigs; + } + + static Lock* makeLock(thread_db* tdbb, MemoryPool& p); + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); + + ScanResult reload(thread_db* tdbb, ObjectBase::Flag flags) + { + return scan(tdbb, flags); + } + + const char* c_name() const override + { + return perm->c_name(); + } + + static const char* objectFamily(void*) + { + return "set of database-wide triggers on"; + } + + static int objectType(); + +private: + DbTriggersHeader* perm; + +public: + decltype(perm) getPermanent() const + { + return perm; + } +}; + +class TrigArray +{ +public: + TrigArray(MemoryPool& p); + Triggers& operator[](int t); + const Triggers& operator[](int t) const; + +private: + Triggers preErase, postErase, preModify, postModify, preStore, postStore; +}; + // view context block to cache view aliases @@ -174,7 +403,7 @@ class RelationPages private: RelationPages* rel_next_free; - SLONG useCount; + SLONG useCount; static const ULONG MAX_DPMAP_ITEMS = 64; @@ -193,117 +422,400 @@ class RelationPages Firebird::SortedArray, ULONG, DPItem> dpMap; ULONG dpMapMark; -friend class jrd_rel; +friend class RelationPermanent; }; -// Primary dependencies from all foreign references to relation's -// primary/unique keys +// Index status -struct prim +enum IndexStatus { - vec* prim_reference_ids; - vec* prim_relations; - vec* prim_indexes; + MET_index_active = 0, + MET_index_inactive = 1, + MET_index_deferred_active = 3, + MET_index_deferred_drop = 4, + MET_index_state_unknown = 999 }; +// Index block + +class IndexPermanent : public Firebird::PermanentStorage +{ +public: + IndexPermanent(thread_db* tdbb, MemoryPool& p, MetaId id, MakeLock*, RelationPermanent* rel) + : PermanentStorage(p), + idp_relation(rel), + idp_id(id) + { } + + ~IndexPermanent() + { + fb_assert((!idp_lock) || (idp_lock->lck_physical == LCK_none && idp_lock->lck_logical == LCK_none)); + } + + static int indexReload(void* ast_object); + + static bool destroy(thread_db* tdbb, IndexPermanent* idp) + { + idp->unlock(tdbb); + return false; + } + + MetaId getId() const + { + return idp_id; + } + + static const int REL_ID_KEY_OFFSET = 16; + void createLock(thread_db* tdbb, MetaId relId, MetaId indId); + + bool exclusiveLock(thread_db* tdbb); + bool sharedLock(thread_db* tdbb); + void unlock(thread_db* tdbb); + + Lock* getRescanLock() + { + return nullptr; + } + + RelationPermanent* getRelation() + { + return idp_relation; + } + + MetaName getName() const + { + return idp_name; + } + + const char* c_name() const; + +public: + MetaName idp_name; // used only as temp mirror for c_name() implementation + +private: + RelationPermanent* idp_relation; + Lock* idp_lock = nullptr; + MetaId idp_id; -// Foreign references to other relations' primary/unique keys + [[noreturn]] void errIndexGone(); +}; -struct frgn +class IndexVersion final : public ObjectBase { - vec* frgn_reference_ids; - vec* frgn_relations; - vec* frgn_indexes; +public: + IndexVersion(MemoryPool& p, Cached::Index* idp); + + static IndexVersion* create(thread_db* tdbb, MemoryPool& p, Cached::Index* idp) + { + return FB_NEW_POOL(p) IndexVersion(p, idp); + } + + static void destroy(thread_db* tdbb, IndexVersion* idv); + + static Lock* makeLock(thread_db* tdbb, MemoryPool& p) + { + return nullptr; + } + + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); + + ScanResult reload(thread_db* tdbb, ObjectBase::Flag flags) + { + return scan(tdbb, flags); + } + + const char* c_name() const override + { + return idv_name.c_str(); + } + + MetaName getName() const + { + return idv_name; + } + + static const char* objectFamily(void*) + { + return "index"; + } + + MetaName getForeignKey() const + { + return idv_foreignKey; + } + + MetaId getId() const + { + return perm->getId(); + } + + Cached::Index* getPermanent() const + { + return perm; + } + + IndexStatus getActive() + { + return idv_active; + } + +private: + Cached::Index* perm; + MetaName idv_name; + SSHORT idv_uniqFlag = 0; + SSHORT idv_segmentCount = 0; + SSHORT idv_type = 0; + MetaName idv_foreignKey; // FOREIGN RELATION NAME + IndexStatus idv_active = MET_index_state_unknown; + +public: + ValueExprNode* idv_expression = nullptr; // node tree for index expression + Statement* idv_expression_statement = nullptr; // statement for index expression evaluation + dsc idv_expression_desc; // descriptor for expression result + BoolExprNode* idv_condition = nullptr; // node tree for index condition + Statement* idv_condition_statement = nullptr; // statement for index condition evaluation }; + // Relation block; one is created for each relation referenced // in the database, though it is not really filled out until // the relation is scanned -class jrd_rel : public pool_alloc +class jrd_rel final : public ObjectBase { - typedef Firebird::HalfStaticArray GCRecordList; - public: - MemoryPool* rel_pool; - USHORT rel_id; - 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* rel_formats; // Known record formats - vec* rel_fields; // vector of field blocks - - RseNode* rel_view_rse; // view record select expression - ViewContexts rel_view_contexts; // sorted array of view contexts + jrd_rel(MemoryPool& p, Cached::Relation* r); - ExternalFile* rel_file; // external file name + MemoryPool* rel_pool; + Cached::Relation* rel_perm; + USHORT rel_current_fmt; // Current format number + Format* rel_current_format; // Current record format + USHORT rel_dbkey_length; // RDB$DBKEY length - GCRecordList rel_gc_records; // records for garbage collection + vec* rel_fields; // vector of field blocks - USHORT rel_use_count; // requests compiled with relation - USHORT rel_sweep_count; // sweep and/or garbage collector threads active - SSHORT rel_scan_count; // concurrent sequential scan count + RseNode* rel_view_rse; // view record select expression + ViewContexts rel_view_contexts; // sorted array of view contexts - Lock* 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 - IndexLock* rel_index_locks; // index existence locks - IndexBlock* rel_index_blocks; // index blocks for caching index info - TrigVector* rel_pre_erase; // Pre-operation erase trigger - TrigVector* rel_post_erase; // Post-operation erase trigger - TrigVector* rel_pre_modify; // Pre-operation modify trigger - TrigVector* rel_post_modify; // Post-operation modify trigger - TrigVector* rel_pre_store; // Pre-operation store trigger - TrigVector* 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 + TrigArray rel_triggers; Firebird::TriState rel_ss_definer; - Firebird::TriState rel_repl_state; // replication state + Firebird::TriState rel_repl_state; // replication state - Firebird::Mutex rel_drop_mutex; - - bool isSystem() const; + 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 isVirtual() const; bool isView() const; + bool isVirtual() const; + bool isSystem() const; + bool isReplicating(thread_db* tdbb); ObjectType getObjectType() const { return isView() ? obj_view : obj_relation; } - bool isReplicating(thread_db* tdbb); + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); // Scan the newly loaded relation for meta data + MetaName getName() const; + MemoryPool& getPool() const; + MetaName getSecurityName() const; + MetaName getOwnerName() const; + ExternalFile* getExtFile() const; - // global temporary relations attributes - RelationPages* getPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, bool allocPages = true); + static void destroy(thread_db* tdbb, jrd_rel *rel); + static jrd_rel* create(thread_db* tdbb, MemoryPool& p, Cached::Relation* perm); - RelationPages* getBasePages() + static Lock* makeLock(thread_db*, MemoryPool&) // ???????????????? { - return &rel_pages_base; + return nullptr; // ignored } - bool delPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, RelationPages* aPages = NULL); - void retainPages(thread_db* tdbb, TraNumber oldNumber, TraNumber newNumber); + ScanResult reload(thread_db* tdbb, ObjectBase::Flag flags) + { + return scan(tdbb, flags); + } - void getRelLockKey(thread_db* tdbb, UCHAR* key); - USHORT getRelLockKeyLength() const; + static const char* objectFamily(RelationPermanent* perm); + static int objectType(); - void cleanUp(); +public: + // bool hasTriggers() const; unused ??????????????????? + void releaseTriggers(thread_db* tdbb, bool destroy); + const Trigger* findTrigger(const MetaName trig_name) const; + const Format* currentFormat() const; + + decltype(rel_perm) getPermanent() const + { + return rel_perm; + } +}; + +// rel_flags + +const ULONG REL_system = 0x0001; +const ULONG REL_get_dependencies = 0x0002; // New relation needs dependencies during scan +const ULONG REL_sql_relation = 0x0004; // Relation defined as sql table +const ULONG REL_check_partners = 0x0008; // Rescan primary dependencies and foreign references +const ULONG REL_temp_tran = 0x0010; // relation is a GTT delete rows +const ULONG REL_temp_conn = 0x0020; // relation is a GTT preserve rows +const ULONG REL_virtual = 0x0040; // relation is virtual +const ULONG REL_jrd_view = 0x0080; // relation is VIEW +const ULONG REL_format = 0x0100; // new format version to be built + +class GCLock +{ +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); + + unsigned getSweepCount() const; // violates rules of atomic counters + // but OK for zerocheck when count can not grow + + static int ast(void* self) + { + try + { + reinterpret_cast(self)->blockingAst(); + } + catch(const Firebird::Exception&) { } + + return 0; + } + + void forcedRelease(thread_db* tdbb); + +private: + void blockingAst(); + void ensureReleased(thread_db* tdbb); + + [[noreturn]] void incrementError(); + +private: + Firebird::AutoPtr lck; + RelationPermanent* relPerm; + +public: + std::atomic 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 CacheVector Indices; + typedef Firebird::HalfStaticArray GCRecordList; + +public: + RelationPermanent(thread_db* tdbb, MemoryPool& p, MetaId id, MakeLock* makeLock, NoData); + ~RelationPermanent(); + static bool destroy(thread_db* tdbb, RelationPermanent* rel); + + void makeLocks(thread_db* tdbb, Cached::Relation* relation); + static constexpr USHORT getRelLockKeyLength(); + Lock* createLock(thread_db* tdbb, lck_t, bool); + Lock* createLock(thread_db* tdbb, MemoryPool& pool, lck_t, bool); + void extFile(thread_db* tdbb, const TEXT* file_name); // impl in ext.cpp + + IndexVersion* lookup_index(thread_db* tdbb, MetaId id, ObjectBase::Flag flags); + IndexVersion* lookup_index(thread_db* tdbb, MetaName name, ObjectBase::Flag flags); + Cached::Index* lookupIndex(thread_db* tdbb, MetaId id, ObjectBase::Flag flags); + Cached::Index* lookupIndex(thread_db* tdbb, MetaName name, ObjectBase::Flag flags); + + void newIndexVersion(thread_db* tdbb, MetaId id) + { + auto chk = rel_indices.makeObject(tdbb, id, CacheFlag::NOCOMMIT); + fb_assert(chk); + } + + void oldIndexVersion(thread_db* tdbb, MetaId id) + { + auto chk = rel_indices.getObject(tdbb, id, CacheFlag::AUTOCREATE); + fb_assert(chk); + } + + Cached::Index* eraseIndex(thread_db* tdbb, MetaId id) // oldIndex to be called before + { + auto idp = rel_indices.erase(tdbb, id); + fb_assert(idp); + return idp; + } + + 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 + + atomics::atomic rel_scan_count; // concurrent sequential scan count class RelPagesSnapshot : public Firebird::Array { public: typedef Firebird::Array inherited; - RelPagesSnapshot(thread_db* tdbb, jrd_rel* relation) + RelPagesSnapshot(thread_db* tdbb, RelationPermanent* relation) { spt_tdbb = tdbb; spt_relation = relation; @@ -314,14 +826,93 @@ class jrd_rel : public pool_alloc void clear(); private: thread_db* spt_tdbb; - jrd_rel* spt_relation; + RelationPermanent* spt_relation; - friend class jrd_rel; + friend class RelationPermanent; }; - void fillPagesSnapshot(RelPagesSnapshot&, const bool AttachmentOnly = false); + 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); + void scan_partners(thread_db* tdbb); // Foreign keys scan - impl. in met.epp + + 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; + } + + MetaName getSecurityName() const + { + return rel_security_name; + } + + ExternalFile* getExtFile() const + { + return rel_file; + } + + void setExtFile(ExternalFile* f) + { + fb_assert(!rel_file); + rel_file = f; + } + + void getRelLockKey(thread_db* tdbb, UCHAR* key); + PageNumber getIndexRootPage(thread_db* tdbb); + + bool isSystem() const; + bool isTemporary() const; + bool isVirtual() const; + bool isView() const; + bool isReplicating(thread_db* tdbb); + + static int partners_ast_relation(void* ast_object); + static int rescan_ast_relation(void* ast_object); + static int blocking_ast_relation(void* ast_object); + + // Relation must be updated on next use or commit + static void tagForUpdate(thread_db* tdbb, const MetaName name); + + vec* rel_formats; // Known record formats + Indices rel_indices; // Active indices + 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; // flags + + Firebird::TriState rel_repl_state; // replication state + + PrimaryDeps* rel_primary_dpnds = nullptr; // foreign dependencies on this relation's primary key + ForeignDeps* rel_foreign_refs = nullptr; // foreign references to other relations' primary keys private: + Firebird::Mutex rel_pages_mutex; + typedef Firebird::SortedArray< RelationPages*, Firebird::EmptyStorage, @@ -335,108 +926,102 @@ class jrd_rel : public pool_alloc RelationPages* getPagesInternal(thread_db* tdbb, TraNumber tran, bool allocPages); -public: - explicit jrd_rel(MemoryPool& p); + ExternalFile* rel_file; +}; - bool hasTriggers() const; - void releaseTriggers(thread_db* tdbb, bool destroy); - void replaceTriggers(thread_db* tdbb, TrigVector** triggers); - static Lock* createLock(thread_db* tdbb, MemoryPool* pool, jrd_rel* relation, lck_t, bool); - static int blocking_ast_gcLock(void*); +inline bool jrd_rel::hasData() const +{ + return rel_perm->rel_name.hasData(); +} - void downgradeGCLock(thread_db* tdbb); - bool acquireGCLock(thread_db* tdbb, int wait); +inline const char* jrd_rel::c_name() const +{ + return rel_perm->rel_name.c_str(); +} - // 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(); +inline MetaName jrd_rel::getName() const +{ + return rel_perm->rel_name; +} - bool gcEnabled() const - { - return m_gcEnabled; - } +inline MemoryPool& jrd_rel::getPool() const +{ + return rel_perm->getPool(); +} - private: - thread_db* m_tdbb; - jrd_rel* m_relation; - bool m_gcEnabled; - }; +inline ExternalFile* jrd_rel::getExtFile() const +{ + return rel_perm->getExtFile(); +} - // 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(); +inline MetaName jrd_rel::getSecurityName() const +{ + return rel_perm->rel_security_name; +} - bool acquire(int wait); - void release(); +inline MetaName jrd_rel::getOwnerName() const +{ + return rel_perm->rel_owner_name; +} - private: - thread_db* m_tdbb; - jrd_rel* m_relation; - Lock* m_lock; - }; -}; +inline MetaId jrd_rel::getId() const +{ + return rel_perm->rel_id; +} -// rel_flags +inline RelationPages* jrd_rel::getPages(thread_db* tdbb, TraNumber tran, bool allocPages) +{ + return rel_perm->getPages(tdbb, tran, allocPages); +} -const ULONG REL_scanned = 0x0001; // Field expressions scanned (or being scanned) -const ULONG REL_system = 0x0002; -const ULONG REL_deleted = 0x0004; // Relation known gonzo -const ULONG REL_get_dependencies = 0x0008; // New relation needs dependencies during scan -const ULONG REL_check_existence = 0x0010; // Existence lock released pending drop of relation -const ULONG REL_blocking = 0x0020; // Blocking someone from dropping relation -const ULONG REL_sql_relation = 0x0080; // Relation defined as sql table -const ULONG REL_check_partners = 0x0100; // Rescan primary dependencies and foreign references -const ULONG REL_being_scanned = 0x0200; // relation scan in progress -const ULONG REL_deleting = 0x0800; // relation delete in progress -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 - - -/// class jrd_rel - -inline jrd_rel::jrd_rel(MemoryPool& p) - : rel_pool(&p), rel_flags(REL_gc_lockneed), - rel_name(p), rel_owner_name(p), rel_security_name(p), - rel_view_contexts(p), rel_gc_records(p), rel_ss_definer(false), - rel_pages_base(p) +inline bool jrd_rel::isTemporary() const +{ + 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 jrd_rel::isReplicating(thread_db* tdbb) +{ + return rel_perm->isReplicating(tdbb); +} + + +inline bool RelationPermanent::isSystem() const { return rel_flags & REL_system; } -inline bool jrd_rel::isTemporary() const +inline bool RelationPermanent::isTemporary() const { return (rel_flags & (REL_temp_tran | REL_temp_conn)); } -inline bool jrd_rel::isVirtual() const +inline bool RelationPermanent::isVirtual() const { return (rel_flags & REL_virtual); } -inline bool jrd_rel::isView() const +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; @@ -444,36 +1029,32 @@ 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) + m_rl(rl), + m_gcEnabled(m_rl->rel_gc_lock.acquire(m_tdbb, LCK_NO_WAIT)) +{ } + +inline GCLock::Shared::~Shared() { - if (m_relation->rel_flags & (REL_gc_blocking | REL_gc_disabled)) - return; + if (m_gcEnabled) + m_rl->rel_gc_lock.downgrade(m_tdbb); +} - 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; - } +/// class GCLock::Exclusive - if ((m_relation->rel_flags & REL_gc_blocking) && !m_relation->rel_sweep_count) - m_relation->downgradeGCLock(m_tdbb); +inline bool GCLock::Exclusive::acquire(int wait) +{ + return m_rl->rel_gc_lock.disable(m_tdbb, wait, m_lock); } -inline jrd_rel::GCShared::~GCShared() +inline void GCLock::Exclusive::release() { - if (m_gcEnabled) - --m_relation->rel_sweep_count; - - if ((m_relation->rel_flags & REL_gc_blocking) && !m_relation->rel_sweep_count) - m_relation->downgradeGCLock(m_tdbb); + return m_rl->rel_gc_lock.enable(m_tdbb, m_lock); } @@ -490,12 +1071,17 @@ class jrd_fld : public pool_alloc ValueExprNode* fld_computation; // computation for virtual field ValueExprNode* fld_source; // source for view fields ValueExprNode* fld_default_value; // default value, if any - ArrayField* fld_array; // array description, if array - MetaName fld_name; // Field name - MetaName fld_security_name; // security class name for field - MetaName fld_generator_name; // identity generator name + ArrayField* fld_array; // array description, if array + MetaName fld_name; // Field name + MetaName fld_security_name; // security class name for field + MetaName fld_generator_name; // identity generator name + MetaName fld_source_name; // RDB%FIELD name MetaNamePair fld_source_rel_field; // Relation/field source name std::optional fld_identity_type; + USHORT fld_length; + USHORT fld_segment_length; + USHORT fld_character_length; + USHORT fld_pos; USHORT fld_flags; public: diff --git a/src/jrd/Resources.cpp b/src/jrd/Resources.cpp new file mode 100644 index 00000000000..3ac4722baaf --- /dev/null +++ b/src/jrd/Resources.cpp @@ -0,0 +1,35 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: Resources.cpp + * DESCRIPTION: Resource used by request / transaction + * + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../jrd/Resources.h" + +#include "../jrd/Relation.h" +#include "../jrd/CharSetContainer.h" +#include "../jrd/Function.h" +#include "../jrd/met.h" + +using namespace Firebird; +using namespace Jrd; + + +void Resources::transfer(thread_db* tdbb, VersionedObjects* to, bool internal) +{ + charSets.transfer(tdbb, to, internal); + relations.transfer(tdbb, to, internal); + procedures.transfer(tdbb, to, internal); + functions.transfer(tdbb, to, internal); + triggers.transfer(tdbb, to, internal); + indices.transfer(tdbb, to, internal); +} + +Resources::~Resources() +{ } + diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h new file mode 100644 index 00000000000..9f583edbca6 --- /dev/null +++ b/src/jrd/Resources.h @@ -0,0 +1,289 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: Resources.h + * DESCRIPTION: Resource used by request / transaction + * + * The contents of this file are subject to the Initial + * Developer's 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.firebirdsql.org/en/initial-developer-s-public-license-version-1-0/ + * + * Software distributed under the License is distributed AS IS, + * 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 Alexander Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2023 Alexander Peshkov + * and all contributors signed below. + */ + +#ifndef JRD_RESOURCES_H +#define JRD_RESOURCES_H + +#include "fb_blk.h" +#include "../jrd/CacheVector.h" +#include + +namespace Jrd { + +class RelationPermanent; +class RoutinePermanent; +class CharSetContainer; +class jrd_rel; +class jrd_prc; +class Function; +class DbTriggersHeader; +class DbTriggers; +class CharSetVers; +class IndexPermanent; +class IndexVersion; + +namespace Cached +{ + // DB objects stored in cache vector + typedef CacheElement Relation; + typedef CacheElement Procedure; + typedef CacheElement CharSet; + typedef CacheElement Function; + typedef CacheElement Triggers; + typedef CacheElement Index; +} + +class Resources; + +// Set of objects cached per particular MDC version + +union VersionedPartPtr +{ + jrd_rel* relation; + jrd_prc* procedure; + Function* function; + CharSetVers* charset; + DbTriggers* triggers; + IndexVersion* index; +}; + +class VersionedObjects : public pool_alloc_rpt, + public Firebird::RefCounted +{ + +public: + VersionedObjects(FB_SIZE_T cnt, MdcVersion ver) : + version(ver), + capacity(cnt) + { } + + template + void put(FB_SIZE_T n, C* obj) + { + fb_assert(n < capacity); + fb_assert(!object(n)); + object(n) = obj; + } + + template + C* get(FB_SIZE_T n) const + { + fb_assert(n < capacity); + return object(n); + } + + FB_SIZE_T getCapacity() + { + return capacity; + } + + const MdcVersion version; // version when created + +private: + FB_SIZE_T capacity; + VersionedPartPtr data[1]; + + template C*& object(FB_SIZE_T n); + template C* object(FB_SIZE_T n) const; +}; + +// specialization +template <> inline Function*& VersionedObjects::object(FB_SIZE_T n) { return data[n].function; } +template <> inline jrd_prc*& VersionedObjects::object(FB_SIZE_T n) { return data[n].procedure; } +template <> inline jrd_rel*& VersionedObjects::object(FB_SIZE_T n) { return data[n].relation; } +template <> inline CharSetVers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].charset; } +template <> inline DbTriggers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].triggers; } +template <> inline IndexVersion*& VersionedObjects::object(FB_SIZE_T n) { return data[n].index; } + +template <> inline Function* VersionedObjects::object(FB_SIZE_T n) const { return data[n].function; } +template <> inline jrd_prc* VersionedObjects::object(FB_SIZE_T n) const { return data[n].procedure; } +template <> inline jrd_rel* VersionedObjects::object(FB_SIZE_T n) const { return data[n].relation; } +template <> inline CharSetVers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].charset; } +template <> inline DbTriggers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].triggers; } +template <> inline IndexVersion* VersionedObjects::object(FB_SIZE_T n) const { return data[n].index; } + + +template +class CachedResource +{ +public: + CachedResource(CacheElement* elem, FB_SIZE_T version) + : cacheElement(elem), versionOffset(version) + { } + + CachedResource() + : cacheElement(nullptr) + { } + + OBJ* operator()(const VersionedObjects* runTime) const + { + return cacheElement ? runTime->get(versionOffset) : nullptr; + } + + OBJ* operator()(const VersionedObjects& runTime) const + { + return cacheElement ? runTime.get(versionOffset) : nullptr; + } + + OBJ* operator()(thread_db* tdbb) const + { + return cacheElement ? cacheElement->getObject(tdbb, CacheFlag::NOSCAN) : nullptr; + } + + CacheElement* 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* cacheElement; + FB_SIZE_T versionOffset; +}; + + +class Resources final +{ +public: + template + class RscArray : public Firebird::Array> + { + public: + typedef CacheElement StoredElement; + + RscArray(MemoryPool& p, FB_SIZE_T& pos) + : Firebird::Array>(p), + versionCurrentPosition(pos) + { } + + CachedResource& registerResource(StoredElement* res) + { + FB_SIZE_T pos; + if (!this->find([res](const CachedResource& elem) { + auto* e = elem(); + return e == res ? 0 : std::less{}(e, res) ? -1 : 1; + }, pos)) + { + CachedResource newPtr(res, versionCurrentPosition++); + pos = this->add(newPtr); + } + + return this->getElement(pos); + } + + void transfer(thread_db* tdbb, VersionedObjects* to, bool internal) + { + for (auto& resource : *this) + { + to->put(resource.getOffset(), resource()->getObject(tdbb, + internal ? CacheFlag::NOSCAN : CacheFlag::AUTOCREATE)); + } + } + + private: + FB_SIZE_T& versionCurrentPosition; + }; + + void transfer(thread_db* tdbb, VersionedObjects* to, bool internal); + void release(thread_db* tdbb); + +#ifdef DEV_BUILD + MemoryPool* getPool() const + { + return &charSets.getPool(); + } +#endif + +private: + FB_SIZE_T versionCurrentPosition; + +public: + template const RscArray& objects() const; + + Resources(MemoryPool& p) + : versionCurrentPosition(0), + charSets(p, versionCurrentPosition), + relations(p, versionCurrentPosition), + procedures(p, versionCurrentPosition), + functions(p, versionCurrentPosition), + triggers(p, versionCurrentPosition), + indices(p, versionCurrentPosition) + { } + + ~Resources(); + + RscArray charSets; + RscArray relations; + RscArray procedures; + RscArray functions; + RscArray triggers; + RscArray indices; +}; + +// specialization +template <> inline const Resources::RscArray& Resources::objects() const { return relations; } +template <> inline const Resources::RscArray& Resources::objects() const { return procedures; } +template <> inline const Resources::RscArray& Resources::objects() const { return functions; } +template <> inline const Resources::RscArray& Resources::objects() const { return charSets; } +template <> inline const Resources::RscArray& Resources::objects() const { return triggers; } +template <> inline const Resources::RscArray& Resources::objects() const { return indices; } + +namespace Rsc +{ + typedef CachedResource Rel; + typedef CachedResource Proc; + typedef CachedResource Fun; + typedef CachedResource CSet; + typedef CachedResource Trig; + typedef CachedResource Idx; +}; //namespace Rsc + + +} // namespace Jrd + +#endif // JRD_RESOURCES_H + diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index 4876f5d5f7c..e54029b66a9 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -26,14 +26,27 @@ #include "../jrd/jrd.h" #include "../jrd/exe.h" #include "../common/StatusHolder.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/par_proto.h" +#include "../jrd/met.h" using namespace Firebird; namespace Jrd { +RoutinePermanent::RoutinePermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, MakeLock* makeLock, NoData) + : PermanentStorage(p), + id(metaId), + name(p), + securityName(p), + subRoutine(false), + existenceLock(makeLock(tdbb, p)) +{ + existenceLock->setKey(metaId); + existenceLock->lck_object = this; +} + // Create a MsgMetadata from a parameters array. MsgMetadata* Routine::createMetadata(const Array >& parameters, bool isExtern) @@ -85,7 +98,7 @@ Format* Routine::createFormat(MemoryPool& pool, IMessageMetadata* params, bool a status.check(); desc->dsc_sub_type = params->getSubType(&status, i); status.check(); - desc->setTextType(params->getCharSet(&status, i)); + desc->setTextType(TTypeId(params->getCharSet(&status, i))); status.check(); desc->dsc_address = (UCHAR*)(IPTR) descOffset; desc->dsc_flags = (params->isNullable(&status, i) ? DSC_nullable : 0); @@ -132,22 +145,6 @@ void Routine::setStatement(Statement* value) } } -void Routine::checkReload(thread_db* tdbb) -{ - if (!(flags & FLAG_RELOAD)) - return; - - if (!reload(tdbb)) - { - string err; - err.printf("Recompile of %s \"%s\" failed", - getObjectType() == obj_udf ? "FUNCTION" : "PROCEDURE", - getName().toString().c_str()); - - (Arg::Gds(isc_random) << Arg::Str(err)).raise(); - } -} - // Parse routine BLR and debug info. void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id, bid* blobDbg) { @@ -169,14 +166,12 @@ void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id, bid* parseMessages(tdbb, csb, BlrReader(tmp.begin(), (unsigned) tmp.getCount())); - flags &= ~Routine::FLAG_RELOAD; - Statement* statement = getStatement(); - PAR_blr(tdbb, NULL, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb, &statement, false, 0); + PAR_blr(tdbb, nullptr, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb, &statement, false, 0); setStatement(statement); if (csb->csb_g_flags & csb_reload) - flags |= FLAG_RELOAD; + flReload = true; if (!blob_id) setImplemented(false); @@ -255,45 +250,6 @@ void Routine::parseMessages(thread_db* tdbb, CompilerScratch* csb, BlrReader blr } } -// Decrement the routine's use count. -void Routine::release(thread_db* tdbb) -{ - // Actually, it's possible for routines to have intermixed dependencies, so - // this routine can be called for the routine which is being freed itself. - // Hence we should just silently ignore such a situation. - - if (!useCount) - return; - - if (intUseCount > 0) - intUseCount--; - - --useCount; - -#ifdef DEBUG_PROCS - { - string buffer; - buffer.printf( - "Called from CMP_decrement():\n\t Decrementing use count of %s\n", - getName().toString().c_str()); - JRD_print_procedure_info(tdbb, buffer.c_str()); - } -#endif - - // 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 (useCount == 0 && !checkCache(tdbb)) - { - if (getStatement()) - releaseStatement(tdbb); - - flags &= ~Routine::FLAG_BEING_ALTERED; - remove(tdbb); - } -} - void Routine::releaseStatement(thread_db* tdbb) { if (getStatement()) @@ -304,35 +260,14 @@ void Routine::releaseStatement(thread_db* tdbb) setInputFormat(NULL); setOutputFormat(NULL); - - flags &= ~FLAG_SCANNED; } +/* // Remove a routine from cache. void Routine::remove(thread_db* tdbb) { SET_TDBB(tdbb); - // MET_procedure locks it. Lets unlock it now to avoid troubles later - if (existenceLock) - LCK_release(tdbb, existenceLock); - - // Routine that is being altered may have references - // to it by other routines via pointer to current meta - // data structure, so don't lose the structure or the pointer. - if (checkCache(tdbb) && !(flags & Routine::FLAG_BEING_ALTERED)) - clearCache(tdbb); - - // deallocate all structure which were allocated. The routine - // blr is originally read into a new pool from which all request - // are allocated. That will not be freed up. - - if (existenceLock) - { - delete existenceLock; - existenceLock = NULL; - } - // deallocate input param structures for (Array >::iterator i = getInputFields().begin(); @@ -341,7 +276,6 @@ void Routine::remove(thread_db* tdbb) if (*i) delete i->getObject(); } - getInputFields().clear(); // deallocate output param structures @@ -352,36 +286,37 @@ void Routine::remove(thread_db* tdbb) if (*i) delete i->getObject(); } - getOutputFields().clear(); - if (!useCount) - releaseFormat(); + releaseFormat(); - if (!(flags & Routine::FLAG_BEING_ALTERED) && useCount == 0) - delete this; - else - { - // Fully clear routine block. Some pieces of code check for empty - // routine name, this is why we do it. - setName(QualifiedName()); - setSecurityName(""); - setDefaultCount(0); - releaseExternal(); - flags |= FLAG_CLEARED; - } + // Fully clear routine block. Some pieces of code check for empty + // routine name, this is why we do it. + setName(QualifiedName()); + setSecurityName(""); + setDefaultCount(0); + releaseExternal(); + flags |= FLAG_CLEARED; } +*/ - -bool jrd_prc::checkCache(thread_db* tdbb) const +void RoutinePermanent::releaseLocks(thread_db* tdbb) { - return tdbb->getAttachment()->att_procedures[getId()] == this; + if (existenceLock) + LCK_release(tdbb, existenceLock); } -void jrd_prc::clearCache(thread_db* tdbb) +bool RoutinePermanent::destroy(thread_db* tdbb, RoutinePermanent* routine) { - tdbb->getAttachment()->att_procedures[getId()] = NULL; + routine->releaseLocks(tdbb); + return false; } +void Routine::destroy(thread_db* tdbb, Routine* routine) +{ + if (routine->statement) + routine->statement->release(tdbb); + delete routine; +} } // namespace Jrd diff --git a/src/jrd/Routine.h b/src/jrd/Routine.h index 6481ab29b38..9276968a1c2 100644 --- a/src/jrd/Routine.h +++ b/src/jrd/Routine.h @@ -26,8 +26,12 @@ #include "../common/classes/BlrReader.h" #include "../jrd/MetaName.h" #include "../jrd/QualifiedName.h" +#include "../jrd/CacheVector.h" +#include "../jrd/Resources.h" #include "../common/classes/NestConst.h" #include "../common/MsgMetadata.h" +#include "../common/classes/auto.h" +#include "../common/ThreadStart.h" namespace Jrd { @@ -39,16 +43,56 @@ namespace Jrd class Parameter; class UserId; - class Routine : public Firebird::PermanentStorage + class RoutinePermanent : public Firebird::PermanentStorage { - protected: - explicit Routine(MemoryPool& p) + public: + explicit RoutinePermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, MakeLock* makeLock, NoData); + + explicit RoutinePermanent(MemoryPool& p) : PermanentStorage(p), - id(0), + id(~0), name(p), securityName(p), - statement(NULL), - subRoutine(false), + subRoutine(true), + existenceLock(NULL) + { } + + MetaId getId() const + { + fb_assert(!subRoutine); + return id; + } + + static bool destroy(thread_db* tdbb, RoutinePermanent* routine); + + const QualifiedName& getName() const { return name; } + void setName(const QualifiedName& value) { name = value; } + const char* c_name() const { return name.c_str(); } + + const MetaName& getSecurityName() const { return securityName; } + void setSecurityName(const MetaName& value) { securityName = value; } + + bool hasData() const { return name.hasData(); } + + bool isSubRoutine() const { return subRoutine; } + void setSubRoutine(bool value) { subRoutine = value; } + + void releaseLocks(thread_db* tdbb); + + public: + MetaId id; // routine ID + QualifiedName name; // routine name + MetaName securityName; // security class name + bool subRoutine; // Is this a subroutine? + Lock* existenceLock; // existence lock, if any + MetaName owner; + }; + + class Routine : public ObjectBase + { + protected: + explicit Routine(MemoryPool& p) + : statement(NULL), implemented(true), defined(true), defaultCount(0), @@ -56,11 +100,7 @@ namespace Jrd outputFormat(NULL), inputFields(p), outputFields(p), - flags(0), - useCount(0), - intUseCount(0), - alterCount(0), - existenceLock(NULL), + flReload(false), invoker(NULL) { } @@ -71,52 +111,29 @@ namespace Jrd } 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 const USHORT MAX_ALTER_COUNT = 64; // Number of times an in-cache routine can be altered ????????? static Firebird::MsgMetadata* createMetadata( const Firebird::Array >& parameters, bool isExtern); static Format* createFormat(MemoryPool& pool, Firebird::IMessageMetadata* params, bool addEof); public: - USHORT getId() const - { - fb_assert(!subRoutine); - return id; - } - - void setId(USHORT value) { id = value; } + static void destroy(thread_db* tdbb, Routine* routine); - const QualifiedName& getName() const { return name; } - void setName(const QualifiedName& value) { name = value; } - - const MetaName& getSecurityName() const { return securityName; } - void setSecurityName(const MetaName& value) { securityName = value; } + const QualifiedName& getName() const { return getPermanent()->getName(); } + MetaId getId() const { return getPermanent()->getId(); } + const char* c_name() const override { return getPermanent()->c_name(); } /*const*/ Statement* getStatement() const { return statement; } void setStatement(Statement* value); - bool isSubRoutine() const { return subRoutine; } - void setSubRoutine(bool value) { subRoutine = value; } - bool isImplemented() const { return implemented; } void setImplemented(bool value) { implemented = value; } bool isDefined() const { return defined; } void setDefined(bool value) { defined = value; } - void checkReload(thread_db* tdbb); + virtual void checkReload(thread_db* tdbb) const = 0; USHORT getDefaultCount() const { return defaultCount; } void setDefaultCount(USHORT value) { defaultCount = value; } @@ -136,37 +153,26 @@ namespace Jrd 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 useCount != 0; - } - - void addRef() + virtual void releaseFormat() { - ++useCount; } - virtual void releaseFormat() + void afterDecrement(Jrd::thread_db*); + void releaseStatement(thread_db* tdbb); + //void remove(thread_db* tdbb); + virtual void releaseExternal() { } - void release(thread_db* tdbb); - void releaseStatement(thread_db* tdbb); - void remove(thread_db* tdbb); - virtual void releaseExternal() {}; + void sharedCheckUnlock(thread_db* tdbb); public: virtual int getObjectType() const = 0; virtual SLONG getSclType() const = 0; - virtual bool checkCache(thread_db* tdbb) const = 0; - virtual void clearCache(thread_db* tdbb) = 0; + virtual RoutinePermanent* getPermanent() const = 0; // 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? + Statement* statement; // compiled routine statement bool implemented; // Is the packaged routine missing the body/entrypoint? bool defined; // UDF has its implementation module available USHORT defaultCount; // default input arguments @@ -175,23 +181,82 @@ namespace Jrd Firebird::Array > inputFields; // array of field blocks Firebird::Array > outputFields; // array of field blocks - protected: + public: + bool flReload; + + public: + Jrd::UserId* invoker; // Invoker ID + }; - virtual bool reload(thread_db* tdbb) = 0; + template + class SubRoutine + { public: - USHORT flags; - USHORT useCount; // requests compiled with routine - SSHORT intUseCount; // number of routines compiled with routine, set and - // used internally in the MET_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 - Lock* existenceLock; // existence lock, if any + SubRoutine() + : routine(), subroutine(nullptr) + { } - MetaName owner; - Jrd::UserId* invoker; // Invoker ID + SubRoutine(const CachedResource& r) + : routine(r), subroutine(nullptr) + { } + + SubRoutine(R* r) + : routine(), subroutine(r) + { } + + SubRoutine& operator=(const CachedResource& r) + { + routine = r; + subroutine = nullptr; + return *this; + } + + SubRoutine& operator=(R* r) + { + routine.clear(); + subroutine = r; + return *this; + } + + template + R* operator()(T t) + { + return isSubRoutine() ? subroutine : routine.isSet() ? routine(t) : nullptr; + } + + template + const R* operator()(T t) const + { + return isSubRoutine() ? subroutine : routine.isSet() ? routine(t) : nullptr; + } + + CacheElement* operator()() + { + return isSubRoutine() ? subroutine->getPermanent() : routine.isSet() ? routine() : nullptr; + } + + const CacheElement* operator()() const + { + return isSubRoutine() ? subroutine->getPermanent() : routine.isSet() ? routine() : nullptr; + } + + 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 routine; + R* subroutine; }; + } #endif // JRD_ROUTINE_H diff --git a/src/jrd/RuntimeStatistics.cpp b/src/jrd/RuntimeStatistics.cpp index d854b484c80..4291987b425 100644 --- a/src/jrd/RuntimeStatistics.cpp +++ b/src/jrd/RuntimeStatistics.cpp @@ -26,6 +26,7 @@ #include "../jrd/RuntimeStatistics.h" #include "../jrd/ntrace.h" +#include "../jrd/met.h" using namespace Firebird; @@ -108,14 +109,11 @@ PerformanceInfo* RuntimeStatistics::computeDifference(Attachment* att, // Point TraceCounts to counts array from baseline object if (base_cnts->setToDiff(*new_cnts)) { - jrd_rel* const relation = - rel_id < static_cast(att->att_relations->count()) ? - (*att->att_relations)[rel_id] : NULL; - TraceCounts traceCounts; traceCounts.trc_relation_id = rel_id; traceCounts.trc_counters = base_cnts->getCounterVector(); - traceCounts.trc_relation_name = relation ? relation->rel_name.c_str() : NULL; + auto relation = att->att_database->dbb_mdc->lookupRelationNoChecks(rel_id); + traceCounts.trc_relation_name = relation ? relation->c_name() : NULL; temp.add(traceCounts); } @@ -124,15 +122,12 @@ PerformanceInfo* RuntimeStatistics::computeDifference(Attachment* att, } else { - jrd_rel* const relation = - rel_id < static_cast(att->att_relations->count()) ? - (*att->att_relations)[rel_id] : NULL; - // Point TraceCounts to counts array from object with updated counters TraceCounts traceCounts; traceCounts.trc_relation_id = rel_id; traceCounts.trc_counters = new_cnts->getCounterVector(); - traceCounts.trc_relation_name = relation ? relation->rel_name.c_str() : NULL; + auto relation = att->att_database->dbb_mdc->lookupRelationNoChecks(rel_id); + traceCounts.trc_relation_name = relation ? relation->c_name() : NULL; temp.add(traceCounts); } }; @@ -144,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() diff --git a/src/jrd/Savepoint.cpp b/src/jrd/Savepoint.cpp index 852df16f181..29838a9322e 100644 --- a/src/jrd/Savepoint.cpp +++ b/src/jrd/Savepoint.cpp @@ -400,7 +400,7 @@ void Savepoint::cleanupTempData() for (VerbAction* action = m_actions; action; action = action->vct_next) { - if (action->vct_relation->rel_flags & REL_temp_tran) + if (action->vct_relation->rel_perm->rel_flags & REL_temp_tran) { RecordBitmap::reset(action->vct_records); diff --git a/src/jrd/Savepoint.h b/src/jrd/Savepoint.h index cc628cf637e..768509bf71c 100644 --- a/src/jrd/Savepoint.h +++ b/src/jrd/Savepoint.h @@ -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() diff --git a/src/jrd/SharedReadVector.h b/src/jrd/SharedReadVector.h new file mode 100644 index 00000000000..023f2bc22ea --- /dev/null +++ b/src/jrd/SharedReadVector.h @@ -0,0 +1,225 @@ +/* + * PROGRAM: Engine Code + * MODULE: SharedReadVector.h + * DESCRIPTION: Shared read here means that any thread + * can read from vector using HP to it's generation. + * Vector can be modified only in single thread, + * and it's caller's responsibility that modifying + * thread is single. + * + * The contents of this file are subject to the Initial + * Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * 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 Alexander Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2021 Alexander Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef JRD_SHAREDREADVECTOR_H +#define JRD_SHAREDREADVECTOR_H + +#include "../jrd/HazardPtr.h" + +/* +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/auto.h" +*/ +#include "fb_blk.h" +/* +#include + +#include "../jrd/tdbb.h" +#include "../jrd/Database.h" +*/ + +namespace Jrd { + +template +class SharedReadVector +{ +public: + class Generation : public HazardObject, public pool_alloc_rpt + { + private: + Generation(FB_SIZE_T size) + : count(0), capacity(size) + { } + + FB_SIZE_T count, capacity; + T data[1]; + + public: + static Generation* create(FB_SIZE_T cap) + { + return FB_NEW_RPT(*getDefaultMemoryPool(), cap) Generation(cap); + } + + FB_SIZE_T getCount() const + { + return count; + } + + FB_SIZE_T getCapacity() const + { + return capacity; + } + + T* begin() + { + return &data[0]; + } + + T* end() + { + return &data[count]; + } + + const T& value(FB_SIZE_T i) const + { + fb_assert(i < count); + return data[i]; + } + + T& value(FB_SIZE_T i) + { + fb_assert(i < count); + return data[i]; + } + + bool hasSpace(FB_SIZE_T needs = 1) const + { + return count + needs <= capacity; + } + + bool add(const Generation* from) + { + if (!hasSpace(from->count)) + return false; + memcpy(&data[count], from->data, from->count * sizeof(T)); + count += from->count; + return true; + } + + T* addStart() + { + if (!hasSpace()) + return nullptr; + return &data[count]; + } + + void addComplete() + { + ++count; + } + + void truncate(const T& notValue) + { + while (count && data[count - 1] == notValue) + count--; + } + + void clear() + { + count = 0; + } + + void grow(const FB_SIZE_T newCount) + { + fb_assert(newCount >= count); + fb_assert(newCount <= capacity); + memset(data + count, 0, sizeof(T) * (newCount - count)); + count = newCount; + } + + static void destroy(Generation* gen) + { + // delay delete - someone else may access it + gen->retire(); + } + }; + + typedef HazardPtr ReadAccessor; + typedef Generation* WriteAccessor; + + SharedReadVector() + : currentData(Generation::create(CAP)) + { } + + WriteAccessor writeAccessor() + { + return currentData.load(std::memory_order_acquire); + } + + ReadAccessor readAccessor() const + { + return HazardPtr(currentData); + } + + void grow(FB_SIZE_T const newSize, bool arrGrow) + { + // decide how much vector grows + Generation* const oldGeneration = writeAccessor(); + FB_SIZE_T cap = oldGeneration->getCapacity(); + FB_SIZE_T doubleSize = oldGeneration->getCapacity() * 2; + if (newSize > doubleSize) + doubleSize = newSize; + FB_SIZE_T singleSize = newSize ? newSize : doubleSize; + + if (cap >= singleSize) + { + // grow old generation inplace + if (arrGrow) + oldGeneration->grow(singleSize); + return; + } + + // create new generation and store it in the vector + Generation* const newGeneration = Generation::create(doubleSize); + newGeneration->add(oldGeneration); + if (arrGrow) + newGeneration->grow(singleSize); + currentData.store(newGeneration, std::memory_order_release); + + // cleanup + Generation::destroy(oldGeneration); + } + + ~SharedReadVector() + { + Generation::destroy(currentData.load(std::memory_order_acquire)); + } + + void clear() + { + // expected to be called when going to destroy an object + writeAccessor()->clear(); + } + + bool hasData() + { + return readAccessor()->getCount() != 0; + } + +private: + atomics::atomic currentData; +}; + +} // namespace Jrd + +#endif // JRD_SHAREDREADVECTOR_H diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 3aac47c4d17..4d4142f45bf 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -31,11 +31,12 @@ #include "../dsql/StmtNodes.h" #include "../jrd/Function.h" #include "../jrd/cmp_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/exe_proto.h" #include "../jrd/met_proto.h" #include "../jrd/scl_proto.h" #include "../jrd/Collation.h" +#include "../jrd/met.h" #include "../jrd/recsrc/Cursor.h" using namespace Firebird; @@ -63,10 +64,9 @@ ULONG CompilerScratch::allocImpure(ULONG align, ULONG size) Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) : pool(p), rpbsSetup(*p), - requests(*p), + requests(), externalList(*p), accessList(*p), - resources(*p), triggerName(*p), triggerInvoker(NULL), parentStatement(NULL), @@ -75,8 +75,15 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) localTables(*p), invariants(*p), blr(*p), - mapFieldInfo(*p) + mapFieldInfo(*p), + resources(nullptr) { + if (csb->csb_resources) + { + fb_assert(csb->csb_resources->getPool() == pool); + resources = csb->csb_resources; + } + try { makeSubRoutines(tdbb, this, csb, csb->subProcedures); @@ -93,73 +100,12 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) mapFieldInfo.takeOwnership(csb->csb_map_field_info); - resources = csb->csb_resources; // Assign array contents - csb->csb_resources.clear(); - impureSize = csb->csb_impure; //if (csb->csb_g_flags & csb_blr_version4) // flags |= FLAG_VERSION4; blrVersion = csb->blrVersion; - // Take out existence locks on resources used in statement. This is - // a little complicated since relation locks MUST be taken before - // index locks. - - for (Resource* resource = resources.begin(); resource != resources.end(); ++resource) - { - switch (resource->rsc_type) - { - case Resource::rsc_relation: - { - jrd_rel* relation = resource->rsc_rel; - MET_post_existence(tdbb, relation); - break; - } - - case Resource::rsc_index: - { - jrd_rel* relation = resource->rsc_rel; - IndexLock* index = CMP_get_index_lock(tdbb, relation, resource->rsc_id); - if (index) - { - ++index->idl_count; - if (index->idl_count == 1) { - LCK_lock(tdbb, index->idl_lock, LCK_SR, LCK_WAIT); - } - } - break; - } - - case Resource::rsc_procedure: - case Resource::rsc_function: - { - Routine* routine = resource->rsc_routine; - routine->addRef(); - -#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 = resource->rsc_coll; - coll->incUseCount(tdbb); - break; - } - - default: - BUGCHECK(219); // msg 219 request of unknown resource - } - } - // make a vector of all used RSEs fors = csb->csb_fors; csb->csb_fors.clear(); @@ -177,6 +123,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 @@ -195,11 +143,18 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) rpb->rpb_stream_flags |= RPB_s_skipLocked; rpb->rpb_relation = tail->csb_relation; + if (rpb->rpb_relation()) + resources->relations.registerResource(rpb->rpb_relation()); delete tail->csb_fields; tail->csb_fields = NULL; } + // versioned metadata support + if (csb->csb_g_flags & csb_internal) + flags |= FLAG_INTERNAL; + loadResources(tdbb, nullptr); + if (csb->csb_variables) csb->csb_variables->clear(); @@ -215,6 +170,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&) { @@ -229,6 +185,47 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) } } +void Statement::loadResources(thread_db* tdbb, Request* req) +{ + const MdcVersion currentMdcVersion = MetadataCache::get(tdbb)->getVersion(); + if ((!latest) || (latest->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 = latest ? latest->getCapacity() : + resources->charSets.getCount() + resources->relations.getCount() + resources->procedures.getCount() + + resources->functions.getCount() + resources->triggers.getCount(); + + latest = FB_NEW_RPT(*pool, resourceCount) VersionedObjects(resourceCount, currentMdcVersion); + resources->transfer(tdbb, latest, flags & FLAG_INTERNAL); + } + + if (req && req->getResources() != latest) + { + req->setResources(latest); + + // setup correct jrd_rel pointers in rpbs + req->req_rpb.grow(rpbsSetup.getCount()); + fb_assert(req->req_rpb.getCount() == rpbsSetup.getCount()); + for (FB_SIZE_T n = 0; n < rpbsSetup.getCount(); ++n) + { + req->req_rpb[n] = rpbsSetup[n]; + req->req_rpb[n].rpb_relation = rpbsSetup[n].rpb_relation(latest); + } + } +} + +bool Statement::streamsFormatCompare(thread_db* tdbb) +{ + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // check whether all streams used by statement remain unchanged + + return true; +} + // Turn a parsed scratch into a statement. Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, std::function beforeCsbRelease) @@ -239,6 +236,15 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool const auto dbb = tdbb->getDatabase(); fb_assert(dbb); + MemoryPool* defPool = tdbb->getDefaultPool(); + for (FB_SIZE_T i = 1; i < dbb->dbb_pools.getCount(); ++i) + { + if (dbb->dbb_pools[i] == defPool) + goto found; + } + fb_assert(!"wrong pool in makeStatement"); +found: + const auto attachment = tdbb->getAttachment(); const auto old_request = tdbb->getRequest(); @@ -372,7 +378,11 @@ Statement* Statement::makeValueExpression(thread_db* tdbb, ValueExprNode*& node, Request* Statement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) { Statement* statement = makeStatement(tdbb, csb, internalFlag); - return statement->getRequest(tdbb, 0); + + Request* req = statement->getRequest(tdbb, statement->requests.readAccessor(), 0); + statement->loadResources(tdbb, req); + + return req; } // Returns function or procedure routine. @@ -386,16 +396,21 @@ const Routine* Statement::getRoutine() const return function; } -// Determine if any request of this statement are active. -bool Statement::isActive() const +void Statement::restartRequests(thread_db* tdbb, jrd_tra* trans) { - for (const Request* const* request = requests.begin(); request != requests.end(); ++request) + auto g = requests.readAccessor(); + for (FB_SIZE_T n = 0; n < g->getCount(); ++n) { - if (*request && ((*request)->req_flags & req_in_use)) - return true; - } + Request* request = g->value(n); + + if (request && request->req_transaction) + { + fb_assert(request->req_attachment == tdbb->getAttachment()); - return false; + EXE_unwind(tdbb, request); + EXE_start(tdbb, request, trans); + } + } } Request* Statement::findRequest(thread_db* tdbb, bool unique) @@ -407,68 +422,100 @@ Request* Statement::findRequest(thread_db* tdbb, bool unique) if (!thisPointer) BUGCHECK(167); /* msg 167 invalid SEND request */ - // Search clones for one request in use by this attachment. + // Search clones for one request used whenever by this attachment. // If not found, return first inactive request. - Request* clone = NULL; - USHORT count = 0; - const USHORT clones = requests.getCount(); - USHORT n; - for (n = 0; n < clones; ++n) + do { - Request* next = getRequest(tdbb, n); + USHORT count = 0; + auto g = requests.readAccessor(); + const USHORT clones = g->getCount(); + USHORT n; - if (next->req_attachment == attachment) + for (n = 0; n < clones; ++n) { - if (!(next->req_flags & req_in_use)) + Request* next = getRequest(tdbb, g, n); + + if (next->req_attachment == attachment) { - clone = next; - break; - } + if (!next->isUsed()) + { + clone = next; + break; + } - if (unique) - return NULL; + if (unique) + return NULL; - ++count; + ++count; + } + else if (!(next->isUsed()) && !clone) + clone = next; } - else if (!(next->req_flags & req_in_use) && !clone) - clone = next; - } - if (count > MAX_CLONES) - ERR_post(Arg::Gds(isc_req_max_clones_exceeded)); + if (count > MAX_CLONES) + ERR_post(Arg::Gds(isc_req_max_clones_exceeded)); - if (!clone) - clone = getRequest(tdbb, n); + if (!clone) + clone = getRequest(tdbb, g, n); + + } while (!clone->setUsed()); clone->setAttachment(attachment); clone->req_stats.reset(); clone->req_base_stats.reset(); - clone->req_flags |= req_in_use; + + loadResources(tdbb, clone); return clone; } -Request* Statement::getRequest(thread_db* tdbb, USHORT level) +Request* Statement::getRequest(thread_db* tdbb, const Requests::ReadAccessor& g, USHORT level) { SET_TDBB(tdbb); - Jrd::Attachment* const attachment = tdbb->getAttachment(); Database* const dbb = tdbb->getDatabase(); fb_assert(dbb); - if (level < requests.getCount() && requests[level]) - return requests[level]; + if (level < g->getCount() && g->value(level)) + return g->value(level); // Create the request. - AutoMemoryPool reqPool(MemoryPool::createPool(pool)); - const auto request = FB_NEW_POOL(*reqPool) Request(reqPool, attachment, this); + AutoMemoryPool reqPool(MemoryPool::createPool(ALLOC_ARGS1 pool)); +#ifdef DEBUG_LOST_POOLS + fprintf(stderr, "%p %s %s\n", reqPool->mp(), sqlText ? sqlText->c_str() : "", + procedure ? procedure->c_name() : ""); +#endif + auto request = FB_NEW_POOL(*reqPool) Request(reqPool, dbb, this); + loadResources(tdbb, request); + + Request* arrivedRq; + { // mutex scope + MutexLockGuard guard(requestsGrow, FB_FUNCTION); + + auto g = requests.writeAccessor(); + + if (level >= g->getCount() || !g->value(level)) + { + requests.grow(level + 1, true); + + g = requests.writeAccessor(); + g->value(level) = request; + return request; + } + + arrivedRq = g->value(level); + } - requests.grow(level + 1); - requests[level] = request; + delete request; + return arrivedRq; +} - return request; +// Invoke request obtained earlier using compileRequest() API call +Request* Statement::getUserRequest(thread_db* tdbb, USHORT level) +{ + return getRequest(tdbb, requests.readAccessor(), level); } // Check that we have enough rights to access all resources this request touches including @@ -486,12 +533,12 @@ void Statement::verifyAccess(thread_db* tdbb) for (ExternalAccess* item = external.begin(); item != external.end(); ++item) { - const Routine* routine = NULL; + Routine* routine = nullptr; int aclType; if (item->exa_action == ExternalAccess::exa_procedure) { - routine = MET_lookup_procedure_id(tdbb, item->exa_prc_id, false, false, 0); + routine = MetadataCache::lookup_procedure_id(tdbb, item->exa_prc_id, 0); if (!routine) { string name; @@ -502,7 +549,7 @@ void Statement::verifyAccess(thread_db* tdbb) } else if (item->exa_action == ExternalAccess::exa_function) { - routine = Function::lookup(tdbb, item->exa_fun_id, false, false, 0); + routine = Function::lookup(tdbb, item->exa_fun_id, 0); if (!routine) { @@ -515,7 +562,7 @@ void Statement::verifyAccess(thread_db* tdbb) } else { - jrd_rel* relation = MET_lookup_relation_id(tdbb, item->exa_rel_id, false); + jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, item->exa_rel_id, CacheFlag::AUTOCREATE); if (!relation) continue; @@ -523,24 +570,24 @@ void Statement::verifyAccess(thread_db* tdbb) MetaName userName = item->user; if (item->exa_view_id) { - jrd_rel* view = MET_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, CacheFlag::AUTOCREATE); + 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); @@ -559,8 +606,8 @@ void Statement::verifyAccess(thread_db* tdbb) if (access.acc_ss_rel_id) { - const jrd_rel* view = MET_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, CacheFlag::AUTOCREATE); + if (view && (view->getId() >= USER_DEF_REL_INIT_ID)) userName = view->rel_owner_name; } @@ -627,8 +674,8 @@ void Statement::verifyAccess(thread_db* tdbb) if (access->acc_ss_rel_id) { - const jrd_rel* view = MET_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, CacheFlag::AUTOCREATE); + if (view && (view->getId() >= USER_DEF_REL_INIT_ID)) userName = view->rel_owner_name; } @@ -656,54 +703,13 @@ void Statement::release(thread_db* tdbb) (*subStatement)->release(tdbb); } - // Release existence locks on references. - - for (Resource* resource = resources.begin(); resource != resources.end(); ++resource) - { - switch (resource->rsc_type) - { - case Resource::rsc_relation: - { - jrd_rel* relation = resource->rsc_rel; - MET_release_existence(tdbb, relation); - break; - } - - case Resource::rsc_index: - { - jrd_rel* relation = resource->rsc_rel; - IndexLock* index = CMP_get_index_lock(tdbb, relation, resource->rsc_id); - if (index && index->idl_count) - { - --index->idl_count; - if (!index->idl_count) - LCK_release(tdbb, index->idl_lock); - } - break; - } - - case Resource::rsc_procedure: - case Resource::rsc_function: - resource->rsc_routine->release(tdbb); - break; - - case Resource::rsc_collation: - { - Collation* coll = resource->rsc_coll; - coll->decUseCount(tdbb); - break; - } - - default: - BUGCHECK(220); // msg 220 release of unknown resource - break; - } - } - - for (Request** instance = requests.begin(); instance != requests.end(); ++instance) + // ok to use write accessor w/o lock - we are in a kind of "dtor" + auto g = requests.writeAccessor(); + for (Request** instance = g->begin(); instance != g->end(); ++instance) { if (*instance) { + fb_assert(!((*instance)->isUsed())); EXE_release(tdbb, *instance); MemoryPool::deletePool((*instance)->req_pool); *instance = nullptr; @@ -712,14 +718,20 @@ void Statement::release(thread_db* tdbb) const auto attachment = tdbb->getAttachment(); - if (!attachment->att_statements.findAndRemove(this)) - fb_assert(false); + if (attachment) + { + if (!attachment->att_statements.findAndRemove(this)) + fb_assert(false); + } sqlText = NULL; // Sub statement pool is the same of the main statement, so don't delete it. if (!parentStatement) - attachment->deletePool(pool); + { + Database* dbb = tdbb->getDatabase(); + dbb->deletePool(pool); + } } // Returns a formatted textual plan for all RseNode's in the specified request @@ -743,23 +755,22 @@ void Statement::getPlan(thread_db* tdbb, PlanEntry& planEntry) const } // Check that we have enough rights to access all resources this list of triggers touches. -void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, - TrigVector* triggers, MetaName userName) +void Statement::verifyTriggerAccess(thread_db* tdbb, const jrd_rel* ownerRelation, + const Triggers& triggers, MetaName userName) { if (!triggers) return; SET_TDBB(tdbb); - for (FB_SIZE_T i = 0; i < triggers->getCount(); i++) + for (auto t : triggers) { - Trigger& t = (*triggers)[i]; - t.compile(tdbb); - if (!t.statement) + t->compile(tdbb); + if (!t->statement) continue; - for (const AccessItem* access = t.statement->accessList.begin(); - access != t.statement->accessList.end(); ++access) + for (const AccessItem* access = t->statement->accessList.begin(); + access != t->statement->accessList.end(); ++access) { // If this is not a system relation, we don't post access check if: // @@ -770,15 +781,15 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, // trigger can be owned by only one table for now, we know for sure that // it's a trigger defined on our target table. - if (!(ownerRelation->rel_flags & REL_system)) + if (!ownerRelation->isSystem()) { 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; } @@ -787,12 +798,12 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, // a direct access to an object from this trigger if (access->acc_ss_rel_id) { - const jrd_rel* view = MET_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, CacheFlag::AUTOCREATE); + if (view && (view->getId() >= USER_DEF_REL_INIT_ID)) userName = view->rel_owner_name; } - else if (t.ssDefiner.asBool()) - userName = t.owner; + else if (t->ssDefiner.asBool()) + userName = t->owner; Attachment* attachment = tdbb->getAttachment(); UserId* effectiveUser = userName.hasData() ? attachment->getUserId(userName) : attachment->att_ss_user; @@ -800,7 +811,7 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str()); - SCL_check_access(tdbb, sec_class, id_trigger, t.statement->triggerName, access->acc_mask, + SCL_check_access(tdbb, sec_class, id_trigger, t->statement->triggerName, access->acc_mask, access->acc_type, true, access->acc_name, access->acc_r_name); } } @@ -808,20 +819,18 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, // 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(); i++) + for (auto t : tvec) { - Trigger& t = (*tvec)[i]; - t.compile(tdbb); - - if (t.statement) + t->compile(tdbb); + if (t->statement) { - const MetaName& userName = t.ssDefiner.asBool() ? t.owner : user; - t.statement->buildExternalAccess(tdbb, list, userName); + const MetaName& userName = t->ssDefiner.asBool() ? t->owner : user; + t->statement->buildExternalAccess(tdbb, list, userName); } } } @@ -837,7 +846,7 @@ void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, c // Add externals recursively if (item->exa_action == ExternalAccess::exa_procedure) { - jrd_prc* const procedure = MET_lookup_procedure_id(tdbb, item->exa_prc_id, false, false, 0); + auto procedure = MetadataCache::lookup_procedure_id(tdbb, item->exa_prc_id, 0); if (procedure && procedure->getStatement()) { item->user = procedure->invoker ? MetaName(procedure->invoker->getUserName()) : user; @@ -849,7 +858,7 @@ void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, c } else if (item->exa_action == ExternalAccess::exa_function) { - Function* const function = Function::lookup(tdbb, item->exa_fun_id, false, false, 0); + auto function = Function::lookup(tdbb, item->exa_fun_id, 0); if (function && function->getStatement()) { item->user = function->invoker ? MetaName(function->invoker->getUserName()) : user; @@ -861,42 +870,59 @@ void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, c } else { - jrd_rel* relation = MET_lookup_relation_id(tdbb, item->exa_rel_id, false); - + jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, item->exa_rel_id, CacheFlag::AUTOCREATE); if (!relation) continue; - RefPtr 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.asBool() ? relation->rel_owner_name : user; + item->user = relation->rel_ss_definer.asBool() ? getPermanent(relation)->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); } } } +// verify_request_synchronization +// +// @brief Finds the sub-request at the given level. If that specific +// sub-request is not found, throw the dreaded "request synchronization error". +// This function replaced a chunk of code repeated four times. +// +// @param level The level of the sub-request we need to find. +Request* Statement::verifyRequestSynchronization(USHORT level) +{ + auto g = requests.readAccessor(); + fb_assert(g->getCount() > 0); + if (level && (level >= g->getCount() || !g->value(level))) + ERR_post(Arg::Gds(isc_req_sync)); + + return g->value(level); +} + + // Make sub routines. template static void makeSubRoutines(thread_db* tdbb, Statement* statement, CompilerScratch* csb, T& subs) @@ -939,6 +965,77 @@ template static void makeSubRoutines(thread_db* tdbb, Statement* st } } +bool Request::hasInternalStatement() const +{ + return statement->flags & Statement::FLAG_INTERNAL; +} + +bool Request::hasPowerfulStatement() const +{ + return statement->flags & Statement::FLAG_POWERFUL; +} + +bool Request::isRoot() const +{ + return this == statement->rootRequest(); +} + +StmtNumber Request::getRequestId() const +{ + if (!req_id) + { + req_id = isRoot() ? + statement->getStatementId() : + JRD_get_thread_data()->getDatabase()->generateStatementId(); + } + + return req_id; +} + +Request::Request(Firebird::AutoMemoryPool& pool, Database* dbb, /*const*/ Statement* aStatement) + : statement(aStatement), + req_inUse(false), + 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, dbb), + req_rpb(*req_pool), + impureArea(*req_pool) +{ + fb_assert(statement); + req_rpb = statement->rpbsSetup; + impureArea.grow(statement->impureSize); + + pool->setStatsGroup(req_memory_stats); + pool.release(); +} + +bool Request::setUsed() +{ + bool old = isUsed(); + if (old) + return false; + return req_inUse.compare_exchange_strong(old, true); +} + +void Request::setUnused() +{ + fb_assert(isUsed()); + req_inUse.store(false, std::memory_order_release); +} + +bool Request::isUsed() const +{ + return req_inUse.load(std::memory_order_relaxed); +} #ifdef DEV_BUILD diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index da27d6ff88c..c1d646db798 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -23,7 +23,10 @@ #include "../include/fb_blk.h" #include "../jrd/exe.h" +#include "../jrd/req.h" #include "../jrd/EngineInterface.h" +#include "../jrd/SharedReadVector.h" +#include "../jrd/intl.h" #include namespace Jrd { @@ -33,6 +36,8 @@ class PlanEntry; // Compiled statement. class Statement : public pool_alloc { + typedef SharedReadVector Requests; + public: static const unsigned FLAG_SYS_TRIGGER = 0x01; static const unsigned FLAG_INTERNAL = 0x02; @@ -46,6 +51,7 @@ class Statement : public pool_alloc private: Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb); + Request* getRequest(thread_db* tdbb, const Requests::ReadAccessor& g, USHORT level); public: static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, @@ -59,6 +65,15 @@ class Statement : public pool_alloc static Request* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); + Request* makeRootRequest(thread_db* tdbb) + { + auto g = requests.readAccessor(); + if (g->getCount()) + return g->value(0); + + return getRequest(tdbb, g, 0); + } + StmtNumber getStatementId() const { if (!id) @@ -72,35 +87,56 @@ class Statement : public pool_alloc } const Routine* getRoutine() const; - bool isActive() const; + //bool isActive() const; Request* findRequest(thread_db* tdbb, bool unique = false); - Request* getRequest(thread_db* tdbb, USHORT level); + Request* getUserRequest(thread_db* tdbb, USHORT level); + + Request* rootRequest() + { + auto g = requests.readAccessor(); + return g->getCount() == 0 ? nullptr : g->value(0); + } + + void restartRequests(thread_db* tdbb, jrd_tra* trans); + void verifyAccess(thread_db* tdbb); + Request* verifyRequestSynchronization(USHORT level); void release(thread_db* tdbb); Firebird::string getPlan(thread_db* tdbb, bool detailed) const; void getPlan(thread_db* tdbb, PlanEntry& planEntry) const; + const Resources* getResources() + { + return resources; + } + private: - static void verifyTriggerAccess(thread_db* tdbb, 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, Request* req); + bool streamsFormatCompare(thread_db* tdbb); + public: MemoryPool* pool; unsigned flags; // statement flags unsigned blrVersion; ULONG impureSize; // Size of impure area mutable StmtNumber id; // statement identifier - USHORT charSetId; // client character set (CS_METADATA for internal statements) - Firebird::Array rpbsSetup; - Firebird::Array requests; // vector of requests + CSetId charSetId; // client character set (CS_METADATA for internal statements) + Firebird::Array rpbsSetup; + +private: + SharedReadVector requests; // vector of requests + Firebird::Mutex requestsGrow; // requests' vector protection when adding new element + +public: 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,6 +150,10 @@ class Statement : public pool_alloc Firebird::RefStrPtr sqlText; // SQL text (encoded in the metadata charset) Firebird::Array blr; // BLR for non-SQL query MapFieldInfo mapFieldInfo; // Map field name to field info + +private: + Resources* resources; // Resources (relations, routines, etc.) + Firebird::RefPtr latest; // want std::atomic or mutex is needed }; diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 027fa1a1aee..41e6d7d4da2 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -62,6 +62,7 @@ #include "../common/classes/FpeControl.h" #include "../jrd/extds/ExtDS.h" #include "../jrd/align.h" +#include "../jrd/met.h" #include "firebird/impl/types_pub.h" #include @@ -221,7 +222,7 @@ void setParamsInt64(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, void setParamsSecondInteger(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); // helper functions for setParams -void setParamVarying(dsc* param, USHORT textType, bool condition = false); +void setParamVarying(dsc* param, TTypeId textType, bool condition = false); bool dscHasData(const dsc* param); // specific setParams functions @@ -687,7 +688,7 @@ void setParamsUnicodeVal(DataTypeUtilBase*, const SysFunction*, int argsCount, d } -void setParamVarying(dsc* param, USHORT textType, bool condition) +void setParamVarying(dsc* param, TTypeId textType, bool condition) { if (!param) return; @@ -1280,7 +1281,7 @@ bool makeBlobAppendBlob(dsc* result, const dsc* arg, bid* blob_id = nullptr) if (arg->isText()) { - USHORT ttype = arg->getTextType(); + auto ttype = arg->getTextType(); if (ttype == ttype_binary) result->makeBlob(isc_blob_untyped, ttype_binary, ptr); else @@ -3731,7 +3732,7 @@ dsc* evlEncodeDecodeHex(thread_db* tdbb, bool encodeFlag, const SysFunction* fun } else { - if (encodeFlag && arg->getStringLength() * 2 > MAX_VARY_COLUMN_SIZE) + if (encodeFlag && arg->getStringLength() * 2 > int(MAX_VARY_COLUMN_SIZE)) { outBlob.reset(blb::create2(tdbb, tdbb->getRequest()->req_transaction, &impure->vlu_misc.vlu_bid, sizeof(streamBpb), streamBpb)); @@ -4582,7 +4583,7 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar const string nameStr(MOV_make_string2(tdbb, name, ttype_none)); string resultStr; - USHORT resultType = ttype_none; + auto resultType = ttype_none; request->req_flags |= req_null; if (nameSpaceStr == SYSTEM_NAMESPACE) // Handle system variables @@ -5390,11 +5391,11 @@ dsc* evlMakeDbkey(Jrd::thread_db* tdbb, const SysFunction* function, const NestV MetaName relName; CVT2_make_metaname(argDsc, relName, tdbb->getAttachment()->att_dec_status); - const jrd_rel* const relation = MET_lookup_relation(tdbb, relName); + jrd_rel* relation = MetadataCache::lookup_relation(tdbb, relName, CacheFlag::AUTOCREATE); if (!relation) (Arg::Gds(isc_relnotdef) << Arg::Str(relName)).raise(); - relId = relation->rel_id; + relId = relation->getId(); } else { @@ -5633,7 +5634,7 @@ dsc* evlOverlay(thread_db* tdbb, const SysFunction* function, const NestValueArr Arg::Str(function->name)); } - const USHORT resultTextType = DataTypeUtil::getResultTextType(value, placing); + const auto resultTextType = DataTypeUtil::getResultTextType(value, placing); CharSet* cs = INTL_charset_lookup(tdbb, resultTextType); MoveBuffer temp1; @@ -5800,7 +5801,7 @@ dsc* evlPad(thread_db* tdbb, const SysFunction* function, const NestValueArray& return NULL; } - const USHORT ttype = value1->getTextType(); + const auto ttype = value1->getTextType(); CharSet* cs = INTL_charset_lookup(tdbb, ttype); MoveBuffer buffer1; @@ -5978,7 +5979,7 @@ dsc* evlPosition(thread_db* tdbb, const SysFunction* function, const NestValueAr impure->vlu_desc.makeLong(0, &impure->vlu_misc.vlu_long); // we'll use the collation from the second string - const USHORT ttype = value2->getTextType(); + const auto ttype = value2->getTextType(); TextType* tt = INTL_texttype_lookup(tdbb, ttype); CharSet* cs = tt->getCharSet(); const UCHAR canonicalWidth = tt->getCanonicalWidth(); @@ -6162,7 +6163,7 @@ dsc* evlReplace(thread_db* tdbb, const SysFunction*, const NestValueArray& args, firstBlob = values[i]; } - const USHORT ttype = values[0]->getTextType(); + const auto ttype = values[0]->getTextType(); TextType* tt = INTL_texttype_lookup(tdbb, ttype); CharSet* cs = tt->getCharSet(); const UCHAR canonicalWidth = tt->getCanonicalWidth(); diff --git a/src/jrd/SystemPackages.h b/src/jrd/SystemPackages.h index 7bf4cc352b2..acb47b379b3 100644 --- a/src/jrd/SystemPackages.h +++ b/src/jrd/SystemPackages.h @@ -25,6 +25,7 @@ #include "firebird.h" #include "../common/status.h" +#include "../common/dsc.h" #include "../common/classes/init.h" #include "../common/classes/array.h" #include "../common/classes/objects_array.h" diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index fc3d3d407f9..f131bae35d7 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -1283,7 +1283,7 @@ void beforeInsertUserPrivilege(thread_db* tdbb, Record* record) void SystemTriggers::executeBeforeDeleteTriggers(thread_db* tdbb, jrd_rel* relation, Record* record) { - switch ((RIDS) relation->rel_id) + switch ((RIDS) relation->getId()) { case rel_ccon: beforeDeleteCheckConstraint(tdbb, record); @@ -1317,7 +1317,7 @@ void SystemTriggers::executeBeforeDeleteTriggers(thread_db* tdbb, jrd_rel* relat void SystemTriggers::executeAfterDeleteTriggers(thread_db* tdbb, jrd_rel* relation, Record* record) { - switch ((RIDS) relation->rel_id) + switch ((RIDS) relation->getId()) { case rel_ccon: afterDeleteCheckConstraint(tdbb, record); @@ -1335,7 +1335,7 @@ void SystemTriggers::executeAfterDeleteTriggers(thread_db* tdbb, jrd_rel* relati void SystemTriggers::executeBeforeInsertTriggers(thread_db* tdbb, jrd_rel* relation, Record* record) { - switch ((RIDS) relation->rel_id) + switch ((RIDS) relation->getId()) { case rel_priv: beforeInsertUserPrivilege(tdbb, record); @@ -1353,7 +1353,7 @@ void SystemTriggers::executeBeforeInsertTriggers(thread_db* tdbb, jrd_rel* relat void SystemTriggers::executeBeforeUpdateTriggers(thread_db* tdbb, jrd_rel* relation, Record* orgRecord, Record* newRecord) { - switch ((RIDS) relation->rel_id) + switch ((RIDS) relation->getId()) { case rel_ccon: beforeUpdateCheckConstraint(tdbb, orgRecord, newRecord); diff --git a/src/jrd/TimeZone.cpp b/src/jrd/TimeZone.cpp index 10ea8bf3dab..7997f8817ee 100644 --- a/src/jrd/TimeZone.cpp +++ b/src/jrd/TimeZone.cpp @@ -56,12 +56,12 @@ TimeZoneSnapshot::TimeZoneSnapshot(thread_db* tdbb, MemoryPool& pool) TimeZonesTableScan::TimeZonesTableScan(CompilerScratch* csb, const string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : VirtualTableScan(csb, alias, stream, relation) { } -const Format* TimeZonesTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +const Format* TimeZonesTableScan::getFormat(thread_db* tdbb, RelationPermanent* relation) const { return tdbb->getTransaction()->getTimeZoneSnapshot(tdbb)->getData(relation)->getFormat(); } @@ -73,7 +73,7 @@ const Format* TimeZonesTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) bool TimeZonesTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const { - return tdbb->getTransaction()->getTimeZoneSnapshot(tdbb)->getData(relation)->fetch(position, record); + return tdbb->getTransaction()->getTimeZoneSnapshot(tdbb)->getData(getPermanent(relation))->fetch(position, record); } diff --git a/src/jrd/TimeZone.h b/src/jrd/TimeZone.h index 4c6c8eafaf6..eb86d7773ac 100644 --- a/src/jrd/TimeZone.h +++ b/src/jrd/TimeZone.h @@ -46,10 +46,10 @@ class TimeZoneSnapshot : public SnapshotData class TimeZonesTableScan final : public VirtualTableScan { public: - TimeZonesTableScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, jrd_rel* relation); + TimeZonesTableScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, Rsc::Rel relation); protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; + const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const override; bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; }; diff --git a/src/jrd/UserManagement.cpp b/src/jrd/UserManagement.cpp index d09b8a45eb5..d410f1cb359 100644 --- a/src/jrd/UserManagement.cpp +++ b/src/jrd/UserManagement.cpp @@ -141,7 +141,7 @@ namespace bool present; }; - class ChangeCharset : public AutoSetRestore + class ChangeCharset : public AutoSetRestore { public: ChangeCharset(Attachment* att) @@ -150,7 +150,7 @@ namespace }; } // anonymous namespace -const Format* UsersTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const +const Format* UsersTableScan::getFormat(thread_db* tdbb, RelationPermanent* relation) const { jrd_tra* const transaction = tdbb->getTransaction(); return transaction->getUserManagement()->getList(tdbb, relation)->getFormat(); @@ -161,7 +161,7 @@ bool UsersTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const { jrd_tra* const transaction = tdbb->getTransaction(); - return transaction->getUserManagement()->getList(tdbb, relation)->fetch(position, record); + return transaction->getUserManagement()->getList(tdbb, getPermanent(relation))->fetch(position, record); } @@ -582,10 +582,10 @@ void UserManagement::list(IUser* u, unsigned cachePosition) } } -RecordBuffer* UserManagement::getList(thread_db* tdbb, jrd_rel* relation) +RecordBuffer* UserManagement::getList(thread_db* tdbb, RelationPermanent* 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) diff --git a/src/jrd/UserManagement.h b/src/jrd/UserManagement.h index 3334d19155b..eb4980fc78c 100644 --- a/src/jrd/UserManagement.h +++ b/src/jrd/UserManagement.h @@ -41,12 +41,12 @@ class UsersTableScan: public VirtualTableScan { public: UsersTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : VirtualTableScan(csb, alias, stream, relation) {} protected: - const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override; + const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const override; bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override; }; @@ -65,7 +65,7 @@ class UserManagement : public SnapshotData // commit transaction in security database void commit(); // return users list for SEC$USERS - RecordBuffer* getList(thread_db* tdbb, jrd_rel* relation); + RecordBuffer* getList(thread_db* tdbb, RelationPermanent* relation); // callback for users display void list(Firebird::IUser* u, unsigned cachePosition); diff --git a/src/jrd/VirtualTable.cpp b/src/jrd/VirtualTable.cpp index 1c3485425a3..55c2aa4636d 100644 --- a/src/jrd/VirtualTable.cpp +++ b/src/jrd/VirtualTable.cpp @@ -33,7 +33,7 @@ #include "../jrd/cmp_proto.h" #include "../jrd/err_proto.h" #include "../jrd/evl_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/vio_proto.h" @@ -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)) diff --git a/src/jrd/WorkerAttachment.cpp b/src/jrd/WorkerAttachment.cpp index 969f54a38ec..bb2dcabb9ba 100644 --- a/src/jrd/WorkerAttachment.cpp +++ b/src/jrd/WorkerAttachment.cpp @@ -34,10 +34,11 @@ #include "../common/classes/ClumpletWriter.h" #include "../jrd/jrd.h" #include "../jrd/ini_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/pag_proto.h" #include "../jrd/tra_proto.h" #include "../jrd/status.h" +#include "../jrd/Monitoring.h" using namespace Firebird; @@ -128,8 +129,6 @@ void WorkerStableAttachment::fini() attachment->releaseLocks(tdbb); LCK_fini(tdbb, LCK_OWNER_attachment); - - attachment->releaseRelations(tdbb); } destroy(attachment); diff --git a/src/jrd/blb.cpp b/src/jrd/blb.cpp index 923161dc8b4..99e9c66cccb 100644 --- a/src/jrd/blb.cpp +++ b/src/jrd/blb.cpp @@ -52,6 +52,7 @@ #include "../common/sdl.h" #include "../jrd/intl.h" #include "../jrd/cch.h" +#include "../jrd/met.h" #include "../common/gdsassert.h" #include "../jrd/blb_proto.h" #include "../jrd/blf_proto.h" @@ -72,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; @@ -79,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*); @@ -472,7 +468,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()); @@ -485,7 +481,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()); } } } @@ -511,7 +507,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())) @@ -524,7 +520,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()); } } } @@ -538,7 +534,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()); @@ -1048,7 +1044,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 @@ -1105,11 +1101,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. @@ -1128,7 +1124,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) && @@ -1254,7 +1250,6 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, break; } - blob->blb_relation = relation; blob->blb_sub_type = to_desc->getBlobSubType(); blob->blb_charset = to_desc->getCharSet(); #ifdef CHECK_BLOB_FIELD_ACCESS_FOR_SELECT @@ -1263,7 +1258,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) @@ -1445,27 +1440,23 @@ blb* blb::open2(thread_db* tdbb, if (try_relations) { - // Ordinarily, we would call MET_relation to get the relation id. + // Ordinarily, we call with AUTOCREATE set to get the relation by id. // However, since the blob id must be considered suspect, this is // not a good idea. On the other hand, if we don't already // know about the relation, the blob id has got to be invalid // anyway. - vec* vector = tdbb->getAttachment()->att_relations; - - if (blobId.bid_internal.bid_relation_id >= vector->count() || - !(blob->blb_relation = (*vector)[blobId.bid_internal.bid_relation_id] ) ) - { + jrd_rel* relation = MetadataCache::lookup_relation_id(tdbb, blobId.bid_internal.bid_relation_id, 0); + if (!relation) ERR_post(Arg::Gds(isc_bad_segstr_id)); - } - blob->blb_pg_space_id = blob->blb_relation->getPages(tdbb)->rel_pg_space_id; - DPM_get_blob(tdbb, blob, blobId.get_permanent_number(), false, 0); + blob->blb_pg_space_id = relation->getPages(tdbb)->rel_pg_space_id; + DPM_get_blob(tdbb, blob, relation, blobId.get_permanent_number(), false, 0); #ifdef CHECK_BLOB_FIELD_ACCESS_FOR_SELECT - if (!blob->blb_relation->isSystem() && blob->blb_fld_id < blob->blb_relation->rel_fields->count()) + if (!relation->isSystem() && blob->blb_fld_id < relation->rel_fields->count()) { - jrd_fld* fld = (*blob->blb_relation->rel_fields)[blob->blb_fld_id]; + jrd_fld* fld = (*relation->rel_fields)[blob->blb_fld_id]; transaction->checkBlob(tdbb, &blobId, fld, true); } #endif @@ -1753,13 +1744,9 @@ void blb::put_slice(thread_db* tdbb, if (SDL_info(tdbb->tdbb_status_vector, sdl, &info, 0)) ERR_punt(); - jrd_rel* relation; - if (info.sdl_info_relation.length()) { - relation = MET_lookup_relation(tdbb, info.sdl_info_relation); - } - else { - relation = MET_relation(tdbb, info.sdl_info_rid); - } + jrd_rel* relation = info.sdl_info_relation.length() ? + MetadataCache::lookup_relation(tdbb, info.sdl_info_relation, CacheFlag::AUTOCREATE) : + MetadataCache::lookup_relation_id(tdbb, info.sdl_info_rid, CacheFlag::AUTOCREATE); if (!relation) { IBERROR(196); // msg 196 relation for array not known @@ -1773,10 +1760,7 @@ void blb::put_slice(thread_db* tdbb, n = info.sdl_info_fid; } - // Make sure relation is scanned - MET_scan_relation(tdbb, relation); - - jrd_fld* field = NULL; + jrd_fld* field; if (n < 0 || !(field = MET_get_field(relation, n))) { IBERROR(197); // msg 197 field for array not known } @@ -2287,15 +2271,14 @@ 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 blb* blob = allocate_blob(tdbb, attachment->getSysTransaction()); - blob->blb_relation = relation; blob->blb_pg_space_id = relation->getPages(tdbb)->rel_pg_space_id; - prior_page = DPM_get_blob(tdbb, blob, blob_id->get_permanent_number(), true, prior_page); + prior_page = DPM_get_blob(tdbb, blob, relation, blob_id->get_permanent_number(), true, prior_page); if (!(blob->blb_flags & BLB_damaged)) blob->delete_blob(tdbb, prior_page); @@ -2582,12 +2565,12 @@ static void move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc **************************************/ SET_TDBB (tdbb); - const UCHAR charSet = INTL_GET_CHARSET(from_desc); + const auto charSet = INTL_GET_CHARSET(from_desc); UCHAR* fromstr = NULL; MoveBuffer buffer; const int length = MOV_make_string2(tdbb, from_desc, charSet, &fromstr, buffer); - const UCHAR toCharSet = to_desc->getCharSet(); + const auto toCharSet = to_desc->getCharSet(); if ((charSet == CS_NONE || charSet == CS_BINARY || charSet == toCharSet) && toCharSet != CS_NONE && toCharSet != CS_BINARY) @@ -2700,9 +2683,9 @@ static void move_to_string(thread_db* tdbb, dsc* fromDesc, dsc* toDesc) blobAsText.dsc_dtype = dtype_text; if (DTYPE_IS_TEXT(toDesc->dsc_dtype)) - blobAsText.dsc_ttype() = toDesc->dsc_ttype(); + blobAsText.setTextType(toDesc->getTextType()); else - blobAsText.dsc_ttype() = ttype_ascii; + blobAsText.setTextType(ttype_ascii); Request* request = tdbb->getRequest(); jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction(); @@ -2714,7 +2697,7 @@ static void move_to_string(thread_db* tdbb, dsc* fromDesc, dsc* toDesc) blb* blob = blb::open2(tdbb, transaction, (bid*) fromDesc->dsc_address, bpb.getCount(), bpb.begin()); - const CharSet* fromCharSet = INTL_charset_lookup(tdbb, fromDesc->dsc_scale); + const CharSet* fromCharSet = INTL_charset_lookup(tdbb, fromDesc->getCharSet()); const CharSet* toCharSet = INTL_charset_lookup(tdbb, INTL_GET_CHARSET(&blobAsText)); HalfStaticArray buffer; @@ -2833,7 +2816,7 @@ static void slice_callback(array_slice* arg, ULONG /*count*/, DSC* descriptors) DynamicVaryStr<1024> tmp_buffer; const USHORT tmp_len = array_desc->dsc_length; const char* p; - const USHORT len = MOV_make_string(tdbb, slice_desc, INTL_TEXT_TYPE(*array_desc), &p, + const USHORT len = MOV_make_string(tdbb, slice_desc, INTL_GET_TTYPE(array_desc), &p, tmp_buffer.getBuffer(tmp_len), tmp_len); memcpy(array_desc->dsc_address, &len, sizeof(USHORT)); memcpy(array_desc->dsc_address + sizeof(USHORT), p, (int) len); @@ -2948,7 +2931,7 @@ void blb::fromPageHeader(const Ods::blh* header) blb_max_segment = header->blh_max_segment; blb_level = header->blh_level; blb_sub_type = header->blh_sub_type; - blb_charset = header->blh_charset; + blb_charset = CSetId(header->blh_charset); #ifdef CHECK_BLOB_FIELD_ACCESS_FOR_SELECT blb_fld_id = header->blh_fld_id; #endif diff --git a/src/jrd/blb.h b/src/jrd/blb.h index cad5e0d836c..6fdad646753 100644 --- a/src/jrd/blb.h +++ b/src/jrd/blb.h @@ -38,6 +38,7 @@ #include "firebird/Interface.h" #include "../common/classes/ImplementHelper.h" #include "../common/dsc.h" +#include "../jrd/Resources.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; @@ -73,14 +75,13 @@ class blb : public pool_alloc { } - jrd_rel* blb_relation; // Relation, if known JBlob* blb_interface; ULONG blb_length; // Total length of data sans segments USHORT blb_flags; // Interesting stuff (see below) SSHORT blb_sub_type; // Blob's declared sub-type - UCHAR blb_charset; // Blob's charset + CSetId blb_charset; // Blob's charset // inline functions bool hasBuffer() const; @@ -103,22 +104,22 @@ class blb : public pool_alloc 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); diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 71d5e6224cd..35528130998 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -45,6 +45,8 @@ #include "../jrd/lck.h" #include "../jrd/cch.h" #include "../jrd/sort.h" +#include "../jrd/met.h" +#include "../jrd/Statement.h" #include "../jrd/val.h" #include "../common/gdsassert.h" #include "../jrd/btr_proto.h" @@ -56,11 +58,13 @@ #include "../yvalve/gds_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/tra_proto.h" +#include "../jrd/tpc_proto.h" +#include "../dsql/DdlNodes.h" using namespace Jrd; using namespace Ods; @@ -71,6 +75,9 @@ using namespace Firebird; //Debug page numbers into log file //#define DEBUG_BTR_PAGES +//Debug root page content to stdout +//#define DEBUG_INDEX_ROOT + namespace { const unsigned MAX_LEVELS = 16; @@ -185,6 +192,32 @@ namespace temporary_key jumpKey; }; + inline int indexCacheState(thread_db* tdbb, TraNumber descTrans, Cached::Relation* rel, MetaId idxId, bool creating) + { + auto checkPresence = [tdbb, rel, idxId]()->bool + { + auto* index = rel->lookup_index(tdbb, idxId, CacheFlag::AUTOCREATE); + return index && index->getActive() == MET_index_active; + }; + + return TipCache::traState(tdbb, descTrans, checkPresence, creating); + } + + inline int transactionState(thread_db* tdbb, TraNumber descTrans, Cached::Relation* rel, MetaId idxId, bool creating) + { + auto checkPresence = [tdbb, rel, idxId]()->bool + { + auto* iperm = rel->lookupIndex(tdbb, idxId, CacheFlag::AUTOCREATE); + if (!iperm) + return false; + + auto* ivar = iperm->getObject(tdbb, MAX_TRA_NUMBER, CacheFlag::AUTOCREATE); + return ivar && ivar->getActive() == MET_index_active; + }; + + return TipCache::traState(tdbb, descTrans, checkPresence, creating); + } + } // namespace static ULONG add_node(thread_db*, WIN*, index_insertion*, temporary_key*, RecordNumber*, @@ -194,10 +227,10 @@ static void compress(thread_db*, const dsc*, const SSHORT scale, temporary_key*, static USHORT compress_root(thread_db*, index_root_page*); static void copy_key(const temporary_key*, temporary_key*); static contents delete_node(thread_db*, WIN*, UCHAR*); -static void delete_tree(thread_db*, USHORT, USHORT, PageNumber, PageNumber); +static void delete_tree(thread_db*, MetaId, MetaId, PageNumber, PageNumber); static ULONG fast_load(thread_db*, IndexCreation&, SelectivityList&); -static index_root_page* fetch_root(thread_db*, WIN*, const jrd_rel*, const RelationPages*); +static const index_root_page* fetch_root(thread_db*, WIN*, const RelationPermanent*, const RelationPages*); static UCHAR* find_node_start_point(btree_page*, temporary_key*, UCHAR*, USHORT*, bool, int, bool = false, RecordNumber = NO_VALUE); @@ -224,10 +257,104 @@ static contents remove_leaf_node(thread_db*, index_insertion*, WIN*); static bool scan(thread_db*, UCHAR*, RecordBitmap**, RecordBitmap*, index_desc*, const IndexRetrieval*, USHORT, temporary_key*, bool&, const temporary_key&, USHORT); -static void update_selectivity(index_root_page*, USHORT, const SelectivityList&); +static void update_selectivity(index_root_page*, MetaId, const SelectivityList&); static void checkForLowerKeySkip(bool&, const bool, const IndexNode&, const temporary_key&, const index_desc&, const IndexRetrieval*); +namespace { +enum class ModifyIrtRepeatValue { Skip, Modified, Relock, Deleted }; + +class Flags +{ +public: + static const char* state(const index_root_page::irt_repeat& rpt) + { + switch (rpt.getState()) + { + case irt_in_progress: return "irt_in_progress"; + case irt_commit: return "irt_commit"; + case irt_drop: return "irt_drop"; + case irt_rollback: return "irt_rollback"; + case irt_normal: return "irt_normal"; + case irt_kill: return "irt_kill"; + case irt_unused: return "irt_unused"; + } + + return "** UNKNOWN **"; + } +}; + +#ifdef DEBUG_INDEX_ROOT + +void dumpIndexRoot(const char* up, const char* from, thread_db* tdbb, WIN* window, const index_root_page* root) +{ + if (root->irt_relation > 127) + { + auto* rel = MetadataCache::lookupRelation(tdbb, root->irt_relation, 0); + printf("\n%sFrom %s page=%" ULONGFORMAT " len=%d rel=%s(%d) tra=%" SQUADFORMAT "\n", + up, from, window->win_page.getPageNum(), root->irt_count, rel->c_name(), root->irt_relation, + tdbb->getTransaction() ? tdbb->getTransaction()->tra_number : 0); + for (MetaId i = 0; i < root->irt_count; ++i) + { + auto* idp = rel->lookupIndex(tdbb, i, 0); + auto& rpt = root->irt_rpt[i]; + printf("Index %d '%s' root %d tra %" SQUADFORMAT " %s\n", i, idp ? idp->getName().c_str() : "not-found", + rpt.getRoot(), rpt.getTransaction(), Flags::state(rpt)); + } + printf("\n"); + } +} + +#else // DEBUG_INDEX_ROOT + +void dumpIndexRoot(...) { } + +#endif // DEBUG_INDEX_ROOT +} + +static bool checkIrtRepeat(thread_db* tdbb, const index_root_page::irt_repeat* irt_desc, + Cached::Relation* relation, WIN* window, MetaId indexId); +static ModifyIrtRepeatValue modifyIrtRepeat(thread_db* tdbb, index_root_page::irt_repeat* irt_desc, + Cached::Relation* relation, WIN* window, MetaId indexId, bool deletable = true); + +index_root_page* BTR_fetch_root_for_update(const char* from, thread_db* tdbb, WIN* window) +{ + index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, window, LCK_write, pag_root); + + dumpIndexRoot("Upd", from, tdbb, window, root); + + return root; +} + +const index_root_page* BTR_fetch_root(const char* from, thread_db* tdbb, WIN* window) +{ + const index_root_page* root = (const index_root_page*) CCH_FETCH(tdbb, window, LCK_read, pag_root); + + dumpIndexRoot("", from, tdbb, window, root); + + return root; +} + + +// IndexRetrieval class + +jrd_rel* IndexRetrieval::getRelation(thread_db* tdbb) const +{ + if (irb_jrd_relation) + return irb_jrd_relation; + + auto* rq = tdbb->getRequest(); + if (rq) + return irb_rsc_relation(rq->getResources()); + + return irb_rsc_relation(tdbb); +} + +Cached::Relation* IndexRetrieval::getPermRelation() const +{ + return irb_jrd_relation ? getPermanent(irb_jrd_relation) : irb_rsc_relation(); +} + // BtrPageLock class BtrPageGCLock::BtrPageGCLock(thread_db* tdbb) @@ -281,13 +408,17 @@ 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 USHORT indexId = isLocationDefined ? m_location.indexId : m_index->idx_id; + auto* relation = getPermanent(isLocationDefined ? m_location.relation : m_relation); + const MetaId indexId = isLocationDefined ? m_location.indexId : m_index->idx_id; MetaName indexName(m_indexName), constraintName; if (indexName.isEmpty()) - MET_lookup_index(tdbb, indexName, relationName, indexId + 1); + { + auto* index = relation->lookup_index(tdbb, indexId, CacheFlag::AUTOCREATE); + if (index) + indexName = index->getName(); + } if (indexName.hasData()) MET_lookup_cnstrt_for_index(tdbb, constraintName, indexName); @@ -308,13 +439,13 @@ void IndexErrorContext::raise(thread_db* tdbb, idx_e result, Record* record) case idx_e_foreign_target_doesnt_exist: ERR_post_nothrow(Arg::Gds(isc_foreign_key) << - Arg::Str(constraintName) << Arg::Str(relationName) << + Arg::Str(constraintName) << Arg::Str(relation->getName()) << Arg::Gds(isc_foreign_key_target_doesnt_exist)); break; case idx_e_foreign_references_present: ERR_post_nothrow(Arg::Gds(isc_foreign_key) << - Arg::Str(constraintName) << Arg::Str(relationName) << + Arg::Str(constraintName) << Arg::Str(relation->getName()) << Arg::Gds(isc_foreign_key_references_present)); break; @@ -322,7 +453,7 @@ void IndexErrorContext::raise(thread_db* tdbb, idx_e result, Record* record) if (haveConstraint) { ERR_post_nothrow(Arg::Gds(isc_unique_key_violation) << - Arg::Str(constraintName) << Arg::Str(relationName)); + Arg::Str(constraintName) << Arg::Str(relation->getName())); } else ERR_post_nothrow(Arg::Gds(isc_no_dup) << Arg::Str(indexName)); @@ -350,8 +481,8 @@ IndexCondition::IndexCondition(thread_db* tdbb, index_desc* idx) if (!(idx->idx_flags & idx_condition)) return; - fb_assert(idx->idx_condition); - m_condition = idx->idx_condition; + fb_assert(idx->idx_condition_node); + m_condition = idx->idx_condition_node; fb_assert(idx->idx_condition_statement); const auto orgRequest = tdbb->getRequest(); @@ -365,8 +496,7 @@ IndexCondition::IndexCondition(thread_db* tdbb, index_desc* idx) fb_assert(!m_request->req_caller); m_request->req_caller = orgRequest; - m_request->req_flags &= req_in_use; - m_request->req_flags |= req_active; + m_request->req_flags = req_active; TRA_attach_request(tdbb->getTransaction(), m_request); fb_assert(m_request->req_transaction); @@ -386,8 +516,8 @@ IndexCondition::~IndexCondition() { EXE_unwind(m_tdbb, m_request); - m_request->req_flags &= ~req_in_use; m_request->req_attachment = nullptr; + m_request->setUnused(); } } @@ -455,8 +585,8 @@ IndexExpression::IndexExpression(thread_db* tdbb, index_desc* idx) if (!(idx->idx_flags & idx_expression)) return; - fb_assert(idx->idx_expression); - m_expression = idx->idx_expression; + fb_assert(idx->idx_expression_node); + m_expression = idx->idx_expression_node; fb_assert(idx->idx_expression_statement); const auto orgRequest = tdbb->getRequest(); @@ -470,8 +600,7 @@ IndexExpression::IndexExpression(thread_db* tdbb, index_desc* idx) fb_assert(!m_request->req_caller); m_request->req_caller = orgRequest; - m_request->req_flags &= req_in_use; - m_request->req_flags |= req_active; + m_request->req_flags = req_active; TRA_attach_request(tdbb->getTransaction(), m_request); fb_assert(m_request->req_transaction); @@ -492,8 +621,8 @@ IndexExpression::~IndexExpression() { EXE_unwind(m_tdbb, m_request); - m_request->req_flags &= ~req_in_use; m_request->req_attachment = nullptr; + m_request->setUnused(); } } @@ -678,14 +807,15 @@ idx_e IndexKey::compose(Record* record) error.value()[1] == isc_expression_eval_index)) { MetaName indexName; - MET_lookup_index(m_tdbb, indexName, m_relation->rel_name, m_index->idx_id + 1); + auto* idv = m_relation->getPermanent()->lookup_index(m_tdbb, m_index->idx_id, CacheFlag::AUTOCREATE); + if (idv) + indexName = idv->getName(); if (indexName.isEmpty()) indexName = "***unknown***"; error.prepend(Arg::Gds(isc_expression_eval_index) << - Arg::Str(indexName) << - Arg::Str(m_relation->rel_name)); + indexName << m_relation->getName()); } error.copyTo(m_tdbb->tdbb_status_vector); @@ -795,13 +925,13 @@ void IndexScanListIterator::makeKeys(thread_db* tdbb, temporary_key* lower, temp if (errorCode != idx_e_ok) { index_desc temp_idx = m_retrieval->irb_desc; - IndexErrorContext context(m_retrieval->irb_relation, &temp_idx); + IndexErrorContext context(m_retrieval->getRelation(tdbb), &temp_idx); context.raise(tdbb, errorCode); } } -void BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescList& idxList, RelationPages* relPages) +void BTR_all(thread_db* tdbb, Cached::Relation* relation, IndexDescList& idxList, RelationPages* relPages) { /************************************** * @@ -821,7 +951,7 @@ void BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescList& idxList, Relatio WIN window(relPages->rel_pg_space_id, -1); - index_root_page* const root = fetch_root(tdbb, &window, relation, relPages); + const index_root_page* const root = fetch_root(tdbb, &window, relation, relPages); if (!root) return; @@ -829,10 +959,42 @@ void BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescList& idxList, Relatio CCH_RELEASE(tdbb, &window); }); - for (USHORT i = 0; i < root->irt_count; i++) + for (MetaId id = 0; id < root->irt_count; id++) { + const index_root_page::irt_repeat* irt_desc = root->irt_rpt + id; + const TraNumber descTrans = irt_desc->getTransaction(); + + switch (irt_desc->getState()) + { + case irt_normal: + break; + + case irt_in_progress: // index creation - skip + case irt_drop: // index removal - skip + continue; + + case irt_rollback: // to be removed when irt_transaction dead + switch (indexCacheState(tdbb, descTrans, relation, id, true)) + { + case tra_dead: // skip - index failed creation + continue; + } + break; + + case irt_commit: // change state on irt_transaction completion + switch (indexCacheState(tdbb, descTrans, relation, id, false)) + { + case tra_committed: // skip - index to be dropped + continue; + } + break; + } + + if (!relation->lookup_index(tdbb, id, CacheFlag::AUTOCREATE)) + continue; + index_desc idx; - if (BTR_description(tdbb, relation, root, &idx, i)) + if (BTR_description(tdbb, relation, root, &idx, id)) idxList.add(idx); } } @@ -886,18 +1048,29 @@ void BTR_create(thread_db* tdbb, // Index is created. Go back to the index root page and update it to // point to the index. - RelationPages* const relPages = relation->getPages(tdbb); - WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); - index_root_page* const root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_root); + WIN window(relation->getPermanent()->getIndexRootPage(tdbb)); + index_root_page* const root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); CCH_MARK(tdbb, &window); - root->irt_rpt[idx->idx_id].setRoot(idx->idx_root); + + switch(creation.forRollback) + { + case IdxCreate::AtOnce: + root->irt_rpt[idx->idx_id].setNormal(idx->idx_root); + break; + case IdxCreate::ForRollback: + jrd_tra* tra = tdbb->getTransaction(); + fb_assert(tra && tra->tra_number); + root->irt_rpt[idx->idx_id].setRollback(idx->idx_root, tra ? tra->tra_number : 0); + break; + } + update_selectivity(root, idx->idx_id, selectivity); CCH_RELEASE(tdbb, &window); } -bool BTR_delete_index(thread_db* tdbb, WIN* window, USHORT id) +bool BTR_delete_index(thread_db* tdbb, WIN* window, MetaId id) { /************************************** * @@ -917,32 +1090,220 @@ bool BTR_delete_index(thread_db* tdbb, WIN* window, USHORT id) // Get index descriptor. If index doesn't exist, just leave. index_root_page* const root = (index_root_page*) window->win_buffer; - bool tree_exists = false; - if (id >= root->irt_count) - CCH_RELEASE(tdbb, window); - else + if (id < root->irt_count) { index_root_page::irt_repeat* irt_desc = root->irt_rpt + id; + CCH_MARK(tdbb, window); - const PageNumber next(window->win_page.getPageSpaceID(), irt_desc->getRoot()); - tree_exists = (irt_desc->getRoot() != 0); + const auto rootPage = irt_desc->getRoot(); + const PageNumber next(window->win_page.getPageSpaceID(), rootPage); + bool tree_exists = (rootPage != 0); // remove the pointer to the top-level index page before we delete it - irt_desc->setRoot(0); - irt_desc->irt_flags = 0; - const PageNumber prior = window->win_page; + irt_desc->setEmpty(); + const PageNumber prior(window->win_page); const USHORT relation_id = root->irt_relation; CCH_RELEASE(tdbb, window); + delete_tree(tdbb, relation_id, id, next, prior); + + // clear RDB$INDEX_ID + DropIndexNode::clearId(tdbb, relation_id, id); + + return tree_exists; + } + + CCH_RELEASE(tdbb, window); + return false; +} + + +static void checkTransactionNumber(const index_root_page::irt_repeat* irt_desc, jrd_tra* tra, const char* msg) +{ + if (irt_desc->getTransaction() != tra->tra_number) + { + fb_assert(false); + fatal_exception::raiseFmt("Index root transaction number doesn't match when %s", msg); + } +} + + +static void badState [[noreturn]] (const index_root_page::irt_repeat* irt_desc, const char* set, const char* msg) +{ + fb_assert(false); + fatal_exception::raiseFmt("Invalid index state %s (%d, %s) when %s", Flags::state(*irt_desc), irt_desc->getState(), set, msg); +} + + +void BTR_mark_index_for_delete(thread_db* tdbb, Cached::Relation* rel, MetaId id, WIN* window, index_root_page* root) +{ +/*********************************************************** + * + * B T R _ m a r k _ i n d e x _ f o r _ d e l e t e + * + *********************************************************** + * + * Functional description + * Mark index to be deleted when possible. + * + **************************************/ + SET_TDBB(tdbb); + const Database* dbb = tdbb->getDatabase(); + CHECK_DBB(dbb); + + // Get index descriptor. If index doesn't exist, just leave. + if (id < root->irt_count) + { + auto* irt_desc = root->irt_rpt + id; + + jrd_tra* tra = tdbb->getTransaction(); + fb_assert(tra); + + if (tra) + { + bool marked = false; + + switch(modifyIrtRepeat(tdbb, irt_desc, rel, window, id)) + { + case ModifyIrtRepeatValue::Skip: + break; + + case ModifyIrtRepeatValue::Modified: + marked = true; + break; + + case ModifyIrtRepeatValue::Relock: + root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, window); + irt_desc = root->irt_rpt + id; + break; + + case ModifyIrtRepeatValue::Deleted: + fb_assert(false); // This should not happen + return; // May be we can recover + } + + auto msg = "mark index for delete"; + + switch (irt_desc->getState()) + { + case irt_in_progress: + case irt_commit: + case irt_drop: + case irt_kill: + case irt_unused: + badState(irt_desc, "not irt_rollback/irt_normal", msg); + + case irt_rollback: // created not long ago + checkTransactionNumber(irt_desc, tra, msg); + if (!marked) + CCH_MARK(tdbb, window); + irt_desc->setKill(tra->tra_number); + break; + + case irt_normal: + if (!marked) + CCH_MARK(tdbb, window); + irt_desc->setCommit(tra->tra_number); + break; + } + } + } + + CCH_RELEASE(tdbb, window); +} + + +void BTR_activate_index(thread_db* tdbb, Cached::Relation* relation, MetaId id) +{ +/************************************** + * + * B T R _ a c t i v a t e _ i n d e x + * + ************************************** + * + * Functional description + * Undo delete for an index. + * + **************************************/ + SET_TDBB(tdbb); + const Database* dbb = tdbb->getDatabase(); + CHECK_DBB(dbb); + + WIN window(relation->getIndexRootPage(tdbb)); + index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); + + // Get index descriptor. If index doesn't exist, just leave. + if (id < root->irt_count) + { + auto* irt_desc = root->irt_rpt + id; + + CCH_MARK(tdbb, &window); + + jrd_tra* tra = tdbb->getTransaction(); + fb_assert(tra); + TraNumber descTrans = irt_desc->getTransaction(); + + if (tra) + { + bool marked = false; + + switch(modifyIrtRepeat(tdbb, irt_desc, relation, &window, id, false)) + { + case ModifyIrtRepeatValue::Skip: + break; + + case ModifyIrtRepeatValue::Modified: + marked = true; + break; + + case ModifyIrtRepeatValue::Relock: + root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); + irt_desc = root->irt_rpt + id; + break; + + case ModifyIrtRepeatValue::Deleted: + fb_assert(false); // This should not happen + fatal_exception::raise("Index is gone unexpectedly"); + break; + } + + auto msg = "activate index"; + + switch (irt_desc->getState()) + { + case irt_in_progress: + case irt_unused: + case irt_rollback: + case irt_normal: + badState(irt_desc, "irt_in_progress/irt_rollback/irt_normal", msg); + + case irt_commit: // removed not long ago + checkTransactionNumber(irt_desc, tra, msg); + // fall down... + + case irt_drop: // removed a little more time ago - but still valid + if (!marked) + CCH_MARK(tdbb, &window); + irt_desc->setNormal(); + break; + + case irt_kill: + checkTransactionNumber(irt_desc, tra, msg); + if (!marked) + CCH_MARK(tdbb, &window); + irt_desc->setRollback(tra->tra_number); + break; + } + } } - return tree_exists; + CCH_RELEASE(tdbb, &window); } -bool BTR_description(thread_db* tdbb, jrd_rel* relation, index_root_page* root, index_desc* idx, - USHORT id) +bool BTR_description(thread_db* tdbb, Cached::Relation* relation, const index_root_page* root, index_desc* idx, + MetaId id) { /************************************** * @@ -962,22 +1323,21 @@ bool BTR_description(thread_db* tdbb, jrd_rel* relation, index_root_page* root, const index_root_page::irt_repeat* irt_desc = &root->irt_rpt[id]; - if (irt_desc->getRoot() == 0) + const ULONG rootPage = irt_desc->getRoot(); + if (!rootPage) return false; idx->idx_id = id; - idx->idx_root = irt_desc->getRoot(); + idx->idx_root = rootPage; idx->idx_count = irt_desc->irt_keys; idx->idx_flags = irt_desc->irt_flags; idx->idx_runtime_flags = 0; - idx->idx_foreign_primaries = nullptr; - idx->idx_foreign_relations = nullptr; - idx->idx_foreign_indexes = nullptr; + idx->idx_foreign_deps = nullptr; idx->idx_primary_relation = 0; idx->idx_primary_index = 0; - idx->idx_expression = nullptr; + idx->idx_expression_node = nullptr; idx->idx_expression_statement = nullptr; - idx->idx_condition = nullptr; + idx->idx_condition_node = nullptr; idx->idx_condition_statement = nullptr; idx->idx_fraction = 1.0; @@ -995,11 +1355,11 @@ bool BTR_description(thread_db* tdbb, jrd_rel* relation, index_root_page* root, idx->idx_selectivity = idx->idx_rpt[idx->idx_count - 1].idx_selectivity; ISC_STATUS error = 0; - if (idx->idx_flags & idx_expression) + if (idx->idx_flags & (idx_expression | idx_condition)) { - MET_lookup_index_expression(tdbb, relation, idx); + MET_lookup_index_code(tdbb, relation, idx); - if (!idx->idx_expression) + if (idx->idx_flags & idx_expression && !idx->idx_expression_node) { if (tdbb->tdbb_flags & TDBB_sweeper) return false; @@ -1007,13 +1367,7 @@ bool BTR_description(thread_db* tdbb, jrd_rel* relation, index_root_page* root, // Definition of index expression is not found for index @1 error = isc_idx_expr_not_found; } - } - - if (!error && idx->idx_flags & idx_condition) - { - MET_lookup_index_condition(tdbb, relation, idx); - - if (!idx->idx_condition) + else if (idx->idx_flags & idx_condition && !idx->idx_condition_node) { if (tdbb->tdbb_flags & TDBB_sweeper) return false; @@ -1026,7 +1380,9 @@ bool BTR_description(thread_db* tdbb, jrd_rel* relation, index_root_page* root, if (error) { MetaName indexName; - MET_lookup_index(tdbb, indexName, relation->rel_name, idx->idx_id + 1); + auto* idv = relation->lookup_index(tdbb, idx->idx_id, CacheFlag::AUTOCREATE); + if (idv) + indexName = idv->getName(); Arg::StatusVector status; @@ -1139,7 +1495,7 @@ void BTR_evaluate(thread_db* tdbb, const IndexRetrieval* retrieval, RecordBitmap **************************************/ SET_TDBB(tdbb); - RelationPages* relPages = retrieval->irb_relation->getPages(tdbb); + RelationPages* relPages = retrieval->getPermRelation()->getPages(tdbb); WIN window(relPages->rel_pg_space_id, -1); temporary_key lowerKey, upperKey; @@ -1335,13 +1691,13 @@ btree_page* BTR_find_page(thread_db* tdbb, SET_TDBB(tdbb); - RelationPages* relPages = retrieval->irb_relation->getPages(tdbb); + RelationPages* relPages = retrieval->getPermRelation()->getPages(tdbb); fb_assert(window->win_page.getPageSpaceID() == relPages->rel_pg_space_id); window->win_page = relPages->rel_index_root; - index_root_page* rpage = (index_root_page*) CCH_FETCH(tdbb, window, LCK_read, pag_root); + const index_root_page* rpage = BTR_fetch_root(FB_FUNCTION, tdbb, window); - if (!BTR_description(tdbb, retrieval->irb_relation, rpage, idx, retrieval->irb_index)) + if (!BTR_description(tdbb, retrieval->getPermRelation(), rpage, idx, retrieval->irb_index)) { CCH_RELEASE(tdbb, window); IBERROR(260); // msg 260 index unexpectedly deleted @@ -1449,7 +1805,7 @@ void BTR_insert(thread_db* tdbb, WIN* root_window, index_insertion* insertion) // The top of the index has split. We need to make a new level and // update the index root page. Oh boy. - index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, root_window, LCK_write, pag_root); + index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, root_window); window.win_page = root->irt_rpt[idx->idx_id].getRoot(); bucket = (btree_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_index); @@ -1611,7 +1967,7 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx) // one byte value. See comments in compress const SLONG prefix = (idx->idx_flags & idx_descending) ? 1 : 0; - const Format* format = MET_current(tdbb, relation); + const Format* format = relation->currentFormat(); index_desc::idx_repeat* tail = idx->idx_rpt; SLONG length; @@ -1658,7 +2014,7 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx) default: if (idx->idx_flags & idx_expression) { - fb_assert(idx->idx_expression); + fb_assert(idx->idx_expression_node); length = idx->idx_expression_desc.dsc_length; if (idx->idx_expression_desc.dsc_dtype == dtype_varying) { @@ -1731,7 +2087,7 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx) } -bool BTR_lookup(thread_db* tdbb, jrd_rel* relation, USHORT id, index_desc* buffer, +bool BTR_lookup(thread_db* tdbb, Cached::Relation* relation, MetaId id, index_desc* buffer, RelationPages* relPages) { /************************************** @@ -1747,7 +2103,7 @@ bool BTR_lookup(thread_db* tdbb, jrd_rel* relation, USHORT id, index_desc* buffe SET_TDBB(tdbb); WIN window(relPages->rel_pg_space_id, -1); - index_root_page* const root = fetch_root(tdbb, &window, relation, relPages); + const index_root_page* const root = fetch_root(tdbb, &window, relation, relPages); if (!root) return false; @@ -1826,7 +2182,7 @@ bool BTR_make_bounds(thread_db* tdbb, const IndexRetrieval* retrieval, if (errorCode != idx_e_ok) { index_desc temp_idx = *idx; // to avoid constness issues - IndexErrorContext context(retrieval->irb_relation, &temp_idx); + IndexErrorContext context(retrieval->getRelation(tdbb), &temp_idx); context.raise(tdbb, errorCode); } } @@ -2085,7 +2441,162 @@ void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* ke } -bool BTR_next_index(thread_db* tdbb, jrd_rel* relation, jrd_tra* transaction, index_desc* idx, WIN* window) +// checks is there a need to modify index descriptor +// if yes - we release index root window + +static bool checkIrtRepeat(thread_db* tdbb, const index_root_page::irt_repeat* irt_desc, + Cached::Relation* relation, WIN* window, MetaId indexId) +{ + const TraNumber irtTrans = irt_desc->getTransaction(); + const TraNumber oldestActive = tdbb->getDatabase()->dbb_oldest_active; + + switch (irt_desc->getState()) + { + case irt_unused: + case irt_normal: + // normal processing + return false; + + case irt_in_progress: + // index creation - should wait to know what to do + CCH_RELEASE(tdbb, window); + + // Wait for completion + { + IndexCreateLock crtLock(tdbb, relation->getId()); + crtLock.shared(indexId); + } + break; + + case irt_rollback: + case irt_commit: + case irt_kill: + // this three states require modification if irtTrans is completed + switch (TPC_cache_state(tdbb, irtTrans)) + { + case tra_committed: + case tra_dead: + break; + + default: + return false; + } + CCH_RELEASE(tdbb, window); + break; + + case irt_drop: + // drop index when OAT >= irtTrans + if (oldestActive < irtTrans) + return false; + CCH_RELEASE(tdbb, window); + break; + + default: + fb_assert(false); + return false; + } + + return true; +} + + +// checks is there a need to modify index descriptor +// if yes - modifies it up to index deletion + +static ModifyIrtRepeatValue modifyIrtRepeat(thread_db* tdbb, index_root_page::irt_repeat* irt_desc, + Cached::Relation* relation, WIN* window, MetaId indexId, bool deletable) +{ + const TraNumber irtTrans = irt_desc->getTransaction(); + const TraNumber oldestActive = tdbb->getDatabase()->dbb_oldest_active; + + switch (irt_desc->getState()) + { + case irt_unused: + case irt_normal: + // normal processing + return ModifyIrtRepeatValue::Skip; + + case irt_in_progress: + // index creation - should wait to know what to do + CCH_RELEASE(tdbb, window); + + // Wait for completion + { + IndexCreateLock crtLock(tdbb, relation->getId()); + crtLock.shared(indexId); + } + + return ModifyIrtRepeatValue::Relock; + + case irt_rollback: + switch (transactionState(tdbb, irtTrans, relation, indexId, true)) + { + case tra_committed: // switch to normal state + CCH_MARK(tdbb, window); + irt_desc->setNormal(); + return ModifyIrtRepeatValue::Modified; + + case tra_dead: // drop index on rollback + break; + + default: + return ModifyIrtRepeatValue::Skip; + } + break; + + case irt_kill: + switch (TPC_cache_state(tdbb, irtTrans)) + { + case tra_committed: + case tra_dead: // drop index when transaction ended + break; + + default: + return ModifyIrtRepeatValue::Skip; + } + break; + + case irt_commit: + switch (transactionState(tdbb, irtTrans, relation, indexId, false)) + { + case tra_committed: // switch to drop state + CCH_MARK(tdbb, window); + irt_desc->setDrop(TransactionNumber::next(tdbb)); + return ModifyIrtRepeatValue::Modified; + + case tra_dead: // switch to normal state + CCH_MARK(tdbb, window); + irt_desc->setNormal(); + return ModifyIrtRepeatValue::Modified; + + default: + return ModifyIrtRepeatValue::Skip; + } + fb_assert(false); + return ModifyIrtRepeatValue::Skip; + + case irt_drop: + // drop index when OAT >= irtTrans + if (oldestActive >= irtTrans) + break; + return ModifyIrtRepeatValue::Skip; + + default: + fb_assert(false); + return ModifyIrtRepeatValue::Skip; + } + + // drop index + if (deletable) + { + BTR_delete_index(tdbb, window, indexId); + return ModifyIrtRepeatValue::Deleted; + } + return ModifyIrtRepeatValue::Skip; +} + + +bool BTR_next_index(thread_db* tdbb, Cached::Relation* relation, jrd_tra* transaction, index_desc* idx, WIN* window) { /************************************** * @@ -2100,7 +2611,7 @@ bool BTR_next_index(thread_db* tdbb, jrd_rel* relation, jrd_tra* transaction, in **************************************/ SET_TDBB(tdbb); - USHORT id; + MetaId id; if (idx->idx_id == idx_invalid) { id = 0; @@ -2109,9 +2620,9 @@ bool BTR_next_index(thread_db* tdbb, jrd_rel* relation, jrd_tra* transaction, in else id = idx->idx_id + 1; - index_root_page* root; + const index_root_page* root; if (window->win_bdb) - root = (index_root_page*) window->win_buffer; + root = (const index_root_page*) window->win_buffer; else { RelationPages* const relPages = transaction ? @@ -2123,28 +2634,28 @@ bool BTR_next_index(thread_db* tdbb, jrd_rel* relation, jrd_tra* transaction, in for (; id < root->irt_count; ++id) { + bool needWrite = false; + bool rls = true; + const index_root_page::irt_repeat* irt_desc = root->irt_rpt + id; - if (irt_desc->getTransaction() && transaction) - { - const TraNumber trans = irt_desc->getTransaction(); - CCH_RELEASE(tdbb, window); - const int trans_state = TRA_wait(tdbb, transaction, trans, jrd_tra::tra_wait); - if ((trans_state == tra_dead) || (trans_state == tra_committed)) - { - // clean up this left-over index - root = (index_root_page*) CCH_FETCH(tdbb, window, LCK_write, pag_root); - irt_desc = root->irt_rpt + id; - if (irt_desc->getTransaction() == trans) - BTR_delete_index(tdbb, window, id); - else - CCH_RELEASE(tdbb, window); + if (checkIrtRepeat(tdbb, irt_desc, relation, window, id)) + { + auto* root_write = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, window); + auto* irt_write = root_write->irt_rpt + id; - root = (index_root_page*) CCH_FETCH(tdbb, window, LCK_read, pag_root); - continue; + auto modValue = modifyIrtRepeat(tdbb, irt_write, relation, window, id); + switch (modValue) + { + case ModifyIrtRepeatValue::Skip: + case ModifyIrtRepeatValue::Modified: + CCH_RELEASE(tdbb, window); + break; } - root = (index_root_page*) CCH_FETCH(tdbb, window, LCK_read, pag_root); + root = BTR_fetch_root(FB_FUNCTION, tdbb, window); + if (modValue == ModifyIrtRepeatValue::Deleted) + continue; } if (BTR_description(tdbb, relation, root, idx, id)) @@ -2201,7 +2712,7 @@ void BTR_remove(thread_db* tdbb, WIN* root_window, index_insertion* insertion) CCH_RELEASE(tdbb, &window); CCH_RELEASE(tdbb, root_window); - index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, root_window, LCK_write, pag_root); + index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, root_window); page = (btree_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_index); // get the page number of the child, and check to make sure @@ -2242,7 +2753,7 @@ void BTR_remove(thread_db* tdbb, WIN* root_window, index_insertion* insertion) } -void BTR_reserve_slot(thread_db* tdbb, IndexCreation& creation) +void BTR_reserve_slot(thread_db* tdbb, IndexCreation& creation, IndexCreateLock& createLock) { /************************************** * @@ -2275,11 +2786,10 @@ void BTR_reserve_slot(thread_db* tdbb, IndexCreation& creation) // Index id for temporary index instance of global temporary table is // already assigned, use it. const bool use_idx_id = (relPages->rel_instance_id != 0); - if (use_idx_id) - fb_assert(idx->idx_id <= dbb->dbb_max_idx); + fb_assert((!use_idx_id) || (idx->idx_id <= dbb->dbb_max_idx)); WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); - index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_root); + index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); CCH_MARK(tdbb, &window); // check that we create no more indexes than will fit on a single root page @@ -2359,16 +2869,19 @@ void BTR_reserve_slot(thread_db* tdbb, IndexCreation& creation) fb_assert(idx->idx_count <= MAX_UCHAR); slot->irt_keys = (UCHAR) idx->idx_count; slot->irt_flags = idx->idx_flags; - slot->setTransaction(transaction->tra_number); + slot->setProgress(transaction->tra_number); // Exploit the fact idx_repeat structure matches ODS IRTD one memcpy(desc, idx->idx_rpt, len); + // Take creation lock on new index before releasing a window + // Will be always taken cause nobody except us knows about this index ID + createLock.exclusive(idx->idx_id); CCH_RELEASE(tdbb, &window); } -void BTR_selectivity(thread_db* tdbb, jrd_rel* relation, USHORT id, SelectivityList& selectivity) +void BTR_selectivity(thread_db* tdbb, Cached::Relation* relation, MetaId id, SelectivityList& selectivity) { /************************************** * @@ -2389,17 +2902,17 @@ void BTR_selectivity(thread_db* tdbb, jrd_rel* relation, USHORT id, SelectivityL RelationPages* relPages = relation->getPages(tdbb); WIN window(relPages->rel_pg_space_id, -1); - index_root_page* root = fetch_root(tdbb, &window, relation, relPages); + const index_root_page* root = fetch_root(tdbb, &window, relation, relPages); if (!root) return; - ULONG page; - if (id >= root->irt_count || !(page = root->irt_rpt[id].getRoot())) + if (id >= root->irt_count || !root->irt_rpt[id].getRoot()) { CCH_RELEASE(tdbb, &window); return; } + ULONG page = root->irt_rpt[id].getRoot(); const bool descending = (root->irt_rpt[id].irt_flags & irt_descending); const ULONG segments = root->irt_rpt[id].irt_keys; @@ -2566,9 +3079,9 @@ void BTR_selectivity(thread_db* tdbb, jrd_rel* relation, USHORT id, SelectivityL // Store the selectivity on the root page window.win_page = relPages->rel_index_root; window.win_flags = 0; - root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_root); + auto* write_root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); CCH_MARK(tdbb, &window); - update_selectivity(root, id, selectivity); + update_selectivity(write_root, id, selectivity); CCH_RELEASE(tdbb, &window); } @@ -2864,7 +3377,7 @@ static void compress(thread_db* tdbb, to.dsc_flags = 0; to.dsc_sub_type = 0; to.dsc_scale = 0; - to.dsc_ttype() = ttype_sort_key; + to.setTextType(ttype_sort_key); to.dsc_length = MIN(MAX_COLUMN_SIZE, MAX_KEY * 4); ptr = to.dsc_address = reinterpret_cast(buffer.vary_string); multiKeyLength = length = INTL_string_to_key(tdbb, itype, desc, &to, key_type); @@ -3660,7 +4173,7 @@ static contents delete_node(thread_db* tdbb, WIN* window, UCHAR* pointer) static void delete_tree(thread_db* tdbb, - USHORT rel_id, USHORT idx_id, PageNumber next, PageNumber prior) + MetaId rel_id, MetaId idx_id, PageNumber next, PageNumber prior) { /************************************** * @@ -3814,7 +4327,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; @@ -4169,7 +4682,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; @@ -4498,7 +5011,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)); } @@ -4509,8 +5022,8 @@ static ULONG fast_load(thread_db* tdbb, } -static index_root_page* fetch_root(thread_db* tdbb, WIN* window, const jrd_rel* relation, - const RelationPages* relPages) +static const index_root_page* fetch_root(thread_db* tdbb, WIN* window, const RelationPermanent* relation, + const RelationPages* relPages) { /************************************** * @@ -4528,7 +5041,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); @@ -4539,7 +5052,7 @@ static index_root_page* fetch_root(thread_db* tdbb, WIN* window, const jrd_rel* window->win_page = relPages->rel_index_root; } - return (index_root_page*) CCH_FETCH(tdbb, window, LCK_read, pag_root); + return BTR_fetch_root(FB_FUNCTION, tdbb, window); } @@ -6453,16 +6966,12 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re * Convert index key into textual representation. * **************************************/ - fb_assert(relation && idx && record); + string key; - if (!(relation->rel_flags & REL_scanned) || - (relation->rel_flags & REL_being_scanned)) - { - MET_scan_relation(tdbb, relation); - } + fb_assert(relation && idx && record); const FB_SIZE_T MAX_KEY_STRING_LEN = 250; - string key, value; + string value; try { @@ -6477,7 +6986,7 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re for (USHORT i = 0; i < idx->idx_count; i++) { const USHORT field_id = idx->idx_rpt[i].idx_field; - const jrd_fld* const field = MET_get_field(relation, field_id); + jrd_fld* field = MET_get_field(relation, field_id); if (field) value.printf("\"%s\"", field->fld_name.c_str()); @@ -6944,7 +7453,7 @@ static bool scan(thread_db* tdbb, UCHAR* pointer, RecordBitmap** bitmap, RecordB } -void update_selectivity(index_root_page* root, USHORT id, const SelectivityList& selectivity) +void update_selectivity(index_root_page* root, MetaId id, const SelectivityList& selectivity) { /************************************** * @@ -6967,3 +7476,41 @@ void update_selectivity(index_root_page* root, USHORT id, const SelectivityList& for (int i = 0; i < idx_count; i++, key_descriptor++) key_descriptor->irtd_selectivity = selectivity[i]; } + + +IndexCreateLock::IndexCreateLock(thread_db* tdbb, MetaId relId) + : tdbb(tdbb), relId(relId) +{ } + +IndexCreateLock::~IndexCreateLock() +{ + if (lck) + { + LCK_release(tdbb, lck); + delete lck; + } +} + +void IndexCreateLock::exclusive(MetaId indexId) +{ + makeLock(indexId); + bool rc = LCK_lock(tdbb, lck, LCK_EX, LCK_NO_WAIT); + fb_assert(rc); +} + +void IndexCreateLock::shared(MetaId indexId) +{ + makeLock(indexId); + auto* tra = tdbb->getTransaction(); + auto wait = tra ? tra->getLockWait() : LCK_WAIT; + if (!LCK_lock(tdbb, lck, LCK_SR, wait)) + fatal_exception::raise("Timeout waiting for index to be created"); +} + +void IndexCreateLock::makeLock(MetaId indexId) +{ + fb_assert(!lck); + lck = FB_NEW_RPT(getPool(), 0) Lock(tdbb, 0, LCK_idx_create); + lck->setKey((FB_UINT64(relId) << IndexPermanent::REL_ID_KEY_OFFSET) + indexId); +} + diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 7f1bc99303e..9ca10839d73 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -32,9 +32,12 @@ #include "../include/fb_blk.h" #include "../jrd/err_proto.h" // Index error types +#include "../jrd/Resources.h" #include "../jrd/RecordNumber.h" #include "../jrd/sbm.h" #include "../jrd/lck.h" +#include "../jrd/pag.h" +#include "../jrd/val.h" struct dsc; @@ -51,25 +54,46 @@ class Sort; class PartitionedSort; struct sort_key_def; +enum class IdxCreate {AtOnce, ForRollback}; + +// Dependencies from/to foreign references + +struct dep +{ + int dep_reference_id; + int dep_relation; + int dep_index; + + dep() = default; +}; + +// Primary dependencies from all foreign references to relation's +// primary/unique keys + +typedef Firebird::HalfStaticArray PrimaryDeps; + +// Foreign references to other relations' primary/unique keys + +typedef Firebird::HalfStaticArray ForeignDeps; + + // Index descriptor block -- used to hold info from index root page struct index_desc { ULONG idx_root; // Index root float idx_selectivity; // selectivity of index - USHORT idx_id; - UCHAR idx_flags; + MetaId idx_id; + USHORT idx_flags; UCHAR idx_runtime_flags; // flags used at runtime, not stored on disk - USHORT idx_primary_index; // id for primary key partner index - USHORT idx_primary_relation; // id for primary key partner relation + MetaId idx_primary_index; // id for primary key partner index + MetaId idx_primary_relation; // id for primary key partner relation USHORT idx_count; // number of keys - vec* idx_foreign_primaries; // ids for primary/unique indexes with partners - vec* idx_foreign_relations; // ids for foreign key partner relations - vec* idx_foreign_indexes; // ids for foreign key partner indexes - ValueExprNode* idx_expression; // node tree for indexed expression + ForeignDeps* idx_foreign_deps; // foreign key partners + ValueExprNode* idx_expression_node; // node tree for indexed expression dsc idx_expression_desc; // descriptor for expression result Statement* idx_expression_statement; // stored statement for expression evaluation - BoolExprNode* idx_condition; // node tree for index condition + BoolExprNode* idx_condition_node; // node tree for index condition Statement* idx_condition_statement; // stored statement for index condition float idx_fraction; // fraction of keys included in the index // This structure should exactly match IRTD structure for current ODS @@ -110,15 +134,16 @@ const int idx_first_intl_string = 64; // .. MAX (short) Range of computed key st const int idx_offset_intl_range = (0x7FFF + idx_first_intl_string); -// these flags must match the irt_flags (see ods.h) +// these flags match the irt_flags in ods.h -const int idx_unique = 1; -const int idx_descending = 2; -const int idx_in_progress = 4; -const int idx_foreign = 8; -const int idx_primary = 16; -const int idx_expression = 32; -const int idx_condition = 64; +const USHORT idx_unique = 1; +const USHORT idx_descending = 2; +//const USHORT idx_state_a = 4; +const USHORT idx_foreign = 8; +const USHORT idx_primary = 16; +const USHORT idx_expression = 32; +const USHORT idx_condition = 64; +//const USHORT idx_state_b = 128; // these flags are for idx_runtime_flags @@ -188,16 +213,16 @@ class IndexRetrieval { public: IndexRetrieval(jrd_rel* relation, const index_desc* idx, USHORT count, temporary_key* key) - : irb_relation(relation), irb_index(idx->idx_id), + : irb_rsc_relation(), irb_jrd_relation(relation), irb_index(idx->idx_id), irb_generic(0), irb_lower_count(count), irb_upper_count(count), irb_key(key), irb_name(nullptr), irb_value(nullptr), irb_list(nullptr), irb_scale(nullptr) { memcpy(&irb_desc, idx, sizeof(irb_desc)); } - IndexRetrieval(MemoryPool& pool, jrd_rel* relation, const index_desc* idx, + IndexRetrieval(MemoryPool& pool, Rsc::Rel relation, const index_desc* idx, const MetaName& name) - : irb_relation(relation), irb_index(idx->idx_id), + : irb_rsc_relation(relation), irb_jrd_relation(nullptr), irb_index(idx->idx_id), irb_generic(0), irb_lower_count(0), irb_upper_count(0), irb_key(NULL), irb_name(FB_NEW_POOL(pool) MetaName(name)), irb_value(FB_NEW_POOL(pool) ValueExprNode*[idx->idx_count * 2]), @@ -213,8 +238,16 @@ class IndexRetrieval delete[] irb_scale; } + jrd_rel* getRelation(thread_db* tdbb) const; + Cached::Relation* getPermRelation() const; + index_desc irb_desc; // Index descriptor - jrd_rel* irb_relation; // Relation for retrieval + +private: + Rsc::Rel irb_rsc_relation; // Relation for retrieval + jrd_rel* irb_jrd_relation; // when used in different contexts + +public: USHORT irb_index; // Index id USHORT irb_generic; // Flags for generic search USHORT irb_lower_count; // Number of segments for retrieval @@ -303,6 +336,7 @@ struct IndexCreation USHORT nullIndLen; SINT64 dup_recno; Firebird::AtomicCounter duplicates; + IdxCreate forRollback; }; // Class used to report any index related errors @@ -337,6 +371,27 @@ class IndexErrorContext bool isLocationDefined = false; }; + +// Index construction lock holder + +class IndexCreateLock : public Firebird::AutoStorage +{ +public: + IndexCreateLock(thread_db* tdbb, MetaId relId); + ~IndexCreateLock(); + + void exclusive(MetaId indexId); + void shared(MetaId indexId); + +private: + thread_db* tdbb; // may be stored here cause IndexCreateLock is always on stack + MetaId relId; + Lock* lck = nullptr; + + void makeLock(MetaId indexId); +}; + + // Helper classes to allow efficient evaluation of index conditions/expressions class IndexCondition diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index 4eb3f8a8073..f5fcc245160 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -29,12 +29,13 @@ #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::Cached::Relation*, Jrd::IndexDescList&, Jrd::RelationPages*); +void BTR_activate_index(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId); 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); -bool BTR_description(Jrd::thread_db*, Jrd::jrd_rel*, Ods::index_root_page*, Jrd::index_desc*, USHORT); -dsc* BTR_eval_expression(Jrd::thread_db*, Jrd::index_desc*, Jrd::Record*); +bool BTR_delete_index(Jrd::thread_db*, Jrd::win*, MetaId); +bool BTR_description(Jrd::thread_db*, Jrd::Cached::Relation*, const Ods::index_root_page*, Jrd::index_desc*, MetaId); +DSC* BTR_eval_expression(Jrd::thread_db*, Jrd::index_desc*, Jrd::Record*); void BTR_evaluate(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::RecordBitmap**, Jrd::RecordBitmap*); UCHAR* BTR_find_leaf(Ods::btree_page*, Jrd::temporary_key*, UCHAR*, USHORT*, bool, int); Ods::btree_page* BTR_find_page(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::win*, Jrd::index_desc*, @@ -42,16 +43,19 @@ Ods::btree_page* BTR_find_page(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd: void BTR_insert(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); USHORT BTR_key_length(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); Ods::btree_page* BTR_left_handoff(Jrd::thread_db*, Jrd::win*, Ods::btree_page*, SSHORT); -bool BTR_lookup(Jrd::thread_db*, Jrd::jrd_rel*, USHORT, Jrd::index_desc*, Jrd::RelationPages*); +bool BTR_lookup(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId, Jrd::index_desc*, Jrd::RelationPages*); bool BTR_make_bounds(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::IndexScanListIterator*, - Jrd::temporary_key*, Jrd::temporary_key*, USHORT&); -Jrd::idx_e BTR_make_key(Jrd::thread_db*, USHORT, const Jrd::ValueExprNode* const*, const SSHORT* scale, - const Jrd::index_desc*, Jrd::temporary_key*, USHORT, bool*); + Jrd::temporary_key*, Jrd::temporary_key*, USHORT&); +Jrd::idx_e BTR_make_key(Jrd::thread_db*, USHORT, const Jrd::ValueExprNode* const*, const SSHORT*, + const Jrd::index_desc*, Jrd::temporary_key*, USHORT, bool*); void BTR_make_null_key(Jrd::thread_db*, const Jrd::index_desc*, Jrd::temporary_key*); -bool BTR_next_index(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::jrd_tra*, Jrd::index_desc*, Jrd::win*); +void BTR_mark_index_for_delete(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId, Jrd::win*, Ods::index_root_page*); +bool BTR_next_index(Jrd::thread_db*, Jrd::Cached::Relation*, Jrd::jrd_tra*, Jrd::index_desc*, Jrd::win*); void BTR_remove(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); -void BTR_reserve_slot(Jrd::thread_db*, Jrd::IndexCreation&); -void BTR_selectivity(Jrd::thread_db*, Jrd::jrd_rel*, USHORT, Jrd::SelectivityList&); +void BTR_reserve_slot(Jrd::thread_db*, Jrd::IndexCreation&, Jrd::IndexCreateLock&); +void BTR_selectivity(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId, Jrd::SelectivityList&); bool BTR_types_comparable(const dsc& target, const dsc& source); +Ods::index_root_page* BTR_fetch_root_for_update(const char* from, Jrd::thread_db* tdbb, Jrd::win* window); +const Ods::index_root_page* BTR_fetch_root(const char* from, Jrd::thread_db* tdbb, Jrd::win* window); #endif // JRD_BTR_PROTO_H diff --git a/src/jrd/cch.cpp b/src/jrd/cch.cpp index e909f7f0c8f..e4e42186aa1 100644 --- a/src/jrd/cch.cpp +++ b/src/jrd/cch.cpp @@ -42,6 +42,7 @@ #include "../jrd/tra.h" #include "../jrd/sbm.h" #include "../jrd/nbak.h" +#include "../jrd/met.h" #include "../common/gdsassert.h" #include "../jrd/cch_proto.h" #include "../jrd/err_proto.h" @@ -49,7 +50,7 @@ #include "../common/isc_proto.h" #include "../common/isc_s_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/pag_proto.h" #include "../jrd/ods_proto.h" #include "../jrd/os/pio_proto.h" @@ -3121,8 +3122,6 @@ void BufferControl::cache_writer(BufferControl* bcb) Monitoring::cleanupAttachment(tdbb); attachment->releaseLocks(tdbb); LCK_fini(tdbb, LCK_OWNER_attachment); - - attachment->releaseRelations(tdbb); } // try catch (const Firebird::Exception& ex) { @@ -5153,7 +5152,7 @@ void requeueRecentlyUsed(BufferControl* bcb) BufferControl* BufferControl::create(Database* dbb) { - MemoryPool* const pool = dbb->createPool(); + MemoryPool* const pool = dbb->createPool(ALLOC_ARGS0); BufferControl* const bcb = FB_NEW_POOL(*pool) BufferControl(*pool, dbb->dbb_memory_stats); pool->setStatsGroup(bcb->bcb_memory_stats); return bcb; @@ -5492,48 +5491,6 @@ void BCBHashTable::remove(BufferDesc* bdb) #ifdef HASH_USE_CDS_LIST -/// class ListNodeAllocator - -class InitPool -{ -public: - explicit InitPool(MemoryPool&) - : m_pool(InitCDS::createPool()), - m_stats(m_pool->getStatsGroup()) - { } - - ~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; @@ -5550,4 +5507,9 @@ void ListNodeAllocator::deallocate(T* p, std::size_t /* n */) MemoryPool::globalFree(p); } +void suspend() +{ + cds::backoff::pause(); +} + #endif // HASH_USE_CDS_LIST diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index 87930ad8b64..1b91ed1d6c9 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -56,6 +56,7 @@ #include "../jrd/intl.h" #include "../jrd/btr.h" #include "../jrd/sort.h" +#include "../jrd/met.h" #include "../common/gdsassert.h" #include "../jrd/cmp_proto.h" #include "../common/dsc_proto.h" @@ -67,7 +68,7 @@ #include "../jrd/intl_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/par_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" @@ -148,11 +149,11 @@ Statement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool Statement* statement = nullptr; SET_TDBB(tdbb); - const auto att = tdbb->getAttachment(); + const auto dbb = tdbb->getDatabase(); // 26.09.2002 Nickolay Samofatov: default memory pool will become statement pool // and will be freed by CMP_release - const auto newPool = att->createPool(); + const auto newPool = dbb->createPool(ALLOC_ARGS0); try { @@ -173,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()); @@ -192,7 +193,7 @@ Statement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool if (statement) statement->release(tdbb); else - att->deletePool(newPool); + dbb->deletePool(newPool); ERR_punt(); } @@ -214,7 +215,11 @@ Request* CMP_compile_request(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, SET_TDBB(tdbb); auto statement = CMP_compile(tdbb, blr, blrLength, internalFlag, 0, nullptr); - auto request = statement->getRequest(tdbb, 0); + auto request = statement->makeRootRequest(tdbb); + + request->setAttachment(tdbb->getAttachment()); + request->req_stats.reset(); + request->req_base_stats.reset(); return request; } @@ -263,9 +268,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 = tail->csb_relation(tdbb)->currentFormat(); 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 @@ -276,52 +281,6 @@ const Format* CMP_format(thread_db* tdbb, CompilerScratch* csb, StreamType strea } -IndexLock* CMP_get_index_lock(thread_db* tdbb, jrd_rel* relation, USHORT id) -{ -/************************************** - * - * C M P _ g e t _ i n d e x _ l o c k - * - ************************************** - * - * Functional description - * Get index lock block for index. If one doesn't exist, - * make one. - * - **************************************/ - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - - DEV_BLKCHK(relation, type_rel); - - if (relation->rel_id < (USHORT) rel_MAX) { - return NULL; - } - - // for to find an existing block - - for (IndexLock* index = relation->rel_index_locks; index; index = index->idl_next) - { - if (index->idl_id == id) { - return index; - } - } - - IndexLock* index = FB_NEW_POOL(*relation->rel_pool) IndexLock(); - index->idl_next = relation->rel_index_locks; - relation->rel_index_locks = index; - index->idl_relation = relation; - index->idl_id = id; - index->idl_count = 0; - - Lock* lock = FB_NEW_RPT(*relation->rel_pool, 0) Lock(tdbb, sizeof(SLONG), LCK_idx_exist); - index->idl_lock = lock; - lock->setKey((relation->rel_id << 16) | id); - - return index; -} - - void CMP_post_access(thread_db* tdbb, CompilerScratch* csb, const MetaName& security_name, @@ -362,45 +321,6 @@ void CMP_post_access(thread_db* tdbb, } -void CMP_post_resource( ResourceList* rsc_ptr, void* obj, Resource::rsc_s type, USHORT id) -{ -/************************************** - * - * C M P _ p o s t _ r e s o u r c e - * - ************************************** - * - * Functional description - * Post a resource usage to the compiler scratch block. - * - **************************************/ - // Initialize resource block - Resource resource(type, id, NULL, NULL, NULL); - switch (type) - { - case Resource::rsc_relation: - case Resource::rsc_index: - resource.rsc_rel = (jrd_rel*) obj; - break; - case Resource::rsc_procedure: - case Resource::rsc_function: - resource.rsc_routine = (Routine*) obj; - break; - case Resource::rsc_collation: - resource.rsc_coll = (Collation*) obj; - break; - default: - BUGCHECK(220); // msg 220 unknown resource - break; - } - - // Add it into list if not present already - FB_SIZE_T pos; - if (!rsc_ptr->find(resource, pos)) - rsc_ptr->insert(pos, resource); -} - - void CMP_release(thread_db* tdbb, Request* request) { /************************************** @@ -641,7 +561,7 @@ bool CMP_procedure_arguments( } -void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedure) +void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, Cached::Procedure* procedure) { /************************************** * @@ -668,13 +588,13 @@ void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* p if (procedure->getName().package.isEmpty()) { CMP_post_access(tdbb, csb, procedure->getSecurityName(), - (csb->csb_view ? csb->csb_view->rel_id : 0), + (csb->csb_view ? csb->csb_view()->getId() : 0), SCL_execute, obj_procedures, procedure->getName().identifier); } else { CMP_post_access(tdbb, csb, procedure->getSecurityName(), - (csb->csb_view ? csb->csb_view->rel_id : 0), + (csb->csb_view ? csb->csb_view()->getId() : 0), SCL_execute, obj_packages, procedure->getName().package); } diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index f974f9972eb..451b80725b4 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -25,13 +25,9 @@ #define JRD_CMP_PROTO_H #include "../jrd/req.h" -// req.h includes exe.h => Jrd::CompilerScratch and Jrd::CompilerScratch::csb_repeat. +#include "../jrd/exe.h" #include "../jrd/scl.h" - -namespace Jrd -{ - class RelationSourceNode; -} +#include "../jrd/Resources.h" StreamType* CMP_alloc_map(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType stream); Jrd::ValueExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); @@ -42,7 +38,6 @@ Jrd::Statement* CMP_compile(Jrd::thread_db* tdbb, const UCHAR* blr, ULONG blrLen Jrd::Request* CMP_compile_request(Jrd::thread_db*, const UCHAR* blr, ULONG blrLength, bool internalFlag); Jrd::CompilerScratch::csb_repeat* CMP_csb_element(Jrd::CompilerScratch*, StreamType element); const Jrd::Format* CMP_format(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType); -Jrd::IndexLock* CMP_get_index_lock(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); Jrd::Request* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool); Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&); @@ -62,8 +57,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_resource(Jrd::ResourceList*, void*, Jrd::Resource::rsc_s, USHORT); +void CMP_post_procedure_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::Cached::Procedure*); Jrd::RecordSource* CMP_post_rse(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::RseNode*); void CMP_release(Jrd::thread_db*, Jrd::Request*); diff --git a/src/jrd/constants.h b/src/jrd/constants.h index ce322ee78d6..f6c2258b690 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -323,7 +323,7 @@ enum TriggerAction { TRIGGER_UPDATE = 2, TRIGGER_DELETE = 3, TRIGGER_CONNECT = 4, - TRIGGER_DISCONNECT = 5, + TRIGGER_DISCONNECT = 5, TRIGGER_TRANS_START = 6, TRIGGER_TRANS_COMMIT = 7, TRIGGER_TRANS_ROLLBACK = 8, @@ -342,7 +342,8 @@ const unsigned DB_TRIGGER_DISCONNECT = 1; const unsigned DB_TRIGGER_TRANS_START = 2; const unsigned DB_TRIGGER_TRANS_COMMIT = 3; const unsigned DB_TRIGGER_TRANS_ROLLBACK = 4; -const unsigned DB_TRIGGER_MAX = 5; +const unsigned DB_TRIGGER_DDL = 5; +const unsigned DB_TRIGGERS_COUNT = 6; static const char* const DDL_TRIGGER_ACTION_NAMES[][2] = { diff --git a/src/jrd/cvt.cpp b/src/jrd/cvt.cpp index a26f8ec6093..b14481b813f 100644 --- a/src/jrd/cvt.cpp +++ b/src/jrd/cvt.cpp @@ -38,6 +38,7 @@ #include #include "../jrd/jrd.h" #include "../jrd/req.h" +#include "../jrd/Statement.h" #include "../jrd/val.h" #include "iberror.h" #include "../jrd/intl.h" @@ -204,7 +205,7 @@ UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, v MOVE_CLEAR(&desc, sizeof(desc)); desc.dsc_dtype = dtype_text; - desc.dsc_ttype() = ttype_ascii; + desc.setTextType(ttype_ascii); desc.dsc_length = length; desc.dsc_address = const_cast(string); // The above line allows the assignment, but "string" is treated as const @@ -457,9 +458,9 @@ ISC_TIMESTAMP_TZ CVT_get_timestamp_tz(const dsc* desc) Firebird::GlobalPtr EngineCallbacks::instance; -bool EngineCallbacks::transliterate(const dsc* from, dsc* to, CHARSET_ID& charset2) +bool EngineCallbacks::transliterate(const dsc* from, dsc* to, CSetId& charset2) { - CHARSET_ID charset1; + CSetId charset1; if (INTL_TTYPE(from) == ttype_dynamic) charset1 = INTL_charset(NULL, INTL_TTYPE(from)); else @@ -488,7 +489,7 @@ bool EngineCallbacks::transliterate(const dsc* from, dsc* to, CHARSET_ID& charse } -CharSet* EngineCallbacks::getToCharset(CHARSET_ID charSetId) +CharSet* EngineCallbacks::getToCharset(CSetId charSetId) { thread_db* tdbb = JRD_get_thread_data(); return INTL_charset_lookup(tdbb, charSetId); @@ -502,7 +503,7 @@ void EngineCallbacks::validateData(CharSet* toCharSet, SLONG length, const UCHAR } -ULONG EngineCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start, +ULONG EngineCallbacks::validateLength(CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start, const USHORT size) { fb_assert(charSet); @@ -532,7 +533,7 @@ ULONG EngineCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, UL } -ULONG TruncateCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start, +ULONG TruncateCallbacks::validateLength(CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start, const USHORT size) { fb_assert(charSet); @@ -568,7 +569,7 @@ ULONG TruncateCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, } -CHARSET_ID EngineCallbacks::getChid(const dsc* to) +CSetId EngineCallbacks::getChid(const dsc* to) { if (INTL_TTYPE(to) == ttype_dynamic) return INTL_charset(NULL, INTL_TTYPE(to)); diff --git a/src/jrd/cvt2.cpp b/src/jrd/cvt2.cpp index f6b4e0979ff..2f1bcc5347e 100644 --- a/src/jrd/cvt2.cpp +++ b/src/jrd/cvt2.cpp @@ -411,11 +411,11 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt */ SET_TDBB(tdbb); - CHARSET_ID charset1 = INTL_TTYPE(arg1); + CSetId charset1 = INTL_TTYPE(arg1); if (charset1 == ttype_dynamic) charset1 = INTL_charset(tdbb, charset1); - CHARSET_ID charset2 = INTL_TTYPE(arg2); + CSetId charset2 = INTL_TTYPE(arg2); if (charset2 == ttype_dynamic) charset2 = INTL_charset(tdbb, charset2); @@ -431,7 +431,7 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt UCHAR* p1 = NULL; UCHAR* p2 = NULL; - USHORT t1, t2; // unused later + TTypeId t1, t2; // unused later USHORT length = CVT_get_string_ptr(arg1, &t1, &p1, NULL, 0, decSt); USHORT length2 = CVT_get_string_ptr(arg2, &t2, &p2, NULL, 0, decSt); @@ -663,7 +663,7 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt if (arg2->isText()) { UCHAR* p = NULL; - USHORT ttype; + TTypeId ttype; const USHORT length = CVT_get_string_ptr(arg2, &ttype, &p, NULL, 0, decSt); // Compare DBKEY with a compatible binary string with respect to @@ -746,7 +746,6 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2, DecimalStatus decSt) **************************************/ SLONG l1, l2; - USHORT ttype2; int ret_val = 0; thread_db* tdbb = NULL; @@ -757,9 +756,9 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2, DecimalStatus decSt) if (arg1->dsc_dtype != dtype_blob) ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_datnotsup)); - USHORT ttype1; + TTypeId ttype1, ttype2; if (arg1->dsc_sub_type == isc_blob_text) - ttype1 = arg1->dsc_blob_ttype(); // Load blob character set and collation + ttype1 = arg1->getTextType(); // Load blob character set and collation else ttype1 = ttype_binary; @@ -782,7 +781,7 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2, DecimalStatus decSt) } if (arg2->dsc_sub_type == isc_blob_text) - ttype2 = arg2->dsc_blob_ttype(); // Load blob character set and collation + ttype2 = arg2->getTextType(); // Load blob character set and collation else ttype2 = ttype_binary; @@ -870,7 +869,7 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2, DecimalStatus decSt) // The second parameter should be a string. if (arg2->dsc_dtype <= dtype_varying) { - if ((ttype2 = arg2->dsc_ttype()) != ttype_binary) + if ((ttype2 = arg2->getTextType()) != ttype_binary) ttype2 = ttype1; } else @@ -936,7 +935,7 @@ void CVT2_make_metaname(const dsc* desc, MetaName& name, DecimalStatus decSt) } -USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, MoveBuffer& temp, DecimalStatus decSt) +USHORT CVT2_make_string2(const dsc* desc, TTypeId to_interp, UCHAR** address, MoveBuffer& temp, DecimalStatus decSt) { /************************************** * @@ -952,7 +951,7 @@ USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, Mov **************************************/ UCHAR* from_buf; USHORT from_len; - USHORT from_interp; + TTypeId from_interp; fb_assert(desc != NULL); fb_assert(address != NULL); @@ -990,8 +989,8 @@ USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, Mov } thread_db* tdbb = JRD_get_thread_data(); - const USHORT cs1 = INTL_charset(tdbb, to_interp); - const USHORT cs2 = INTL_charset(tdbb, from_interp); + const auto cs1 = INTL_charset(tdbb, to_interp); + const auto cs2 = INTL_charset(tdbb, from_interp); if (cs1 == cs2) { *address = from_buf; diff --git a/src/jrd/cvt2_proto.h b/src/jrd/cvt2_proto.h index e97400f267a..2db1caf9385 100644 --- a/src/jrd/cvt2_proto.h +++ b/src/jrd/cvt2_proto.h @@ -25,13 +25,14 @@ #define JRD_CVT2_PROTO_H #include "../jrd/jrd.h" +#include "../jrd/intl.h" extern const BYTE CVT2_compare_priority[]; bool CVT2_get_binary_comparable_desc(dsc*, const dsc*, const dsc*); int CVT2_compare(const dsc*, const dsc*, Firebird::DecimalStatus); int CVT2_blob_compare(const dsc*, const dsc*, Firebird::DecimalStatus); +USHORT CVT2_make_string2(const dsc*, TTypeId, UCHAR**, Jrd::MoveBuffer&, Firebird::DecimalStatus); void CVT2_make_metaname(const dsc* desc, Jrd::MetaName& name, Firebird::DecimalStatus); -USHORT CVT2_make_string2(const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&, Firebird::DecimalStatus); #endif // JRD_CVT2_PROTO_H diff --git a/src/jrd/cvt_proto.h b/src/jrd/cvt_proto.h index 76eb08bde74..be85df5feb7 100644 --- a/src/jrd/cvt_proto.h +++ b/src/jrd/cvt_proto.h @@ -28,6 +28,11 @@ #include "../common/cvt.h" #include "../jrd/err_proto.h" +namespace Firebird +{ + class CharSet; +} + double CVT_date_to_double(const dsc*); void CVT_double_to_date(double, SLONG[2]); UCHAR CVT_get_numeric(const UCHAR*, const USHORT, SSHORT*, void*); @@ -53,11 +58,11 @@ namespace Jrd } public: - virtual bool transliterate(const dsc* from, dsc* to, CHARSET_ID&); - virtual CHARSET_ID getChid(const dsc* d); - virtual Firebird::CharSet* getToCharset(CHARSET_ID charset2); + virtual bool transliterate(const dsc* from, dsc* to, CSetId&); + virtual CSetId getChid(const dsc* d); + virtual Firebird::CharSet* getToCharset(CSetId charset2); virtual void validateData(Firebird::CharSet* toCharset, SLONG length, const UCHAR* q); - virtual ULONG validateLength(Firebird::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start, + virtual ULONG validateLength(Firebird::CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start, const USHORT size); virtual SLONG getLocalDate(); virtual ISC_TIMESTAMP getCurrentGmtTimeStamp(); @@ -76,7 +81,7 @@ namespace Jrd { } - virtual ULONG validateLength(Firebird::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start, + virtual ULONG validateLength(Firebird::CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start, const USHORT size); private: @@ -89,7 +94,7 @@ inline void CVT_move(const dsc* from, dsc* to, Firebird::DecimalStatus decSt) CVT_move_common(from, to, decSt, &Jrd::EngineCallbacks::instance); } -inline USHORT CVT_get_string_ptr(const dsc* desc, USHORT* ttype, UCHAR** address, +inline USHORT CVT_get_string_ptr(const dsc* desc, TTypeId* ttype, UCHAR** address, vary* temp, USHORT length, Firebird::DecimalStatus decSt) { return CVT_get_string_ptr_common(desc, ttype, address, temp, length, decSt, diff --git a/src/jrd/dflt.h b/src/jrd/dflt.h index 2e2181bcea6..c20323a7259 100644 --- a/src/jrd/dflt.h +++ b/src/jrd/dflt.h @@ -24,6 +24,8 @@ #ifndef JRD_DFLT_H #define JRD_DFLT_H +#include "firebird/impl/blr.h" + /* This file contains the blr for the default values for fields in system relations. The GDEF source for these fields is in DFLT.GDL in the JRD component. When modifying a system field default value, diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 1edc4ff4ee4..54927e232c2 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -54,8 +54,7 @@ * but when between RPB setup and actual modification garbage was collected (this * was noticed with GC thread active, but may happen due to any read of the record), * BUGCHECK(291) took place. To avoid that issue, it was decided not to modify data - * in system transaction. An exception is RDB$FORMATS relation, which is always modified - * by transaction zero. Also an aspect of 'dirty' access from system transaction was + * in system transaction. Also an aspect of 'dirty' access from system transaction was * taken into an account in make_version() and create_index(). * */ @@ -76,6 +75,7 @@ #include "../jrd/ods.h" #include "../jrd/btr.h" #include "../jrd/req.h" +#include "../jrd/Statement.h" #include "../jrd/exe.h" #include "../jrd/scl.h" #include "../jrd/blb.h" @@ -105,7 +105,7 @@ #include "../jrd/intl_proto.h" #include "../common/isc_f_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" @@ -128,6 +128,7 @@ #include "../jrd/CryptoManager.h" #include "../jrd/Mapping.h" #include "../jrd/shut_proto.h" +#include "../jrd/ProtectRelations.h" #ifdef HAVE_UNISTD_H #include @@ -352,94 +353,9 @@ public: DeferredJob() : work(NULL), end(&work) { } }; - -// Lock relation with protected_read level or raise existing relation lock -// to this level to ensure nobody can write to this relation. -// Used when new index is built. -// releaseLock set to true if there was no existing lock before -class ProtectRelations -{ -public: - ProtectRelations(thread_db* tdbb, jrd_tra* transaction) : - m_tdbb(tdbb), - m_transaction(transaction), - m_locks() - { - } - - ProtectRelations(thread_db* tdbb, jrd_tra* transaction, jrd_rel* relation) : - m_tdbb(tdbb), - m_transaction(transaction), - m_locks() - { - addRelation(relation); - lock(); - } - - ~ProtectRelations() - { - unlock(); - } - - void addRelation(jrd_rel* relation) - { - FB_SIZE_T pos; - if (!m_locks.find(relation->rel_id, pos)) - m_locks.insert(pos, relLock(relation)); - } - - bool exists(USHORT rel_id) const - { - FB_SIZE_T pos; - return m_locks.find(rel_id, pos); - } - - void lock() - { - relLock* item = m_locks.begin(); - const relLock* const end = m_locks.end(); - for (; item < end; item++) - item->takeLock(m_tdbb, m_transaction); - } - - void unlock() - { - relLock* item = m_locks.begin(); - const relLock* const end = m_locks.end(); - for (; item < end; item++) - item->releaseLock(m_tdbb, m_transaction); - } - -private: - struct relLock - { - relLock(jrd_rel* relation = NULL) : - m_relation(relation), - m_lock(NULL), - m_release(false) - { - } - - void takeLock(thread_db* tdbb, jrd_tra* transaction); - void releaseLock(thread_db* tdbb, jrd_tra* transaction); - - static const USHORT generate(const relLock& item) - { - return item.m_relation->rel_id; - } - - jrd_rel* m_relation; - Lock* m_lock; - bool m_release; - }; - - thread_db* m_tdbb; - jrd_tra* m_transaction; - SortedArray, USHORT, relLock> m_locks; -}; - } // namespace Jrd + /*================================================================== * * NOTE: @@ -452,12 +368,11 @@ private: static bool add_shadow(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_shadow(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool compute_security(thread_db*, SSHORT, DeferredWork*, jrd_tra*); -static bool modify_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool create_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool commit_relation(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool create_relation(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_relation(thread_db*, SSHORT, DeferredWork*, jrd_tra*); -static bool scan_relation(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool create_trigger(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_trigger(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool modify_trigger(thread_db*, SSHORT, DeferredWork*, jrd_tra*); @@ -472,7 +387,6 @@ static bool modify_field(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_global(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_parameter(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_rfr(thread_db*, SSHORT, DeferredWork*, jrd_tra*); -static bool make_version(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool add_difference(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_difference(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool begin_backup(thread_db*, SSHORT, DeferredWork*, jrd_tra*); @@ -492,28 +406,14 @@ static string remove_icu_info_from_attributes(const string&, const string&); // ---------------------------------------------------------------- -static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, - jrd_tra* transaction); static void check_computed_dependencies(thread_db* tdbb, jrd_tra* transaction, const MetaName& fieldName); -static void check_dependencies(thread_db*, const TEXT*, const TEXT*, const TEXT*, int, jrd_tra*); static void check_filename(const Firebird::string&, bool); -static void cleanup_index_creation(thread_db*, DeferredWork*, jrd_tra*); -static bool formatsAreEqual(const Format*, const Format*); static bool find_depend_in_dfw(thread_db*, TEXT*, USHORT, USHORT, jrd_tra*); -static void get_array_desc(thread_db*, const TEXT*, Ods::InternalArrayDesc*); static void get_trigger_dependencies(DeferredWork*, bool, jrd_tra*); -static Format* make_format(thread_db*, jrd_rel*, USHORT *, TemporaryField*); -static void put_summary_blob(thread_db* tdbb, blb*, enum rsr_t, bid*, jrd_tra*); -static void put_summary_record(thread_db* tdbb, blb*, enum rsr_t, const UCHAR*, ULONG); -static void setup_array(thread_db*, blb*, const TEXT*, USHORT, TemporaryField*); -static blb* setup_triggers(thread_db*, jrd_rel*, bool, TrigVector**, blb*); -static void setup_trigger_details(thread_db*, jrd_rel*, blb*, TrigVector**, const TEXT*, bool); -static bool validate_text_type (thread_db*, const TemporaryField*); - -static void check_partners(thread_db*, const USHORT); + static string get_string(const dsc* desc); -static void setupSpecificCollationAttributes(thread_db*, jrd_tra*, const USHORT, const char*, bool); +static void setupSpecificCollationAttributes(thread_db*, jrd_tra*, const CSetId, const char*, bool); static ISC_STATUS getErrorCodeByObjectType(int obj_type) { @@ -559,6 +459,25 @@ static ISC_STATUS getErrorCodeByObjectType(int obj_type) return err_code; } +static ISC_STATUS getErrorNotFound(int obj_type) +{ + ISC_STATUS err_code = 0; + + switch (obj_type) + { + case obj_procedure: + err_code = isc_dyn_proc_not_found; + break; + case obj_udf: + err_code = isc_dyn_func_not_found; + break; + default: + fb_assert(false); + } + + return err_code; +} + static void raiseDatabaseInUseError(bool timeout) { if (timeout) @@ -581,31 +500,15 @@ static void raiseObjectInUseError(const string& obj_type, const string& obj_name Arg::Gds(isc_obj_in_use) << Arg::Str(name)); } -static void raiseRelationInUseError(const jrd_rel* relation) +void DFW_raiseRelationInUseError(const Cached::Relation* relation) { const string obj_type = relation->isView() ? "VIEW" : "TABLE"; - const string obj_name = relation->rel_name.c_str(); + const string obj_name = relation->c_name(); raiseObjectInUseError(obj_type, obj_name); } -static void raiseRoutineInUseError(const Routine* routine, const QualifiedName& name) -{ - const string obj_type = - (routine->getObjectType() == obj_udf) ? "FUNCTION" : "PROCEDURE"; - const string obj_name = routine->getName().toString(); - - raiseObjectInUseError(obj_type, obj_name.hasData() ? obj_name : name.toString()); -} - -static void raiseTooManyVersionsError(const int obj_type, const string& obj_name) -{ - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(getErrorCodeByObjectType(obj_type)) << Arg::Str(obj_name) << - Arg::Gds(isc_version_err)); -} - void Jrd::ProtectRelations::relLock::takeLock(thread_db* tdbb, jrd_tra* transaction) { m_lock = RLCK_transaction_relation_lock(tdbb, transaction, m_relation); @@ -629,7 +532,7 @@ void Jrd::ProtectRelations::relLock::takeLock(thread_db* tdbb, jrd_tra* transact } if (inUse) - raiseRelationInUseError(m_relation); + DFW_raiseRelationInUseError(m_relation); } @@ -663,6 +566,13 @@ static const UCHAR nonnull_validation_blr[] = blr_eoc }; +static void DFW_check_dependencies(thread_db* tdbb, + const TEXT* dpdo_name, + const TEXT* field_name, + const TEXT* package_name, + int dpdo_type, + jrd_tra* transaction); + typedef bool (*dfw_task_routine) (thread_db*, SSHORT, DeferredWork*, jrd_tra*); struct deferred_task { @@ -670,12 +580,12 @@ struct deferred_task dfw_task_routine task_routine; }; + namespace { template class RoutineManager { @@ -685,27 +595,60 @@ namespace jrd_tra* transaction) { SET_TDBB(tdbb); + const bool compile = !work->findArg(dfw_arg_check_blr); + ObjectBase::Flag flag = compile ? CacheFlag::NOSCAN : 0; switch (phase) { + case 0: + { + T* routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN); + if (routine) + routine->rollback(tdbb); + + return false; + } + case 1: case 2: - case 3: - case 4: return true; - case 5: + case 3: { - 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); - + QualifiedName(work->dfw_name, work->dfw_package), flag); if (!routine) return false; - break; + return true; + } + + case 4: + { + T* routine = lookupById(tdbb, work->dfw_id, flag); + if (!routine) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(getErrorNotFound(routine->getObjectType())) << Arg::Num(work->dfw_id)); + } + + return true; + } + + case 5: + case 6: + return true; + + case 7: + { + T* routine = lookupById(tdbb, work->dfw_id, flag); + fb_assert(routine); + if (routine) + routine->commit(tdbb); + + return false; } } @@ -718,17 +661,18 @@ namespace { SET_TDBB(tdbb); const QualifiedName name(work->dfw_name, work->dfw_package); - Routine* routine; + T* routine; switch (phase) { case 0: - routine = lookupById(tdbb, work->dfw_id, false, true, 0); - if (!routine) - return false; - - if (routine->existenceLock) - LCK_convert(tdbb, routine->existenceLock, LCK_SR, transaction->getLockWait()); + routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN); + if (routine) + { + if (routine->existenceLock) + LCK_release(tdbb, routine->existenceLock); + routine->rollback(tdbb); + } return false; @@ -737,7 +681,7 @@ namespace return true; case 3: - routine = lookupById(tdbb, work->dfw_id, false, true, 0); + routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN); if (!routine) return false; @@ -745,63 +689,18 @@ namespace { // Let routine be deleted if only this transaction is using it - if (!LCK_convert(tdbb, routine->existenceLock, LCK_EX, - transaction->getLockWait())) - { - raiseRoutineInUseError(routine, name); - } + //if (!routine->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, false, true, 0); + routine = lookupById(tdbb, work->dfw_id, 0); if (!routine) return false; - // Do not allow to modify routine used by user requests - if (routine->isUsed() && MET_routine_in_use(tdbb, routine)) - { - ///raiseRoutineInUseError(routine, name); - gds__log("Modifying %s %s which is currently in use by active user requests", - Self::getTypeStr(), name.toString().c_str()); - - USHORT alterCount = routine->alterCount; - - if (alterCount > Routine::MAX_ALTER_COUNT) - raiseTooManyVersionsError(routine->getObjectType(), work->dfw_name); - - if (routine->existenceLock) - LCK_release(tdbb, routine->existenceLock); - - Self::clearId(tdbb->getAttachment(), routine->getId()); - - if (!(routine = lookupById(tdbb, work->dfw_id, false, - true, Routine::FLAG_BEING_ALTERED))) - { - return false; - } - - routine->alterCount = ++alterCount; - } - - routine->flags |= Routine::FLAG_BEING_ALTERED; - - if (routine->getStatement()) - { - if (routine->getStatement()->isActive()) - raiseRoutineInUseError(routine, name); - - // release the request - - routine->releaseStatement(tdbb); - } - // delete dependency lists if (work->dfw_package.isEmpty()) @@ -813,20 +712,15 @@ namespace 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->existenceLock) LCK_release(tdbb, routine->existenceLock); - // remove routine from cache - - routine->remove(tdbb); - // 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); + // routine->flags &= ~(Routine::FLAG_OBSOLETE | Routine::FLAG_BEING_ALTERED); return true; } @@ -836,14 +730,14 @@ namespace { SSHORT validBlr = FALSE; - Jrd::Attachment* attachment = tdbb->getAttachment(); - MemoryPool* newPool = attachment->createPool(); + Jrd::Database* dbb = tdbb->getDatabase(); + MemoryPool* newPool = dbb->createPool(ALLOC_ARGS0); try { Jrd::ContextPoolHolder context(tdbb, newPool); // compile the routine to know if the BLR is still valid - if (loadById(tdbb, work->dfw_id, false, 0)) + if (lookupById(tdbb, work->dfw_id, CacheFlag::AUTOCREATE)) validBlr = TRUE; } catch (const Firebird::Exception&) @@ -851,7 +745,7 @@ namespace fb_utils::init_status(tdbb->tdbb_status_vector); } - attachment->deletePool(newPool); + dbb->deletePool(newPool); Self::validate(tdbb, transaction, work, validBlr); } @@ -859,7 +753,17 @@ namespace case 6: Self::checkParamDependencies(tdbb, work, transaction); - break; + return true; + + case 7: + { + T* routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN); + fb_assert(routine); + if (routine) + routine->commit(tdbb); + + return false; + } } return false; @@ -876,38 +780,26 @@ namespace switch (phase) { case 0: - routine = lookupById(tdbb, work->dfw_id, false, true, 0); - if (!routine) - return false; - - if (routine->existenceLock) - LCK_convert(tdbb, routine->existenceLock, LCK_SR, transaction->getLockWait()); + routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN); + if (routine) + { + if (routine->existenceLock) + LCK_release(tdbb, routine->existenceLock); + routine->rollback(tdbb); + } return false; case 1: - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, work->dfw_package.c_str(), + DFW_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, false, true, 0); + routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN); if (!routine) return false; - if (routine->existenceLock) - { - if (!LCK_convert(tdbb, routine->existenceLock, LCK_EX, - transaction->getLockWait())) - { - 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 3: @@ -915,51 +807,38 @@ namespace case 4: { - routine = lookupById(tdbb, work->dfw_id, true, true, 0); + routine = lookupById(tdbb, work->dfw_id, CacheFlag::RET_ERASED | CacheFlag::NOSCAN); if (!routine) return false; - // Do not allow to drop routine used by user requests - if (routine->isUsed() && MET_routine_in_use(tdbb, routine)) - { - ///raiseRoutineInUseError(routine, name); - gds__log("Deleting %s %s which is currently in use by active user requests", - Self::getTypeStr(), name.toString().c_str()); - - if (work->dfw_package.isEmpty()) - MET_delete_dependencies(tdbb, work->dfw_name, objType, transaction); - - if (routine->existenceLock) - LCK_release(tdbb, routine->existenceLock); - - Self::clearId(tdbb->getAttachment(), routine->getId()); - return false; - } - - const USHORT old_flags = routine->flags; - routine->flags |= Routine::FLAG_OBSOLETE; - + /* if (routine->getStatement()) { - if (routine->getStatement()->isActive()) - { - routine->flags = old_flags; - raiseRoutineInUseError(routine, name); - } - routine->releaseStatement(tdbb); - } + }*/ // delete dependency lists if (work->dfw_package.isEmpty()) MET_delete_dependencies(tdbb, work->dfw_name, objType, transaction); - if (routine->existenceLock) - LCK_release(tdbb, routine->existenceLock); + //if (routine->existenceLock) + // routine->existenceLock->releaseLock(tdbb, ExistenceLock::ReleaseMethod::DropObject); - break; + return true; } + + case 5: + case 6: + return true; + + case 7: + routine = lookupById(tdbb, work->dfw_id, CacheFlag::RET_ERASED | CacheFlag::NOSCAN); + fb_assert(routine); + if (routine) + routine->commit(tdbb); + + return false; } // switch return false; @@ -971,7 +850,7 @@ namespace static void getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction) { thread_db* tdbb = JRD_get_thread_data(); - Jrd::Attachment* attachment = tdbb->getAttachment(); + Jrd::Database* dbb = tdbb->getDatabase(); if (compile) compile = !tdbb->getAttachment()->isGbak(); @@ -980,9 +859,7 @@ namespace blobId.clear(); Routine* routine = Self::lookupBlobId(tdbb, work, blobId, compile); -#ifdef DEV_BUILD - MET_verify_cache(tdbb); -#endif + MetadataCache::verify_cache(tdbb); // get any dependencies now by parsing the blr @@ -996,13 +873,12 @@ namespace { Statement* statement = NULL; // Nickolay Samofatov: allocate statement memory pool... - MemoryPool* new_pool = attachment->createPool(); - // block is used to ensure MET_verify_cache + MemoryPool* new_pool = dbb->createPool(ALLOC_ARGS0); + // block is used to ensure verify_cache() // works in not deleted context { Jrd::ContextPoolHolder context(tdbb, new_pool); - - MET_get_dependencies(tdbb, NULL, NULL, 0, NULL, &blobId, + MET_get_dependencies(tdbb, nullptr, NULL, 0, NULL, &blobId, (compile ? &statement : NULL), NULL, depName, (work->dfw_package.isEmpty() ? objType : obj_package_body), @@ -1011,12 +887,12 @@ namespace if (statement) statement->release(tdbb); else - attachment->deletePool(new_pool); + dbb->deletePool(new_pool); } } else { - Array dependencies; + Array dependencies; const auto allParameters = {&routine->getInputFields(), &routine->getOutputFields()}; @@ -1026,21 +902,22 @@ namespace { if (parameter->prm_type_of_table.hasData()) { - CompilerScratch::Dependency dependency(obj_relation); - dependency.relation = MET_lookup_relation(tdbb, parameter->prm_type_of_table); - dependency.subName = ¶meter->prm_type_of_column; + 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())) { - CompilerScratch::Dependency dependency(obj_field); - dependency.name = ¶meter->prm_field_source; + Dependency dependency(obj_field); + dependency.name = parameter->prm_field_source; dependencies.push(dependency); } if (parameter->prm_text_type.has_value()) { - CompilerScratch::Dependency dependency(obj_collation); + Dependency dependency(obj_collation); dependency.number = parameter->prm_text_type.value(); dependencies.push(dependency); } @@ -1052,14 +929,12 @@ namespace transaction); } -#ifdef DEV_BUILD - MET_verify_cache(tdbb); -#endif + MetadataCache::verify_cache(tdbb); } }; - class FunctionManager : public RoutineManager + class FunctionManager : public RoutineManager { public: static const char* const getTypeStr() @@ -1067,19 +942,14 @@ namespace return "function"; } - static void clearId(Jrd::Attachment* attachment, USHORT id) - { - attachment->att_functions[id] = NULL; - } - 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 checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); }; - class ProcedureManager : public RoutineManager + class ProcedureManager : public RoutineManager { public: static const char* const getTypeStr() @@ -1087,11 +957,6 @@ namespace return "procedure"; } - static void clearId(Jrd::Attachment* attachment, USHORT id) - { - attachment->att_procedures[id] = NULL; - } - 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); @@ -1105,7 +970,7 @@ namespace { Jrd::Attachment* attachment = tdbb->getAttachment(); AutoCacheRequest handle(tdbb, irq_c_fun_dpd, IRQ_REQUESTS); - Routine* routine = NULL; + Routine* routine = nullptr; FOR(REQUEST_HANDLE handle) X IN RDB$FUNCTIONS WITH @@ -1113,8 +978,8 @@ namespace 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); + routine = Function::lookup(tdbb, QualifiedName(work->dfw_name, work->dfw_package), + (compile ? 0 : CacheFlag::NOSCAN) | CacheFlag::AUTOCREATE); } END_FOR @@ -1187,9 +1052,9 @@ namespace Routine* ProcedureManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile) { - Jrd::Attachment* attachment = tdbb->getAttachment(); + Attachment* attachment = tdbb->getAttachment(); AutoCacheRequest handle(tdbb, irq_c_prc_dpd, IRQ_REQUESTS); - Routine* routine = NULL; + Routine* routine = nullptr; FOR(REQUEST_HANDLE handle) X IN RDB$PROCEDURES WITH @@ -1197,8 +1062,8 @@ namespace X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '') { blobId = X.RDB$PROCEDURE_BLR; - routine = MET_lookup_procedure(tdbb, - QualifiedName(work->dfw_name, work->dfw_package), !compile); + routine = MetadataCache::lookup_procedure(tdbb, + QualifiedName(work->dfw_name, work->dfw_package), compile ? 0 : CacheFlag::NOSCAN); } END_FOR @@ -1269,12 +1134,14 @@ namespace } } // namespace + + static const deferred_task task_table[] = { + { dfw_add_shadow, add_shadow }, - { dfw_delete_index, modify_index }, + { dfw_delete_index, delete_index }, { dfw_delete_rfr, delete_rfr }, - { dfw_delete_relation, delete_relation }, { dfw_delete_shadow, delete_shadow }, { dfw_delete_shadow_nodelete, delete_shadow }, { dfw_create_field, create_field }, @@ -1282,18 +1149,18 @@ static const deferred_task task_table[] = { dfw_modify_field, modify_field }, { dfw_delete_global, delete_global }, { dfw_create_relation, create_relation }, - { dfw_update_format, make_version }, - { dfw_scan_relation, scan_relation }, + { dfw_delete_relation, delete_relation }, { dfw_compute_security, compute_security }, - { dfw_create_index, modify_index }, - { dfw_create_expression_index, modify_index }, + { dfw_create_index, create_index }, { dfw_grant, grant_privileges }, { dfw_create_trigger, create_trigger }, - { dfw_delete_trigger, delete_trigger }, { dfw_modify_trigger, modify_trigger }, + { dfw_delete_trigger, delete_trigger }, +/* { 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 }, @@ -1312,57 +1179,158 @@ static const deferred_task task_table[] = { dfw_end_backup, end_backup }, { dfw_user_management, user_management }, { dfw_check_not_null, check_not_null }, - { dfw_store_view_context_type, store_view_context_type }, +// { dfw_store_view_context_type, store_view_context_type }, { dfw_db_crypt, db_crypt }, { dfw_set_linger, set_linger }, { dfw_clear_cache, clear_cache }, { dfw_change_repl_state, change_repl_state }, + { dfw_commit_relation, commit_relation }, { dfw_null, NULL } }; -USHORT DFW_assign_index_type(thread_db* tdbb, const MetaName& name, SSHORT field_type, - SSHORT ttype) +static void DFW_check_dependencies(thread_db* tdbb, + const TEXT* dpdo_name, + const TEXT* field_name, + const TEXT* package_name, + int dpdo_type, + jrd_tra* transaction) { /************************************** * - * D F W _ a s s i g n _ i n d e x _ t y p e + * c h e c k _ d e p e n d e n c i e s * ************************************** * * Functional description - * Define the index segment type based - * on the field's type and subtype. + * Check the dependency list for relation or relation.field + * before deleting such. * **************************************/ SET_TDBB(tdbb); + Jrd::Attachment* attachment = tdbb->getAttachment(); - if (field_type == dtype_varying || field_type == dtype_cstring || field_type == dtype_text) - { - switch (ttype) - { - case ttype_none: - return idx_string; - case ttype_binary: - return idx_byte_array; - case ttype_metadata: - return idx_metadata; - case ttype_ascii: - return idx_string; - } + const MetaName packageName(package_name); - // Dynamic text cannot occur here as this is for an on-disk - // index, which must be bound to a text type. + SLONG dep_counts[obj_type_MAX]; + for (int i = 0; i < obj_type_MAX; i++) + dep_counts[i] = 0; - fb_assert(ttype != ttype_dynamic); + if (field_name) + { + AutoCacheRequest request(tdbb, irq_ch_f_dpd, IRQ_REQUESTS); - if (INTL_defined_type(tdbb, ttype)) - return INTL_TEXT_TO_INDEX(ttype); + FOR(REQUEST_HANDLE request) + DEP IN RDB$DEPENDENCIES + WITH DEP.RDB$DEPENDED_ON_NAME EQ dpdo_name + AND DEP.RDB$DEPENDED_ON_TYPE = dpdo_type + AND DEP.RDB$FIELD_NAME EQ field_name + AND DEP.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') + REDUCED TO DEP.RDB$DEPENDENT_NAME + { + // If the found object is also being deleted, there's no dependency - ERR_post_nothrow(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_random) << Arg::Str(name)); - INTL_texttype_lookup(tdbb, ttype); // should punt - ERR_punt(); // if INTL_texttype_lookup hasn't punt + if (!find_depend_in_dfw(tdbb, DEP.RDB$DEPENDENT_NAME, DEP.RDB$DEPENDENT_TYPE, + 0, transaction)) + { + ++dep_counts[DEP.RDB$DEPENDENT_TYPE]; + } + } + END_FOR + } + else + { + AutoCacheRequest request(tdbb, irq_ch_dpd, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + DEP IN RDB$DEPENDENCIES + WITH DEP.RDB$DEPENDED_ON_NAME EQ dpdo_name + AND DEP.RDB$DEPENDED_ON_TYPE = dpdo_type + AND DEP.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') + REDUCED TO DEP.RDB$DEPENDENT_NAME + { + // 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)) + { + ++dep_counts[DEP.RDB$DEPENDENT_TYPE]; + } + } + END_FOR + } + + SLONG total = 0; + for (int i = 0; i < obj_type_MAX; i++) + total += dep_counts[i]; + + if (!total) + return; + + if (field_name) + { + string fld_name(dpdo_name); + fld_name.append("."); + fld_name.append(field_name); + + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_no_delete) << // Msg353: can not delete + Arg::Gds(isc_field_name) << Arg::Str(fld_name) << + Arg::Gds(isc_dependency) << Arg::Num(total)); // Msg310: there are %ld dependencies + } + else + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_no_delete) << // can not delete + Arg::Gds(getErrorCodeByObjectType(dpdo_type)) << + Arg::Str(QualifiedName(dpdo_name, packageName).toString()) << + Arg::Gds(isc_dependency) << Arg::Num(total)); // there are %ld dependencies + } +} + + +USHORT DFW_assign_index_type(thread_db* tdbb, const MetaName& name, SSHORT field_type, + TTypeId ttype) +{ +/************************************** + * + * D F W _ a s s i g n _ i n d e x _ t y p e + * + ************************************** + * + * Functional description + * Define the index segment type based + * on the field's type and subtype. + * + **************************************/ + SET_TDBB(tdbb); + + if (field_type == dtype_varying || field_type == dtype_cstring || field_type == dtype_text) + { + switch (ttype) + { + case ttype_none: + return idx_string; + case ttype_binary: + return idx_byte_array; + case ttype_metadata: + return idx_metadata; + case ttype_ascii: + return idx_string; + } + + // Dynamic text cannot occur here as this is for an on-disk + // index, which must be bound to a text type. + + fb_assert(ttype != ttype_dynamic); + + if (INTL_defined_type(tdbb, ttype)) + return INTL_TEXT_TO_INDEX(ttype); + + ERR_post_nothrow(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_random) << Arg::Str(name)); + INTL_texttype_lookup(tdbb, ttype); // should punt + ERR_punt(); // if INTL_texttype_lookup hasn't punt } switch (field_type) @@ -1542,7 +1510,9 @@ void DFW_perform_work(thread_db* tdbb, jrd_tra* transaction) SET_TDBB(tdbb); - tdbb->getAttachment()->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb); + auto* dsql = tdbb->getAttachment()->att_dsql_instance; + if (dsql && dsql->dbb_statement_cache) + dsql->dbb_statement_cache->purgeAllAttachments(tdbb); Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool); @@ -1974,6 +1944,36 @@ static bool add_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr return false; } +static bool delete_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) +{ +/************************************** + * + * d e l e t e _ s h a d o w + * + ************************************** + * + * Functional description + * Provide deferred work interface to + * MET_delete_shadow. + * + **************************************/ + + SET_TDBB(tdbb); + + switch (phase) + { + case 1: + case 2: + return true; + + case 3: + MET_delete_shadow(tdbb, work->dfw_id); + break; + } + + return false; +} + static bool add_difference(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) { /************************************** @@ -2170,20 +2170,22 @@ static bool set_linger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr return false; } -static bool clear_cache(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) +static bool set_generator(thread_db* tdbb, + SSHORT phase, + DeferredWork* work, + jrd_tra* transaction) { /************************************** * - * c l e a r _ c a c h e + * s e t _ g e n e r a t o r * ************************************** * - * Clear security names mapping cache + * Functional description + * Set the generator to the given value. * **************************************/ - SET_TDBB(tdbb); - Database* const dbb = tdbb->getDatabase(); switch (phase) { @@ -2192,28 +2194,68 @@ static bool clear_cache(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_t return true; case 3: - Mapping::clearCache(dbb->dbb_filename.c_str(), work->dfw_id); + { + const SLONG id = MET_lookup_generator(tdbb, work->dfw_name); + if (id >= 0) + { + fb_assert(id == work->dfw_id); + SINT64 value = 0; + if (transaction->getGenIdCache()->get(id, value)) + { + transaction->getGenIdCache()->remove(id); + DPM_gen_id(tdbb, id, true, value); + } + } +#ifdef DEV_BUILD + else // This is a test only + status_exception::raise(Arg::Gds(isc_cant_modify_sysobj) << "generator" << work->dfw_name); +#endif + } break; } return false; } -static bool check_not_null(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool delete_generator(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * c h e c k _ n o t _ n u l l + * d e l e t e _ g e n e r a t o r * ************************************** * - * Scan relation to detect NULLs in fields being changed to NOT NULL. + * Functional description + * Check if it is allowable to delete + * a generator, and if so, clean up after it. + * CVC: This function was modelled after delete_exception. * **************************************/ SET_TDBB(tdbb); + const char* gen_name = work->dfw_name.c_str(); - Jrd::Attachment* attachment = tdbb->getAttachment(); + switch (phase) + { + case 1: + DFW_check_dependencies(tdbb, gen_name, NULL, NULL, obj_generator, transaction); + break; + } + + return false; +} + +static bool user_management(thread_db* /*tdbb*/, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ +/************************************** + * + * u s e r _ m a n a g e m e n t + * + ************************************** + * + * Commit in security database + * + **************************************/ switch (phase) { @@ -2222,204 +2264,54 @@ static bool check_not_null(thread_db* tdbb, SSHORT phase, DeferredWork* work, jr return true; case 3: - { - jrd_rel* relation = MET_lookup_relation(tdbb, work->dfw_name); - if (!relation || relation->rel_view_rse || work->dfw_ids.isEmpty()) - break; - - // Protect relation from modification - ProtectRelations protectRelation(tdbb, transaction, relation); + transaction->getUserManagement()->execute(work->dfw_id); + return true; - SortedArray fields; - AutoRequest handle; + case 4: + case 5: + return true; - for (SortedArray::iterator itr(work->dfw_ids.begin()); - itr != work->dfw_ids.end(); - ++itr) - { - FOR(REQUEST_HANDLE handle) - RFL IN RDB$RELATION_FIELDS CROSS - FLD IN RDB$FIELDS - WITH RFL.RDB$RELATION_NAME EQ work->dfw_name.c_str() AND - FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE AND - RFL.RDB$FIELD_ID EQ *itr AND - (RFL.RDB$NULL_FLAG = TRUE OR FLD.RDB$NULL_FLAG = TRUE) - { - fields.add(RFL.RDB$FIELD_ID); - } - END_FOR - } + case 6: + transaction->getUserManagement()->commit(); // safe to be called multiple times + break; + } - if (fields.hasData()) - { - UCharBuffer blr; + return false; +} - blr.add(blr_version5); - blr.add(blr_begin); - blr.add(blr_message); - blr.add(1); // message number - blr.add(fields.getCount() & 0xFF); - blr.add(fields.getCount() >> 8); +static bool change_repl_state(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) +{ +/************************************** + * + * c h a n g e _ r e p l _ s t a t e + * + ************************************** + * + * Functional description + * Signal other processes to refresh their replication state. + * + **************************************/ + SET_TDBB(tdbb); + Database* const dbb = tdbb->getDatabase(); + Attachment* const attachment = tdbb->getAttachment(); - for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) - { - blr.add(blr_short); - blr.add(0); - } + switch (phase) + { + case 1: + case 2: + case 3: + return true; - blr.add(blr_for); - blr.add(blr_stall); - blr.add(blr_rse); - blr.add(1); - blr.add(blr_rid); - blr.add(relation->rel_id & 0xFF); - blr.add(relation->rel_id >> 8); - blr.add(0); // stream - blr.add(blr_boolean); - - for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) - { - if (i != fields.getCount() - 1) - blr.add(blr_or); - - blr.add(blr_missing); - blr.add(blr_fid); - blr.add(0); // stream - blr.add(USHORT(fields[i]) & 0xFF); - blr.add(USHORT(fields[i]) >> 8); - } - - blr.add(blr_end); - - blr.add(blr_send); - blr.add(1); - blr.add(blr_begin); - - for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) - { - blr.add(blr_assignment); - - blr.add(blr_value_if); - blr.add(blr_missing); - blr.add(blr_fid); - blr.add(0); // stream - blr.add(USHORT(fields[i]) & 0xFF); - blr.add(USHORT(fields[i]) >> 8); - - blr.add(blr_literal); - blr.add(blr_short); - blr.add(0); - blr.add(1); - blr.add(0); - - blr.add(blr_literal); - blr.add(blr_short); - blr.add(0); - blr.add(0); - blr.add(0); - - blr.add(blr_parameter); - blr.add(1); // message number - blr.add(i & 0xFF); - blr.add(i >> 8); - } - - blr.add(blr_end); - - blr.add(blr_send); - blr.add(1); - blr.add(blr_begin); - - for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) - { - blr.add(blr_assignment); - blr.add(blr_literal); - blr.add(blr_short); - blr.add(0); - blr.add(0); - blr.add(0); - blr.add(blr_parameter); - blr.add(1); // message number - blr.add(i & 0xFF); - blr.add(i >> 8); - } - - blr.add(blr_end); - blr.add(blr_end); - blr.add(blr_eoc); - - AutoRequest request; - request.compile(tdbb, blr.begin(), blr.getCount()); - - HalfStaticArray hasRecord; - - EXE_start(tdbb, request, transaction); - EXE_receive(tdbb, request, 1, fields.getCount() * sizeof(USHORT), - (UCHAR*) hasRecord.getBuffer(fields.getCount())); - - Arg::Gds errs(isc_no_meta_update); - bool hasError = false; - - for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) - { - if (hasRecord[i]) - { - hasError = true; - errs << Arg::Gds(isc_cannot_make_not_null) << - (*relation->rel_fields)[fields[i]]->fld_name << - relation->rel_name; - } - } - - if (hasError) - ERR_post(errs); - } + case 4: + if (work->dfw_id == 0) + { + // replication state is changed + dbb->invalidateReplState(tdbb, true); } - - break; - } - - return false; -} - - -// Store RDB$CONTEXT_TYPE in RDB$VIEW_RELATIONS when restoring legacy backup. -static bool store_view_context_type(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ - SET_TDBB(tdbb); - - switch (phase) - { - case 1: + else { - // If RDB$PACKAGE_NAME IS NOT NULL or no record is found in RDB$RELATIONS, - // the context is a procedure; - ViewContextType vct = VCT_PROCEDURE; - - AutoRequest handle1; - FOR (REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction) - VRL IN RDB$VIEW_RELATIONS CROSS - REL IN RDB$RELATIONS OVER RDB$RELATION_NAME WITH - VRL.RDB$VIEW_NAME = work->dfw_name.c_str() AND - VRL.RDB$VIEW_CONTEXT = work->dfw_id AND - VRL.RDB$PACKAGE_NAME MISSING - { - vct = (REL.RDB$VIEW_BLR.NULL ? VCT_TABLE : VCT_VIEW); - } - END_FOR - - AutoRequest handle2; - FOR (REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) - VRL IN RDB$VIEW_RELATIONS WITH - VRL.RDB$VIEW_NAME = work->dfw_name.c_str() AND - VRL.RDB$VIEW_CONTEXT = work->dfw_id - { - MODIFY VRL USING - VRL.RDB$CONTEXT_TYPE.NULL = FALSE; - VRL.RDB$CONTEXT_TYPE = (SSHORT) vct; - END_MODIFY - } - END_FOR + // replication set is changed + attachment->invalidateReplSet(tdbb, true); } break; } @@ -2427,320 +2319,374 @@ static bool store_view_context_type(thread_db* tdbb, SSHORT phase, DeferredWork* return false; } - -static bool user_management(thread_db* /*tdbb*/, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static void check_filename(const Firebird::string& name, bool shareExpand) { /************************************** * - * u s e r _ m a n a g e m e n t + * c h e c k _ f i l e n a m e * ************************************** * - * Commit in security database + * Functional description + * Make sure that a file path doesn't contain an + * inet node name. * **************************************/ + const Firebird::PathName file_name(name.ToPathName()); + const bool valid = file_name.find("::") == Firebird::PathName::npos; - switch (phase) - { - case 1: - case 2: - return true; - - case 3: - transaction->getUserManagement()->execute(work->dfw_id); - return true; - - case 4: - transaction->getUserManagement()->commit(); // safe to be called multiple times - break; + if (!valid || ISC_check_if_remote(file_name, shareExpand)) { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_node_name_err)); + // Msg305: A node name is not permitted in a secondary, shadow, or log file name } - return false; + if (!JRD_verify_database_access(file_name)) { + ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") << + Arg::Str(name)); + } } -// Drop dependencies of a package header. -static bool drop_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +//#define DEBUG_REBUILD_INTL + +namespace DbgRebuildIntl { + +#ifdef DEBUG_REBUILD_INTL +static int ind = 0; +#endif + +void out(const char* format, ...) { - SET_TDBB(tdbb); +#ifdef DEBUG_REBUILD_INTL + for (int n = 0; n < ind; ++n) + putc(' ', stderr); - switch (phase) + va_list params; + va_start(params, format); + vfprintf(stderr, format, params); + va_end(params); +#endif +} + +class Lvl +{ +public: + Lvl() { - case 1: - MET_delete_dependencies(tdbb, work->dfw_name, obj_package_body, transaction); - MET_delete_dependencies(tdbb, work->dfw_name, obj_package_header, transaction); - break; +#ifdef DEBUG_REBUILD_INTL + ind += 2; +#endif } - return false; -} + ~Lvl() + { +#ifdef DEBUG_REBUILD_INTL + ind -= 2; +#endif + } +}; + +} // namespace DbgRebuildIntl -// Drop dependencies of a package header. -static bool modify_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static string remove_icu_info_from_attributes(const string& charsetName, const string& specificAttributes) { - SET_TDBB(tdbb); + DbgRebuildIntl::Lvl debLevel; - switch (phase) + Firebird::AutoPtr cs(FB_NEW charset); + memset(cs, 0, sizeof(*cs)); + + if (IntlManager::lookupCharSet(charsetName, cs)) { - case 1: - MET_delete_dependencies(tdbb, work->dfw_name, obj_package_header, transaction); - break; + DbgRebuildIntl::out("Remove_icu_info_from_attributes for charset '%s'\n", charsetName.c_str()); + + AutoPtr charSet(Firebird::CharSet::createInstance(*getDefaultMemoryPool(), 0, cs)); + IntlUtil::SpecificAttributesMap map; + if (IntlUtil::parseSpecificAttributes(charSet, specificAttributes.length(), + (const UCHAR*) specificAttributes.begin(), &map)) + { + map.remove(ATTR_ICU_VERSION); + map.remove(ATTR_COLL_VERSION); + return IntlUtil::generateSpecificAttributes(charSet, map); + } } + else + DbgRebuildIntl::out("Remove_icu_info_from_attributes - NO CHARSET '%s'\n", charsetName.c_str()); - return false; + return specificAttributes; } -// Drop dependencies of a package body. -static bool drop_package_body(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +bool DFW_setupCollationAttributes(const string& collationName, const string& charSetName, + const string& oldSpecificAttributes, string& newSpecificAttributes, bool dropIcuInfo) { - SET_TDBB(tdbb); + string oldCleanedAttributes = dropIcuInfo ? + remove_icu_info_from_attributes(charSetName.c_str(), oldSpecificAttributes) : oldSpecificAttributes; - switch (phase) - { - case 1: - MET_delete_dependencies(tdbb, work->dfw_name, obj_package_body, transaction); - break; - } + DbgRebuildIntl::out("dropIcuInfo %d oldCleanedAttributes %s\n", dropIcuInfo, oldCleanedAttributes.c_str()); - return false; + return IntlManager::setupCollationAttributes(collationName, charSetName, oldCleanedAttributes, newSpecificAttributes); } -static bool grant_privileges(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static void setupSpecificCollationAttributes(thread_db* tdbb, jrd_tra* transaction, + const CSetId charSetId, const char* collationName, bool dropIcuInfo) { /************************************** * - * g r a n t _ p r i v i l e g e s + * setupSpecificCollationAttributes * ************************************** * * Functional description - * Compute access control list from SQL privileges. * **************************************/ - switch (phase) - { - case 1: - return true; - - case 2: - GRANT_privileges(tdbb, work->dfw_name, work->dfw_id, transaction); - break; + DbgRebuildIntl::Lvl debLevel; - default: - break; - } + SET_TDBB(tdbb); + Jrd::Attachment* const attachment = tdbb->getAttachment(); + AutoCacheRequest handle(tdbb, drq_m_coll_attrs, DYN_REQUESTS); - return false; -} + DbgRebuildIntl::out("setupSpecificCollationAttributes: dropIcuInfo=%d\n", dropIcuInfo); + FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) + COLL IN RDB$COLLATIONS + CROSS CS IN RDB$CHARACTER_SETS + OVER RDB$CHARACTER_SET_ID + WITH COLL.RDB$COLLATION_NAME EQ collationName AND + COLL.RDB$CHARACTER_SET_ID EQ charSetId + { + SLONG length = 0; + HalfStaticArray buffer; -static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, - jrd_tra* transaction) + if (!COLL.RDB$SPECIFIC_ATTRIBUTES.NULL) + { + blb* blob = blb::open(tdbb, transaction, &COLL.RDB$SPECIFIC_ATTRIBUTES); + length = blob->blb_length + 10; + length = blob->BLB_get_data(tdbb, buffer.getBuffer(length), length); + } + + const string specificAttributes((const char*) buffer.begin(), length); + DbgRebuildIntl::out("Try collation %s with %s\n", collationName, specificAttributes.c_str()); + + // ASF: If setupCollationAttributes fail we store the original + // attributes. This should be what we want for new databases + // and restores. + string newSpecificAttributes; + if (DFW_setupCollationAttributes( + fb_utils::exact_name(COLL.RDB$BASE_COLLATION_NAME.NULL ? + COLL.RDB$COLLATION_NAME : COLL.RDB$BASE_COLLATION_NAME), + fb_utils::exact_name(CS.RDB$CHARACTER_SET_NAME), + specificAttributes, newSpecificAttributes, dropIcuInfo) && + newSpecificAttributes != specificAttributes) // if nothing changed, we do nothing + { + DbgRebuildIntl::out(" Recreate collation %s\n", collationName); + + MODIFY COLL USING + if (newSpecificAttributes.isEmpty()) + COLL.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE; + else + { + COLL.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, + &COLL.RDB$SPECIFIC_ATTRIBUTES, newSpecificAttributes); + } + END_MODIFY + } + else if (newSpecificAttributes == specificAttributes) + DbgRebuildIntl::out(" nothing changed\n"); + else + DbgRebuildIntl::out(" setupCollationAttributes() failed\n"); + } + END_FOR +} + + +void DFW_reset_icu(thread_db* tdbb) { /************************************** * - * c r e a t e _ e x p r e s s i o n _ i n d e x + * r e s e t I c u * ************************************** * * Functional description - * Create a new expression index. + * Fix database for use with other ICU version. + * Formally has nothing to do with DFW, + * but adding new .epp module is worse. + * Next, it's using some DFW code. * **************************************/ - switch (phase) - { - case 0: - cleanup_index_creation(tdbb, work, transaction); - MET_delete_dependencies(tdbb, work->dfw_name, obj_index_expression, transaction); - MET_delete_dependencies(tdbb, work->dfw_name, obj_index_condition, transaction); - return false; - - case 1: - case 2: - return true; + SET_TDBB(tdbb); - case 3: - { - jrd_rel* relation = nullptr; - CompilerScratch* csb = nullptr; + jrd_tra* transaction = NULL; + jrd_tra* oldTransaction = tdbb->getTransaction(); - const auto dbb = tdbb->getDatabase(); - const auto attachment = tdbb->getAttachment(); + try + { + Attachment* attachment = tdbb->getAttachment(); + transaction = TRA_start(tdbb, 0, 0); + tdbb->setTransaction(transaction); - index_desc idx; - MOVE_CLEAR(&idx, sizeof(index_desc)); + SortedArray indices; + ProtectRelations tables(tdbb, transaction); - AutoCacheRequest request(tdbb, irq_c_exp_index, IRQ_REQUESTS); + // Get list of affected indices & tables + const char* indSql = + "select ind.RDB$INDEX_NAME, rel.RDB$RELATION_ID," + " coalesce(coll.RDB$BASE_COLLATION_NAME, coll.RDB$COLLATION_NAME), cs.RDB$CHARACTER_SET_NAME, " + " coll.RDB$SPECIFIC_ATTRIBUTES " + "from RDB$INDICES ind " + "join RDB$RELATIONS rel on ind.RDB$RELATION_NAME = rel.RDB$RELATION_NAME " + "join RDB$INDEX_SEGMENTS seg on ind.RDB$INDEX_NAME = seg.RDB$INDEX_NAME " + "join RDB$RELATION_FIELDS rfl on rfl.RDB$RELATION_NAME = ind.RDB$RELATION_NAME " + " and rfl.RDB$FIELD_NAME = seg.RDB$FIELD_NAME " + "join RDB$FIELDS fld on rfl.RDB$FIELD_SOURCE = fld.RDB$FIELD_NAME " + "join RDB$COLLATIONS coll on fld.RDB$CHARACTER_SET_ID = coll.RDB$CHARACTER_SET_ID " + " and coalesce(rfl.RDB$COLLATION_ID, fld.RDB$COLLATION_ID) = coll.RDB$COLLATION_ID " + "join RDB$CHARACTER_SETS cs on coll.RDB$CHARACTER_SET_ID = cs.RDB$CHARACTER_SET_ID " + "where coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%' " + " and coalesce(ind.RDB$INDEX_INACTIVE, 0) = 0 " + " and coalesce(rel.RDB$RELATION_TYPE, 0) = 0 " // rel_persistent + "group by ind.RDB$INDEX_NAME, rel.RDB$RELATION_ID, coll.RDB$BASE_COLLATION_NAME, " + " coll.RDB$COLLATION_NAME, cs.RDB$CHARACTER_SET_NAME, coll.RDB$SPECIFIC_ATTRIBUTES"; - FOR(REQUEST_HANDLE request) - IDX IN RDB$INDICES CROSS - REL IN RDB$RELATIONS OVER RDB$RELATION_NAME WITH - IDX.RDB$EXPRESSION_BLR NOT MISSING AND - IDX.RDB$INDEX_NAME EQ work->dfw_name.c_str() + { // scope + AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, indSql)); + AutoResultSet rs(ps->executeQuery(tdbb, transaction)); + while(rs->fetch(tdbb)) { - if (!relation) - { - relation = MET_relation(tdbb, REL.RDB$RELATION_ID); - if (relation->rel_name.length() == 0) - relation->rel_name = REL.RDB$RELATION_NAME; - - if (IDX.RDB$INDEX_ID && IDX.RDB$STATISTICS < 0.0) - { - SelectivityList selectivity(*tdbb->getDefaultPool()); - const USHORT localId = IDX.RDB$INDEX_ID - 1; - IDX_statistics(tdbb, relation, localId, selectivity); - DFW_update_index(work->dfw_name.c_str(), localId, selectivity, transaction); - - return false; - } - - if (IDX.RDB$INDEX_ID) - { - IDX_delete_index(tdbb, relation, IDX.RDB$INDEX_ID - 1); - MET_delete_dependencies(tdbb, work->dfw_name, obj_index_expression, transaction); - MET_delete_dependencies(tdbb, work->dfw_name, obj_index_condition, transaction); - MODIFY IDX - IDX.RDB$INDEX_ID.NULL = TRUE; - END_MODIFY - } - - if (IDX.RDB$INDEX_INACTIVE) - return false; - - if (IDX.RDB$SEGMENT_COUNT) - { - // Msg359: segments not allowed in expression index %s - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_no_segments_err) << Arg::Str(work->dfw_name)); - } - if (IDX.RDB$UNIQUE_FLAG) - idx.idx_flags |= idx_unique; - if (IDX.RDB$INDEX_TYPE == 1) - idx.idx_flags |= idx_descending; - - MET_scan_relation(tdbb, relation); - - // Allocate a new pool to contain the expression tree - // for index expression - const auto new_pool = attachment->createPool(); - - try - { - Jrd::ContextPoolHolder context(tdbb, new_pool); - - MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$EXPRESSION_BLR, - nullptr, &csb, work->dfw_name, obj_index_expression, 0, - transaction); - - idx.idx_expression_statement = Statement::makeValueExpression(tdbb, - idx.idx_expression, idx.idx_expression_desc, csb, false); + MetaName t(rs->getMetaName(tdbb, 1)); - // fake a description of the index + const MetaName collationName(rs->getMetaName(tdbb, 3)); + const MetaName charsetName(rs->getMetaName(tdbb, 4)); + const string specificAttributes(rs->getString(tdbb, 5)); + string icuLessAttributes = remove_icu_info_from_attributes(charsetName.c_str(), specificAttributes); + DbgRebuildIntl::out("check index %s SA:'%s' ILA:'%s'\n", + t.c_str(), specificAttributes.c_str(), icuLessAttributes.c_str()); + string newSpecificAttributes; + if (!IntlManager::setupCollationAttributes(collationName.c_str(), + charsetName.c_str(), icuLessAttributes, newSpecificAttributes)) + { + DbgRebuildIntl::out("setupCollationAttributes failed for %s\n", collationName.c_str()); + continue; + } + DbgRebuildIntl::out("newSpecificAttributes '%s'\n", newSpecificAttributes.c_str()); + if (newSpecificAttributes == specificAttributes) + continue; - idx.idx_count = 1; - idx.idx_flags |= idx_expression; - idx.idx_rpt[0].idx_itype = - DFW_assign_index_type(tdbb, work->dfw_name, - idx.idx_expression_desc.dsc_dtype, - idx.idx_expression_desc.dsc_sub_type); - idx.idx_rpt[0].idx_selectivity = 0; - } - catch (const Exception&) - { - attachment->deletePool(new_pool); - throw; - } + DbgRebuildIntl::out("Add index\n"); + if (!indices.exist(t)) + indices.add(rs->getMetaName(tdbb, 1)); - if (!IDX.RDB$CONDITION_BLR.NULL) - { - // Allocate a new pool to contain the expression tree - // for index condition - const auto new_pool = attachment->createPool(); + USHORT rel_id = rs->getInt(tdbb, 2); + if (!tables.exists(rel_id)) + { + auto* relation = MetadataCache::lookupRelation(tdbb, rel_id, CacheFlag::AUTOCREATE); + if (relation) + tables.addRelation(relation); + } + } + } - try - { - Jrd::ContextPoolHolder context(tdbb, new_pool); + DbgRebuildIntl::out("locking tables\n"); - MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$CONDITION_BLR, - nullptr, &csb, work->dfw_name, obj_index_condition, 0, - transaction); + // Lock the tables + tables.lock(); - idx.idx_condition_statement = Statement::makeBoolExpression(tdbb, - idx.idx_condition, csb, false); + // Change collation's attributes + const char* collSql = + "select coll.RDB$COLLATION_NAME, coll.RDB$CHARACTER_SET_ID from RDB$COLLATIONS coll " + "where coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%'"; + { // scope + AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, collSql)); + AutoResultSet rs(ps->executeQuery(tdbb, transaction)); + while(rs->fetch(tdbb)) + { + MetaName collName(rs->getMetaName(tdbb, 1)); + const CSetId charSetId(rs->getSmallInt(tdbb, 2)); - idx.idx_flags |= idx_condition; - } - catch (const Exception&) - { - attachment->deletePool(new_pool); - throw; - } - } - } + setupSpecificCollationAttributes(tdbb, transaction, charSetId, collName.c_str(), true); } - END_FOR + } - if (!relation) + // Reactivate indices + { // scope + for (MetaName* idx = indices.begin(); idx != indices.end(); ++idx) { - // Msg308: can't create index %s - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_idx_create_err) << Arg::Str(work->dfw_name)); - } - - delete csb; - - // Actually create the index + AutoRequest request; - // Protect relation from modification to create consistent index - ProtectRelations protectRelation(tdbb, transaction, relation); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES + WITH IDX.RDB$INDEX_NAME EQ idx->c_str() + { + MODIFY IDX + DbgRebuildIntl::out("Re-activate index %s\n", idx->c_str()); + IDX.RDB$INDEX_INACTIVE.NULL = FALSE; + IDX.RDB$INDEX_INACTIVE = FALSE; + END_MODIFY + } + END_FOR + } + } - SelectivityList selectivity(*tdbb->getDefaultPool()); + // Commit + TRA_commit(tdbb, transaction, false); + transaction = NULL; + tdbb->setTransaction(oldTransaction); + } + catch (const Firebird::Exception&) + { + if (transaction) + { + TRA_rollback(tdbb, transaction, false, true); + } + tdbb->setTransaction(oldTransaction); - jrd_tra* const current_transaction = tdbb->getTransaction(); - Request* const current_request = tdbb->getRequest(); + throw; + } +} - try - { - fb_assert(work->dfw_id <= dbb->dbb_max_idx); - idx.idx_id = work->dfw_id; - IDX_create_index(tdbb, relation, &idx, work->dfw_name.c_str(), &work->dfw_id, - transaction, selectivity); - fb_assert(work->dfw_id == idx.idx_id); - } - catch (const Exception&) - { - tdbb->setTransaction(current_transaction); - tdbb->setRequest(current_request); +static string get_string(const dsc* desc) +{ +/************************************** + * + * g e t _ s t r i n g + * + ************************************** + * + * Get string for a given descriptor. + * + **************************************/ + const char* str; + VaryStr temp;// Must hold largest metadata field or filename - // Get rid of the expression/condition statements - idx.idx_expression_statement->release(tdbb); - if (idx.idx_condition_statement) - idx.idx_condition_statement->release(tdbb); + if (!desc) + { + return string(); + } - throw; - } + // Find the actual length of the string, searching until the claimed + // end of the string, or the terminating \0, whichever comes first. - tdbb->setTransaction(current_transaction); - tdbb->setRequest(current_request); + USHORT length = MOV_make_string(JRD_get_thread_data(), desc, ttype_metadata, &str, &temp, sizeof(temp)); - DFW_update_index(work->dfw_name.c_str(), idx.idx_id, selectivity, transaction); + const char* p = str; + const char* const q = str + length; + while (p < q && *p) + { + ++p; + } - // Get rid of the expression/condition statements - idx.idx_expression_statement->release(tdbb); - if (idx.idx_condition_statement) - idx.idx_condition_statement->release(tdbb); - } - break; + // Trim trailing blanks (bug 3355) - default: - break; - } + while (--p >= str && *p == ' ') + ; + length = (p + 1) - str; - return false; + return string(str, length); } @@ -2804,297 +2750,378 @@ static void check_computed_dependencies(thread_db* tdbb, jrd_tra* transaction, } -static void check_dependencies(thread_db* tdbb, - const TEXT* dpdo_name, - const TEXT* field_name, - const TEXT* package_name, - int dpdo_type, - jrd_tra* transaction) +static bool find_depend_in_dfw(thread_db* tdbb, + TEXT* object_name, + USHORT dep_type, + USHORT rel_id, + jrd_tra* transaction) { /************************************** * - * c h e c k _ d e p e n d e n c i e s + * f i n d _ d e p e n d _ i n _ d f w * ************************************** * * Functional description - * Check the dependency list for relation or relation.field - * before deleting such. + * Check the object to see if it is being + * deleted as part of the deferred work. + * Return true if it is, false otherwise. * **************************************/ SET_TDBB(tdbb); Jrd::Attachment* attachment = tdbb->getAttachment(); - const MetaName packageName(package_name); - - SLONG dep_counts[obj_type_MAX]; - for (int i = 0; i < obj_type_MAX; i++) - dep_counts[i] = 0; - - if (field_name) + fb_utils::exact_name(object_name); + enum dfw_t dfw_type; + switch (dep_type) { - AutoCacheRequest request(tdbb, irq_ch_f_dpd, IRQ_REQUESTS); + case obj_view: + dfw_type = dfw_delete_relation; + break; + case obj_trigger: + dfw_type = dfw_delete_trigger; + break; + case obj_computed: + dfw_type = rel_id ? dfw_delete_rfr : dfw_delete_global; + break; + case obj_validation: + dfw_type = dfw_delete_global; + break; + case obj_procedure: + dfw_type = dfw_delete_procedure; + break; + case obj_index_expression: + case obj_index_condition: + dfw_type = dfw_delete_index; + break; + case obj_package_header: + dfw_type = dfw_drop_package_header; + break; + case obj_package_body: + dfw_type = dfw_drop_package_body; + break; + case obj_udf: + dfw_type = dfw_delete_function; + break; + default: + fb_assert(false); + break; + } - FOR(REQUEST_HANDLE request) - DEP IN RDB$DEPENDENCIES - WITH DEP.RDB$DEPENDED_ON_NAME EQ dpdo_name - AND DEP.RDB$DEPENDED_ON_TYPE = dpdo_type - AND DEP.RDB$FIELD_NAME EQ field_name - AND DEP.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') - REDUCED TO DEP.RDB$DEPENDENT_NAME + // Look to see if an object of the desired type is being deleted or modified. + // For an object being modified we verify dependencies separately when we parse its BLR. + for (const DeferredWork* work = transaction->tra_deferred_job->work; work; work = work->getNext()) + { + if ((work->dfw_type == dfw_type || + (work->dfw_type == dfw_modify_procedure && dfw_type == dfw_delete_procedure) || + (work->dfw_type == dfw_modify_field && dfw_type == dfw_delete_global) || + (work->dfw_type == dfw_modify_trigger && dfw_type == dfw_delete_trigger) || + (work->dfw_type == dfw_modify_function && dfw_type == dfw_delete_function)) && + work->dfw_name == object_name && work->dfw_package.isEmpty() && + (!rel_id || rel_id == work->dfw_id)) { - // If the found object is also being deleted, there's no dependency + if (work->dfw_type == dfw_modify_procedure || work->dfw_type == dfw_modify_function) + { + // Don't consider that routine is in DFW if we are only checking the BLR + if (!work->findArg(dfw_arg_check_blr)) + return true; + } + else + { + return true; + } + } - if (!find_depend_in_dfw(tdbb, DEP.RDB$DEPENDENT_NAME, DEP.RDB$DEPENDENT_TYPE, - 0, transaction)) + if (work->dfw_type == dfw_type && dfw_type == dfw_delete_index) + { + for (FB_SIZE_T i = 0; i < work->dfw_args.getCount(); ++i) { - ++dep_counts[DEP.RDB$DEPENDENT_TYPE]; + const DeferredWork* arg = work->dfw_args[i]; + if (arg->dfw_type == dfw_arg_index_name && + arg->dfw_name == object_name) + { + return true; + } } } - END_FOR } - else + + if (dfw_type == dfw_delete_global) { - AutoCacheRequest request(tdbb, irq_ch_dpd, IRQ_REQUESTS); + if (dep_type == obj_computed) + { + // Computed fields are more complicated. If the global field isn't being + // deleted, see if all of the fields it is the source for, are. - FOR(REQUEST_HANDLE request) - DEP IN RDB$DEPENDENCIES - WITH DEP.RDB$DEPENDED_ON_NAME EQ dpdo_name - AND DEP.RDB$DEPENDED_ON_TYPE = dpdo_type - AND DEP.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') - REDUCED TO DEP.RDB$DEPENDENT_NAME + AutoCacheRequest request(tdbb, irq_ch_cmp_dpd, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + FLD IN RDB$FIELDS CROSS + RFR IN RDB$RELATION_FIELDS CROSS + REL IN RDB$RELATIONS + WITH FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE + AND FLD.RDB$FIELD_NAME EQ object_name + AND REL.RDB$RELATION_NAME EQ RFR.RDB$RELATION_NAME + { + if (!find_depend_in_dfw(tdbb, RFR.RDB$FIELD_NAME, obj_computed, + REL.RDB$RELATION_ID, transaction)) + { + return false; + } + } + END_FOR + + return true; + } + + if (dep_type == obj_validation) { - // If the found object is also being deleted, there's no dependency + // Maybe it's worth caching in the future? + AutoRequest request; - if (!find_depend_in_dfw(tdbb, DEP.RDB$DEPENDENT_NAME, DEP.RDB$DEPENDENT_TYPE, - 0, transaction)) + FOR(REQUEST_HANDLE request) + FLD IN RDB$FIELDS WITH + FLD.RDB$FIELD_NAME EQ object_name { - ++dep_counts[DEP.RDB$DEPENDENT_TYPE]; + if (!FLD.RDB$VALIDATION_BLR.NULL) + return false; } + END_FOR + + return true; } - END_FOR } - SLONG total = 0; - for (int i = 0; i < obj_type_MAX; i++) - total += dep_counts[i]; + return false; +} - if (!total) - return; - if (field_name) - { - string fld_name(dpdo_name); - fld_name.append("."); - fld_name.append(field_name); - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_no_delete) << // Msg353: can not delete - Arg::Gds(isc_field_name) << Arg::Str(fld_name) << - Arg::Gds(isc_dependency) << Arg::Num(total)); // Msg310: there are %ld dependencies - } - else - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_no_delete) << // can not delete - Arg::Gds(getErrorCodeByObjectType(dpdo_type)) << - Arg::Str(QualifiedName(dpdo_name, packageName).toString()) << - Arg::Gds(isc_dependency) << Arg::Num(total)); // there are %ld dependencies - } -} + +// +// Per-type create/alter/drop support. +// -static void check_filename(const Firebird::string& name, bool shareExpand) +static bool create_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * c h e c k _ f i l e n a m e + * c r e a t e _ f i e l d * ************************************** * * Functional description - * Make sure that a file path doesn't contain an - * inet node name. + * Store dependencies of a field. * **************************************/ - const Firebird::PathName file_name(name.ToPathName()); - const bool valid = file_name.find("::") == Firebird::PathName::npos; - - if (!valid || ISC_check_if_remote(file_name, shareExpand)) { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_node_name_err)); - // Msg305: A node name is not permitted in a secondary, shadow, or log file name - } - - if (!JRD_verify_database_access(file_name)) { - ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") << - Arg::Str(name)); - } -} - -static void cleanup_index_creation(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction) -{ - Database* const dbb = tdbb->getDatabase(); - - AutoRequest request; + SET_TDBB(tdbb); + Jrd::Attachment* attachment = tdbb->getAttachment(); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDXN IN RDB$INDICES CROSS - IREL IN RDB$RELATIONS OVER RDB$RELATION_NAME - WITH IDXN.RDB$INDEX_NAME EQ work->dfw_name.c_str() - // dimitr: I have no idea why the condition below is required here - AND IREL.RDB$VIEW_BLR MISSING // views do not have indices + switch (phase) { - jrd_rel* const relation = MET_lookup_relation(tdbb, IDXN.RDB$RELATION_NAME); - RelationPages* const relPages = relation->getPages(tdbb, MAX_TRA_NUMBER, false); - - if (relPages && relPages->rel_index_root) + case 1: { - // We need to special handle temp tables with ON PRESERVE ROWS only - const bool isTempIndex = (relation->rel_flags & REL_temp_conn) && - (relPages->rel_instance_id != 0); - - // Fetch the root index page and mark MUST_WRITE, and then - // delete the index. It will also clean the index slot. - - if (work->dfw_id != dbb->dbb_max_idx) - { - WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); - CCH_FETCH(tdbb, &window, LCK_write, pag_root); - CCH_MARK_MUST_WRITE(tdbb, &window); - const bool tree_exists = BTR_delete_index(tdbb, &window, work->dfw_id); - - if (!isTempIndex) { - work->dfw_id = dbb->dbb_max_idx; - } - else if (tree_exists) - { - IndexLock* const idx_lock = CMP_get_index_lock(tdbb, relation, work->dfw_id); - - if (idx_lock) - { - if (!--idx_lock->idl_count) - LCK_release(tdbb, idx_lock->idl_lock); - } - } - } + const MetaName depName(work->dfw_name); + AutoRequest handle; + bid validation; + validation.clear(); - if (!IDXN.RDB$INDEX_ID.NULL) + FOR(REQUEST_HANDLE handle) + FLD IN RDB$FIELDS WITH + FLD.RDB$FIELD_NAME EQ depName.c_str() { - MODIFY IDXN USING - IDXN.RDB$INDEX_ID.NULL = TRUE; - END_MODIFY + if (!FLD.RDB$VALIDATION_BLR.NULL) + validation = FLD.RDB$VALIDATION_BLR; } + END_FOR - if (!IDXN.RDB$FOREIGN_KEY.NULL) + if (!validation.isEmpty()) { - index_desc idx; - idx.idx_id = idx_invalid; - idx.idx_flags = idx_foreign; - - jrd_rel* partner_relation = NULL; - if (MET_lookup_partner(tdbb, relation, &idx, work->dfw_name.c_str())) - { - partner_relation = MET_lookup_relation_id(tdbb, idx.idx_primary_relation, true); - } + Jrd::Database* dbb = tdbb->getDatabase(); + MemoryPool* new_pool = dbb->createPool(ALLOC_ARGS0); + Jrd::ContextPoolHolder context(tdbb, new_pool); - if (partner_relation) - { - // signal to other processes about new constraint - relation->rel_flags |= REL_check_partners; - LCK_lock(tdbb, relation->rel_partners_lock, LCK_EX, LCK_WAIT); - LCK_release(tdbb, relation->rel_partners_lock); + MET_get_dependencies(tdbb, nullptr, NULL, 0, NULL, &validation, + NULL, NULL, depName, obj_validation, 0, transaction, depName); - if (relation != partner_relation) - { - partner_relation->rel_flags |= REL_check_partners; - LCK_lock(tdbb, partner_relation->rel_partners_lock, LCK_EX, LCK_WAIT); - LCK_release(tdbb, partner_relation->rel_partners_lock); - } - } + dbb->deletePool(new_pool); } } + // fall through + + case 2: + case 3: + return true; + + case 4: // after scan_relation (phase 3) + check_computed_dependencies(tdbb, transaction, work->dfw_name); + break; } - END_FOR + + return false; } -static bool formatsAreEqual(const Format* old_format, const Format* new_format) +static bool delete_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * Functional description - * Compare two format blocks + * d e l e t e _ f i e l d * - **************************************/ + ************************************** + * + * Functional description + * This whole routine exists just to + * return an error if someone attempts to + * delete a global field that is in use + * + **************************************/ - if ((old_format->fmt_length != new_format->fmt_length) || - (old_format->fmt_count != new_format->fmt_count)) + int field_count; + AutoRequest handle; + + SET_TDBB(tdbb); + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + switch (phase) { - return false; - } + case 1: + // Look up the field in RFR. If we can't find the field, go ahead with the delete. - Format::fmt_desc_const_iterator old_desc = old_format->fmt_desc.begin(); - const Format::fmt_desc_const_iterator old_end = old_format->fmt_desc.end(); + handle.reset(); + field_count = 0; - Format::fmt_desc_const_iterator new_desc = new_format->fmt_desc.begin(); + FOR(REQUEST_HANDLE handle) + RFR IN RDB$RELATION_FIELDS CROSS + REL IN RDB$RELATIONS + OVER RDB$RELATION_NAME + WITH RFR.RDB$FIELD_SOURCE EQ work->dfw_name.c_str() + { + // If the rfr field is also being deleted, there's no dependency + if (!find_depend_in_dfw(tdbb, RFR.RDB$FIELD_NAME, obj_computed, REL.RDB$RELATION_ID, + transaction)) + { + field_count++; + } + } + END_FOR - while (old_desc != old_end) - { - if ((old_desc->dsc_dtype != new_desc->dsc_dtype) || - (old_desc->dsc_scale != new_desc->dsc_scale) || - (old_desc->dsc_length != new_desc->dsc_length) || - (old_desc->dsc_sub_type != new_desc->dsc_sub_type) || - (old_desc->dsc_flags != new_desc->dsc_flags) || - (old_desc->dsc_address != new_desc->dsc_address)) + if (field_count) { - return false; + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_no_delete) << // Msg353: can not delete + Arg::Gds(isc_domain_name) << Arg::Str(work->dfw_name) << + Arg::Gds(isc_dependency) << Arg::Num(field_count)); // Msg310: there are %ld dependencies } - ++new_desc; - ++old_desc; + DFW_check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_field, transaction); + + case 2: + return true; + + case 3: + MET_delete_dependencies(tdbb, work->dfw_name, obj_computed, transaction); + MET_delete_dependencies(tdbb, work->dfw_name, obj_validation, transaction); + break; } - return true; + return false; } -static bool compute_security(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) +static bool modify_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * c o m p u t e _ s e c u r i t y + * m o d i f y _ f i e l d * ************************************** * * Functional description - * There was a change in a security class. Recompute everything - * it touches. + * Handle constraint dependencies of a field. * **************************************/ + SET_TDBB(tdbb); Jrd::Attachment* attachment = tdbb->getAttachment(); switch (phase) { case 1: - case 2: - return true; - - case 3: { - // Get security class. This may return NULL if it doesn't exist + const MetaName depName(work->dfw_name); + AutoRequest handle; - SCL_clear_classes(tdbb, work->dfw_name.c_str()); + // If a domain is being changed to NOT NULL, schedule validation of involved relations. + if (work->findArg(dfw_arg_field_not_null)) + { + FOR(REQUEST_HANDLE handle) + RFL IN RDB$RELATION_FIELDS CROSS + REL IN RDB$RELATIONS + WITH REL.RDB$RELATION_NAME EQ RFL.RDB$RELATION_NAME AND + RFL.RDB$FIELD_SOURCE EQ depName.c_str() AND + (RFL.RDB$NULL_FLAG MISSING OR RFL.RDB$NULL_FLAG = FALSE) AND + REL.RDB$VIEW_BLR MISSING + REDUCED TO RFL.RDB$RELATION_NAME, RFL.RDB$FIELD_ID + { + dsc desc; + desc.makeText(static_cast(strlen(RFL.RDB$RELATION_NAME)), CS_METADATA, + (UCHAR*) RFL.RDB$RELATION_NAME); - AutoRequest handle; - FOR(REQUEST_HANDLE handle) X IN RDB$DATABASE - WITH X.RDB$SECURITY_CLASS EQ work->dfw_name.c_str() + DeferredWork* work = DFW_post_work(transaction, dfw_check_not_null, &desc, 0); + SortedArray& ids = DFW_get_ids(work); + + FB_SIZE_T pos; + if (!ids.find(RFL.RDB$FIELD_ID, pos)) + ids.insert(pos, RFL.RDB$FIELD_ID); + } + END_FOR + } + + bid validation; + validation.clear(); + + handle.reset(); + + FOR(REQUEST_HANDLE handle) + FLD IN RDB$FIELDS WITH + FLD.RDB$FIELD_NAME EQ depName.c_str() { - attachment->att_security_class = SCL_get_class(tdbb, work->dfw_name.c_str()); + if (!FLD.RDB$VALIDATION_BLR.NULL) + validation = FLD.RDB$VALIDATION_BLR; } END_FOR + + const DeferredWork* const arg = work->findArg(dfw_arg_new_name); + + // ASF: If there are procedures depending on the domain, it can't be renamed. + if (arg && depName != arg->dfw_name.c_str()) + DFW_check_dependencies(tdbb, depName.c_str(), NULL, NULL, obj_field, transaction); + + MET_delete_dependencies(tdbb, depName, obj_validation, transaction); + + if (!validation.isEmpty()) + { + Jrd::Database* dbb = tdbb->getDatabase(); + MemoryPool* new_pool = dbb->createPool(ALLOC_ARGS0); + Jrd::ContextPoolHolder context(tdbb, new_pool); + + MET_get_dependencies(tdbb, nullptr, NULL, 0, NULL, &validation, + NULL, NULL, depName, obj_validation, 0, transaction, depName); + + dbb->deletePool(new_pool); + } } + // fall through + + case 2: + case 3: + return true; + + case 4: // after scan_relation (phase 3) + check_computed_dependencies(tdbb, transaction, work->dfw_name); break; } @@ -3102,455 +3129,155 @@ static bool compute_security(thread_db* tdbb, SSHORT phase, DeferredWork* work, } -static bool modify_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool grant_privileges(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * m o d i f y _ i n d e x + * g r a n t _ p r i v i l e g e s * ************************************** * * Functional description - * Create\drop an index or change the state of an index between active/inactive. - * If index owns by global temporary table with on commit preserve rows scope - * change index instance for this temporary table too. For "create index" work - * item create base index instance before temp index instance. For index - * deletion delete temp index instance first to release index usage counter - * before deletion of base index instance. + * Compute access control list from SQL privileges. + * **************************************/ - - SET_TDBB(tdbb); - Jrd::Attachment* attachment = transaction->getAttachment(); - - bool is_create = true; - dfw_task_routine task_routine = NULL; - - switch (work->dfw_type) + switch (phase) { - case dfw_create_index : - task_routine = create_index; - break; + case 1: + return true; - case dfw_create_expression_index : - task_routine = create_expression_index; - break; + case 2: + GRANT_privileges(tdbb, work->dfw_name, work->dfw_id, transaction); + break; - case dfw_delete_index : - task_routine = delete_index; - is_create = false; - break; + default: + break; } - fb_assert(task_routine); - bool more = false, more2 = false; + return false; +} - if (is_create) { - more = (*task_routine)(tdbb, phase, work, transaction); - } - bool gtt_preserve = false; - jrd_rel* relation = NULL; +static bool create_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ +/************************************** + * + * c r e a t e _ c o l l a t i o n + * + ************************************** + * + * Functional description + * Get collation id or setup specific attributes. + * + **************************************/ + + SET_TDBB(tdbb); + TTypeId id(work->dfw_id); - if (is_create) + switch (phase) { - PreparedStatement::Builder sql; - SLONG rdbRelationID; - SLONG rdbRelationType; - sql << "select" - << sql("rel.rdb$relation_id,", rdbRelationID) - << sql("rel.rdb$relation_type", rdbRelationType) - << "from rdb$indices idx join rdb$relations rel using (rdb$relation_name)" - << "where idx.rdb$index_name = " << work->dfw_name - << " and rel.rdb$relation_id is not null"; - AutoPreparedStatement ps(attachment->prepareStatement(tdbb, - attachment->getSysTransaction(), sql)); - AutoResultSet rs(ps->executeQuery(tdbb, attachment->getSysTransaction())); - - while (rs->fetch(tdbb)) + case 1: + setupSpecificCollationAttributes(tdbb, transaction, id, + work->dfw_name.c_str(), false); + return true; + + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + + case 7: { - gtt_preserve = (rdbRelationType == rel_global_temp_preserve); - relation = MET_lookup_relation_id(tdbb, rdbRelationID, false); + auto* cs = MetadataCache::getCharSet(tdbb, id, 0); + // fb_assert(cs); -------------- breaks INI_ini() + if (cs) + cs->commit(tdbb); } - } - else if (work->dfw_id > 0) - { - relation = MET_lookup_relation_id(tdbb, work->dfw_id, false); - gtt_preserve = (relation) && (relation->rel_flags & REL_temp_conn); - } + return false; - if (gtt_preserve && relation) - { - tdbb->tdbb_flags &= ~TDBB_use_db_page_space; - try { - if (relation->getPages(tdbb, MAX_TRA_NUMBER, false)) { - more2 = (*task_routine) (tdbb, phase, work, transaction); - } - tdbb->tdbb_flags |= TDBB_use_db_page_space; - } - catch (...) + case 0: { - tdbb->tdbb_flags |= TDBB_use_db_page_space; - throw; + auto* cs = MetadataCache::getCharSet(tdbb, id, CacheFlag::NOSCAN); + if (cs) + cs->rollback(tdbb); } + return false; } - if (!is_create) { - more = (*task_routine)(tdbb, phase, work, transaction); - } - - return (more || more2); + return false; } -static bool create_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool delete_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * c r e a t e _ i n d e x + * d e l e t e _ c o l l a t i o n * ************************************** * * Functional description - * Create a new index or change the state of an index between active/inactive. + * Check if it is allowable to delete + * a collation, and if so, clean up after it. * **************************************/ - AutoCacheRequest request; - jrd_rel* relation; - jrd_rel* partner_relation; - index_desc idx; - int key_count; SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - Database* dbb = tdbb->getDatabase(); switch (phase) { - case 0: - cleanup_index_creation(tdbb, work, transaction); - return false; - case 1: + DFW_check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_collation, transaction); + return true; + case 2: return true; case 3: - key_count = 0; - relation = NULL; - idx.idx_flags = 0; - - // Fetch the information necessary to create the index. On the first - // time thru, check to see if the index already exists. If so, delete - // it. If the index inactive flag is set, don't create the index + //full reload instead INTL_texttype_unload(tdbb, work->dfw_id); + break; + } - request.reset(tdbb, irq_c_index, IRQ_REQUESTS); + return false; +} - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - IDX IN RDB$INDICES CROSS - REL IN RDB$RELATIONS OVER RDB$RELATION_NAME - WITH IDX.RDB$INDEX_NAME EQ work->dfw_name.c_str() - { - relation = MET_lookup_relation_id(tdbb, REL.RDB$RELATION_ID, false); - if (!relation) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_idx_create_err) << Arg::Str(work->dfw_name)); - // Msg308: can't create index %s - } - if (IDX.RDB$INDEX_ID && IDX.RDB$STATISTICS < 0.0) - { - // we need to know if this relation is temporary or not - MET_scan_relation(tdbb, relation); +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. + * + **************************************/ - // no need to recalculate statistics for base instance of GTT - RelationPages* relPages = relation->getPages(tdbb, MAX_TRA_NUMBER, false); - const bool isTempInstance = relation->isTemporary() && - relPages && (relPages->rel_instance_id != 0); + SET_TDBB(tdbb); - if (isTempInstance || !relation->isTemporary()) - { - SelectivityList selectivity(*tdbb->getDefaultPool()); - const USHORT id = IDX.RDB$INDEX_ID - 1; - IDX_statistics(tdbb, relation, id, selectivity); - DFW_update_index(work->dfw_name.c_str(), id, selectivity, transaction); - } + 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 - return false; - } + 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)); - if (IDX.RDB$INDEX_ID) - { - IDX_delete_index(tdbb, relation, (USHORT)(IDX.RDB$INDEX_ID - 1)); - - AutoCacheRequest request2(tdbb, irq_c_index_m, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - IDXM IN RDB$INDICES WITH IDXM.RDB$INDEX_NAME EQ work->dfw_name.c_str() - { - MODIFY IDXM - IDXM.RDB$INDEX_ID.NULL = TRUE; - END_MODIFY - } - END_FOR - } - - if (IDX.RDB$INDEX_INACTIVE) - return false; - - idx.idx_count = IDX.RDB$SEGMENT_COUNT; - - if (!idx.idx_count || idx.idx_count > MAX_INDEX_SEGMENTS) - { - if (!idx.idx_count) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_idx_seg_err) << Arg::Str(work->dfw_name)); - // Msg304: segment count of 0 defined for index %s - } - else - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_idx_key_err) << Arg::Str(work->dfw_name)); - // Msg311: too many keys defined for index %s - } - } - - if (IDX.RDB$UNIQUE_FLAG) - idx.idx_flags |= idx_unique; - if (IDX.RDB$INDEX_TYPE == 1) - idx.idx_flags |= idx_descending; - if (!IDX.RDB$FOREIGN_KEY.NULL) - idx.idx_flags |= idx_foreign; - - AutoCacheRequest rc_request(tdbb, irq_c_index_rc, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE rc_request TRANSACTION_HANDLE transaction) - RC IN RDB$RELATION_CONSTRAINTS WITH - RC.RDB$INDEX_NAME EQ work->dfw_name.c_str() AND - RC.RDB$CONSTRAINT_TYPE = PRIMARY_KEY - { - idx.idx_flags |= idx_primary; - } - END_FOR - - idx.idx_condition = nullptr; - idx.idx_condition_statement = nullptr; - - if (!IDX.RDB$CONDITION_BLR.NULL) - { - // Allocate a new pool to contain the expression tree - // for index condition - const auto new_pool = attachment->createPool(); - CompilerScratch* csb = nullptr; - - try - { - Jrd::ContextPoolHolder context(tdbb, new_pool); - - MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$CONDITION_BLR, - nullptr, &csb, work->dfw_name, obj_index_condition, 0, - transaction); - - idx.idx_condition_statement = Statement::makeBoolExpression(tdbb, - idx.idx_condition, csb, false); - - idx.idx_flags |= idx_condition; - } - catch (const Exception&) - { - attachment->deletePool(new_pool); - throw; - } - - delete csb; - } - - // Here we need dirty reads from database (first of all from - // RDB$RELATION_FIELDS and RDB$FIELDS - tables not directly related - // with index to be created and it's dfw_name). Missing it breaks gbak, - // and appears can break other applications. - - AutoCacheRequest seg_request(tdbb, irq_c_index_seg, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE seg_request) - SEG IN RDB$INDEX_SEGMENTS CROSS - RFR IN RDB$RELATION_FIELDS CROSS - FLD IN RDB$FIELDS - WITH SEG.RDB$INDEX_NAME EQ work->dfw_name.c_str() - AND RFR.RDB$RELATION_NAME EQ relation->rel_name.c_str() - AND RFR.RDB$FIELD_NAME EQ SEG.RDB$FIELD_NAME - AND FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE - { - if (++key_count > idx.idx_count || SEG.RDB$FIELD_POSITION > idx.idx_count || - FLD.RDB$FIELD_TYPE == blr_blob || !FLD.RDB$DIMENSIONS.NULL) - { - if (key_count > idx.idx_count) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_idx_key_err) << Arg::Str(work->dfw_name)); - // Msg311: too many keys defined for index %s - } - else if (SEG.RDB$FIELD_POSITION > idx.idx_count) - { - fb_utils::exact_name(RFR.RDB$FIELD_NAME); - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_inval_key_posn) << - // Msg358: invalid key position - Arg::Gds(isc_field_name) << Arg::Str(RFR.RDB$FIELD_NAME) << - Arg::Gds(isc_index_name) << Arg::Str(work->dfw_name)); - } - else if (FLD.RDB$FIELD_TYPE == blr_blob) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_blob_idx_err) << Arg::Str(work->dfw_name)); - // Msg350: attempt to index blob column in index %s - } - else - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_array_idx_err) << Arg::Str(work->dfw_name)); - // Msg351: attempt to index array column in index %s - } - } - - idx.idx_rpt[SEG.RDB$FIELD_POSITION].idx_field = RFR.RDB$FIELD_ID; - - if (FLD.RDB$CHARACTER_SET_ID.NULL) - FLD.RDB$CHARACTER_SET_ID = CS_NONE; - - SSHORT collate; - if (!RFR.RDB$COLLATION_ID.NULL) - collate = RFR.RDB$COLLATION_ID; - else if (!FLD.RDB$COLLATION_ID.NULL) - collate = FLD.RDB$COLLATION_ID; - else - collate = COLLATE_NONE; - - const SSHORT text_type = INTL_CS_COLL_TO_TTYPE(FLD.RDB$CHARACTER_SET_ID, collate); - idx.idx_rpt[SEG.RDB$FIELD_POSITION].idx_itype = - DFW_assign_index_type(tdbb, work->dfw_name, gds_cvt_blr_dtype[FLD.RDB$FIELD_TYPE], text_type); - - // Initialize selectivity to zero. Otherwise random rubbish makes its way into database - idx.idx_rpt[SEG.RDB$FIELD_POSITION].idx_selectivity = 0; - } - END_FOR - } - END_FOR - - if (!relation) - { - // The record was not found in RDB$INDICES. - // Apparently the index was dropped in the same transaction. - return false; - } - - if (key_count != idx.idx_count) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_key_field_err) << Arg::Str(work->dfw_name)); - // Msg352: too few key columns found for index %s (incorrect column name?) - } - - // Make sure the relation info is all current - - MET_scan_relation(tdbb, relation); - - if (relation->rel_view_rse) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_idx_create_err) << Arg::Str(work->dfw_name)); - // Msg308: can't create index %s - } - - // Actually create the index - - partner_relation = NULL; - - // Protect relation from modification to create consistent index - ProtectRelations protectRelations(tdbb, transaction); - protectRelations.addRelation(relation); - - if (idx.idx_flags & idx_foreign) - { - idx.idx_id = idx_invalid; - - if (MET_lookup_partner(tdbb, relation, &idx, work->dfw_name.c_str())) - { - partner_relation = MET_lookup_relation_id(tdbb, idx.idx_primary_relation, true); - } - - if (!partner_relation) - { - MetaName constraint_name; - MET_lookup_cnstrt_for_index(tdbb, constraint_name, work->dfw_name); - ERR_post(Arg::Gds(isc_partner_idx_not_found) << Arg::Str(constraint_name)); - } - - // Get an protected_read lock on the both relations if the index being - // defined enforces a foreign key constraint. This will prevent - // the constraint from being violated during index construction. - - protectRelations.addRelation(partner_relation); - - int bad_segment; - if (!IDX_check_master_types(tdbb, idx, partner_relation, bad_segment)) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_partner_idx_incompat_type) << Arg::Num(bad_segment + 1)); - } - - /*** hvlad: this code was never called but i preserve it for Claudio review and decision - - // CVC: Currently, the server doesn't enforce FK creation more than at DYN level. - // If DYN is bypassed, then FK creation succeeds and operation will fail at run-time. - // The aim is to check REFERENCES at DDL time instead of DML time and behave accordingly - // to ANSI SQL rules for REFERENCES rights. - // For testing purposes, I'm calling SCL_check_index, although most of the DFW ops are - // carried using internal metadata structures that are refreshed from system tables. - - // Don't bother if the master's owner is the same than the detail's owner. - // If both tables aren't defined in the same session, partner_relation->rel_owner_name - // won't be loaded hence, we need to be careful about null pointers. - - if (relation->rel_owner_name.length() == 0 || - partner_relation->rel_owner_name.length() == 0 || - relation->rel_owner_name != partner_relation->rel_owner_name) - { - SCL_check_index(tdbb, partner_relation->rel_name, - idx.idx_id + 1, SCL_references); - } - ***/ - } - - protectRelations.lock(); - - fb_assert(work->dfw_id <= dbb->dbb_max_idx); - idx.idx_id = work->dfw_id; - SelectivityList selectivity(*tdbb->getDefaultPool()); - IDX_create_index(tdbb, relation, &idx, work->dfw_name.c_str(), - &work->dfw_id, transaction, selectivity); - fb_assert(work->dfw_id == idx.idx_id); - DFW_update_index(work->dfw_name.c_str(), idx.idx_id, selectivity, transaction); - - if (idx.idx_condition_statement) - idx.idx_condition_statement->release(tdbb); - - if (partner_relation) - { - // signal to other processes about new constraint - relation->rel_flags |= REL_check_partners; - LCK_lock(tdbb, relation->rel_partners_lock, LCK_EX, LCK_WAIT); - LCK_release(tdbb, relation->rel_partners_lock); - - if (relation != partner_relation) - { - partner_relation->rel_flags |= REL_check_partners; - LCK_lock(tdbb, partner_relation->rel_partners_lock, LCK_EX, LCK_WAIT); - LCK_release(tdbb, partner_relation->rel_partners_lock); - } + DFW_check_dependencies(tdbb, arg->dfw_name.c_str(), work->dfw_name.c_str(), + obj_procedure, transaction); } - + */ break; } @@ -3558,184 +3285,107 @@ static bool create_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ } -static bool create_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +void DFW_check_partners(thread_db* tdbb, const USHORT rel_id) { /************************************** * - * c r e a t e _ r e l a t i o n + * D F W _ c h e c k _ p a r t n e r s * ************************************** * * Functional description - * Create a new relation. + * Signal other processes to check partners of relation rel_id + * Used when FK index was dropped * **************************************/ - AutoCacheRequest request; - jrd_rel* relation; - USHORT rel_id, external_flag; - bid blob_id; - AutoRequest handle; - Lock* lock; + auto* relation = MetadataCache::get(tdbb)->lookupRelation(tdbb, rel_id, CacheFlag::AUTOCREATE); + fb_assert(relation); - blob_id.clear(); + relation->rel_flags |= REL_check_partners; + LCK_lock(tdbb, relation->rel_partners_lock, LCK_EX, LCK_WAIT); + LCK_release(tdbb, relation->rel_partners_lock); +} + + +/* + * In index-related DFW dfw_id is relation id, dfw_name is index name + */ + +static bool create_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ +/************************************** + * + * c r e a t e _ i n d e x + * + ************************************** + * + * Functional description + * Create a new index or change the state of an index between active/inactive. + * + **************************************/ + jrd_rel* relation = nullptr; SET_TDBB(tdbb); Jrd::Attachment* attachment = tdbb->getAttachment(); Database* dbb = tdbb->getDatabase(); - - const USHORT local_min_relation_id = USER_DEF_REL_INIT_ID; + AutoRequest request; switch (phase) { case 0: - // We need to cleanup RDB$PAGES and pages if they were added at phase 3. - request.reset(tdbb, irq_c_relation3, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - X IN RDB$RELATIONS WITH - X.RDB$RELATION_NAME EQ work->dfw_name.c_str() AND - X.RDB$RELATION_ID NOT MISSING - { - rel_id = X.RDB$RELATION_ID; - - if ( (relation = MET_lookup_relation_id(tdbb, rel_id, false)) ) - { - RelationPages* const relPages = relation->getBasePages(); - - if (relPages->rel_index_root) - IDX_delete_indices(tdbb, relation, relPages); - - if (relPages->rel_pages) - DPM_delete_relation(tdbb, relation); - - // Mark relation in the cache as dropped - relation->rel_flags |= REL_deleted; - } - } - END_FOR - - if (work->dfw_lock) - { - LCK_release(tdbb, work->dfw_lock); - delete work->dfw_lock; - work->dfw_lock = NULL; - } - break; + return false; case 1: case 2: return true; case 3: - // Take a relation lock on rel id -1 before actually generating a relation id. - - work->dfw_lock = lock = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) - Lock(tdbb, sizeof(SLONG), LCK_relation); - lock->setKey(-1); - - LCK_lock(tdbb, lock, LCK_EX, LCK_WAIT); - - /* Assign a relation ID and dbkey length to the new relation. - Probe the candidate relation ID returned from the system - relation RDB$DATABASE to make sure it isn't already assigned. - This can happen from nefarious manipulation of RDB$DATABASE - or wraparound of the next relation ID. Keep looking for a - usable relation ID until the search space is exhausted. */ - - rel_id = 0; - request.reset(tdbb, irq_c_relation, IRQ_REQUESTS); - + // set statistics support FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - X IN RDB$DATABASE CROSS Y IN RDB$RELATIONS WITH - Y.RDB$RELATION_NAME EQ work->dfw_name.c_str() + IDX IN RDB$INDICES + WITH IDX.RDB$INDEX_NAME EQ work->dfw_name.c_str() { - blob_id = Y.RDB$VIEW_BLR; - external_flag = Y.RDB$EXTERNAL_FILE[0]; - - MODIFY X USING - rel_id = X.RDB$RELATION_ID; + // Request to recalculate statistics? + if (IDX.RDB$INDEX_ID && IDX.RDB$STATISTICS < 0.0) + { + // we need to know if this relation is temporary or not + Cached::Relation* relation = + MetadataCache::lookupRelation(tdbb, work->dfw_id, CacheFlag::AUTOCREATE); - if (rel_id < local_min_relation_id || rel_id > MAX_RELATION_ID) - rel_id = X.RDB$RELATION_ID = local_min_relation_id; + // no need to recalculate statistics for base instance of GTT + RelationPages* relPages = relation->getPages(tdbb, MAX_TRA_NUMBER, false); + const bool isTempInstance = relation->isTemporary() && + relPages && (relPages->rel_instance_id != 0); - // Roman Simakov: We need to return deleted relations to skip them. - // This maybe result of cleanup failure after phase 3. - while ( (relation = MET_lookup_relation_id(tdbb, rel_id++, true)) ) + if (isTempInstance || !relation->isTemporary()) { - if (rel_id < local_min_relation_id || rel_id > MAX_RELATION_ID) - rel_id = local_min_relation_id; - - if (rel_id == X.RDB$RELATION_ID) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_table_name) << Arg::Str(work->dfw_name) << - Arg::Gds(isc_imp_exc)); - } + SelectivityList selectivity(*tdbb->getDefaultPool()); + const USHORT id = IDX.RDB$INDEX_ID - 1; + IDX_statistics(tdbb, relation, id, selectivity); + DFW_update_index(work->dfw_name.c_str(), id, selectivity, transaction); } - X.RDB$RELATION_ID = (rel_id > MAX_RELATION_ID) ? local_min_relation_id : rel_id; - - MODIFY Y USING - Y.RDB$RELATION_ID = --rel_id; - if (blob_id.isEmpty()) - Y.RDB$DBKEY_LENGTH = 8; - else - { - // update the dbkey length to include each of the base relations - Y.RDB$DBKEY_LENGTH = 0; - - handle.reset(); - - FOR(REQUEST_HANDLE handle) - Z IN RDB$VIEW_RELATIONS CROSS - R IN RDB$RELATIONS OVER RDB$RELATION_NAME - WITH Z.RDB$VIEW_NAME = work->dfw_name.c_str() AND - (Z.RDB$CONTEXT_TYPE = VCT_TABLE OR - Z.RDB$CONTEXT_TYPE = VCT_VIEW) - { - Y.RDB$DBKEY_LENGTH += R.RDB$DBKEY_LENGTH; - } - END_FOR - } - END_MODIFY - END_MODIFY + return false; + } } END_FOR - - LCK_release(tdbb, lock); - delete lock; - work->dfw_lock = NULL; - - // if this is not a view, create the relation - - if (rel_id && blob_id.isEmpty() && !external_flag) - { - relation = MET_relation(tdbb, rel_id); - DPM_create_relation(tdbb, relation); - } - return true; case 4: + case 5: + case 6: + return true; - // get the relation and flag it to check for dependencies - // in the view blr (if it exists) and any computed fields - - request.reset(tdbb, irq_c_relation2, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - X IN RDB$RELATIONS WITH - X.RDB$RELATION_NAME EQ work->dfw_name.c_str() + case 7: { - rel_id = X.RDB$RELATION_ID; - relation = MET_relation(tdbb, rel_id); - relation->rel_flags |= REL_get_dependencies; - - relation->rel_flags &= ~REL_scanned; - DFW_post_work(transaction, dfw_scan_relation, NULL, rel_id); + auto* relation = MetadataCache::lookupRelation(tdbb, work->dfw_id, 0); + if (relation) + { + auto* index = relation->lookupIndex(tdbb, work->dfw_name, 0); + if (index) + index->commit(tdbb); + } } - END_FOR - break; } @@ -3743,56 +3393,43 @@ static bool create_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, j } -static bool create_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool delete_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * c r e a t e _ t r i g g e r + * d e l e t e _ i n d e x * ************************************** * * Functional description - * Perform required actions on creation of trigger. * **************************************/ - SET_TDBB(tdbb); switch (phase) { + case 0: + return false; + case 1: - case 2: + DFW_check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_index, transaction); return true; + case 2: case 3: - { - const bool compile = !work->findArg(dfw_arg_check_blr); - get_trigger_dependencies(work, compile, transaction); - return true; - } - case 4: + case 5: + case 6: + return true; + + case 7: { - if (!work->findArg(dfw_arg_rel_name)) + auto* relation = MetadataCache::lookupRelation(tdbb, work->dfw_id, CacheFlag::ERASED); + if (relation) { - const DeferredWork* const arg = work->findArg(dfw_arg_trg_type); - fb_assert(arg); - - if (arg) - { - // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT - if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) - { - unsigned triggerKind = arg->dfw_id & ~TRIGGER_TYPE_DB; - MET_release_triggers(tdbb, &tdbb->getAttachment()->att_triggers[triggerKind], true); - MET_load_db_triggers(tdbb, triggerKind); - } - else if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) - { - MET_release_triggers(tdbb, &tdbb->getAttachment()->att_ddl_triggers, true); - MET_load_ddl_triggers(tdbb); - } - } + auto* index = relation->lookupIndex(tdbb, work->dfw_name, CacheFlag::ERASED); + if (index) + index->commit(tdbb); } } break; @@ -3802,180 +3439,170 @@ static bool create_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jr } -//#define DEBUG_REBUILD_INTL - -namespace DbgRebuildIntl { - -#ifdef DEBUG_REBUILD_INTL -static int ind = 0; -#endif - -void out(const char* format, ...) +static bool create_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { -#ifdef DEBUG_REBUILD_INTL - for (int n = 0; n < ind; ++n) - putc(' ', stderr); - - va_list params; - va_start(params, format); - vfprintf(stderr, format, params); - va_end(params); -#endif -} - -class Lvl -{ -public: - Lvl() - { -#ifdef DEBUG_REBUILD_INTL - ind += 2; -#endif - } - - ~Lvl() - { -#ifdef DEBUG_REBUILD_INTL - ind -= 2; -#endif - } -}; - -} // namespace DbgRebuildIntl - - -static string remove_icu_info_from_attributes(const string& charsetName, const string& specificAttributes) -{ - DbgRebuildIntl::Lvl debLevel; - - Firebird::AutoPtr cs(FB_NEW charset); - memset(cs, 0, sizeof(*cs)); +/************************************** + * + * c r e a t e _ r e l a t i o n + * + ************************************** + * + * Functional description + * Create a new relation. + * + **************************************/ + SET_TDBB(tdbb); - if (IntlManager::lookupCharSet(charsetName, cs)) + switch (phase) { - DbgRebuildIntl::out("Remove_icu_info_from_attributes for charset '%s'\n", charsetName.c_str()); + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + return true; - AutoPtr charSet(Firebird::CharSet::createInstance(*getDefaultMemoryPool(), 0, cs)); - IntlUtil::SpecificAttributesMap map; - if (IntlUtil::parseSpecificAttributes(charSet, specificAttributes.length(), - (const UCHAR*) specificAttributes.begin(), &map)) + case 7: { - map.remove(ATTR_ICU_VERSION); - map.remove(ATTR_COLL_VERSION); - return IntlUtil::generateSpecificAttributes(charSet, map); + auto* relation = MetadataCache::lookupRelation(tdbb, work->dfw_name, 0); + if (relation) + relation->commit(tdbb); } + break; } - else - DbgRebuildIntl::out("Remove_icu_info_from_attributes - NO CHARSET '%s'\n", charsetName.c_str()); - return specificAttributes; + return false; } -static void setupSpecificCollationAttributes(thread_db* tdbb, jrd_tra* transaction, - const USHORT charSetId, const char* collationName, bool dropIcuInfo) +static bool delete_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * setupSpecificCollationAttributes + * d e l e t e _ r e l a t i o n * ************************************** * * Functional description + * Check if it is allowable to delete + * a relation, and if so, clean up after it. * **************************************/ - DbgRebuildIntl::Lvl debLevel; + AutoRequest request; + jrd_rel* relation; + USHORT view_count; + bool adjusted; SET_TDBB(tdbb); - Jrd::Attachment* const attachment = tdbb->getAttachment(); - AutoCacheRequest handle(tdbb, drq_m_coll_attrs, DYN_REQUESTS); - - DbgRebuildIntl::out("setupSpecificCollationAttributes: dropIcuInfo=%d\n", dropIcuInfo); + Jrd::Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); - FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) - COLL IN RDB$COLLATIONS - CROSS CS IN RDB$CHARACTER_SETS - OVER RDB$CHARACTER_SET_ID - WITH COLL.RDB$COLLATION_NAME EQ collationName AND - COLL.RDB$CHARACTER_SET_ID EQ charSetId + switch (phase) { - SLONG length = 0; - HalfStaticArray buffer; - - if (!COLL.RDB$SPECIFIC_ATTRIBUTES.NULL) + case 0: { - blb* blob = blb::open(tdbb, transaction, &COLL.RDB$SPECIFIC_ATTRIBUTES); - length = blob->blb_length + 10; - length = blob->BLB_get_data(tdbb, buffer.getBuffer(length), length); + auto* relation = MetadataCache::lookupRelation(tdbb, work->dfw_id, CacheFlag::ERASED); + if (relation) + { + if (relation->isDropped()) + relation->rollback(tdbb); + } } - const string specificAttributes((const char*) buffer.begin(), length); - DbgRebuildIntl::out("Try collation %s with %s\n", collationName, specificAttributes.c_str()); + return false; - MetaName charsetName(CS.RDB$CHARACTER_SET_NAME); - string icuLessAttributes = dropIcuInfo ? - remove_icu_info_from_attributes(charsetName.c_str(), specificAttributes) : specificAttributes; - DbgRebuildIntl::out("dropIcuInfo %d icuLessAttributes %s\n", dropIcuInfo, icuLessAttributes.c_str()); - string newSpecificAttributes; + case 1: + // check if any views use this as a base relation - // ASF: If setupCollationAttributes fail we store the original - // attributes. This should be what we want for new databases - // and restores. CREATE COLLATION will fail in DYN. - if (IntlManager::setupCollationAttributes( - fb_utils::exact_name(COLL.RDB$BASE_COLLATION_NAME.NULL ? - COLL.RDB$COLLATION_NAME : COLL.RDB$BASE_COLLATION_NAME), - fb_utils::exact_name(CS.RDB$CHARACTER_SET_NAME), - icuLessAttributes, newSpecificAttributes) && - newSpecificAttributes != specificAttributes) // if nothing changed, we do nothing + request.reset(); + view_count = 0; + FOR(REQUEST_HANDLE request) + X IN RDB$VIEW_RELATIONS WITH + X.RDB$RELATION_NAME EQ work->dfw_name.c_str() { - DbgRebuildIntl::out(" Recreate collation %s\n", collationName); + // If the view is also being deleted, there's no dependency + if (!find_depend_in_dfw(tdbb, X.RDB$VIEW_NAME, obj_view, 0, transaction)) + { + view_count++; + } + } + END_FOR - MODIFY COLL USING - if (newSpecificAttributes.isEmpty()) - COLL.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE; - else - { - COLL.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE; - attachment->storeMetaDataBlob(tdbb, transaction, - &COLL.RDB$SPECIFIC_ATTRIBUTES, newSpecificAttributes); - } - END_MODIFY + if (view_count) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_no_delete) << // Msg353: can not delete + Arg::Gds(isc_table_name) << Arg::Str(work->dfw_name) << + Arg::Gds(isc_dependency) << Arg::Num(view_count)); + // Msg310: there are %ld dependencies } - else if (newSpecificAttributes == specificAttributes) - DbgRebuildIntl::out(" nothing changed\n"); - else - DbgRebuildIntl::out(" setupCollationAttributes() failed\n"); + + { + auto* relation = MetadataCache::lookupRelation(tdbb, work->dfw_id, CacheFlag::ERASED); + if (!relation) + return false; + + DFW_check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, + (relation->isView() ? obj_view : obj_relation), transaction); + } + return true; + + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + + case 7: + { + auto* relation = MetadataCache::lookupRelation(tdbb, work->dfw_id, CacheFlag::ERASED); + if (relation) + relation->commit(tdbb); + } + break; } - END_FOR -} + return false; +} -static bool create_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool commit_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * c r e a t e _ c o l l a t i o n + * c o m m i t _ r e l a t i o n * ************************************** * * Functional description - * Get collation id or setup specific attributes. + * Commit changes in relation at phase 7. * **************************************/ + AutoRequest request; + jrd_rel* relation; + USHORT view_count; + bool adjusted; SET_TDBB(tdbb); + Jrd::Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); switch (phase) { case 1: - setupSpecificCollationAttributes(tdbb, transaction, TTYPE_TO_CHARSET(work->dfw_id), - work->dfw_name.c_str(), false); + case 2: + case 3: + case 4: + case 5: + case 6: + return true; - if (!(transaction->tra_flags & TRA_system) && // avoid run during database creation - !INTL_defined_type(tdbb, work->dfw_id)) + case 7: { - setupSpecificCollationAttributes(tdbb, transaction, TTYPE_TO_CHARSET(work->dfw_id), - work->dfw_name.c_str(), true); + auto* relation = MetadataCache::lookupRelation(tdbb, work->dfw_id, 0); + if (relation) + relation->commit(tdbb); } break; } @@ -3984,179 +3611,138 @@ static bool create_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work, } -void DFW_reset_icu(thread_db* tdbb) +static bool delete_rfr(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * r e s e t I c u + * d e l e t e _ r f r * ************************************** * * Functional description - * Fix database for use with other ICU version. - * Formally has nothing to do with DFW, - * but adding new .epp module is worse. - * Next, it's using some DFW code. + * This whole routine exists just to + * return an error if someone attempts to + * 1. delete a field from a relation if the relation + * is used in a view and the field is referenced in + * the view. + * 2. drop the last column of a table * **************************************/ - SET_TDBB(tdbb); + int rel_exists, field_count; + AutoRequest handle; + MetaName f; - jrd_tra* transaction = NULL; - jrd_tra* oldTransaction = tdbb->getTransaction(); + SET_TDBB(tdbb); + Jrd::Attachment* attachment = tdbb->getAttachment(); - try + switch (phase) { - Attachment* attachment = tdbb->getAttachment(); - transaction = TRA_start(tdbb, 0, 0); - tdbb->setTransaction(transaction); - - SortedArray indices; - ProtectRelations tables(tdbb, transaction); - - // Get list of affected indices & tables - const char* indSql = - "select ind.RDB$INDEX_NAME, rel.RDB$RELATION_ID," - " coalesce(coll.RDB$BASE_COLLATION_NAME, coll.RDB$COLLATION_NAME), cs.RDB$CHARACTER_SET_NAME, " - " coll.RDB$SPECIFIC_ATTRIBUTES " - "from RDB$INDICES ind " - "join RDB$RELATIONS rel on ind.RDB$RELATION_NAME = rel.RDB$RELATION_NAME " - "join RDB$INDEX_SEGMENTS seg on ind.RDB$INDEX_NAME = seg.RDB$INDEX_NAME " - "join RDB$RELATION_FIELDS rfl on rfl.RDB$RELATION_NAME = ind.RDB$RELATION_NAME " - " and rfl.RDB$FIELD_NAME = seg.RDB$FIELD_NAME " - "join RDB$FIELDS fld on rfl.RDB$FIELD_SOURCE = fld.RDB$FIELD_NAME " - "join RDB$COLLATIONS coll on fld.RDB$CHARACTER_SET_ID = coll.RDB$CHARACTER_SET_ID " - " and coalesce(rfl.RDB$COLLATION_ID, fld.RDB$COLLATION_ID) = coll.RDB$COLLATION_ID " - "join RDB$CHARACTER_SETS cs on coll.RDB$CHARACTER_SET_ID = cs.RDB$CHARACTER_SET_ID " - "where coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%' " - " and coalesce(ind.RDB$INDEX_INACTIVE, 0) = 0 " - " and coalesce(rel.RDB$RELATION_TYPE, 0) = 0 " // rel_persistent - "group by ind.RDB$INDEX_NAME, rel.RDB$RELATION_ID, coll.RDB$BASE_COLLATION_NAME, " - " coll.RDB$COLLATION_NAME, cs.RDB$CHARACTER_SET_NAME, coll.RDB$SPECIFIC_ATTRIBUTES"; + case 1: + // first check if there are any fields used explicitly by the view - { // scope - AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, indSql)); - AutoResultSet rs(ps->executeQuery(tdbb, transaction)); - while(rs->fetch(tdbb)) + handle.reset(); + field_count = 0; + FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS CROSS + VR IN RDB$VIEW_RELATIONS OVER RDB$RELATION_NAME CROSS + VFLD IN RDB$RELATION_FIELDS WITH + REL.RDB$RELATION_ID EQ work->dfw_id AND + VFLD.RDB$VIEW_CONTEXT EQ VR.RDB$VIEW_CONTEXT AND + VFLD.RDB$RELATION_NAME EQ VR.RDB$VIEW_NAME AND + VFLD.RDB$BASE_FIELD EQ work->dfw_name.c_str() + { + // If the view is also being deleted, there's no dependency + if (!find_depend_in_dfw(tdbb, VR.RDB$VIEW_NAME, obj_view, 0, transaction)) { - MetaName t(rs->getMetaName(tdbb, 1)); + f = VFLD.RDB$BASE_FIELD; + field_count++; + } + } + END_FOR - const MetaName collationName(rs->getMetaName(tdbb, 3)); - const MetaName charsetName(rs->getMetaName(tdbb, 4)); - const string specificAttributes(rs->getString(tdbb, 5)); - string icuLessAttributes = remove_icu_info_from_attributes(charsetName.c_str(), specificAttributes); - DbgRebuildIntl::out("check index %s SA:'%s' ILA:'%s'\n", - t.c_str(), specificAttributes.c_str(), icuLessAttributes.c_str()); - string newSpecificAttributes; - if (!IntlManager::setupCollationAttributes(collationName.c_str(), - charsetName.c_str(), icuLessAttributes, newSpecificAttributes)) - { - DbgRebuildIntl::out("setupCollationAttributes failed for %s\n", collationName.c_str()); - continue; - } - DbgRebuildIntl::out("newSpecificAttributes '%s'\n", newSpecificAttributes.c_str()); - if (newSpecificAttributes == specificAttributes) - continue; + if (field_count) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_no_delete) << // Msg353: can not delete + Arg::Gds(isc_field_name) << Arg::Str(f) << + Arg::Gds(isc_dependency) << Arg::Num(field_count)); + // Msg310: there are %ld dependencies + } - DbgRebuildIntl::out("Add index\n"); - if (!indices.exist(t)) - indices.add(rs->getMetaName(tdbb, 1)); + // now check if there are any dependencies generated through the blr + // that defines the relation - USHORT rel_id = rs->getInt(tdbb, 2); - if (!tables.exists(rel_id)) - { - jrd_rel* relation = MET_lookup_relation_id(tdbb, rel_id, false); - if (relation) - tables.addRelation(relation); - } - } + auto* relation = MetadataCache::lookup_relation_id(tdbb, work->dfw_id, CacheFlag::AUTOCREATE | CacheFlag::NOCOMMIT); + if (relation) + { + DFW_check_dependencies(tdbb, relation->c_name(), work->dfw_name.c_str(), NULL, + (relation->isView() ? obj_view : obj_relation), + transaction); } - DbgRebuildIntl::out("locking tables\n"); + // see if the relation itself is being dropped - // Lock the tables - tables.lock(); + handle.reset(); + rel_exists = 0; + FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ work->dfw_id + { + rel_exists++; + } + END_FOR - // Change collation's attributes - const char* collSql = - "select coll.RDB$COLLATION_NAME, coll.RDB$CHARACTER_SET_ID from RDB$COLLATIONS coll " - "where coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%'"; - { // scope - AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, collSql)); - AutoResultSet rs(ps->executeQuery(tdbb, transaction)); - while(rs->fetch(tdbb)) - { - MetaName collName(rs->getMetaName(tdbb, 1)); - const USHORT charSetId(rs->getSmallInt(tdbb, 2)); + // if table exists, check if this is the last column in the table - setupSpecificCollationAttributes(tdbb, transaction, charSetId, collName.c_str(), true); - } - } + if (rel_exists) + { + field_count = 0; + handle.reset(); - // Reactivate indices - { // scope - for (MetaName* idx = indices.begin(); idx != indices.end(); ++idx) - { - AutoRequest request; + FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS CROSS + RFLD IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME + WITH REL.RDB$RELATION_ID EQ work->dfw_id + field_count++; + END_FOR - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - IDX IN RDB$INDICES - WITH IDX.RDB$INDEX_NAME EQ idx->c_str() - { - MODIFY IDX - DbgRebuildIntl::out("Re-activate index %s\n", idx->c_str()); - IDX.RDB$INDEX_INACTIVE.NULL = FALSE; - IDX.RDB$INDEX_INACTIVE = FALSE; - END_MODIFY - } - END_FOR + if (!field_count) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_del_last_field)); + // Msg354: last column in a relation cannot be deleted } } - // Commit - TRA_commit(tdbb, transaction, false); - transaction = NULL; - tdbb->setTransaction(oldTransaction); + break; } - catch (const Firebird::Exception&) - { - if (transaction) - { - TRA_rollback(tdbb, transaction, false, true); - } - tdbb->setTransaction(oldTransaction); - throw; - } + return false; } -static bool delete_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool clear_cache(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) { /************************************** * - * d e l e t e _ c o l l a t i o n + * c l e a r _ c a c h e * ************************************** * - * Functional description - * Check if it is allowable to delete - * a collation, and if so, clean up after it. + * Clear security names mapping cache * **************************************/ SET_TDBB(tdbb); + Database* const dbb = tdbb->getDatabase(); switch (phase) { case 1: - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_collation, transaction); - return true; - case 2: return true; case 3: - INTL_texttype_unload(tdbb, work->dfw_id); + Mapping::clearCache(dbb->dbb_filename.c_str(), work->dfw_id); break; } @@ -4183,7 +3769,7 @@ static bool delete_exception(thread_db* tdbb, SSHORT phase, DeferredWork* work, switch (phase) { case 1: - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_exception, transaction); + DFW_check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_exception, transaction); break; } @@ -4191,22 +3777,21 @@ static bool delete_exception(thread_db* tdbb, SSHORT phase, DeferredWork* work, } -static bool set_generator(thread_db* tdbb, - SSHORT phase, - DeferredWork* work, - jrd_tra* transaction) +static bool compute_security(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) { /************************************** * - * s e t _ g e n e r a t o r + * c o m p u t e _ s e c u r i t y * ************************************** * * Functional description - * Set the generator to the given value. + * There was a change in a security class. Recompute everything + * it touches. * **************************************/ SET_TDBB(tdbb); + Jrd::Attachment* attachment = tdbb->getAttachment(); switch (phase) { @@ -4216,21 +3801,17 @@ static bool set_generator(thread_db* tdbb, case 3: { - const SLONG id = MET_lookup_generator(tdbb, work->dfw_name); - if (id >= 0) + // Get security class. This may return NULL if it doesn't exist + + SCL_clear_classes(tdbb, work->dfw_name.c_str()); + + AutoRequest handle; + FOR(REQUEST_HANDLE handle) X IN RDB$DATABASE + WITH X.RDB$SECURITY_CLASS EQ work->dfw_name.c_str() { - fb_assert(id == work->dfw_id); - SINT64 value = 0; - if (transaction->getGenIdCache()->get(id, value)) - { - transaction->getGenIdCache()->remove(id); - DPM_gen_id(tdbb, id, true, value); - } + attachment->att_security_class = SCL_get_class(tdbb, work->dfw_name.c_str()); } -#ifdef DEV_BUILD - else // This is a test only - status_exception::raise(Arg::Gds(isc_cant_modify_sysobj) << "generator" << work->dfw_name); -#endif + END_FOR } break; } @@ -4239,28 +3820,44 @@ static bool set_generator(thread_db* tdbb, } -static bool delete_generator(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool delete_global(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * d e l e t e _ g e n e r a t o r + * d e l e t e _ g l o b a l * ************************************** * * Functional description - * Check if it is allowable to delete - * a generator, and if so, clean up after it. - * CVC: This function was modelled after delete_exception. + * If a local field has been deleted, + * check to see if its global field + * is computed. If so, delete all its + * dependencies under the assumption + * that a global computed field has only + * one local field. * **************************************/ - SET_TDBB(tdbb); - const char* gen_name = work->dfw_name.c_str(); + Jrd::Attachment* attachment = tdbb->getAttachment(); switch (phase) { case 1: - check_dependencies(tdbb, gen_name, NULL, NULL, obj_generator, transaction); + case 2: + return true; + + case 3: + { + AutoRequest handle; + FOR(REQUEST_HANDLE handle) + FLD IN RDB$FIELDS WITH + FLD.RDB$FIELD_NAME EQ work->dfw_name.c_str() AND + FLD.RDB$COMPUTED_BLR NOT MISSING + { + MET_delete_dependencies(tdbb, work->dfw_name, obj_computed, transaction); + } + END_FOR + } break; } @@ -4268,59 +3865,70 @@ static bool delete_generator(thread_db* tdbb, SSHORT phase, DeferredWork* work, } -static bool create_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static void triggerCommitRollback(thread_db* tdbb, USHORT id, bool doCommit) +{ + switch (id & TRIGGER_TYPE_MASK) // id is RDB$TRIGGER_TYPE truncated to USHORT + { + case TRIGGER_TYPE_DB: + case TRIGGER_TYPE_DDL: + { + auto* triggersSet = MetadataCache::get(tdbb)->getTriggersSet(tdbb, id); + fb_assert(triggersSet); + if (triggersSet) + { + if (doCommit) + triggersSet->commit(tdbb); + else + triggersSet->rollback(tdbb); + } + } + break; + + case TRIGGER_TYPE_DML: + break; + } +} + + +static bool create_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * c r e a t e _ f i e l d + * c r e a t e _ t r i g g e r * ************************************** * * Functional description - * Store dependencies of a field. + * Perform required actions on creation of trigger. * **************************************/ SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); switch (phase) { - case 1: - { - const MetaName depName(work->dfw_name); - AutoRequest handle; - bid validation; - validation.clear(); - - FOR(REQUEST_HANDLE handle) - FLD IN RDB$FIELDS WITH - FLD.RDB$FIELD_NAME EQ depName.c_str() - { - if (!FLD.RDB$VALIDATION_BLR.NULL) - validation = FLD.RDB$VALIDATION_BLR; - } - END_FOR - - if (!validation.isEmpty()) - { - MemoryPool* new_pool = attachment->createPool(); - Jrd::ContextPoolHolder context(tdbb, new_pool); + case 0: + triggerCommitRollback(tdbb, work->dfw_id, false); + break; - MET_get_dependencies(tdbb, NULL, NULL, 0, NULL, &validation, - NULL, NULL, depName, obj_validation, 0, transaction, depName); + case 1: + case 2: + return true; - attachment->deletePool(new_pool); - } + case 3: + { + const bool compile = !work->findArg(dfw_arg_check_blr); + get_trigger_dependencies(work, compile, transaction); } - // fall through + return true; - case 2: - case 3: + case 4: + case 5: + case 6: return true; - case 4: // after scan_relation (phase 3) - check_computed_dependencies(tdbb, transaction, work->dfw_name); + case 7: + triggerCommitRollback(tdbb, work->dfw_id, true); break; } @@ -4328,163 +3936,144 @@ static bool create_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ } -static bool delete_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool modify_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * d e l e t e _ f i e l d + * m o d i f y _ t r i g g e r * ************************************** * * Functional description - * This whole routine exists just to - * return an error if someone attempts to - * delete a global field that is in use + * Perform required actions when modifying trigger. * **************************************/ - int field_count; - AutoRequest handle; - SET_TDBB(tdbb); - Jrd::Attachment* const attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + Attachment* attachment = tdbb->getAttachment(); switch (phase) { - case 1: - // Look up the field in RFR. If we can't find the field, go ahead with the delete. - - handle.reset(); - field_count = 0; - - FOR(REQUEST_HANDLE handle) - RFR IN RDB$RELATION_FIELDS CROSS - REL IN RDB$RELATIONS - OVER RDB$RELATION_NAME - WITH RFR.RDB$FIELD_SOURCE EQ work->dfw_name.c_str() - { - // If the rfr field is also being deleted, there's no dependency - if (!find_depend_in_dfw(tdbb, RFR.RDB$FIELD_NAME, obj_computed, REL.RDB$RELATION_ID, - transaction)) - { - field_count++; - } - } - END_FOR - - if (field_count) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_no_delete) << // Msg353: can not delete - Arg::Gds(isc_domain_name) << Arg::Str(work->dfw_name) << - Arg::Gds(isc_dependency) << Arg::Num(field_count)); // Msg310: there are %ld dependencies - } - - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_field, transaction); + case 0: + triggerCommitRollback(tdbb, work->dfw_id, false); + break; + case 1: case 2: return true; case 3: - MET_delete_dependencies(tdbb, work->dfw_name, obj_computed, transaction); - MET_delete_dependencies(tdbb, work->dfw_name, obj_validation, transaction); - break; - } - - return false; -} - + { + bool compile = !work->findArg(dfw_arg_check_blr); -static bool modify_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ -/************************************** - * - * m o d i f y _ f i e l d - * - ************************************** - * - * Functional description - * Handle constraint dependencies of a field. - * - **************************************/ + // get rid of old dependencies, bring in the new + MET_delete_dependencies(tdbb, work->dfw_name, obj_trigger, transaction); + get_trigger_dependencies(work, compile, transaction); + } + return true; - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); + case 4: + { // scope + const DeferredWork* arg = work->findArg(dfw_arg_check_blr); + if (arg) + { + const MetaName relation_name(arg->dfw_name); + fb_assert(relation_name.hasData()); - switch (phase) - { - case 1: - { - const MetaName depName(work->dfw_name); - AutoRequest handle; + auto* relation = MetadataCache::lookupRelation(tdbb, relation_name, 0); + fb_assert(relation); // must be scanned earlier + if (relation) + { + AutoSetRestore2 tempTrans(tdbb, + &thread_db::getTransaction, + &thread_db::setTransaction, + transaction); + relation->tagForUpdate(tdbb); + DFW_post_work(transaction, dfw_commit_relation, nullptr, relation->getId()); + } + } + } // scope + return true; - // If a domain is being changed to NOT NULL, schedule validation of involved relations. - if (work->findArg(dfw_arg_field_not_null)) + case 5: + { // scope + const DeferredWork* arg = work->findArg(dfw_arg_check_blr); + if (arg) { - FOR(REQUEST_HANDLE handle) - RFL IN RDB$RELATION_FIELDS CROSS - REL IN RDB$RELATIONS - WITH REL.RDB$RELATION_NAME EQ RFL.RDB$RELATION_NAME AND - RFL.RDB$FIELD_SOURCE EQ depName.c_str() AND - (RFL.RDB$NULL_FLAG MISSING OR RFL.RDB$NULL_FLAG = FALSE) AND - REL.RDB$VIEW_BLR MISSING - REDUCED TO RFL.RDB$RELATION_NAME, RFL.RDB$FIELD_ID + const MetaName relation_name(arg->dfw_name); + fb_assert(relation_name.hasData()); + SSHORT valid_blr = FALSE; + + try { - dsc desc; - desc.makeText(static_cast(strlen(RFL.RDB$RELATION_NAME)), CS_METADATA, - (UCHAR*) RFL.RDB$RELATION_NAME); + auto* relation = MetadataCache::lookupRelation(tdbb, relation_name, 0); + fb_assert(relation); // must be scanned earlier - DeferredWork* work = DFW_post_work(transaction, dfw_check_not_null, &desc, 0); - SortedArray& ids = DFW_get_ids(work); + if (relation) + { + if (relation->isAvailable(tdbb) == Cached::Relation::MODIFIED) + relation->commit(tdbb); - FB_SIZE_T pos; - if (!ids.find(RFL.RDB$FIELD_ID, pos)) - ids.insert(pos, RFL.RDB$FIELD_ID); - } - END_FOR - } + auto* rel = relation->getObject(tdbb, 0); - bid validation; - validation.clear(); + MemoryPool* new_pool = dbb->createPool(ALLOC_ARGS0); + TrigArray triggers(*new_pool); - handle.reset(); + try + { + Jrd::ContextPoolHolder context(tdbb, new_pool); - FOR(REQUEST_HANDLE handle) - FLD IN RDB$FIELDS WITH - FLD.RDB$FIELD_NAME EQ depName.c_str() - { - if (!FLD.RDB$VALIDATION_BLR.NULL) - validation = FLD.RDB$VALIDATION_BLR; - } - END_FOR + MET_load_trigger(tdbb, rel, work->dfw_name, + [&triggers](int t)->Triggers& {return triggers[t];}); - const DeferredWork* const arg = work->findArg(dfw_arg_new_name); + for (int i = 1; i < TRIGGER_MAX; ++i) + { + if (triggers[i]) + { + for (auto t : triggers[i]) + t->compile(tdbb); - // ASF: If there are procedures depending on the domain, it can't be renamed. - if (arg && depName != arg->dfw_name.c_str()) - check_dependencies(tdbb, depName.c_str(), NULL, NULL, obj_field, transaction); + triggers[i].release(tdbb, true); + } + } - MET_delete_dependencies(tdbb, depName, obj_validation, transaction); + valid_blr = TRUE; + } + catch (const Firebird::Exception&) + { + dbb->deletePool(new_pool); + throw; + } - if (!validation.isEmpty()) - { - MemoryPool* new_pool = attachment->createPool(); - Jrd::ContextPoolHolder context(tdbb, new_pool); + dbb->deletePool(new_pool); + } + } + catch (const Firebird::Exception&) + { + } - MET_get_dependencies(tdbb, NULL, NULL, 0, NULL, &validation, - NULL, NULL, depName, obj_validation, 0, transaction, depName); + AutoCacheRequest request(tdbb, irq_trg_validate, IRQ_REQUESTS); - attachment->deletePool(new_pool); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + TRG IN RDB$TRIGGERS WITH + TRG.RDB$TRIGGER_NAME EQ work->dfw_name.c_str() AND TRG.RDB$TRIGGER_BLR NOT MISSING + { + MODIFY TRG USING + TRG.RDB$VALID_BLR = valid_blr; + TRG.RDB$VALID_BLR.NULL = FALSE; + END_MODIFY + } + END_FOR } - } - // fall through + } // scope + return true; - case 2: - case 3: + case 6: return true; - case 4: // after scan_relation (phase 3) - check_computed_dependencies(tdbb, transaction, work->dfw_name); + case 7: + triggerCommitRollback(tdbb, work->dfw_id, true); break; } @@ -4492,44 +4081,43 @@ static bool modify_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ } -static bool delete_global(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool delete_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * d e l e t e _ g l o b a l + * d e l e t e _ t r i g g e r * ************************************** * * Functional description - * If a local field has been deleted, - * check to see if its global field - * is computed. If so, delete all its - * dependencies under the assumption - * that a global computed field has only - * one local field. + * Cleanup after a deleted trigger. * **************************************/ + SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); switch (phase) { + case 0: + triggerCommitRollback(tdbb, work->dfw_id, false); + break; + case 1: case 2: return true; case 3: - { - AutoRequest handle; - FOR(REQUEST_HANDLE handle) - FLD IN RDB$FIELDS WITH - FLD.RDB$FIELD_NAME EQ work->dfw_name.c_str() AND - FLD.RDB$COMPUTED_BLR NOT MISSING - { - MET_delete_dependencies(tdbb, work->dfw_name, obj_computed, transaction); - } - END_FOR - } + // get rid of dependencies + MET_delete_dependencies(tdbb, work->dfw_name, obj_trigger, transaction); + return true; + + case 4: + case 5: + case 6: + return true; + + case 7: + triggerCommitRollback(tdbb, work->dfw_id, true); break; } @@ -4537,2163 +4125,256 @@ static bool delete_global(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd } -static void check_partners(thread_db* tdbb, const USHORT rel_id) +static void get_trigger_dependencies(DeferredWork* work, bool compile, jrd_tra* transaction) { /************************************** * - * c h e c k _ p a r t n e r s + * g e t _ t r i g g e r _ d e p e n d e n c i e s * ************************************** * * Functional description - * Signal other processes to check partners of relation rel_id - * Used when FK index was dropped + * Get relations and fields on which this + * trigger depends, either when it's being + * created or when it's modified. * **************************************/ - const Jrd::Attachment* att = tdbb->getAttachment(); - vec* relations = att->att_relations; + thread_db* tdbb = JRD_get_thread_data(); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); - fb_assert(relations); - fb_assert(rel_id < relations->count()); + if (compile) + compile = !tdbb->getAttachment()->isGbak(); - jrd_rel *relation = (*relations)[rel_id]; - fb_assert(relation); + Cached::Relation* relation = NULL; + bid blob_id; + blob_id.clear(); - relation->rel_flags |= REL_check_partners; - LCK_lock(tdbb, relation->rel_partners_lock, LCK_EX, LCK_WAIT); - LCK_release(tdbb, relation->rel_partners_lock); + ISC_UINT64 type = 0; + + AutoCacheRequest handle(tdbb, irq_c_trigger, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE handle) + X IN RDB$TRIGGERS WITH + X.RDB$TRIGGER_NAME EQ work->dfw_name.c_str() + { + blob_id = X.RDB$TRIGGER_BLR; + type = (ISC_UINT64) X.RDB$TRIGGER_TYPE; + relation = MetadataCache::lookupRelation(tdbb, X.RDB$RELATION_NAME, CacheFlag::AUTOCREATE); + } + END_FOR + + // get any dependencies now by parsing the blr + + if ((relation || (type & TRIGGER_TYPE_MASK) != TRIGGER_TYPE_DML) && !blob_id.isEmpty()) + { + Statement* statement = NULL; + // Nickolay Samofatov: allocate statement memory pool... + MemoryPool* new_pool = dbb->createPool(ALLOC_ARGS0); + USHORT par_flags; + + Cleanup mem([&] + { + if (statement) + statement->release(tdbb); + else + dbb->deletePool(new_pool); + }); + + if ((type & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DML) + par_flags = (USHORT) ((type & 1) ? csb_pre_trigger : csb_post_trigger); + else + par_flags = 0; + + Jrd::ContextPoolHolder context(tdbb, new_pool); + const MetaName depName(work->dfw_name); + MET_get_dependencies(tdbb, relation, NULL, 0, NULL, &blob_id, (compile ? &statement : NULL), + NULL, depName, obj_trigger, par_flags, transaction); + } } -static bool delete_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool check_not_null(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** * - * d e l e t e _ i n d e x + * c h e c k _ n o t _ n u l l * ************************************** * - * Functional description + * Scan relation to detect NULLs in fields being changed to NOT NULL. * **************************************/ - IndexLock* index = NULL; SET_TDBB(tdbb); - const DeferredWork* arg = work->findArg(dfw_arg_index_name); - - fb_assert(arg); - fb_assert(arg->dfw_id > 0); - const USHORT id = arg->dfw_id - 1; - // Maybe a permanent check? - //if (id == idx_invalid) - // ERR_post(...); - - // Look up the relation. If we can't find the relation, - // don't worry about the index. - - jrd_rel* relation = MET_lookup_relation_id(tdbb, work->dfw_id, false); - if (!relation) { - return false; - } - - RelationPages* relPages = relation->getPages(tdbb, MAX_TRA_NUMBER, false); - if (!relPages) { - return false; - } - // we need to special handle temp tables with ON PRESERVE ROWS only - const bool isTempIndex = (relation->rel_flags & REL_temp_conn) && - (relPages->rel_instance_id != 0); + Jrd::Attachment* attachment = tdbb->getAttachment(); switch (phase) { - case 0: - index = CMP_get_index_lock(tdbb, relation, id); - if (index) - { - if (!index->idl_count) - LCK_release(tdbb, index->idl_lock); - } - return false; - case 1: - check_dependencies(tdbb, arg->dfw_name.c_str(), NULL, NULL, obj_index, transaction); - return true; - case 2: return true; case 3: - // Make sure nobody is currently using the index - - // If we about to delete temp index instance then usage counter - // will remains 1 and will be decremented by IDX_delete_index at - // phase 4 - - index = CMP_get_index_lock(tdbb, relation, id); - if (index) { - // take into account lock probably used by temp index instance - bool temp_lock_released = false; - if (isTempIndex && (index->idl_count == 1)) - { - index_desc idx; - if (BTR_lookup(tdbb, relation, id, &idx, relPages)) - { - index->idl_count--; - LCK_release(tdbb, index->idl_lock); - temp_lock_released = true; - } - } + if (work->dfw_ids.isEmpty()) + break; + jrd_rel* relation = MetadataCache::lookup_relation(tdbb, work->dfw_name, CacheFlag::AUTOCREATE); + if ((!relation) || relation->isView()) + break; - // Try to clear trigger cache to release lock - if (index->idl_count) - MET_clear_cache(tdbb); + // Protect relation from modification + ProtectRelations protectRelation(tdbb, transaction, relation->getPermanent()); + + SortedArray fields; + AutoRequest handle; - if (!isTempIndex) + for (SortedArray::iterator itr(work->dfw_ids.begin()); + itr != work->dfw_ids.end(); + ++itr) { - if (index->idl_count || - !LCK_lock(tdbb, index->idl_lock, LCK_EX, transaction->getLockWait())) + FOR(REQUEST_HANDLE handle) + RFL IN RDB$RELATION_FIELDS CROSS + FLD IN RDB$FIELDS + WITH RFL.RDB$RELATION_NAME EQ work->dfw_name.c_str() AND + FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE AND + RFL.RDB$FIELD_ID EQ *itr AND + (RFL.RDB$NULL_FLAG = TRUE OR FLD.RDB$NULL_FLAG = TRUE) { - // restore lock used by temp index instance - if (temp_lock_released) - { - LCK_lock(tdbb, index->idl_lock, LCK_SR, LCK_WAIT); - index->idl_count++; - } - - raiseObjectInUseError("INDEX", arg->dfw_name); - } - index->idl_count++; - } - } - - return true; - - case 4: - index = CMP_get_index_lock(tdbb, relation, id); - if (isTempIndex && index) - index->idl_count++; - IDX_delete_index(tdbb, relation, id); - - if (isTempIndex) - return false; - - MET_delete_dependencies(tdbb, arg->dfw_name, obj_index_expression, transaction); - MET_delete_dependencies(tdbb, arg->dfw_name, obj_index_condition, transaction); - - // if index was bound to deleted FK constraint - // then work->dfw_args was set in VIO_erase - arg = work->findArg(dfw_arg_partner_rel_id); - - if (arg) { - if (arg->dfw_id) { - check_partners(tdbb, relation->rel_id); - if (relation->rel_id != arg->dfw_id) { - check_partners(tdbb, arg->dfw_id); + fields.add(RFL.RDB$FIELD_ID); } + END_FOR } - else { - // partner relation was not found in VIO_erase - // we must check partners of all relations in database - MET_update_partners(tdbb); - } - } - if (index) - { - /* in order for us to have gotten the lock in phase 3 - * idl_count HAD to be 0, therefore after having incremented - * it for the exclusive lock it would have to be 1. - * IF now it is NOT 1 then someone else got a lock to - * the index and something is seriously wrong */ - fb_assert(index->idl_count == 1); - if (!--index->idl_count) + if (fields.hasData()) { - // Release index existence lock and memory. + UCharBuffer blr; - for (IndexLock** ptr = &relation->rel_index_locks; *ptr; ptr = &(*ptr)->idl_next) - { - if (*ptr == index) - { - *ptr = index->idl_next; - break; - } - } - if (index->idl_lock) + blr.add(blr_version5); + blr.add(blr_begin); + blr.add(blr_message); + blr.add(1); // message number + blr.add(fields.getCount() & 0xFF); + blr.add(fields.getCount() >> 8); + + for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) { - LCK_release(tdbb, index->idl_lock); - delete index->idl_lock; + blr.add(blr_short); + blr.add(0); } - delete index; - // Release index refresh lock and memory. + blr.add(blr_for); + blr.add(blr_stall); + blr.add(blr_rse); + blr.add(1); + blr.add(blr_rid); + blr.add(relation->getId() & 0xFF); + blr.add(relation->getId() >> 8); + blr.add(0); // stream + blr.add(blr_boolean); - for (IndexBlock** iptr = &relation->rel_index_blocks; *iptr; iptr = &(*iptr)->idb_next) + for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) { - if ((*iptr)->idb_id == id) - { - IndexBlock* index_block = *iptr; - *iptr = index_block->idb_next; - - // Lock was released in IDX_delete_index(). + if (i != fields.getCount() - 1) + blr.add(blr_or); - delete index_block->idb_lock; - delete index_block; - break; - } + blr.add(blr_missing); + blr.add(blr_fid); + blr.add(0); // stream + blr.add(USHORT(fields[i]) & 0xFF); + blr.add(USHORT(fields[i]) >> 8); } - } - } - break; - } - return false; -} + blr.add(blr_end); + blr.add(blr_send); + blr.add(1); + blr.add(blr_begin); -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. - * - **************************************/ + for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) + { + blr.add(blr_assignment); - SET_TDBB(tdbb); + blr.add(blr_value_if); + blr.add(blr_missing); + blr.add(blr_fid); + blr.add(0); // stream + blr.add(USHORT(fields[i]) & 0xFF); + blr.add(USHORT(fields[i]) >> 8); - 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 + blr.add(blr_literal); + blr.add(blr_short); + blr.add(0); + blr.add(1); + blr.add(0); - if (MET_lookup_procedure_id(tdbb, work->dfw_id, false, true, 0)) - { - const DeferredWork* arg = work->dfw_args; - fb_assert(arg && (arg->dfw_type == dfw_arg_proc_name)); + blr.add(blr_literal); + blr.add(blr_short); + blr.add(0); + blr.add(0); + blr.add(0); - check_dependencies(tdbb, arg->dfw_name.c_str(), work->dfw_name.c_str(), - obj_procedure, transaction); - } - */ - break; - } + blr.add(blr_parameter); + blr.add(1); // message number + blr.add(i & 0xFF); + blr.add(i >> 8); + } - return false; -} + blr.add(blr_end); + blr.add(blr_send); + blr.add(1); + blr.add(blr_begin); -static bool delete_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ -/************************************** - * - * d e l e t e _ r e l a t i o n - * - ************************************** - * - * Functional description - * Check if it is allowable to delete - * a relation, and if so, clean up after it. - * - **************************************/ - AutoRequest request; - jrd_rel* relation; - Resource* rsc; - USHORT view_count; - bool adjusted; + for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) + { + blr.add(blr_assignment); + blr.add(blr_literal); + blr.add(blr_short); + blr.add(0); + blr.add(0); + blr.add(0); + blr.add(blr_parameter); + blr.add(1); // message number + blr.add(i & 0xFF); + blr.add(i >> 8); + } - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - Database* dbb = tdbb->getDatabase(); + blr.add(blr_end); + blr.add(blr_end); + blr.add(blr_eoc); - switch (phase) - { - case 0: - relation = MET_lookup_relation_id(tdbb, work->dfw_id, true); - if (!relation) { - return false; - } + AutoRequest request; + request.compile(tdbb, blr.begin(), blr.getCount()); - if (relation->rel_existence_lock && !(relation->rel_flags & REL_deleted)) - { - LCK_convert(tdbb, relation->rel_existence_lock, LCK_SR, transaction->getLockWait()); - } + HalfStaticArray hasRecord; - if (relation->rel_flags & REL_deleting) - { - relation->rel_flags &= ~REL_deleting; - relation->rel_drop_mutex.leave(); - } + EXE_start(tdbb, request, transaction); + EXE_receive(tdbb, request, 1, fields.getCount() * sizeof(USHORT), + (UCHAR*) hasRecord.getBuffer(fields.getCount())); - return false; + Arg::Gds errs(isc_no_meta_update); + bool hasError = false; - case 1: - // check if any views use this as a base relation + for (FB_SIZE_T i = 0; i < fields.getCount(); ++i) + { + if (hasRecord[i]) + { + hasError = true; + errs << Arg::Gds(isc_cannot_make_not_null) << + (*relation->rel_fields)[fields[i]]->fld_name << + relation->getName(); + } + } - request.reset(); - view_count = 0; - FOR(REQUEST_HANDLE request) - X IN RDB$VIEW_RELATIONS WITH - X.RDB$RELATION_NAME EQ work->dfw_name.c_str() - { - // If the view is also being deleted, there's no dependency - if (!find_depend_in_dfw(tdbb, X.RDB$VIEW_NAME, obj_view, 0, transaction)) - { - view_count++; + if (hasError) + ERR_post(errs); } } - END_FOR - - if (view_count) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_no_delete) << // Msg353: can not delete - Arg::Gds(isc_table_name) << Arg::Str(work->dfw_name) << - Arg::Gds(isc_dependency) << Arg::Num(view_count)); - // Msg310: there are %ld dependencies - } - - relation = MET_lookup_relation_id(tdbb, work->dfw_id, false); - if (!relation) - return false; - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, - (relation->isView() ? obj_view : obj_relation), transaction); - return true; - - case 2: - relation = MET_lookup_relation_id(tdbb, work->dfw_id, false); - if (!relation) { - return false; - } - - // Let relation be deleted if only this transaction is using it - - adjusted = false; - if (relation->rel_use_count == 1) - { - for (rsc = transaction->tra_resources.begin(); rsc < transaction->tra_resources.end(); - rsc++) - { - if (rsc->rsc_rel == relation) - { - --relation->rel_use_count; - adjusted = true; - break; - } - } - } - - if (relation->rel_use_count) - MET_clear_cache(tdbb); - - if (relation->rel_use_count || (relation->rel_existence_lock && - !LCK_convert(tdbb, relation->rel_existence_lock, LCK_EX, transaction->getLockWait()))) - { - if (adjusted) - ++relation->rel_use_count; - - raiseRelationInUseError(relation); - } - - fb_assert(!relation->rel_use_count); - - // Flag relation delete in progress so that active sweep or - // garbage collector threads working on relation can skip over it - - relation->rel_flags |= REL_deleting; - { // scope - EngineCheckout cout(tdbb, FB_FUNCTION); - relation->rel_drop_mutex.enter(FB_FUNCTION); - } - - return true; - - case 3: - return true; - - case 4: - relation = MET_lookup_relation_id(tdbb, work->dfw_id, true); - if (!relation) { - fb_assert(false); - return false; - } - - // The sweep and garbage collector threads have no more than - // a single record latency in responding to the flagged relation - // deletion. Nevertheless, as a defensive programming measure, - // don't wait forever if something has gone awry and the sweep - // count doesn't run down. - - for (int wait = 0; wait < 60; wait++) - { - if (!relation->rel_sweep_count) { - break; - } - - EngineCheckout cout(tdbb, FB_FUNCTION); - Thread::sleep(1 * 1000); - } - - if (relation->rel_sweep_count) - raiseRelationInUseError(relation); - - // Free any memory associated with the relation's garbage collection bitmap - if (dbb->dbb_garbage_collector) { - dbb->dbb_garbage_collector->removeRelation(relation->rel_id); - } - - if (relation->rel_file) { - EXT_fini(relation, false); - } - - if (relation->isTemporary()) - { - // release pages, allocated for current GTT instance - AutoSetRestoreFlag tmpSpace(&tdbb->tdbb_flags, TDBB_use_db_page_space, false); - relation->delPages(tdbb); - } - - RelationPages* const relPages = relation->getBasePages(); - if (relPages->rel_index_root) { - IDX_delete_indices(tdbb, relation, relPages); - } - - if (relPages->rel_pages) { - DPM_delete_relation(tdbb, relation); - } - - // if this is a view (or even if we don't know), delete dependency lists - - if (relation->rel_view_rse || !(relation->rel_flags & REL_scanned)) { - MET_delete_dependencies(tdbb, work->dfw_name, obj_view, transaction); - } - - // Now that the data, pointer, and index pages are gone, - // get rid of the relation itself - - request.reset(); - - FOR(REQUEST_HANDLE request) X IN RDB$FORMATS WITH - X.RDB$RELATION_ID EQ relation->rel_id - { - ERASE X; - } - END_FOR - - // Release relation locks - if (relation->rel_existence_lock) { - LCK_release(tdbb, relation->rel_existence_lock); - } - if (relation->rel_partners_lock) { - LCK_release(tdbb, relation->rel_partners_lock); - } - if (relation->rel_rescan_lock) { - LCK_release(tdbb, relation->rel_rescan_lock); - } - - // Mark relation in the cache as dropped - relation->rel_flags |= REL_deleted; - - if (relation->rel_flags & REL_deleting) - { - relation->rel_flags &= ~REL_deleting; - relation->rel_drop_mutex.leave(); - } - - // Release relation triggers - relation->releaseTriggers(tdbb, true); - - break; - } - - return false; -} - - -static bool delete_rfr(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ -/************************************** - * - * d e l e t e _ r f r - * - ************************************** - * - * Functional description - * This whole routine exists just to - * return an error if someone attempts to - * 1. delete a field from a relation if the relation - * is used in a view and the field is referenced in - * the view. - * 2. drop the last column of a table - * - **************************************/ - int rel_exists, field_count; - AutoRequest handle; - MetaName f; - jrd_rel* relation; - - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - - switch (phase) - { - case 1: - // first check if there are any fields used explicitly by the view - - handle.reset(); - field_count = 0; - FOR(REQUEST_HANDLE handle) - REL IN RDB$RELATIONS CROSS - VR IN RDB$VIEW_RELATIONS OVER RDB$RELATION_NAME CROSS - VFLD IN RDB$RELATION_FIELDS WITH - REL.RDB$RELATION_ID EQ work->dfw_id AND - VFLD.RDB$VIEW_CONTEXT EQ VR.RDB$VIEW_CONTEXT AND - VFLD.RDB$RELATION_NAME EQ VR.RDB$VIEW_NAME AND - VFLD.RDB$BASE_FIELD EQ work->dfw_name.c_str() - { - // If the view is also being deleted, there's no dependency - if (!find_depend_in_dfw(tdbb, VR.RDB$VIEW_NAME, obj_view, 0, transaction)) - { - f = VFLD.RDB$BASE_FIELD; - field_count++; - } - } - END_FOR - - if (field_count) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_no_delete) << // Msg353: can not delete - Arg::Gds(isc_field_name) << Arg::Str(f) << - Arg::Gds(isc_dependency) << Arg::Num(field_count)); - // Msg310: there are %ld dependencies - } - - // now check if there are any dependencies generated through the blr - // that defines the relation - - if ( (relation = MET_lookup_relation_id(tdbb, work->dfw_id, false)) ) - { - check_dependencies(tdbb, relation->rel_name.c_str(), work->dfw_name.c_str(), NULL, - (relation->isView() ? obj_view : obj_relation), - transaction); - } - - // see if the relation itself is being dropped - - handle.reset(); - rel_exists = 0; - FOR(REQUEST_HANDLE handle) - REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ work->dfw_id - { - rel_exists++; - } - END_FOR - - // if table exists, check if this is the last column in the table - - if (rel_exists) - { - field_count = 0; - handle.reset(); - - FOR(REQUEST_HANDLE handle) - REL IN RDB$RELATIONS CROSS - RFLD IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME - WITH REL.RDB$RELATION_ID EQ work->dfw_id - field_count++; - END_FOR - - if (!field_count) - { - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_del_last_field)); - // Msg354: last column in a relation cannot be deleted - } - } - - case 2: - return true; - - case 3: - // Unlink field from data structures. Don't try to actually release field and - // friends -- somebody may be pointing to them - - relation = MET_lookup_relation_id(tdbb, work->dfw_id, false); - if (relation) - { - const int id = MET_lookup_field(tdbb, relation, work->dfw_name); - if (id >= 0) - { - vec* vector = relation->rel_fields; - if (vector && (ULONG) id < vector->count() && (*vector)[id]) - { - (*vector)[id] = NULL; - } - } - } - break; - } - - return false; -} - - -static bool delete_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) -{ -/************************************** - * - * d e l e t e _ s h a d o w - * - ************************************** - * - * Functional description - * Provide deferred work interface to - * MET_delete_shadow. - * - **************************************/ - - SET_TDBB(tdbb); - - switch (phase) - { - case 1: - case 2: - return true; - - case 3: - MET_delete_shadow(tdbb, work->dfw_id); - break; - } - - return false; -} - - -static bool delete_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ -/************************************** - * - * d e l e t e _ t r i g g e r - * - ************************************** - * - * Functional description - * Cleanup after a deleted trigger. - * - **************************************/ - - SET_TDBB(tdbb); - - switch (phase) - { - case 1: - case 2: - return true; - - case 3: - // get rid of dependencies - MET_delete_dependencies(tdbb, work->dfw_name, obj_trigger, transaction); - return true; - - case 4: - { - const DeferredWork* arg = work->findArg(dfw_arg_rel_name); - if (!arg) - { - const DeferredWork* arg = work->findArg(dfw_arg_trg_type); - fb_assert(arg); - - // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT - if (arg) - { - if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) - { - MET_release_trigger(tdbb, - &tdbb->getAttachment()->att_triggers[arg->dfw_id & ~TRIGGER_TYPE_DB], - work->dfw_name); - } - else if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) - { - MET_release_trigger(tdbb, - &tdbb->getAttachment()->att_ddl_triggers, - work->dfw_name); - } - } - } - } - break; - } - - return false; -} - - -static bool find_depend_in_dfw(thread_db* tdbb, - TEXT* object_name, - USHORT dep_type, - USHORT rel_id, - jrd_tra* transaction) -{ -/************************************** - * - * f i n d _ d e p e n d _ i n _ d f w - * - ************************************** - * - * Functional description - * Check the object to see if it is being - * deleted as part of the deferred work. - * Return true if it is, false otherwise. - * - **************************************/ - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - - fb_utils::exact_name(object_name); - enum dfw_t dfw_type; - switch (dep_type) - { - case obj_view: - dfw_type = dfw_delete_relation; - break; - case obj_trigger: - dfw_type = dfw_delete_trigger; - break; - case obj_computed: - dfw_type = rel_id ? dfw_delete_rfr : dfw_delete_global; - break; - case obj_validation: - dfw_type = dfw_delete_global; - break; - case obj_procedure: - dfw_type = dfw_delete_procedure; - break; - case obj_index_expression: - case obj_index_condition: - dfw_type = dfw_delete_index; - break; - case obj_package_header: - dfw_type = dfw_drop_package_header; - break; - case obj_package_body: - dfw_type = dfw_drop_package_body; - break; - case obj_udf: - dfw_type = dfw_delete_function; - break; - default: - fb_assert(false); - break; - } - - // Look to see if an object of the desired type is being deleted or modified. - // For an object being modified we verify dependencies separately when we parse its BLR. - for (const DeferredWork* work = transaction->tra_deferred_job->work; work; work = work->getNext()) - { - if ((work->dfw_type == dfw_type || - (work->dfw_type == dfw_modify_procedure && dfw_type == dfw_delete_procedure) || - (work->dfw_type == dfw_modify_field && dfw_type == dfw_delete_global) || - (work->dfw_type == dfw_modify_trigger && dfw_type == dfw_delete_trigger) || - (work->dfw_type == dfw_modify_function && dfw_type == dfw_delete_function)) && - work->dfw_name == object_name && work->dfw_package.isEmpty() && - (!rel_id || rel_id == work->dfw_id)) - { - if (work->dfw_type == dfw_modify_procedure || work->dfw_type == dfw_modify_function) - { - // Don't consider that routine is in DFW if we are only checking the BLR - if (!work->findArg(dfw_arg_check_blr)) - return true; - } - else - { - return true; - } - } - - if (work->dfw_type == dfw_type && dfw_type == dfw_delete_index) - { - for (FB_SIZE_T i = 0; i < work->dfw_args.getCount(); ++i) - { - const DeferredWork* arg = work->dfw_args[i]; - if (arg->dfw_type == dfw_arg_index_name && - arg->dfw_name == object_name) - { - return true; - } - } - } - } - - if (dfw_type == dfw_delete_global) - { - if (dep_type == obj_computed) - { - // Computed fields are more complicated. If the global field isn't being - // deleted, see if all of the fields it is the source for, are. - - AutoCacheRequest request(tdbb, irq_ch_cmp_dpd, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - FLD IN RDB$FIELDS CROSS - RFR IN RDB$RELATION_FIELDS CROSS - REL IN RDB$RELATIONS - WITH FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE - AND FLD.RDB$FIELD_NAME EQ object_name - AND REL.RDB$RELATION_NAME EQ RFR.RDB$RELATION_NAME - { - if (!find_depend_in_dfw(tdbb, RFR.RDB$FIELD_NAME, obj_computed, - REL.RDB$RELATION_ID, transaction)) - { - return false; - } - } - END_FOR - - return true; - } - - if (dep_type == obj_validation) - { - // Maybe it's worth caching in the future? - AutoRequest request; - - FOR(REQUEST_HANDLE request) - FLD IN RDB$FIELDS WITH - FLD.RDB$FIELD_NAME EQ object_name - { - if (!FLD.RDB$VALIDATION_BLR.NULL) - return false; - } - END_FOR - - return true; - } - } - - return false; -} - - -static void get_array_desc(thread_db* tdbb, const TEXT* field_name, Ods::InternalArrayDesc* desc) -{ -/************************************** - * - * g e t _ a r r a y _ d e s c - * - ************************************** - * - * Functional description - * Get array descriptor for an array. - * - **************************************/ - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - - AutoCacheRequest request(tdbb, irq_r_fld_dim, IRQ_REQUESTS); - - Ods::InternalArrayDesc::iad_repeat* ranges = 0; - FOR (REQUEST_HANDLE request) - D IN RDB$FIELD_DIMENSIONS WITH D.RDB$FIELD_NAME EQ field_name - { - if (D.RDB$DIMENSION >= 0 && D.RDB$DIMENSION < desc->iad_dimensions) - { - ranges = desc->iad_rpt + D.RDB$DIMENSION; - ranges->iad_lower = D.RDB$LOWER_BOUND; - ranges->iad_upper = D.RDB$UPPER_BOUND; - } - } - END_FOR - - desc->iad_count = 1; - - for (ranges = desc->iad_rpt + desc->iad_dimensions; --ranges >= desc->iad_rpt;) - { - ranges->iad_length = desc->iad_count; - desc->iad_count *= ranges->iad_upper - ranges->iad_lower + 1; - } - - desc->iad_version = Ods::IAD_VERSION_1; - desc->iad_length = IAD_LEN(MAX(desc->iad_struct_count, desc->iad_dimensions)); - desc->iad_element_length = desc->iad_rpt[0].iad_desc.dsc_length; - desc->iad_total_length = desc->iad_element_length * desc->iad_count; -} - - -static void get_trigger_dependencies(DeferredWork* work, bool compile, jrd_tra* transaction) -{ -/************************************** - * - * g e t _ t r i g g e r _ d e p e n d e n c i e s - * - ************************************** - * - * Functional description - * Get relations and fields on which this - * trigger depends, either when it's being - * created or when it's modified. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - Jrd::Attachment* attachment = tdbb->getAttachment(); - - if (compile) - compile = !tdbb->getAttachment()->isGbak(); - - jrd_rel* relation = NULL; - bid blob_id; - blob_id.clear(); - - ISC_UINT64 type = 0; - - AutoCacheRequest handle(tdbb, irq_c_trigger, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE handle) - X IN RDB$TRIGGERS WITH - X.RDB$TRIGGER_NAME EQ work->dfw_name.c_str() - { - blob_id = X.RDB$TRIGGER_BLR; - type = (ISC_UINT64) X.RDB$TRIGGER_TYPE; - relation = MET_lookup_relation(tdbb, X.RDB$RELATION_NAME); - } - END_FOR - - // get any dependencies now by parsing the blr - - if ((relation || (type & TRIGGER_TYPE_MASK) != TRIGGER_TYPE_DML) && !blob_id.isEmpty()) - { - Statement* statement = NULL; - // Nickolay Samofatov: allocate statement memory pool... - MemoryPool* new_pool = attachment->createPool(); - USHORT par_flags; - - if ((type & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DML) - par_flags = (USHORT) ((type & 1) ? csb_pre_trigger : csb_post_trigger); - else - par_flags = 0; - - Jrd::ContextPoolHolder context(tdbb, new_pool); - const MetaName depName(work->dfw_name); - MET_get_dependencies(tdbb, relation, NULL, 0, NULL, &blob_id, (compile ? &statement : NULL), - NULL, depName, obj_trigger, par_flags, transaction); - - if (statement) - statement->release(tdbb); - else - attachment->deletePool(new_pool); - } -} - - -static Format* make_format(thread_db* tdbb, jrd_rel* relation, USHORT* version, TemporaryField* stack) -{ -/************************************** - * - * m a k e _ f o r m a t - * - ************************************** - * - * Functional description - * Make a format block for a relation. - * - **************************************/ - TemporaryField* tfb; - - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - jrd_tra* sysTransaction = attachment->getSysTransaction(); - Database* dbb = tdbb->getDatabase(); - - // Figure out the highest field id and allocate a format block - - USHORT count = 0; - for (tfb = stack; tfb; tfb = tfb->tfb_next) - count = MAX(count, tfb->tfb_id); - - // For system tables, all supported formats are predefined and preloaded - // into the metadata cache, so we don't need them stored in RDB$FORMATS - - if (relation->isSystem()) - { - // Find and return the format matching new number of fields - - fb_assert(relation->rel_formats); - for (const auto format : *relation->rel_formats) - { - if (format->fmt_count == count + 1) - { - if (version) - *version = format->fmt_version; - - return format; - } - } - - // We should never get here - fb_assert(false); - } - - Format* format = Format::newFormat(*relation->rel_pool, count + 1); - format->fmt_version = version ? *version : 0; - - // Fill in the format block from the temporary field blocks - - for (tfb = stack; tfb; tfb = tfb->tfb_next) - { - dsc* desc = &format->fmt_desc[tfb->tfb_id]; - if (tfb->tfb_flags & TFB_array) - { - desc->dsc_dtype = dtype_array; - desc->dsc_length = sizeof(ISC_QUAD); - } - else - *desc = tfb->tfb_desc; - if (tfb->tfb_flags & TFB_computed) - desc->dsc_dtype |= COMPUTED_FLAG; - - impure_value& defRef = format->fmt_defaults[tfb->tfb_id]; - defRef = tfb->tfb_default; - - if (tfb->tfb_default.vlu_string) - { - fb_assert(defRef.vlu_desc.dsc_dtype == dtype_text); - defRef.vlu_desc.dsc_address = defRef.vlu_string->str_data; - } - else - defRef.vlu_desc.dsc_address = (UCHAR*) &defRef.vlu_misc; - } - - // Compute the offsets of the various fields - - ULONG offset = FLAG_BYTES(count); - - count = 0; - for (Format::fmt_desc_iterator desc2 = format->fmt_desc.begin(); - count < format->fmt_count; - ++count, ++desc2) - { - if (desc2->dsc_dtype & COMPUTED_FLAG) - { - desc2->dsc_dtype &= ~COMPUTED_FLAG; - continue; - } - if (desc2->dsc_dtype) - { - offset = MET_align(&(*desc2), offset); - desc2->dsc_address = (UCHAR *) (IPTR) offset; - offset += desc2->dsc_length; - } - } - - // Release the temporary field blocks - - while ( (tfb = stack) ) - { - stack = tfb->tfb_next; - delete tfb; - } - - if (offset > MAX_RECORD_SIZE) - { - delete format; - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_rec_size_err) << Arg::Num(offset) << - Arg::Gds(isc_table_name) << Arg::Str(relation->rel_name)); - // Msg361: new record size of %ld bytes is too big - } - - format->fmt_length = offset; - - Format* old_format; - if (format->fmt_version && - (old_format = MET_format(tdbb, relation, (format->fmt_version - 1))) && - (formatsAreEqual(old_format, format))) - { - delete format; - *version = old_format->fmt_version; - return old_format; - } - - // Link the format block into the world - - vec* vector = relation->rel_formats = - vec::newVector(*relation->rel_pool, relation->rel_formats, format->fmt_version + 1); - (*vector)[format->fmt_version] = format; - - // Store format in system relation - - AutoCacheRequest request(tdbb, irq_format3, IRQ_REQUESTS); - - STORE(REQUEST_HANDLE request) - FMTS IN RDB$FORMATS - { - FMTS.RDB$FORMAT = format->fmt_version; - FMTS.RDB$RELATION_ID = relation->rel_id; - blb* blob = blb::create(tdbb, sysTransaction, &FMTS.RDB$DESCRIPTOR); - - // Use generic representation of formats with 32-bit offsets - - Firebird::Array odsDescs; - Ods::Descriptor* odsDesc = odsDescs.getBuffer(format->fmt_count); - - for (Format::fmt_desc_const_iterator desc = format->fmt_desc.begin(); - desc < format->fmt_desc.end(); ++desc, ++odsDesc) - { - *odsDesc = *desc; - } - - HalfStaticArray buffer; - - buffer.add(UCHAR(format->fmt_count)); - buffer.add(UCHAR(format->fmt_count >> 8)); - - buffer.add((UCHAR*) odsDescs.begin(), odsDescs.getCount() * sizeof(Ods::Descriptor)); - - const FB_SIZE_T pos = buffer.getCount(); - buffer.add(0); - buffer.add(0); - - USHORT i = 0, dflCount = 0; - for (Format::fmt_defaults_iterator impure = format->fmt_defaults.begin(); - impure != format->fmt_defaults.end(); ++impure, ++i) - { - if (!impure->vlu_desc.isUnknown()) - { - dsc desc = impure->vlu_desc; - desc.dsc_address = NULL; - - Ods::Descriptor odsDflDesc = desc; - - buffer.add(UCHAR(i)); - buffer.add(UCHAR(i >> 8)); - buffer.add((UCHAR*) &odsDflDesc, sizeof(odsDflDesc)); - buffer.add(impure->vlu_desc.dsc_address, impure->vlu_desc.dsc_length); - - ++dflCount; - } - } - - buffer[pos] = UCHAR(dflCount); - buffer[pos + 1] = UCHAR(dflCount >> 8); - - blob->BLB_put_data(tdbb, buffer.begin(), buffer.getCount()); - blob->BLB_close(tdbb); - } - END_STORE - - return format; -} - - -static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ -/************************************** - * - * m a k e _ v e r s i o n - * - ************************************** - * - * Functional description - * Make a new format version for a relation. While we're at it, make - * sure all fields have id's. If the relation is a view, make a - * a format anyway -- used for view updates. - * - * While we're in the vicinity, also check the updatability of fields. - * - **************************************/ - TemporaryField* stack; - TemporaryField* external; - jrd_rel* relation; - //bid blob_id; - //blob_id.clear(); - - USHORT n; - int physical_fields = 0; - bool external_flag = false; - bool computed_field; - TrigVector* triggers[TRIGGER_MAX]; - - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - Database* dbb = tdbb->getDatabase(); - bool null_view; - - switch (phase) - { - case 1: - case 2: - return true; - - case 3: - relation = NULL; - stack = external = NULL; - computed_field = false; - - for (n = 0; n < TRIGGER_MAX; n++) { - triggers[n] = NULL; - } - - AutoCacheRequest request_fmt1(tdbb, irq_format1, IRQ_REQUESTS); - - // User transaction may be safely used instead of system cause - // all required dirty reads are performed in metadata cache. AP-2008. - - FOR(REQUEST_HANDLE request_fmt1 TRANSACTION_HANDLE transaction) - REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ work->dfw_name.c_str() - { - relation = MET_lookup_relation_id(tdbb, REL.RDB$RELATION_ID, false); - if (!relation) - return false; - - const bid blob_id = REL.RDB$VIEW_BLR; - null_view = blob_id.isEmpty(); - external_flag = REL.RDB$EXTERNAL_FILE[0]; - - if (REL.RDB$VIEW_BLR.NULL) - { - if (REL.RDB$FORMAT == MAX_TABLE_VERSIONS) - raiseTooManyVersionsError(obj_relation, work->dfw_name); - } - else - { - if (REL.RDB$FORMAT == MAX_VIEW_VERSIONS) - raiseTooManyVersionsError(obj_view, work->dfw_name); - } - - MODIFY REL USING - blb* blob = blb::create(tdbb, transaction, &REL.RDB$RUNTIME); - AutoCacheRequest request_fmtx(tdbb, irq_format2, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request_fmtx TRANSACTION_HANDLE transaction) - RFR IN RDB$RELATION_FIELDS CROSS - FLD IN RDB$FIELDS WITH - RFR.RDB$RELATION_NAME EQ work->dfw_name.c_str() AND - RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME - SORTED BY RFR.RDB$FIELD_POSITION - { - // Update RFR to reflect new fields id - - if (!RFR.RDB$FIELD_ID.NULL && RFR.RDB$FIELD_ID >= REL.RDB$FIELD_ID) - REL.RDB$FIELD_ID = RFR.RDB$FIELD_ID + 1; - - // force recalculation of RDB$UPDATE_FLAG if field is calculated - if (!FLD.RDB$COMPUTED_BLR.isEmpty()) - { - RFR.RDB$UPDATE_FLAG.NULL = TRUE; - computed_field = true; - } - - if (RFR.RDB$FIELD_ID.NULL || RFR.RDB$UPDATE_FLAG.NULL) - { - MODIFY RFR USING - if (RFR.RDB$FIELD_ID.NULL) - { - if (external_flag) - { - RFR.RDB$FIELD_ID = RFR.RDB$FIELD_POSITION; - // RFR.RDB$FIELD_POSITION.NULL is - // needed to be referenced in the - // code somewhere for GPRE to include - // this field in the structures that - // it generates at the top of this func. - RFR.RDB$FIELD_ID.NULL = RFR.RDB$FIELD_POSITION.NULL; - } - else - { - RFR.RDB$FIELD_ID = REL.RDB$FIELD_ID; - RFR.RDB$FIELD_ID.NULL = FALSE; - - // If the table is being altered, check validity of NOT NULL fields. - if (!REL.RDB$FORMAT.NULL) - { - bool notNull = (RFR.RDB$NULL_FLAG.NULL ? - (FLD.RDB$NULL_FLAG.NULL ? false : (bool) FLD.RDB$NULL_FLAG) : - (bool) RFR.RDB$NULL_FLAG); - - if (notNull) - { - dsc desc; - desc.makeText(static_cast(strlen(REL.RDB$RELATION_NAME)), - CS_METADATA, (UCHAR*) REL.RDB$RELATION_NAME); - - DeferredWork* work = DFW_post_work(transaction, - dfw_check_not_null, &desc, 0); - SortedArray& ids = DFW_get_ids(work); - - FB_SIZE_T pos; - if (!ids.find(RFR.RDB$FIELD_ID, pos)) - ids.insert(pos, RFR.RDB$FIELD_ID); - } - } - } - - REL.RDB$FIELD_ID++; - } - if (RFR.RDB$UPDATE_FLAG.NULL) - { - RFR.RDB$UPDATE_FLAG.NULL = FALSE; - RFR.RDB$UPDATE_FLAG = 1; - if (!FLD.RDB$COMPUTED_BLR.isEmpty()) - { - RFR.RDB$UPDATE_FLAG = 0; - } - if (!null_view && REL.RDB$DBKEY_LENGTH > 8) - { - AutoRequest temp; - RFR.RDB$UPDATE_FLAG = 0; - FOR(REQUEST_HANDLE temp) X IN RDB$TRIGGERS WITH - X.RDB$RELATION_NAME EQ work->dfw_name.c_str() AND - X.RDB$TRIGGER_TYPE EQ 1 - { - RFR.RDB$UPDATE_FLAG = 1; - } - END_FOR - } - } - END_MODIFY - } - - // Store stuff in field summary - - n = RFR.RDB$FIELD_ID; - put_summary_record(tdbb, blob, RSR_field_id, (UCHAR*)&n, sizeof(n)); - put_summary_record(tdbb, blob, RSR_field_name, (UCHAR*) RFR.RDB$FIELD_NAME, - fb_utils::name_length(RFR.RDB$FIELD_NAME)); - if (!FLD.RDB$COMPUTED_BLR.isEmpty() && !RFR.RDB$VIEW_CONTEXT) - { - put_summary_blob(tdbb, blob, RSR_computed_blr, &FLD.RDB$COMPUTED_BLR, transaction); - } - else if (!null_view) - { - n = RFR.RDB$VIEW_CONTEXT; - put_summary_record(tdbb, blob, RSR_view_context, (UCHAR*)&n, sizeof(n)); - put_summary_record(tdbb, blob, RSR_base_field, (UCHAR*) RFR.RDB$BASE_FIELD, - fb_utils::name_length(RFR.RDB$BASE_FIELD)); - } - put_summary_blob(tdbb, blob, RSR_missing_value, &FLD.RDB$MISSING_VALUE, transaction); - - bid* defaultValue = RFR.RDB$DEFAULT_VALUE.isEmpty() ? - &FLD.RDB$DEFAULT_VALUE : &RFR.RDB$DEFAULT_VALUE; - - put_summary_blob(tdbb, blob, RSR_default_value, defaultValue, transaction); - put_summary_blob(tdbb, blob, RSR_validation_blr, &FLD.RDB$VALIDATION_BLR, transaction); - - bool notNull = (RFR.RDB$NULL_FLAG.NULL ? - (FLD.RDB$NULL_FLAG.NULL ? false : (bool) FLD.RDB$NULL_FLAG) : - (bool) RFR.RDB$NULL_FLAG); - - if (notNull) - { - put_summary_record(tdbb, blob, RSR_field_not_null, - nonnull_validation_blr, sizeof(nonnull_validation_blr)); - } - - n = fb_utils::name_length(RFR.RDB$SECURITY_CLASS); - if (!RFR.RDB$SECURITY_CLASS.NULL && n) - { - put_summary_record(tdbb, blob, RSR_security_class, - (UCHAR*) RFR.RDB$SECURITY_CLASS, n); - } - - n = fb_utils::name_length(RFR.RDB$GENERATOR_NAME); - if (!RFR.RDB$GENERATOR_NAME.NULL && n) - { - put_summary_record(tdbb, blob, RSR_field_generator_name, - (UCHAR*) RFR.RDB$GENERATOR_NAME, n); - } - - n = RFR.RDB$IDENTITY_TYPE; - if (!RFR.RDB$IDENTITY_TYPE.NULL) - put_summary_record(tdbb, blob, RSR_field_identity_type, (UCHAR*) &n, sizeof(n)); - - // Make a temporary field block - - TemporaryField* tfb = FB_NEW_POOL(*tdbb->getDefaultPool()) TemporaryField; - tfb->tfb_next = stack; - stack = tfb; - - // for text data types, grab the CHARACTER_SET and - // COLLATION to give the type of international text - - if (FLD.RDB$CHARACTER_SET_ID.NULL) - FLD.RDB$CHARACTER_SET_ID = CS_NONE; - - SSHORT collation = COLLATE_NONE; // codepoint collation - if (!FLD.RDB$COLLATION_ID.NULL) - collation = FLD.RDB$COLLATION_ID; - if (!RFR.RDB$COLLATION_ID.NULL) - collation = RFR.RDB$COLLATION_ID; - - if (!DSC_make_descriptor(&tfb->tfb_desc, FLD.RDB$FIELD_TYPE, - FLD.RDB$FIELD_SCALE, - FLD.RDB$FIELD_LENGTH, - FLD.RDB$FIELD_SUB_TYPE, - FLD.RDB$CHARACTER_SET_ID, collation)) - { - if (null_view && REL.RDB$FORMAT.NULL) - DPM_delete_relation(tdbb, relation); - - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_random) << Arg::Str(work->dfw_name)); - } - - // Make sure the text type specified is implemented - if (!validate_text_type(tdbb, tfb)) - { - if (null_view && REL.RDB$FORMAT.NULL) - DPM_delete_relation(tdbb, relation); - - ERR_post_nothrow(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_random) << Arg::Str(work->dfw_name)); - INTL_texttype_lookup(tdbb, - (DTYPE_IS_TEXT(tfb->tfb_desc.dsc_dtype) ? - tfb->tfb_desc.dsc_ttype() : tfb->tfb_desc.dsc_blob_ttype())); // should punt - ERR_punt(); // if INTL_texttype_lookup hasn't punt - } - - // Store the default value. - - memset(&tfb->tfb_default, 0, sizeof(tfb->tfb_default)); - - if (notNull && !defaultValue->isEmpty()) - { - Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); - Statement* defaultStatement = NULL; - try - { - ValueExprNode* defaultNode = static_cast(MET_parse_blob( - tdbb, relation, defaultValue, NULL, &defaultStatement, false, false)); - - Request* const defaultRequest = defaultStatement->findRequest(tdbb); - - // Attention: this is scoped to the end of this "try". - AutoSetRestore2 autoRequest(tdbb, - &thread_db::getRequest, &thread_db::setRequest, defaultRequest); - - defaultRequest->validateTimeStamp(); - - dsc* result = nullptr; - { // scope - Firebird::Cleanup detach([&defaultRequest] {TRA_detach_request(defaultRequest);}); - TRA_attach_request(transaction, defaultRequest); - result = EVL_expr(tdbb, defaultRequest, defaultNode); - } - - if (result) - { - dsc desc = *result; - MoveBuffer buffer; - - if (desc.isText() || desc.isBlob()) - { - UCHAR* ptr = NULL; - const int len = MOV_make_string2(tdbb, &desc, tfb->tfb_desc.getCharSet(), - &ptr, buffer, true); - fb_assert(ULONG(len) < ULONG(MAX_USHORT)); - desc.makeText(len, tfb->tfb_desc.getCharSet(), ptr); - } - - impure_value tempValue; - MoveBuffer tempBuffer; - - if (!tfb->tfb_desc.isBlob() && !DSC_EQUIV(result, &tfb->tfb_desc, false)) - { - tempValue.vlu_desc = tfb->tfb_desc; - - if (tfb->tfb_desc.isText()) - tempValue.vlu_desc.dsc_address = tempBuffer.getBuffer(tfb->tfb_desc.dsc_length); - else - tempValue.vlu_desc.dsc_address = (UCHAR*) &tempValue.vlu_misc; - - try - { - MOV_move(tdbb, &desc, &tempValue.vlu_desc); - desc = tempValue.vlu_desc; - } - catch (const status_exception&) - { - if (!tdbb->getAttachment()->isGbak()) - throw; - - // If we're restoring a database, ignore the error and use the original desc. - fb_utils::init_status(tdbb->tdbb_status_vector); - } - } - - EVL_make_value(tdbb, &desc, &tfb->tfb_default, relation->rel_pool); - } - } - catch (const Exception&) - { - if (defaultStatement) - defaultStatement->release(tdbb); - throw; - } - - defaultStatement->release(tdbb); - } - - // dimitr: view fields shouldn't be marked as computed - if (null_view && !FLD.RDB$COMPUTED_BLR.isEmpty()) - tfb->tfb_flags |= TFB_computed; - else - ++physical_fields; - - tfb->tfb_id = RFR.RDB$FIELD_ID; - - if ((n = FLD.RDB$DIMENSIONS)) - setup_array(tdbb, blob, FLD.RDB$FIELD_NAME, n, tfb); - - if (external_flag) - { - tfb = FB_NEW_POOL(*tdbb->getDefaultPool()) TemporaryField; - tfb->tfb_next = external; - external = tfb; - fb_assert(FLD.RDB$EXTERNAL_TYPE <= MAX_UCHAR); - tfb->tfb_desc.dsc_dtype = (UCHAR)FLD.RDB$EXTERNAL_TYPE; - fb_assert(FLD.RDB$EXTERNAL_SCALE >= MIN_SCHAR && - FLD.RDB$EXTERNAL_SCALE <= MAX_SCHAR); - tfb->tfb_desc.dsc_scale = (SCHAR)FLD.RDB$EXTERNAL_SCALE; - tfb->tfb_desc.dsc_length = FLD.RDB$EXTERNAL_LENGTH; - tfb->tfb_id = RFR.RDB$FIELD_ID; - } - } - END_FOR - - if (null_view && !physical_fields) - { - if (REL.RDB$FORMAT.NULL) - DPM_delete_relation(tdbb, relation); - - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_table_name) << Arg::Str(work->dfw_name) << - Arg::Gds(isc_must_have_phys_field)); - } - - blob = setup_triggers(tdbb, relation, null_view, triggers, blob); - - blob->BLB_close(tdbb); - USHORT version = REL.RDB$FORMAT.NULL ? 0 : REL.RDB$FORMAT; - version++; - relation->rel_current_format = make_format(tdbb, relation, &version, stack); - REL.RDB$FORMAT.NULL = FALSE; - REL.RDB$FORMAT = version; - - if (!null_view) - { - // update the dbkey length to include each of the base relations - - REL.RDB$DBKEY_LENGTH = 0; - - AutoRequest handle; - FOR(REQUEST_HANDLE handle) - Z IN RDB$VIEW_RELATIONS - CROSS R IN RDB$RELATIONS OVER RDB$RELATION_NAME - WITH Z.RDB$VIEW_NAME = work->dfw_name.c_str() - { - REL.RDB$DBKEY_LENGTH += R.RDB$DBKEY_LENGTH; - } - END_FOR - } - END_MODIFY - } - END_FOR - - // If we didn't find the relation, it is probably being dropped - - if (!relation) - return false; - - // We have just loaded the triggers onto the local vector triggers. - // It's now time to place them at their rightful place inside the relation block. - relation->replaceTriggers(tdbb, triggers); - - // in case somebody changed the view definition or a computed - // field, reset the dependencies by deleting the current ones - // and setting a flag for MET_scan_relation to find the new ones - - if (!null_view) - MET_delete_dependencies(tdbb, work->dfw_name, obj_view, transaction); - - { // begin scope - const DeferredWork* arg = work->findArg(dfw_arg_force_computed); - if (arg) - { - computed_field = true; - MET_delete_dependencies(tdbb, arg->dfw_name, obj_computed, transaction); - } - } // end scope - - if (!null_view || computed_field) - relation->rel_flags |= REL_get_dependencies; - - if (external_flag) - { - AutoRequest temp; - FOR(REQUEST_HANDLE temp) FMTS IN RDB$FORMATS WITH - FMTS.RDB$RELATION_ID EQ relation->rel_id AND - FMTS.RDB$FORMAT EQ 0 - { - ERASE FMTS; - } - END_FOR - - make_format(tdbb, relation, 0, external); - } - - relation->rel_flags &= ~REL_scanned; - DFW_post_work(transaction, dfw_scan_relation, NULL, relation->rel_id); - - // signal others about new format presence - LCK_lock(tdbb, relation->rel_rescan_lock, LCK_EX, LCK_WAIT); - LCK_release(tdbb, relation->rel_rescan_lock); - - break; - } - - return false; -} - - -static bool modify_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ -/************************************** - * - * m o d i f y _ t r i g g e r - * - ************************************** - * - * Functional description - * Perform required actions when modifying trigger. - * - **************************************/ - - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - Jrd::Attachment* attachment = tdbb->getAttachment(); - - switch (phase) - { - case 1: - case 2: - return true; - - case 3: - { - bool compile = !work->findArg(dfw_arg_check_blr); - - // get rid of old dependencies, bring in the new - MET_delete_dependencies(tdbb, work->dfw_name, obj_trigger, transaction); - get_trigger_dependencies(work, compile, transaction); - } - return true; - - case 4: - { - const DeferredWork* arg = work->findArg(dfw_arg_rel_name); - if (!arg) - { - arg = work->findArg(dfw_arg_trg_type); - fb_assert(arg); - - // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT - if (arg && (arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) - { - unsigned triggerKind = arg->dfw_id & ~TRIGGER_TYPE_DB; - MET_release_triggers(tdbb, &tdbb->getAttachment()->att_triggers[triggerKind], true); - MET_load_db_triggers(tdbb, triggerKind); - } - else if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) - { - MET_release_triggers(tdbb, &tdbb->getAttachment()->att_ddl_triggers, true); - MET_load_ddl_triggers(tdbb); - } - } - } - - { // scope - const DeferredWork* arg = work->findArg(dfw_arg_check_blr); - if (arg) - { - const MetaName relation_name(arg->dfw_name); - SSHORT valid_blr = FALSE; - - try - { - jrd_rel* relation = MET_lookup_relation(tdbb, relation_name); - - if (relation) - { - // remove cached triggers from relation - relation->rel_flags &= ~REL_scanned; - MET_scan_relation(tdbb, relation); - - TrigVector* triggers[TRIGGER_MAX]; - - for (int i = 0; i < TRIGGER_MAX; ++i) - triggers[i] = NULL; - - MemoryPool* new_pool = attachment->createPool(); - try - { - Jrd::ContextPoolHolder context(tdbb, new_pool); - - MET_load_trigger(tdbb, relation, work->dfw_name, triggers); - - for (int i = 0; i < TRIGGER_MAX; ++i) - { - if (triggers[i]) - { - for (FB_SIZE_T j = 0; j < triggers[i]->getCount(); ++j) - (*triggers[i])[j].compile(tdbb); - - MET_release_triggers(tdbb, &triggers[i], true); - } - } - - valid_blr = TRUE; - } - catch (const Firebird::Exception&) - { - attachment->deletePool(new_pool); - throw; - } - - attachment->deletePool(new_pool); - } - } - catch (const Firebird::Exception&) - { - } - - AutoCacheRequest request(tdbb, irq_trg_validate, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - TRG IN RDB$TRIGGERS WITH - TRG.RDB$TRIGGER_NAME EQ work->dfw_name.c_str() AND TRG.RDB$TRIGGER_BLR NOT MISSING - { - MODIFY TRG USING - TRG.RDB$VALID_BLR = valid_blr; - TRG.RDB$VALID_BLR.NULL = FALSE; - END_MODIFY - } - END_FOR - } - } // scope - break; - } - - return false; -} - - -static void put_summary_blob(thread_db* tdbb, blb* blob, rsr_t type, bid* blob_id, jrd_tra* transaction) -{ -/************************************** - * - * p u t _ s u m m a r y _ b l o b - * - ************************************** - * - * Functional description - * Put an attribute record to the relation summary blob. - * - **************************************/ - - SET_TDBB(tdbb); - - if (blob_id->isEmpty()) // If blob is null, don't bother. - return; - - // Go ahead and open blob - blb* blr = blb::open(tdbb, transaction, blob_id); - - ULONG length = blr->blb_length; - // We cannot deal with chunks longer than a max blob segment (minus one) - fb_assert(length < MAX_USHORT); - - HalfStaticArray buffer; - UCHAR* p = buffer.getBuffer(length + 1); - *p++ = (UCHAR) type; - - length = blr->BLB_get_data(tdbb, p, length); - - blob->BLB_put_segment(tdbb, buffer.begin(), length + 1); -} - - -static void put_summary_record(thread_db* tdbb, - blb* blob, - rsr_t type, - const UCHAR* data, - ULONG length) -{ -/************************************** - * - * p u t _ s u m m a r y _ r e c o r d - * - ************************************** - * - * Functional description - * Put an attribute record to the relation summary blob. - * - **************************************/ - // We cannot deal with chunks longer than a max blob segment (minus one) - fb_assert(length < MAX_USHORT); - - SET_TDBB(tdbb); - - HalfStaticArray buffer; - UCHAR* p = buffer.getBuffer(length + 1); - *p++ = (UCHAR) type; - memcpy(p, data, length); - - blob->BLB_put_segment(tdbb, buffer.begin(), length + 1); -} - - -static bool scan_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) -{ -/************************************** - * - * s c a n _ r e l a t i o n - * - ************************************** - * - * Functional description - * Call MET_scan_relation with the appropriate - * relation. - * - **************************************/ - - SET_TDBB(tdbb); - - switch (phase) - { - case 1: - case 2: - return true; - - case 3: - // dimitr: I suspect that nobody expects an updated format to - // appear at stage 3, so the logic would work reliably - // if this line is removed (and hence we rely on the - // 4th stage only). But I leave it here for the time being. - MET_scan_relation(tdbb, MET_relation(tdbb, work->dfw_id)); - return true; - - case 4: - MET_scan_relation(tdbb, MET_relation(tdbb, work->dfw_id)); - break; - } - - return false; -} - - -static bool change_repl_state(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) -{ -/************************************** - * - * c h a n g e _ r e p l _ s t a t e - * - ************************************** - * - * Functional description - * Signal other processes to refresh their replication state. - * - **************************************/ - SET_TDBB(tdbb); - Database* const dbb = tdbb->getDatabase(); - Attachment* const attachment = tdbb->getAttachment(); - - switch (phase) - { - case 1: - case 2: - case 3: - return true; - - case 4: - if (work->dfw_id == 0) - { - // replication state is changed - dbb->invalidateReplState(tdbb, true); - } - else - { - // replication set is changed - attachment->invalidateReplSet(tdbb, true); - } - break; - } + break; + } return false; } - - -#ifdef NOT_USED_OR_REPLACED -static bool shadow_defined(thread_db* tdbb) -{ -/************************************** - * - * s h a d o w _ d e f i n e d - * - ************************************** - * - * Functional description - * Return true if any shadows have been has been defined - * for the database else return false. - * - **************************************/ - - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - - bool result = false; - AutoRequest handle; - - FOR(REQUEST_HANDLE handle) FIRST 1 X IN RDB$FILES - WITH X.RDB$SHADOW_NUMBER > 0 - { - result = true; - } - END_FOR - - return result; -} -#endif - - -static void setup_array(thread_db* tdbb, blb* blob, const TEXT* field_name, USHORT n, - TemporaryField* tfb) -{ -/************************************** - * - * s e t u p _ a r r a y - * - ************************************** - * - * Functional description - * - * setup an array descriptor in a tfb - * - **************************************/ - - SLONG stuff[256]; - - put_summary_record(tdbb, blob, RSR_dimensions, (UCHAR*) &n, sizeof(n)); - tfb->tfb_flags |= TFB_array; - Ods::InternalArrayDesc* array = reinterpret_cast(stuff); - MOVE_CLEAR(array, (SLONG) sizeof(Ods::InternalArrayDesc)); - array->iad_dimensions = n; - array->iad_struct_count = 1; - array->iad_rpt[0].iad_desc = tfb->tfb_desc; - get_array_desc(tdbb, field_name, array); - put_summary_record(tdbb, blob, RSR_array_desc, (UCHAR*) array, array->iad_length); -} - - -static blb* setup_triggers(thread_db* tdbb, jrd_rel* relation, bool null_view, - TrigVector** triggers, blb* blob) -{ -/************************************** - * - * s e t u p _ t r i g g e r s - * - ************************************** - * - * Functional description - * - * Get the triggers in the right order, which appears - * to be system triggers first, then user triggers, - * then triggers that implement check constraints. - * - * BUG #8458: Check constraint triggers have to be loaded - * (and hence executed) after the user-defined - * triggers because user-defined triggers can modify - * the values being inserted or updated so that - * the end values stored in the database don't - * fulfill the check constraint. - * - **************************************/ - if (!relation) - return blob; - - Jrd::Attachment* attachment = tdbb->getAttachment(); - - // system triggers - - AutoCacheRequest request_fmtx(tdbb, irq_format4, IRQ_REQUESTS); - - FOR (REQUEST_HANDLE request_fmtx) - TRG IN RDB$TRIGGERS - WITH TRG.RDB$RELATION_NAME = relation->rel_name.c_str() - AND TRG.RDB$SYSTEM_FLAG = 1 - SORTED BY TRG.RDB$TRIGGER_SEQUENCE - { - if (!TRG.RDB$TRIGGER_INACTIVE) - setup_trigger_details(tdbb, relation, blob, triggers, TRG.RDB$TRIGGER_NAME, null_view); - } - END_FOR - - // user triggers - - request_fmtx.reset(tdbb, irq_format5, IRQ_REQUESTS); - - FOR (REQUEST_HANDLE request_fmtx) - TRG IN RDB$TRIGGERS - WITH TRG.RDB$RELATION_NAME EQ relation->rel_name.c_str() - AND TRG.RDB$SYSTEM_FLAG = 0 - AND (NOT ANY - CHK IN RDB$CHECK_CONSTRAINTS CROSS - RCN IN RDB$RELATION_CONSTRAINTS - WITH TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME - AND CHK.RDB$CONSTRAINT_NAME EQ RCN.RDB$CONSTRAINT_NAME - AND (RCN.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT - OR RCN.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) - ) - SORTED BY TRG.RDB$TRIGGER_SEQUENCE - { - if (!TRG.RDB$TRIGGER_INACTIVE) - setup_trigger_details(tdbb, relation, blob, triggers, TRG.RDB$TRIGGER_NAME, null_view); - } - END_FOR - - // check constraint triggers. We're looking for triggers that belong - // to the table and are system triggers (i.e. system flag in (3, 4, 5)) - // or a user looking trigger that's involved in a check constraint - - request_fmtx.reset(tdbb, irq_format6, IRQ_REQUESTS); - - FOR (REQUEST_HANDLE request_fmtx) - TRG IN RDB$TRIGGERS - WITH TRG.RDB$RELATION_NAME = relation->rel_name.c_str() - AND (TRG.RDB$SYSTEM_FLAG BT fb_sysflag_check_constraint AND fb_sysflag_view_check - OR (TRG.RDB$SYSTEM_FLAG = 0 AND ANY - CHK IN RDB$CHECK_CONSTRAINTS CROSS - RCN IN RDB$RELATION_CONSTRAINTS - WITH TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME - AND CHK.RDB$CONSTRAINT_NAME EQ RCN.RDB$CONSTRAINT_NAME - AND (RCN.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT - OR RCN.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) - ) - ) - SORTED BY TRG.RDB$TRIGGER_SEQUENCE - { - if (!TRG.RDB$TRIGGER_INACTIVE) - setup_trigger_details(tdbb, relation, blob, triggers, TRG.RDB$TRIGGER_NAME, null_view); - } - END_FOR - - return blob; -} - - -static void setup_trigger_details(thread_db* tdbb, - jrd_rel* relation, - blb* blob, - TrigVector** triggers, - const TEXT* trigger_name, - bool null_view) -{ -/************************************** - * - * s e t u p _ t r i g g e r _ d e t a i l s - * - ************************************** - * - * Functional description - * Stuff trigger details in places. - * - * for a view, load the trigger temporarily -- - * this is inefficient since it will just be reloaded - * in MET_scan_relation () but it needs to be done - * in case the view would otherwise be non-updatable - * - **************************************/ - - put_summary_record(tdbb, blob, RSR_trigger_name, - (const UCHAR*) trigger_name, fb_utils::name_length(trigger_name)); - - if (!null_view) { - MET_load_trigger(tdbb, relation, trigger_name, triggers); - } -} - - -static bool validate_text_type(thread_db* tdbb, const TemporaryField* tfb) -{ -/************************************** - * - * v a l i d a t e _ t e x t _ t y p e - * - ************************************** - * - * Functional description - * Make sure the text type specified is implemented - * - **************************************/ - - if ((DTYPE_IS_TEXT (tfb->tfb_desc.dsc_dtype) && - !INTL_defined_type(tdbb, tfb->tfb_desc.dsc_ttype())) || - (tfb->tfb_desc.dsc_dtype == dtype_blob && tfb->tfb_desc.dsc_sub_type == isc_blob_text && - !INTL_defined_type(tdbb, tfb->tfb_desc.dsc_blob_ttype()))) - { - return false; - } - - return true; -} - - -static string get_string(const dsc* desc) -{ -/************************************** - * - * g e t _ s t r i n g - * - ************************************** - * - * Get string for a given descriptor. - * - **************************************/ - const char* str; - VaryStr temp;// Must hold largest metadata field or filename - - if (!desc) - { - return string(); - } - - // Find the actual length of the string, searching until the claimed - // end of the string, or the terminating \0, whichever comes first. - - USHORT length = MOV_make_string(JRD_get_thread_data(), desc, ttype_metadata, &str, &temp, sizeof(temp)); - - const char* p = str; - const char* const q = str + length; - while (p < q && *p) - { - ++p; - } - - // Trim trailing blanks (bug 3355) - - while (--p >= str && *p == ' ') - ; - length = (p + 1) - str; - - return string(str, length); -} diff --git a/src/jrd/dfw_proto.h b/src/jrd/dfw_proto.h index 1681e9d727b..c1629ad6ac6 100644 --- a/src/jrd/dfw_proto.h +++ b/src/jrd/dfw_proto.h @@ -24,14 +24,21 @@ #ifndef JRD_DFW_PROTO_H #define JRD_DFW_PROTO_H -#include "../jrd/btr.h" // defines SelectivityList +#include "../jrd/btr.h" // defines SelectivityList +#include "../jrd/intl.h" // defined TTypeId +#include "../jrd/Resources.h" namespace Jrd { enum dfw_t; + + class thread_db; + class jrd_tra; + class DeferredWork; } -USHORT DFW_assign_index_type(Jrd::thread_db*, const Jrd::MetaName&, SSHORT, SSHORT); +USHORT DFW_assign_index_type(Jrd::thread_db*, const Jrd::MetaName&, SSHORT, TTypeId); +void DFW_check_partners(Jrd::thread_db*, const MetaId); void DFW_delete_deferred(Jrd::jrd_tra*, SavNumber); Firebird::SortedArray& DFW_get_ids(Jrd::DeferredWork* work); void DFW_merge_work(Jrd::jrd_tra*, SavNumber, SavNumber); @@ -43,7 +50,10 @@ Jrd::DeferredWork* DFW_post_work(Jrd::jrd_tra*, Jrd::dfw_t, const Firebird::stri const Jrd::MetaName& package = NULL); Jrd::DeferredWork* DFW_post_work_arg(Jrd::jrd_tra*, Jrd::DeferredWork*, const dsc*, USHORT); Jrd::DeferredWork* DFW_post_work_arg(Jrd::jrd_tra*, Jrd::DeferredWork*, const dsc*, USHORT, Jrd::dfw_t); -void DFW_update_index(const TEXT*, USHORT, const Jrd::SelectivityList&, Jrd::jrd_tra*); +void DFW_raiseRelationInUseError(const Jrd::Cached::Relation*); void DFW_reset_icu(Jrd::thread_db*); +bool DFW_setupCollationAttributes(const Firebird::string& collationName, const Firebird::string& charSetName, + const Firebird::string& oldSpecificAttributes, Firebird::string& newSpecificAttributes, bool dropIcuInfo = false); +void DFW_update_index(const TEXT*, USHORT, const Jrd::SelectivityList&, Jrd::jrd_tra*); #endif // JRD_DFW_PROTO_H diff --git a/src/jrd/dpm.epp b/src/jrd/dpm.epp index 09eda67cd7f..fbb5ad5c32c 100644 --- a/src/jrd/dpm.epp +++ b/src/jrd/dpm.epp @@ -49,17 +49,19 @@ #include "../jrd/cch.h" #include "../jrd/pag.h" #include "../jrd/val.h" +#include "../jrd/met.h" #include "../jrd/vio_debug.h" #include "../jrd/cch_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/dpm_proto.h" #include "../jrd/err_proto.h" #include "../jrd/exe_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/ods_proto.h" #include "../jrd/pag_proto.h" +#include "../jrd/tpc_proto.h" #include "../jrd/replication/Publisher.h" #include "../common/StatusArg.h" @@ -74,14 +76,19 @@ using namespace Jrd; using namespace Ods; using namespace Firebird; +inline constexpr SSHORT MRK_commit = 256; // Table to go into pag_drop after transaction commit +inline constexpr SSHORT MRK_drop = 257; // Table to be dropped when OAT >= next transaction at mark-set time +inline constexpr SSHORT MRK_rollback = 258; // Table to be dropped after transaction rollback + +static void set_marker(thread_db*, SSHORT, SSHORT, TraNumber); static void check_swept(thread_db*, record_param*); static USHORT compress(thread_db*, data_page*); static void delete_tail(thread_db*, rhdf*, const USHORT, USHORT); static void fragment(thread_db*, record_param*, SSHORT, Compressor&, SSHORT, const jrd_tra*); -static void extend_relation(thread_db*, jrd_rel*, WIN*, const Jrd::RecordStorageType type); +static void extend_relation(thread_db*, Cached::Relation*, WIN*, const Jrd::RecordStorageType type); static UCHAR* find_space(thread_db*, record_param*, SSHORT, PageStack&, Record*, const Jrd::RecordStorageType type); static bool get_header(WIN*, USHORT, record_param*); -static pointer_page* get_pointer_page(thread_db*, jrd_rel*, RelationPages*, WIN*, ULONG, USHORT); +static pointer_page* get_pointer_page(thread_db*, RelationPermanent*, RelationPages*, WIN*, ULONG, USHORT); static rhd* locate_space(thread_db*, record_param*, SSHORT, PageStack&, Record*, const Jrd::RecordStorageType type); static void mark_full(thread_db*, record_param*); static void store_big_record(thread_db*, record_param*, PageStack&, Compressor&, const Jrd::RecordStorageType type); @@ -100,6 +107,32 @@ namespace return lock.release(); } + + inline int transactionState(thread_db* tdbb, TraNumber markedTran, Cached::Relation* rel, bool creating) + { + auto checkPresence = [tdbb, rel]()->bool + { + auto* jrel = rel->getObject(tdbb, MAX_TRA_NUMBER, CacheFlag::AUTOCREATE); + return jrel != nullptr; + }; + + return TipCache::traState(tdbb, markedTran, checkPresence, creating); + } + + inline ULONG seqPart(TraNumber transaction) + { + return transaction >> 32; + } + + inline ULONG numPart(TraNumber transaction) + { + return transaction & 0xFFFFFFFF; + } + + inline TraNumber transactionFromMarker(ULONG sequence, ULONG number) + { + return (TraNumber(sequence) << 32) + number; + } } @@ -152,7 +185,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 %" @@ -238,7 +271,7 @@ double DPM_cardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) // Get the number of data pages for this relation - const ULONG dataPages = DPM_data_pages(tdbb, relation); + const ULONG dataPages = DPM_data_pages(tdbb, getPermanent(relation)); // Calculate record count and total compressed record length // on the first data page @@ -254,7 +287,7 @@ double DPM_cardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) WIN window(relPages->rel_pg_space_id, -1); const pointer_page* ppage = - get_pointer_page(tdbb, relation, relPages, &window, sequence, LCK_read); + get_pointer_page(tdbb, getPermanent(relation), relPages, &window, sequence, LCK_read); if (!ppage) { BUGCHECK(243); @@ -302,7 +335,7 @@ double DPM_cardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) // Estimate total number of records for this relation if (!format) - format = MET_current(tdbb, relation); + format = relation->currentFormat(); static const double DEFAULT_COMPRESSION_RATIO = 0.5; @@ -348,7 +381,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, @@ -545,7 +578,7 @@ bool DPM_chain( thread_db* tdbb, record_param* org_rpb, record_param* new_rpb) } -void DPM_create_relation( thread_db* tdbb, jrd_rel* relation) +void DPM_create_relation( thread_db* tdbb, Cached::Relation* relation) { /************************************** * @@ -558,26 +591,57 @@ void DPM_create_relation( thread_db* tdbb, jrd_rel* relation) * **************************************/ SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - CHECK_DBB(dbb); #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); // 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*/); + + // Special record directing remove table when transaction rolled back + if (auto tra = tdbb->getTransaction()) + { + if (auto num = tra->tra_number) + set_marker(tdbb, relation->getId(), MRK_rollback, num); + } +} + + +void DPM_mark_relation( thread_db* tdbb, Cached::Relation* relation) +{ +/************************************** + * + * D P M _ m a r k _ r e l a t i o n + * + ************************************** + * + * Functional description + * Mark relation for commit. + * + **************************************/ + SET_TDBB(tdbb); + + // Add special record directing switch to MRK_drop state when transaction committed + if (auto tra = tdbb->getTransaction()) + { + if (auto num = tra->tra_number) + { + DPM_scan_marker(tdbb, relation->getId()); + set_marker(tdbb, relation->getId(), MRK_commit, num); + } + } } -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(); @@ -587,13 +651,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); @@ -607,7 +671,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(); @@ -618,14 +682,14 @@ 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(); } -ULONG DPM_data_pages(thread_db* tdbb, jrd_rel* relation) +ULONG DPM_data_pages(thread_db* tdbb, Cached::Relation* relation) { /************************************** * @@ -644,7 +708,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); @@ -833,7 +897,7 @@ void DPM_delete( thread_db* tdbb, record_param* rpb, ULONG prior_page) relPages = rpb->rpb_relation->getPages(tdbb); pwindow = WIN(relPages->rel_pg_space_id, -1); - if (!(ppage = get_pointer_page(tdbb, rpb->rpb_relation, relPages, &pwindow, + if (!(ppage = get_pointer_page(tdbb, getPermanent(rpb->rpb_relation), relPages, &pwindow, pp_sequence, LCK_write))) { BUGCHECK(245); // msg 245 pointer page disappeared in DPM_delete @@ -952,7 +1016,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. @@ -1009,7 +1073,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) { /************************************** * @@ -1032,7 +1096,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; } @@ -1042,7 +1106,7 @@ void DPM_delete_relation( thread_db* tdbb, jrd_rel* relation) } -void DPM_delete_relation_pages(Jrd::thread_db* tdbb, Jrd::jrd_rel* relation, +void DPM_delete_relation_pages(Jrd::thread_db* tdbb, Jrd::RelationPermanent* relation, Jrd::RelationPages* relPages) { SET_TDBB(tdbb); @@ -1054,12 +1118,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 > pages(*relation->rel_pool); + SortedArray > pages(relation->getPool()); for (ULONG sequence = 0; true; sequence++) { @@ -1149,7 +1213,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); @@ -1212,7 +1276,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 @@ -1277,7 +1341,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 @@ -1462,7 +1526,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); @@ -1491,7 +1555,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_orphan)) && - dpage->dpg_relation == rpb->rpb_relation->rel_id && + dpage->dpg_relation == rpb->rpb_relation->getId() && dpage->dpg_sequence == dpSequence && (dpage->dpg_count > 0); @@ -1508,7 +1572,7 @@ bool DPM_get(thread_db* tdbb, record_param* rpb, SSHORT lock_type) } // Find the pointer page, data page, and record - pointer_page* page = get_pointer_page(tdbb, rpb->rpb_relation, + pointer_page* page = get_pointer_page(tdbb, getPermanent(rpb->rpb_relation), relPages, window, pp_sequence, LCK_read); if (!page) @@ -1540,6 +1604,7 @@ bool DPM_get(thread_db* tdbb, record_param* rpb, SSHORT lock_type) ULONG DPM_get_blob(thread_db* tdbb, blb* blob, + jrd_rel* relation, RecordNumber record_number, bool delete_flag, ULONG prior_page) { /************************************** @@ -1562,15 +1627,14 @@ ULONG DPM_get_blob(thread_db* tdbb, CHECK_DBB(dbb); record_param rpb; - rpb.rpb_relation = blob->blb_relation; + rpb.rpb_relation = relation; rpb.getWindow(tdbb).win_flags = WIN_secondary; #ifdef VIO_DEBUG - jrd_rel* relation = blob->blb_relation; 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 @@ -1583,8 +1647,8 @@ ULONG DPM_get_blob(thread_db* tdbb, // record doesn't exist, or the record isn't a blob, give up and // let somebody else complain. - pointer_page* ppage = get_pointer_page(tdbb, blob->blb_relation, - blob->blb_relation->getPages(tdbb), &rpb.getWindow(tdbb), pp_sequence, LCK_read); + pointer_page* ppage = get_pointer_page(tdbb, relation->rel_perm, + relation->getPages(tdbb), &rpb.getWindow(tdbb), pp_sequence, LCK_read); if (!ppage) { blob->blb_flags |= BLB_damaged; @@ -1660,7 +1724,7 @@ ULONG DPM_get_blob(thread_db* tdbb, // We've been asked (nicely) to delete the blob. So do so. - rpb.rpb_relation = blob->blb_relation; + rpb.rpb_relation = relation; rpb.rpb_page = rpb.getWindow(tdbb).win_page.getPageNum(); rpb.rpb_line = line; DPM_delete(tdbb, &rpb, prior_page); @@ -1694,7 +1758,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); @@ -1704,10 +1768,10 @@ bool DPM_next(thread_db* tdbb, record_param* rpb, USHORT lock_type, FindNextReco { // Try to account for staggered execution of large sequential scans. - window->win_scans = rpb->rpb_relation->rel_scan_count - rpb->rpb_org_scans; + window->win_scans = getPermanent(rpb->rpb_relation)->rel_scan_count - rpb->rpb_org_scans; if (window->win_scans < 1) - window->win_scans = rpb->rpb_relation->rel_scan_count; + window->win_scans = getPermanent(rpb->rpb_relation)->rel_scan_count; } rpb->rpb_prior = NULL; @@ -1755,7 +1819,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_orphan)) && - dpage->dpg_relation == rpb->rpb_relation->rel_id && + dpage->dpg_relation == rpb->rpb_relation->getId() && dpage->dpg_sequence == dpSequence && (dpage->dpg_count > 0); @@ -1783,7 +1847,7 @@ bool DPM_next(thread_db* tdbb, record_param* rpb, USHORT lock_type, FindNextReco while (true) { - const pointer_page* ppage = get_pointer_page(tdbb, rpb->rpb_relation, + const pointer_page* ppage = get_pointer_page(tdbb, getPermanent(rpb->rpb_relation), relPages, window, pp_sequence, LCK_read); if (!ppage) BUGCHECK(249); // msg 249 pointer page vanished from DPM_next @@ -1871,7 +1935,7 @@ bool DPM_next(thread_db* tdbb, record_param* rpb, USHORT lock_type, FindNextReco if (scope == DPM_next_data_page) return false; - if (!(ppage = get_pointer_page(tdbb, rpb->rpb_relation, relPages, window, + if (!(ppage = get_pointer_page(tdbb, getPermanent(rpb->rpb_relation), relPages, window, pp_sequence, LCK_read))) { BUGCHECK(249); // msg 249 pointer page vanished from DPM_next @@ -1904,6 +1968,14 @@ bool DPM_next(thread_db* tdbb, record_param* rpb, USHORT lock_type, FindNextReco } +static void set_marker(thread_db* tdbb, SSHORT rel_id, SSHORT type, TraNumber transaction) +{ + fb_assert(type >= MRK_commit && type <= MRK_rollback); + + DPM_pages(tdbb, rel_id, type, seqPart(transaction), numPart(transaction)); +} + + void DPM_pages(thread_db* tdbb, SSHORT rel_id, int type, ULONG sequence, ULONG page) { /************************************** @@ -1980,7 +2052,7 @@ SLONG DPM_prefetch_bitmap(thread_db* tdbb, jrd_rel* relation, PageBitmap* bitmap DECOMPOSE(number, dbb->dbb_max_records, dp_sequence, line); DECOMPOSE(dp_sequence, dbb->dbb_dp_per_pp, pp_sequence, slot); - const pointer_page* ppage = get_pointer_page(tdbb, relation, &window, pp_sequence, LCK_read); + const pointer_page* ppage = get_pointer_page(tdbb, relation->rel_perm, &window, pp_sequence, LCK_read); if (!ppage) BUGCHECK(249); // msg 249 pointer page vanished from DPM_prefetch_bitmap @@ -2019,7 +2091,7 @@ ULONG DPM_pointer_pages(thread_db* tdbb, jrd_rel* relation) #ifdef VIO_DEBUG VIO_trace(DEBUG_TRACE_ALL, - "DPM_pointer_pages (relation %d)\n", relation->rel_id); + "DPM_pointer_pages (relation %d)\n", relation->getId()); #endif RelationPages* relPages = relation->getPages(tdbb); @@ -2029,7 +2101,7 @@ ULONG DPM_pointer_pages(thread_db* tdbb, jrd_rel* relation) while (true) { const pointer_page* ppage = - get_pointer_page(tdbb, relation, relPages, &window, sequence, LCK_read); + get_pointer_page(tdbb, getPermanent(relation), relPages, &window, sequence, LCK_read); if (!ppage) { @@ -2082,7 +2154,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 = MET_relation(tdbb, 0); + auto* relation = MetadataCache::lookupRelation(tdbb, 0u, CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); RelationPages* relPages = relation->getBasePages(); vcl** address = &relPages->rel_pages; @@ -2103,12 +2175,14 @@ void DPM_scan_pages( thread_db* tdbb) HalfStaticArray tipSeqList; HalfStaticArray genSeqList; + HalfStaticArray markList; AutoCacheRequest request(tdbb, irq_r_pages, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) X IN RDB$PAGES { - relation = MET_relation(tdbb, X.RDB$RELATION_ID); + relation = MetadataCache::lookupRelation(tdbb, X.RDB$RELATION_ID, + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); relPages = relation->getBasePages(); sequence = X.RDB$PAGE_SEQUENCE; @@ -2119,7 +2193,7 @@ void DPM_scan_pages( thread_db* tdbb) break; case pag_pointer: - relPages->rel_pages = vcl::newVector(*relation->rel_pool, relPages->rel_pages, sequence + 1); + relPages->rel_pages = vcl::newVector(relation->getPool(), relPages->rel_pages, sequence + 1); (*relPages->rel_pages)[sequence] = X.RDB$PAGE_NUMBER; break; @@ -2135,6 +2209,15 @@ void DPM_scan_pages( thread_db* tdbb) genSeqList[sequence] = X.RDB$PAGE_NUMBER; break; + case MRK_commit: + case MRK_drop: + case MRK_rollback: + if (!dbb->dbb_tip_cache) + dbb->dbb_flags |= DBB_rescan_pages; + else + markList.push(X.RDB$RELATION_ID); + break; + default: CORRUPT(257); // msg 257 bad record in RDB$PAGES } @@ -2146,6 +2229,103 @@ void DPM_scan_pages( thread_db* tdbb) if (const auto count = genSeqList.getCount()) dbb->copyKnownPages(pag_ids, count, genSeqList.begin()); + + for (auto id : markList) + DPM_scan_marker(tdbb, id); +} + + +void DPM_scan_marker(thread_db* tdbb, MetaId relId) +{ +/************************************** + * + * D P M _ s c a n _ m a r k e r + * + ************************************** + * + * Functional description + * Scan marker for particular relation. + * + **************************************/ + SET_TDBB(tdbb); + Jrd::Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + fb_assert(dbb->dbb_tip_cache); + + bool doDel = false; + auto relation = MetadataCache::lookupRelation(tdbb, relId, CacheFlag::AUTOCREATE | CacheFlag::NOSCAN | CacheFlag::ERASED); + fb_assert(relation); + if (!relation) + return; + + static CachedRequestId id1; + AutoCacheRequest request(tdbb, id1); + + FOR(REQUEST_HANDLE request) X IN RDB$PAGES + WITH X.RDB$RELATION_ID EQ relId + AND X.RDB$PAGE_TYPE GT MAX_UCHAR + { + switch (X.RDB$PAGE_TYPE) + { + case MRK_commit: + switch (transactionState(tdbb, transactionFromMarker(X.RDB$PAGE_SEQUENCE, X.RDB$PAGE_NUMBER), + relation, true)) + { + case tra_dead: + ERASE X; + break; + case tra_committed: + MODIFY X USING + { + auto traNum = TransactionNumber::next(tdbb); + X.RDB$PAGE_TYPE = MRK_drop; + X.RDB$PAGE_SEQUENCE = seqPart(traNum); + X.RDB$PAGE_NUMBER = numPart(traNum); + } + END_MODIFY + break; + } + break; + + case MRK_drop: + if (!dbb->dbb_tip_cache) + { + dbb->dbb_flags |= DBB_rescan_pages; + break; + } + if (tdbb->getDatabase()->dbb_oldest_active >= + transactionFromMarker(X.RDB$PAGE_SEQUENCE, X.RDB$PAGE_NUMBER)) + { + doDel = true; + } + break; + + case MRK_rollback: + if (!dbb->dbb_tip_cache) + { + dbb->dbb_flags |= DBB_rescan_pages; + break; + } + switch (transactionState(tdbb, transactionFromMarker(X.RDB$PAGE_SEQUENCE, X.RDB$PAGE_NUMBER), + relation, true)) + { + case tra_dead: + doDel = true; + break; + case tra_committed: + ERASE X; + break; + } + break; + + default: + CORRUPT(257); // msg 257 bad record in RDB$PAGES + } + } + END_FOR + + if (doDel) + DPM_delete_relation(tdbb, relation); } @@ -2170,7 +2350,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 @@ -2242,7 +2422,7 @@ void DPM_store( thread_db* tdbb, record_param* rpb, PageStack& stack, const Jrd: } -RecordNumber DPM_store_blob(thread_db* tdbb, blb* blob, Record* record) +RecordNumber DPM_store_blob(thread_db* tdbb, blb* blob, jrd_rel* relation, Record* record) { /************************************** * @@ -2259,10 +2439,9 @@ RecordNumber DPM_store_blob(thread_db* tdbb, blb* blob, Record* record) CHECK_DBB(dbb); #ifdef VIO_DEBUG - jrd_rel* relation = blob->blb_relation; 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 @@ -2279,7 +2458,7 @@ RecordNumber DPM_store_blob(thread_db* tdbb, blb* blob, Record* record) record_param rpb; //rpb.getWindow(tdbb).win_flags = 0; redundant. - rpb.rpb_relation = blob->blb_relation; + rpb.rpb_relation = relation; rpb.rpb_transaction_nr = tdbb->getTransaction()->tra_number; rpb.rpb_flags = rpb_blob; @@ -2336,7 +2515,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); @@ -2393,7 +2572,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 %" @@ -2530,7 +2709,7 @@ static void check_swept(thread_db* tdbb, record_param* rpb) line, slot, pp_sequence); pointer_page* ppage = - get_pointer_page(tdbb, rpb->rpb_relation, relPages, window, pp_sequence, LCK_read); + get_pointer_page(tdbb, getPermanent(rpb->rpb_relation), relPages, window, pp_sequence, LCK_read); if (!ppage) return; @@ -2782,7 +2961,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, @@ -2935,7 +3114,7 @@ static void fragment(thread_db* tdbb, } -static void extend_relation(thread_db* tdbb, jrd_rel* relation, WIN* window, const Jrd::RecordStorageType type) +static void extend_relation(thread_db* tdbb, Cached::Relation* relation, WIN* window, const Jrd::RecordStorageType type) { /************************************** * @@ -2957,7 +3136,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. @@ -3003,20 +3182,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; @@ -3053,7 +3232,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; @@ -3068,7 +3247,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); @@ -3114,7 +3293,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 } @@ -3285,7 +3464,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)); @@ -3309,7 +3488,7 @@ static bool get_header(WIN* window, USHORT line, record_param* rpb) static pointer_page* get_pointer_page(thread_db* tdbb, - jrd_rel* relation, RelationPages* relPages, + RelationPermanent* relation, RelationPages* relPages, WIN* window, ULONG sequence, USHORT lock) { /************************************** @@ -3349,14 +3528,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; @@ -3399,7 +3578,7 @@ static rhd* locate_space(thread_db* tdbb, rpb->rpb_number.decompose(dbb->dbb_max_records, dbb->dbb_dp_per_pp, line, slot, pp_sequence); const pointer_page* ppage = - get_pointer_page(tdbb, relation, relPages, window, pp_sequence, LCK_read); + get_pointer_page(tdbb, relation->rel_perm, relPages, window, pp_sequence, LCK_read); if (ppage) { @@ -3432,7 +3611,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); @@ -3479,7 +3658,7 @@ static rhd* locate_space(thread_db* tdbb, relPages->rel_sec_data_space = pp_sequence; const pointer_page* ppage = - get_pointer_page(tdbb, relation, relPages, window, pp_sequence, ppLock); + get_pointer_page(tdbb, relation->rel_perm, relPages, window, pp_sequence, ppLock); if (!ppage) BUGCHECK(254); // msg 254 pointer page vanished from relation list in locate_space @@ -3522,7 +3701,7 @@ static rhd* locate_space(thread_db* tdbb, CCH_RELEASE(tdbb, window); ppLock = LCK_write; - ppage = get_pointer_page(tdbb, relation, relPages, window, pp_sequence, ppLock); + ppage = get_pointer_page(tdbb, relation->rel_perm, relPages, window, pp_sequence, ppLock); if (!ppage) BUGCHECK(254); @@ -3592,7 +3771,7 @@ static rhd* locate_space(thread_db* tdbb, int i; for (i = 0; i < 20; ++i) { - extend_relation(tdbb, relation, window, type); + extend_relation(tdbb, relation->rel_perm, window, type); space = find_space(tdbb, rpb, size, stack, record, type); if (space) @@ -3615,7 +3794,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; @@ -3642,7 +3821,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, @@ -3669,7 +3848,7 @@ static void mark_full(thread_db* tdbb, record_param* rpb) pointer_page* ppage = 0; do { - ppage = get_pointer_page(tdbb, relation, relPages, &pp_window, pp_sequence, LCK_write); + ppage = get_pointer_page(tdbb, relation->rel_perm, relPages, &pp_window, pp_sequence, LCK_write); if (!ppage) BUGCHECK(256); // msg 256 pointer page vanished from mark_full @@ -3801,7 +3980,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. @@ -3827,7 +4006,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); diff --git a/src/jrd/dpm_proto.h b/src/jrd/dpm_proto.h index baa188c5306..a882ec53548 100644 --- a/src/jrd/dpm_proto.h +++ b/src/jrd/dpm_proto.h @@ -27,6 +27,7 @@ #include "../jrd/RecordNumber.h" #include "../jrd/sbm.h" #include "../jrd/vio_proto.h" +#include "../jrd/Resources.h" // fwd. decl. namespace Jrd @@ -59,16 +60,17 @@ void DPM_backout(Jrd::thread_db*, Jrd::record_param*); void DPM_backout_mark(Jrd::thread_db*, Jrd::record_param*, const Jrd::jrd_tra*); double DPM_cardinality(Jrd::thread_db*, Jrd::jrd_rel*, const Jrd::Format*); 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_create_relation(Jrd::thread_db*, Jrd::Cached::Relation*); +ULONG DPM_data_pages(Jrd::thread_db*, Jrd::Cached::Relation*); 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); +void DPM_mark_relation(Jrd::thread_db*, Jrd::Cached::Relation*); 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 @@ -77,11 +79,12 @@ SLONG DPM_prefetch_bitmap(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::PageBitmap*, SLON ULONG DPM_pointer_pages(Jrd::thread_db*, Jrd::jrd_rel*); void DPM_scan_pages(Jrd::thread_db*); void DPM_store(Jrd::thread_db*, Jrd::record_param*, Jrd::PageStack&, const Jrd::RecordStorageType type); -RecordNumber DPM_store_blob(Jrd::thread_db*, Jrd::blb*, Jrd::Record*); +RecordNumber DPM_store_blob(Jrd::thread_db*, Jrd::blb*, Jrd::jrd_rel*, Jrd::Record*); void DPM_rewrite_header(Jrd::thread_db*, Jrd::record_param*); +void DPM_scan_marker(Jrd::thread_db*, MetaId); 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 diff --git a/src/jrd/err.cpp b/src/jrd/err.cpp index 726aaccdc3c..a891ad9a895 100644 --- a/src/jrd/err.cpp +++ b/src/jrd/err.cpp @@ -303,7 +303,7 @@ static void post_nothrow(const unsigned lenToAdd, const ISC_STATUS* toAdd, FbSta } -void ERR_post(const Arg::StatusVector& v) +void ERR_post [[noreturn]] (const Arg::StatusVector& v) /************************************** * * E R R _ p o s t @@ -321,7 +321,7 @@ void ERR_post(const Arg::StatusVector& v) } -void ERR_punt() +void ERR_punt [[noreturn]] () { /************************************** * diff --git a/src/jrd/err_proto.h b/src/jrd/err_proto.h index 742db1fdf3d..da1bac7b533 100644 --- a/src/jrd/err_proto.h +++ b/src/jrd/err_proto.h @@ -53,10 +53,10 @@ void ERR_bugcheck_msg(const TEXT*); void ERR_soft_bugcheck(int, const TEXT*, int); void ERR_corrupt(int); void ERR_error(int); -void ERR_post(const Firebird::Arg::StatusVector& v); +void ERR_post [[noreturn]] (const Firebird::Arg::StatusVector& v); void ERR_post_nothrow(const Firebird::Arg::StatusVector& v, Jrd::FbStatusVector* statusVector = NULL); void ERR_post_nothrow(const Firebird::IStatus* v, Jrd::FbStatusVector* statusVector = NULL); -void ERR_punt(); +void ERR_punt [[noreturn]] (); void ERR_warning(const Firebird::Arg::StatusVector& v); void ERR_log(int, int, const TEXT*); void ERR_append_status(Jrd::FbStatusVector*, const Firebird::Arg::StatusVector& v); diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 9eaa3f643f1..172d054078c 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -69,6 +69,7 @@ #include "../jrd/jrd.h" #include "../jrd/val.h" #include "../jrd/req.h" +#include "../jrd/Statement.h" #include "../jrd/exe.h" #include "../jrd/sbm.h" #include "../jrd/blb.h" @@ -81,6 +82,7 @@ #include "../jrd/sort.h" #include "firebird/impl/blr.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "../common/gdsassert.h" #include "../common/classes/auto.h" #include "../common/classes/timestamp.h" @@ -96,7 +98,7 @@ #include "../jrd/exe_proto.h" #include "../jrd/fun_proto.h" #include "../jrd/intl_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" @@ -322,7 +324,7 @@ void EVL_dbkey_bounds(thread_db* tdbb, const Array& ranges, Aligner 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); @@ -355,7 +357,7 @@ void EVL_dbkey_bounds(thread_db* tdbb, const Array& ranges, Aligner 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); @@ -416,7 +418,7 @@ bool EVL_field(jrd_rel* relation, Record* record, USHORT id, dsc* desc) { thread_db* tdbb = JRD_get_thread_data(); - const Format* const currentFormat = MET_current(tdbb, relation); + const Format* const currentFormat = relation->currentFormat(); while (id >= format->fmt_defaults.getCount() || format->fmt_defaults[id].vlu_desc.isUnknown()) @@ -427,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->getPermanent(), format->fmt_version + 1); fb_assert(format); } @@ -563,7 +565,7 @@ void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value, Memor VaryStr temp; UCHAR* address; - USHORT ttype; + TTypeId ttype; // Get string. If necessary, get_string will convert the string into a // temporary buffer. Since this will always be the result of a conversion, diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 61eccec95dd..5782459773f 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -71,6 +71,7 @@ #include "../jrd/intl.h" #include "../jrd/sbm.h" #include "../jrd/blb.h" +#include "../jrd/met.h" #include "../jrd/SystemTriggers.h" #include "firebird/impl/blr.h" #include "../dsql/ExprNodes.h" @@ -89,7 +90,7 @@ #include "../jrd/intl_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/par_proto.h" @@ -619,15 +620,15 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, TriggerActio return; } - if (attachment->att_triggers[type]) + const Triggers* triggers = MetadataCache::get(tdbb)->getTriggers(tdbb, type | TRIGGER_TYPE_DB); + if (triggers && *triggers) { AutoSetRestore2 tempTrans(tdbb, &thread_db::getTransaction, &thread_db::setTransaction, transaction); - EXE_execute_triggers(tdbb, &attachment->att_triggers[type], - NULL, NULL, trigger_action, StmtNode::ALL_TRIGS); + EXE_execute_triggers(tdbb, *triggers, NULL, NULL, trigger_action, StmtNode::ALL_TRIGS); } } @@ -635,19 +636,31 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, TriggerActio // Execute DDL triggers. void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTriggers, int action) { - const auto attachment = tdbb->getAttachment(); - - // Our caller verifies (ATT_no_db_triggers) if DDL triggers should not run + // Our caller verifies (ATT_no_db_triggers) if DDL triggers should not run. + const Triggers* cachedTriggers = MetadataCache::get(tdbb)->getTriggers(tdbb, TRIGGER_TYPE_DDL); - if (attachment->att_ddl_triggers) + if (cachedTriggers && *cachedTriggers) { - AutoSetRestore2 tempTrans(tdbb, - &thread_db::getTransaction, - &thread_db::setTransaction, - transaction); + Triggers triggers(*(transaction->tra_pool)); + + for (auto t : *cachedTriggers) + { + const bool preTrigger = ((t->type & 0x1) == 0); - EXE_execute_triggers(tdbb, &attachment->att_ddl_triggers, NULL, NULL, TRIGGER_DDL, - preTriggers ? StmtNode::PRE_TRIG : StmtNode::POST_TRIG, action); + if ((t->type & (1LL << action)) && (preTriggers == preTrigger)) + triggers.addTrigger(tdbb, t); + } + + if (triggers) + { + AutoSetRestore2 tempTrans(tdbb, + &thread_db::getTransaction, + &thread_db::setTransaction, + transaction); + + EXE_execute_triggers(tdbb, triggers, NULL, NULL, TRIGGER_DDL, + preTriggers ? StmtNode::PRE_TRIG : StmtNode::POST_TRIG, action); + } } } @@ -838,12 +851,14 @@ void EXE_release(thread_db* tdbb, Request* request) request->req_attachment = nullptr; } - request->req_flags &= ~req_in_use; if (request->req_timer) { request->req_timer->stop(); request->req_timer = nullptr; } + + if (request->isUsed()) + request->setUnused(); } @@ -936,12 +951,12 @@ static void activate_request(thread_db* tdbb, Request* request, jrd_tra* transac 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_in_use | req_restart_ready; + request->req_flags &= req_restart_ready; request->req_flags |= req_active; - request->req_flags &= ~req_reserved; + // ????????? request->req_flags &= ~req_reserved; // set up to count records affected by request @@ -1201,7 +1216,6 @@ void EXE_unwind(thread_db* tdbb, Request* request) } request->req_sorts.unlinkAll(); - TRA_release_request_snapshot(tdbb, request); TRA_detach_request(request); @@ -1277,7 +1291,7 @@ static void execute_looper(thread_db* tdbb, void EXE_execute_triggers(thread_db* tdbb, - TrigVector** triggers, + const Triggers& triggers, record_param* old_rpb, record_param* new_rpb, TriggerAction trigger_action, @@ -1304,7 +1318,7 @@ void EXE_execute_triggers(thread_db* tdbb, if (!(dbb->dbb_flags & DBB_creating) && (old_rpb || new_rpb)) { if (const auto relation = old_rpb ? old_rpb->rpb_relation : new_rpb->rpb_relation; - relation->rel_flags & REL_system) + relation->isSystem()) { switch (which_trig) { @@ -1339,13 +1353,11 @@ void EXE_execute_triggers(thread_db* tdbb, } } - if (!*triggers || (*triggers)->isEmpty()) + if (!triggers) return; Request* const request = tdbb->getRequest(); jrd_tra* const transaction = request ? request->req_transaction : tdbb->getTransaction(); - - RefPtr vector(*triggers); AutoPtr null_rec; const bool is_db_trigger = (!old_rec && !new_rec); @@ -1356,7 +1368,7 @@ void EXE_execute_triggers(thread_db* tdbb, fb_assert(rpb && rpb->rpb_relation); // copy the record MemoryPool& pool = *tdbb->getDefaultPool(); - null_rec = FB_NEW_POOL(pool) Record(pool, MET_current(tdbb, rpb->rpb_relation)); + null_rec = FB_NEW_POOL(pool) Record(pool, rpb->rpb_relation->currentFormat()); // initialize all fields to missing null_rec->nullify(); } @@ -1372,7 +1384,7 @@ void EXE_execute_triggers(thread_db* tdbb, try { - for (TrigVector::iterator ptr = vector->begin(); ptr != vector->end(); ++ptr) + for (auto* ptr : triggers) { if (trigger_action == TRIGGER_DDL && ddl_action) { @@ -1389,7 +1401,6 @@ void EXE_execute_triggers(thread_db* tdbb, } ptr->compile(tdbb); - trigger = ptr->statement->findRequest(tdbb); if (!is_db_trigger) @@ -1443,7 +1454,7 @@ void EXE_execute_triggers(thread_db* tdbb, if (trigger_action == TRIGGER_DISCONNECT) { if (!trigger->req_timer) - trigger->req_timer = FB_NEW_POOL(*tdbb->getAttachment()->att_pool) TimeoutTimer(); + trigger->req_timer = FB_NEW_POOL(MetadataCache::get(tdbb)->getPool()) TimeoutTimer(); const unsigned int timeOut = tdbb->getDatabase()->dbb_config->getOnDisconnectTrigTimeout() * 1000; trigger->req_timer->setup(timeOut, isc_cfg_stmt_timeout); @@ -1460,12 +1471,17 @@ void EXE_execute_triggers(thread_db* tdbb, EXE_unwind(tdbb, trigger); trigger->req_attachment = NULL; - trigger->req_flags &= ~req_in_use; - - if (!ok) - trigger_failure(tdbb, trigger); + Request* t = trigger; trigger = NULL; + + // Use RAII cleanup because trigger_failure is using trigger & may throw + Cleanup cleanSetUsed([&t] { + t->setUnused(); + }); + + if (!ok) + trigger_failure(tdbb, t); } } catch (const Exception& ex) @@ -1474,7 +1490,6 @@ void EXE_execute_triggers(thread_db* tdbb, { EXE_unwind(tdbb, trigger); trigger->req_attachment = NULL; - trigger->req_flags &= ~req_in_use; ex.stuffException(tdbb->tdbb_status_vector); @@ -1485,6 +1500,11 @@ void EXE_execute_triggers(thread_db* tdbb, tdbb->tdbb_flags |= TDBB_stack_trace_done; } + // Use RAII cleanup because trigger_failure is using trigger & may throw + Cleanup cleanSetUsed([&trigger] { + trigger->setUnused(); + }); + trigger_failure(tdbb, trigger); } @@ -1839,6 +1859,7 @@ static void release_blobs(thread_db* tdbb, Request* request) while (true) { const ULONG blob_temp_id = request->req_blobs.current(); + if (transaction->tra_blobs->locate(blob_temp_id)) { BlobIndex *current = &transaction->tra_blobs->current(); @@ -1863,7 +1884,9 @@ static void release_blobs(thread_db* tdbb, Request* request) } // Blob accounting inconsistent, only detected in DEV_BUILD. - fb_assert(false); + // Bug happens when working with index created for new table - and all w/o commits. + // Appears unrelated directly with shared mdc - comment assert for a while. + // fb_assert(false); !!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (!request->req_blobs.getNext()) break; @@ -1922,35 +1945,29 @@ static void trigger_failure(thread_db* tdbb, Request* trigger) } } - -void AutoCacheRequest::cacheRequest() +void AutoCacheRequest::cacheRequest(Request* req) { - thread_db* tdbb = JRD_get_thread_data(); - Attachment* att = tdbb->getAttachment(); - - if (which == CACHED_REQUESTS && id >= att->att_internal_cached_statements.getCount()) - att->att_internal_cached_statements.grow(id + 1); - - Statement** stmt = - which == IRQ_REQUESTS ? &att->att_internal[id] : - which == DYN_REQUESTS ? &att->att_dyn_req[id] : - which == CACHED_REQUESTS ? &att->att_internal_cached_statements[id] : - nullptr; + Jrd::Database* dbb = JRD_get_thread_data()->getDatabase(); + request = dbb->cacheRequest(which, id, req); +} - if (!stmt) +void AutoCacheRequest::release() +{ + if (request) { - fb_assert(false); - return; + EXE_unwind(JRD_get_thread_data(), request); + request->setUnused(); + request = NULL; } +} - if (*stmt) +void AutoRequest::release() +{ + if (request) { - // self resursive call already filled cache - request->getStatement()->release(tdbb); - request = att->findSystemRequest(tdbb, id, which); - fb_assert(request); + request->setUnused(); + CMP_release(JRD_get_thread_data(), request); + request = NULL; } - else - *stmt = request->getStatement(); } diff --git a/src/jrd/exe.h b/src/jrd/exe.h index b61833e7836..c12d7972ea5 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -35,17 +35,20 @@ #include #include "../jrd/blb.h" #include "../jrd/Relation.h" +#include "../jrd/CharSetContainer.h" #include "../common/classes/array.h" #include "../jrd/MetaName.h" #include "../common/classes/auto.h" #include "../common/classes/fb_pair.h" #include "../common/classes/NestConst.h" +#include "../common/classes/Bits.h" #include "iberror.h" #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" @@ -55,6 +58,8 @@ #include "../dsql/Nodes.h" #include "../dsql/Visitors.h" +#include "../jrd/Resources.h" + // This macro enables DSQL tracing code //#define CMP_DEBUG @@ -165,48 +170,6 @@ struct impure_agg_sort }; -// Request resources - -struct Resource -{ - enum rsc_s - { - rsc_relation, - rsc_procedure, - rsc_index, - rsc_collation, - rsc_function - }; - - rsc_s rsc_type; - USHORT rsc_id; // Id of the resource - jrd_rel* rsc_rel; // Relation block - Routine* rsc_routine; // Routine block - Collation* rsc_coll; // Collation block - - static bool greaterThan(const Resource& i1, const Resource& i2) - { - // A few places of the engine depend on fact that rsc_type - // is the first field in ResourceList ordering - if (i1.rsc_type != i2.rsc_type) - return i1.rsc_type > i2.rsc_type; - if (i1.rsc_type == rsc_index) - { - // Sort by relation ID for now - if (i1.rsc_rel->rel_id != i2.rsc_rel->rel_id) - return i1.rsc_rel->rel_id > i2.rsc_rel->rel_id; - } - return i1.rsc_id > i2.rsc_id; - } - - Resource(rsc_s type, USHORT id, jrd_rel* rel, Routine* routine, Collation* coll) - : rsc_type(type), rsc_id(id), rsc_rel(rel), rsc_routine(routine), rsc_coll(coll) - { } -}; - -typedef Firebird::SortedArray, - Resource, Firebird::DefaultKeyValue, Resource> ResourceList; - // Access items // In case we start to use MetaName with required pool parameter, // access item to be reworked! @@ -433,32 +396,32 @@ typedef Firebird::RightPooledMap MapItemInfo; // Compile scratch block -class CompilerScratch : public pool_alloc +struct Dependency { -public: - struct Dependency + explicit Dependency(int aObjType) { - explicit Dependency(int aObjType) - { - memset(this, 0, sizeof(*this)); - objType = aObjType; - } + memset(this, 0, sizeof(*this)); + objType = aObjType; + } - int objType; + int objType; - union - { - jrd_rel* relation; - const Function* function; - const jrd_prc* procedure; - const MetaName* name; - SLONG number; - }; - - const MetaName* subName; - SLONG subNumber; + union + { + Cached::Relation* relation; + Cached::Function* function; + Cached::Procedure* procedure; + MetaName name; + SLONG number; }; + MetaName subName; + SLONG subNumber; +}; + +class CompilerScratch : public pool_alloc +{ +public: explicit CompilerScratch(MemoryPool& p, CompilerScratch* aMainCsb = NULL) : /*csb_node(0), csb_variables(0), @@ -474,7 +437,7 @@ class CompilerScratch : public pool_alloc mainCsb(aMainCsb), csb_external(p), csb_access(p), - csb_resources(p), + csb_resources(FB_NEW_POOL(p) Resources(p)), csb_dependencies(p), csb_fors(p), csb_localTables(p), @@ -553,8 +516,8 @@ class CompilerScratch : public pool_alloc ExternalAccessList csb_external; // Access to outside procedures/triggers to be checked AccessItemList csb_access; // Access items to be checked vec* csb_variables; // Vector of variables, if any - ResourceList csb_resources; // Resources (relations and indexes) - Firebird::Array csb_dependencies; // objects that this statement depends upon + Resources* csb_resources; // Resources (relations, indexes, routines, etc.) + Firebird::Array csb_dependencies; // objects that this statement depends upon /// !!!!!!!!!!!!!!!!! Firebird::Array csb_fors; // select expressions Firebird::Array csb_localTables; // local tables Firebird::Array csb_invariants; // stack of pointer to nodes invariant offsets @@ -580,9 +543,9 @@ class CompilerScratch : public pool_alloc MetaName csb_domain_validation; // Parsing domain constraint in PSQL // used in cmp.cpp/pass1 - jrd_rel* csb_view; + Rsc::Rel csb_view; StreamType csb_view_stream; - jrd_rel* csb_parent_relation; + Rsc::Rel csb_parent_relation; unsigned blrVersion; USHORT csb_remap_variable; bool csb_validate_expr; @@ -616,10 +579,10 @@ class CompilerScratch : public pool_alloc StreamType csb_view_stream; // stream number for view relation, below USHORT csb_flags; - jrd_rel* csb_relation; + Rsc::Rel csb_relation; Firebird::string* csb_alias; // SQL alias name for this instance of relation - jrd_prc* csb_procedure; - jrd_rel* csb_view; // parent view + SubRoutine csb_procedure; + Rsc::Rel csb_view; // parent view IndexDescList* csb_idx; // Packed description of indices MessageNode* csb_message; // Msg for send/receive @@ -642,10 +605,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), diff --git a/src/jrd/exe_proto.h b/src/jrd/exe_proto.h index 8b1095fdc99..d033f44bd6c 100644 --- a/src/jrd/exe_proto.h +++ b/src/jrd/exe_proto.h @@ -31,6 +31,8 @@ namespace Jrd { class Request; class jrd_tra; class AssignmentNode; + + enum InternalRequest : USHORT; } void EXE_assignment(Jrd::thread_db*, const Jrd::AssignmentNode*); @@ -41,16 +43,14 @@ void EXE_assignment(Jrd::thread_db* tdbb, const Jrd::ValueExprNode* to, dsc* fro void EXE_execute_db_triggers(Jrd::thread_db*, Jrd::jrd_tra*, enum TriggerAction); void EXE_execute_ddl_triggers(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, bool preTriggers, int action); +void EXE_execute_triggers(Jrd::thread_db*, const Jrd::Triggers&, Jrd::record_param*, Jrd::record_param*, + enum TriggerAction, Jrd::StmtNode::WhichTrigger, int = 0); void EXE_execute_function(Jrd::thread_db* tdbb, Jrd::Request* request, Jrd::jrd_tra* transaction, ULONG inMsgLength, UCHAR* inMsg, ULONG outMsgLength, UCHAR* outMsg); 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::TrigVector**, Jrd::record_param*, Jrd::record_param*, - enum TriggerAction, Jrd::StmtNode::WhichTrigger, int = 0); - void EXE_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false); void EXE_release(Jrd::thread_db*, Jrd::Request*); void EXE_send(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, const void*); @@ -87,23 +87,23 @@ namespace Jrd class AutoCacheRequest { public: - AutoCacheRequest(thread_db* tdbb, USHORT aId, USHORT aWhich) + AutoCacheRequest(thread_db* tdbb, USHORT aId, InternalRequest aWhich) : id(aId), which(aWhich), - request(tdbb->getAttachment()->findSystemRequest(tdbb, id, which)) + request(tdbb->getDatabase()->findSystemRequest(tdbb, id, which)) { } AutoCacheRequest(thread_db* tdbb, const CachedRequestId& cachedRequestId) : id(cachedRequestId.getId()), which(CACHED_REQUESTS), - request(tdbb->getAttachment()->findSystemRequest(tdbb, id, which)) + request(tdbb->getDatabase()->findSystemRequest(tdbb, id, which)) { } AutoCacheRequest() : id(0), - which(0), + which(NOT_REQUEST), request(NULL) { } @@ -114,13 +114,13 @@ namespace Jrd } public: - void reset(thread_db* tdbb, USHORT aId, USHORT aWhich) + void reset(thread_db* tdbb, USHORT aId, InternalRequest aWhich) { release(); id = aId; which = aWhich; - request = tdbb->getAttachment()->findSystemRequest(tdbb, id, which); + request = tdbb->getDatabase()->findSystemRequest(tdbb, id, which); } void reset(thread_db* tdbb, const CachedRequestId& cachedRequestId) @@ -129,7 +129,7 @@ namespace Jrd id = cachedRequestId.getId(); which = CACHED_REQUESTS; - request = tdbb->getAttachment()->findSystemRequest(tdbb, id, which); + request = tdbb->getDatabase()->findSystemRequest(tdbb, id, which); } void compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength) @@ -137,8 +137,7 @@ namespace Jrd if (request) return; - request = CMP_compile_request(tdbb, blr, blrLength, true); - cacheRequest(); + cacheRequest(CMP_compile_request(tdbb, blr, blrLength, true)); } Request* operator ->() @@ -157,20 +156,12 @@ namespace Jrd } private: - inline void release() - { - if (request) - { - EXE_unwind(JRD_get_thread_data(), request); - request = NULL; - } - } - - void cacheRequest(); + void release(); + void cacheRequest(Request* req); private: USHORT id; - USHORT which; + InternalRequest which; Request* request; }; @@ -199,6 +190,7 @@ namespace Jrd return; request = CMP_compile_request(tdbb, blr, blrLength, true); + request->setUsed(); } Request* operator ->() @@ -217,14 +209,7 @@ namespace Jrd } private: - inline void release() - { - if (request) - { - CMP_release(JRD_get_thread_data(), request); - request = NULL; - } - } + void release(); private: Request* request; diff --git a/src/jrd/ext.cpp b/src/jrd/ext.cpp index b3e588cbf0f..6e3c9a52949 100644 --- a/src/jrd/ext.cpp +++ b/src/jrd/ext.cpp @@ -38,17 +38,18 @@ #include #include #include + +#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; +} // namespace - 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)); - } +void ExternalFile::open(Database* dbb) +{ + fb_assert(ext_sync.locked()); - // 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); + ExternalFileDirectoryList::create(dbb); - if (!ext_file->ext_ifi) + 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))) { - // 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; - } + 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; } - - return ext_file->ext_ifi; } -} // namespace +} -double EXT_cardinality(thread_db* tdbb, jrd_rel* relation) +double ExternalFile::getCardinality(thread_db* tdbb, jrd_rel* relation) noexcept { /************************************** * @@ -170,38 +171,27 @@ 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); + const Format* const format = relation->currentFormat(); fb_assert(format && format->fmt_length); const USHORT offset = (USHORT)(IPTR) format->fmt_desc[0].dsc_address; const ULONG record_length = format->fmt_length - offset; @@ -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, 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 + if (ext_ifi == NULL) + { + 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"); + } - bool doSeek = false; - if (!(file->ext_flags & EXT_last_read)) - { - doSeek = true; - } - else - { - SINT64 offset = FTELL64(file->ext_ifi); - if (offset < 0) + // 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 (!(ext_flags & EXT_last_read)) { - ERR_post(Arg::Gds(isc_io_error) << STRINGIZE(FTELL64) << Arg::Str(file->ext_filename) << - Arg::Gds(isc_io_read_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(offset) != position); } - doSeek = (static_cast(offset) != position); - } - // reset both flags cause we are going to move the file pointer - file->ext_flags &= ~(EXT_last_write | EXT_last_read); + // reset both flags cause we are going to move the file pointer + ext_flags &= ~(EXT_last_write | EXT_last_read); - if (doSeek) - { - if (FSEEK64(file->ext_ifi, position, SEEK_SET) != 0) + if (doSeek) { - ERR_post(Arg::Gds(isc_io_error) << STRINGIZE(FSEEK64) << Arg::Str(file->ext_filename) << - Arg::Gds(isc_io_open_err) << SYS_ERR(errno)); + 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, file->ext_ifi)) - { - return false; + 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*) +void ExternalFile::traAttach(thread_db* tdbb) { /************************************** * @@ -590,11 +522,18 @@ void EXT_tra_attach(ExternalFile* file, jrd_tra*) * 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*) +void ExternalFile::traDetach() noexcept { /************************************** * @@ -608,11 +547,23 @@ void EXT_tra_detach(ExternalFile* file, jrd_tra*) * external file if count is zero. * **************************************/ + MutexLockGuard g(ext_sync, FB_FUNCTION); + + if (--ext_tra_cnt == 0) + { + fb_assert(ext_ifi); + if (ext_ifi) + fclose(ext_ifi); + ext_ifi = NULL; + } +} - file->ext_tra_cnt--; - if (!file->ext_tra_cnt && file->ext_ifi) +void ExternalFile::release() +{ + // lock not needed, closing + if (ext_ifi) { - fclose(file->ext_ifi); - file->ext_ifi = NULL; + fclose(ext_ifi); + ext_ifi = NULL; } } diff --git a/src/jrd/ext.h b/src/jrd/ext.h deleted file mode 100644 index 82aa025a837..00000000000 --- a/src/jrd/ext.h +++ /dev/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 - -namespace Jrd { - -// External file access block - -class ExternalFile : public pool_alloc_rpt -{ -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 diff --git a/src/jrd/ext_proto.h b/src/jrd/ext_proto.h index b0ac772187a..0ec9bf8647d 100644 --- a/src/jrd/ext_proto.h +++ b/src/jrd/ext_proto.h @@ -21,29 +21,77 @@ * Contributor(s): ______________________________________. */ +#include +#include + +#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*); - -void EXT_open(Jrd::Database*, Jrd::ExternalFile*); -void EXT_store(Jrd::thread_db*, Jrd::record_param*); - -void EXT_tra_attach(Jrd::ExternalFile*, Jrd::jrd_tra*); -void EXT_tra_detach(Jrd::ExternalFile*, Jrd::jrd_tra*); + +class jrd_tra; +class RecordSource; +class jrd_rel; +struct record_param; +struct bid; +class Database; +class thread_db; + +// External file access block + +class ExternalFile : public pool_alloc_rpt +{ +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*); + void release(); + +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 diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 005813649e9..74635bfd360 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -39,6 +39,7 @@ #include "../mov_proto.h" #include "../PreparedStatement.h" #include "../Function.h" +#include "../Statement.h" #include "InternalDS.h" #include "ValidatePassword.h" diff --git a/src/jrd/filters.cpp b/src/jrd/filters.cpp index 387b73f0954..7335442ee56 100644 --- a/src/jrd/filters.cpp +++ b/src/jrd/filters.cpp @@ -804,7 +804,7 @@ ISC_STATUS filter_transliterate_text(USHORT action, BlobControl* control) BlobControl* source; ISC_STATUS status; ULONG err_position; - SSHORT source_cs, dest_cs; + TTypeId source_cs, dest_cs; USHORT result_length; switch (action) @@ -818,8 +818,8 @@ ISC_STATUS filter_transliterate_text(USHORT action, BlobControl* control) source = control->ctl_handle; control->ctl_number_segments = source->ctl_number_segments; - source_cs = control->ctl_from_sub_type; - dest_cs = control->ctl_to_sub_type; + source_cs = TTypeId(control->ctl_from_sub_type); + dest_cs = TTypeId(control->ctl_to_sub_type); aux = (ctlaux*) gds__alloc((SLONG) sizeof(*aux)); // FREE: on isc_blob_filter_close in this routine diff --git a/src/jrd/flu.cpp b/src/jrd/flu.cpp index c778f61c913..9fd8c05a553 100644 --- a/src/jrd/flu.cpp +++ b/src/jrd/flu.cpp @@ -134,11 +134,6 @@ namespace { { initialize(); } - - ~UdfDirectoryList() - { - //printf("Destroyed directory list\n"); - } }; Firebird::InitInstance iUdfDirectoryList; } diff --git a/src/jrd/fun.epp b/src/jrd/fun.epp index bc1eb703958..20edf0d3365 100644 --- a/src/jrd/fun.epp +++ b/src/jrd/fun.epp @@ -58,6 +58,7 @@ #include "../jrd/mov_proto.h" #include "../common/isc_s_proto.h" #include "../jrd/blb.h" +#include "../jrd/met.h" #include "../jrd/ExtEngineManager.h" #include "../common/classes/auto.h" #include "../common/utils_proto.h" diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index aabe8453cf8..ffa89df262e 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -53,6 +53,7 @@ #include "../jrd/scl_proto.h" #include "../common/utils_proto.h" #include "../common/classes/array.h" +#include "../common/classes/auto.h" #include "../jrd/constants.h" using namespace Jrd; @@ -68,7 +69,7 @@ inline void CHECK_AND_MOVE(Acl& to, UCHAR from) DATABASE DB = STATIC "yachts.lnk"; -static void define_default_class(thread_db*, const TEXT*, MetaName&, const Acl&, +static void define_default_class(thread_db*, MetaName, MetaName&, const Acl&, jrd_tra*); static void finish_security_class(Acl&, SecurityClass::flags_t); static void get_object_info(thread_db*, const TEXT*, ObjectType, @@ -183,7 +184,7 @@ void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, ObjectType if (restrct) { finish_security_class(default_acl, public_priv); - define_default_class(tdbb, name.c_str(), default_class, default_acl, + define_default_class(tdbb, name, default_class, default_acl, transaction); } } @@ -195,8 +196,20 @@ void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, ObjectType } +static void ensure_relation_reload(thread_db* tdbb, MetaId id, jrd_tra* transaction) +{ + Firebird::AutoSetRestore2 tempTrans(tdbb, + &thread_db::getTransaction, + &thread_db::setTransaction, + transaction); + + MetadataCache::tagForUpdate(tdbb, id); + DFW_post_work(transaction, dfw_commit_relation, nullptr, id); +} + + static void define_default_class(thread_db* tdbb, - const TEXT* relation_name, + MetaName relation_name, MetaName& default_class, const Acl& acl, jrd_tra* transaction) @@ -219,6 +232,9 @@ static void define_default_class(thread_db* tdbb, **************************************/ SET_TDBB(tdbb); + auto* relation = MetadataCache::lookupRelation(tdbb, relation_name, CacheFlag::AUTOCREATE); + fb_assert(relation); + if (default_class.length() == 0) { default_class.printf("%s%" SQUADFORMAT, DEFAULT_CLASS, @@ -228,7 +244,7 @@ static void define_default_class(thread_db* tdbb, FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) REL IN RDB$RELATIONS - WITH REL.RDB$RELATION_NAME EQ relation_name + WITH REL.RDB$RELATION_NAME EQ relation_name.c_str() { MODIFY REL USING REL.RDB$DEFAULT_CLASS.NULL = FALSE; @@ -241,14 +257,7 @@ static void define_default_class(thread_db* tdbb, save_security_class(tdbb, default_class, acl, transaction); - dsc desc; - desc.dsc_dtype = dtype_text; - desc.dsc_sub_type = 0; - desc.dsc_scale = 0; - desc.dsc_ttype() = ttype_metadata; - desc.dsc_address = (UCHAR *) relation_name; - desc.dsc_length = static_cast(strlen(relation_name)); - DFW_post_work(transaction, dfw_scan_relation, &desc, 0); + ensure_relation_reload(tdbb, relation->getId(), transaction); } @@ -327,7 +336,7 @@ static void get_object_info(thread_db* tdbb, ************************************** * * Functional description - * This could be done in MET_scan_relation () or MET_lookup_procedure, + * This could be done in scan_relation () or lookup_procedure (), * but presumably we wish to make sure the information we have is * up-to-the-minute. * @@ -701,11 +710,14 @@ static SecurityClass::flags_t save_field_privileges(thread_db* tdbb, AutoCacheRequest request(tdbb, irq_grant6, IRQ_REQUESTS); AutoRequest request2, request3; + MetaId relId; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - FLD IN RDB$RELATION_FIELDS CROSS - PRV IN RDB$USER_PRIVILEGES + FLD IN RDB$RELATION_FIELDS + CROSS PRV IN RDB$USER_PRIVILEGES OVER RDB$RELATION_NAME, RDB$FIELD_NAME + CROSS REL IN RDB$RELATIONS + OVER RDB$RELATION_NAME WITH PRV.RDB$OBJECT_TYPE EQ obj_relation AND PRV.RDB$RELATION_NAME EQ relation_name AND PRV.RDB$FIELD_NAME NOT MISSING AND @@ -714,6 +726,7 @@ static SecurityClass::flags_t save_field_privileges(thread_db* tdbb, { fb_utils::exact_name_limit(PRV.RDB$USER, sizeof(PRV.RDB$USER)); fb_utils::exact_name_limit(PRV.RDB$FIELD_NAME, sizeof(PRV.RDB$FIELD_NAME)); + relId = REL.RDB$RELATION_ID; // create a control break on field_name,user @@ -837,14 +850,7 @@ static SecurityClass::flags_t save_field_privileges(thread_db* tdbb, finish_security_class(field_acl, (field_public | public_priv)); save_security_class(tdbb, s_class, field_acl, transaction); - dsc desc; - desc.dsc_dtype = dtype_text; - desc.dsc_sub_type = 0; - desc.dsc_scale = 0; - desc.dsc_ttype() = ttype_metadata; - desc.dsc_address = (UCHAR *) relation_name; - desc.dsc_length = static_cast(strlen(relation_name)); - DFW_post_work(transaction, dfw_update_format, &desc, 0); + ensure_relation_reload(tdbb, relId, transaction); } return aggregate_public; diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index 5c22b9b1862..e7624325cc0 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -35,6 +35,7 @@ #include "../jrd/val.h" #include "../jrd/intl.h" #include "../jrd/req.h" +#include "../jrd/Statement.h" #include "../jrd/ods.h" #include "../jrd/btr.h" #include "../jrd/sort.h" @@ -57,8 +58,9 @@ #include "../jrd/idx_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" +#include "../jrd/met.h" #include "../jrd/mov_proto.h" #include "../jrd/vio_proto.h" #include "../jrd/tra_proto.h" @@ -75,14 +77,12 @@ static idx_e check_foreign_key(thread_db*, Record*, jrd_rel*, jrd_tra*, index_de static idx_e check_partner_index(thread_db*, jrd_rel*, Record*, jrd_tra*, index_desc*, jrd_rel*, USHORT); static bool cmpRecordKeys(thread_db*, Record*, jrd_rel*, index_desc*, Record*, jrd_rel*, index_desc*); static bool duplicate_key(const UCHAR*, const UCHAR*, void*); -static PageNumber get_root_page(thread_db*, jrd_rel*); -static int index_block_flush(void*); +static PageNumber get_root_page(thread_db*, Cached::Relation*); static idx_e insert_key(thread_db*, jrd_rel*, Record*, jrd_tra*, WIN *, index_insertion*, IndexErrorContext&); -static void release_index_block(thread_db*, IndexBlock*); -static void signal_index_deletion(thread_db*, jrd_rel*, USHORT); +static void signal_index_deletion(thread_db*, RelationPermanent*, USHORT); -void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_rel* relation) +void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, Cached::Relation* view, Cached::Relation* relation) { /************************************** * @@ -115,18 +115,17 @@ void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_ if (!MET_lookup_partner(tdbb, relation, &idx, 0)) continue; - jrd_rel* referenced_relation = MET_relation(tdbb, idx.idx_primary_relation); - MET_scan_relation(tdbb, referenced_relation); + auto referenced_relation = + MetadataCache::lookup_relation_id(tdbb, idx.idx_primary_relation, CacheFlag::AUTOCREATE); const USHORT index_id = idx.idx_primary_index; // get the description of the primary key index - referenced_window.win_page = get_root_page(tdbb, referenced_relation); + referenced_window.win_page = get_root_page(tdbb, getPermanent(referenced_relation)); referenced_window.win_flags = 0; - index_root_page* referenced_root = - (index_root_page*) CCH_FETCH(tdbb, &referenced_window, LCK_read, pag_root); + auto* referenced_root = BTR_fetch_root(FB_FUNCTION, tdbb, &referenced_window); index_desc referenced_idx; - if (!BTR_description(tdbb, referenced_relation, referenced_root, + if (!BTR_description(tdbb, getPermanent(referenced_relation), referenced_root, &referenced_idx, index_id)) { CCH_RELEASE(tdbb, &referenced_window); @@ -141,14 +140,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); @@ -157,7 +156,7 @@ void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_ } -bool IDX_check_master_types(thread_db* tdbb, index_desc& idx, jrd_rel* partner_relation, int& bad_segment) +bool IDX_check_master_types(thread_db* tdbb, index_desc& idx, Cached::Relation* partner_relation, int& bad_segment) { /********************************************** * @@ -179,7 +178,7 @@ bool IDX_check_master_types(thread_db* tdbb, index_desc& idx, jrd_rel* partner_r // get the index root page for the partner relation WIN window(get_root_page(tdbb, partner_relation)); - index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root); + auto* root = BTR_fetch_root(FB_FUNCTION, tdbb, &window); // get the description of the partner index const bool ok = BTR_description(tdbb, partner_relation, root, &partner_idx, idx.idx_primary_index); @@ -252,7 +251,7 @@ class IndexCreateTask : public Task // preserving the page working sets of other attachments. if (att && (att != m_dbb->dbb_attachments || att->att_next)) { - if (att->isGbak() || DPM_data_pages(tdbb, m_creation->relation) > m_dbb->dbb_bcb->bcb_count) + if (att->isGbak() || DPM_data_pages(tdbb, getPermanent(m_creation->relation)) > m_dbb->dbb_bcb->bcb_count) m_flags |= IS_LARGE_SCAN; } @@ -368,13 +367,11 @@ class IndexCreateTask : public Task m_idx = *creation->index; // copy if (m_ownAttach) { - m_idx.idx_expression = NULL; + m_idx.idx_expression_node = NULL; m_idx.idx_expression_statement = NULL; - m_idx.idx_condition = NULL; + m_idx.idx_foreign_deps = NULL; + m_idx.idx_condition_node = NULL; m_idx.idx_condition_statement = NULL; - m_idx.idx_foreign_indexes = NULL; - m_idx.idx_foreign_primaries = NULL; - m_idx.idx_foreign_relations = NULL; } FPTR_REJECT_DUP_CALLBACK callback = NULL; @@ -463,9 +460,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(), CacheFlag::AUTOCREATE); index_desc* idx = &item->m_idx; jrd_tra* transaction = item->m_tra ? item->m_tra : m_creation->transaction; @@ -525,32 +520,33 @@ 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, + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); partner_index_id = idx->idx_primary_index; } - if ((idx->idx_flags & idx_expression) && (idx->idx_expression == NULL)) + if ((idx->idx_flags & idx_expression) && (idx->idx_expression_node == NULL)) { fb_assert(!m_exprBlob.isEmpty()); CompilerScratch* csb = NULL; - Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); + Jrd::ContextPoolHolder context(tdbb, dbb->createPool(ALLOC_ARGS0)); - idx->idx_expression = static_cast (MET_parse_blob(tdbb, relation, &m_exprBlob, - &csb, &idx->idx_expression_statement, false, false)); + idx->idx_expression_node = static_cast (MET_parse_blob(tdbb, getPermanent(relation), + &m_exprBlob, &csb, &idx->idx_expression_statement, false, false)); delete csb; } - if ((idx->idx_flags & idx_condition) && (idx->idx_condition == NULL)) + if ((idx->idx_flags & idx_condition) && (idx->idx_condition_node == NULL)) { fb_assert(!m_condBlob.isEmpty()); CompilerScratch* csb = NULL; - Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); + Jrd::ContextPoolHolder context(tdbb, dbb->createPool(ALLOC_ARGS0)); - idx->idx_condition = static_cast (MET_parse_blob(tdbb, relation, &m_condBlob, - &csb, &idx->idx_condition_statement, false, false)); + idx->idx_condition_node = static_cast (MET_parse_blob(tdbb, getPermanent(relation), + &m_condBlob, &csb, &idx->idx_condition_statement, false, false)); delete csb; } @@ -562,7 +558,7 @@ bool IndexCreateTask::handler(WorkItem& _item) if (m_flags & IS_LARGE_SCAN) { primary.getWindow(tdbb).win_flags = secondary.getWindow(tdbb).win_flags = WIN_large_scan; - primary.rpb_org_scans = secondary.rpb_org_scans = relation->rel_scan_count++; + primary.rpb_org_scans = secondary.rpb_org_scans = getPermanent(relation)->rel_scan_count++; } const bool isDescending = (idx->idx_flags & idx_descending); @@ -675,7 +671,7 @@ bool IndexCreateTask::handler(WorkItem& _item) } while (stack.hasData() && (record = stack.pop())); if (primary.getWindow(tdbb).win_flags & WIN_large_scan) - --relation->rel_scan_count; + --getPermanent(relation)->rel_scan_count; context.raise(tdbb, result, record); } @@ -688,7 +684,7 @@ bool IndexCreateTask::handler(WorkItem& _item) } while (stack.hasData() && (record = stack.pop())); if (primary.getWindow(tdbb).win_flags & WIN_large_scan) - --relation->rel_scan_count; + --getPermanent(relation)->rel_scan_count; context.raise(tdbb, idx_e_keytoobig, record); } @@ -747,7 +743,7 @@ bool IndexCreateTask::handler(WorkItem& _item) gc_record.release(); if (primary.getWindow(tdbb).win_flags & WIN_large_scan) - --relation->rel_scan_count; + --getPermanent(relation)->rel_scan_count; } catch (const Exception& ex) { @@ -824,6 +820,7 @@ int IndexCreateTask::getMaxWorkers() void IDX_create_index(thread_db* tdbb, + IdxCreate createMethod, jrd_rel* relation, index_desc* idx, const TEXT* index_name, @@ -845,10 +842,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()) { @@ -856,7 +853,7 @@ void IDX_create_index(thread_db* tdbb, Arg::Gds(isc_wish_list)); } - get_root_page(tdbb, relation); + get_root_page(tdbb, getPermanent(relation)); fb_assert(transaction); @@ -884,7 +881,7 @@ void IDX_create_index(thread_db* tdbb, if (isForeign) { - if (!MET_lookup_partner(tdbb, relation, idx, index_name)) { + if (!MET_lookup_partner(tdbb, getPermanent(relation), idx, index_name)) { BUGCHECK(173); // msg 173 referenced index description not found } } @@ -899,8 +896,11 @@ void IDX_create_index(thread_db* tdbb, creation.nullIndLen = nullIndLen; creation.dup_recno = -1; creation.duplicates.setValue(0); + creation.forRollback = createMethod; + + IndexCreateLock crtLock(tdbb, relation->getId()); - BTR_reserve_slot(tdbb, creation); + BTR_reserve_slot(tdbb, creation, crtLock); if (index_id) *index_id = idx->idx_id; @@ -966,94 +966,96 @@ void IDX_create_index(thread_db* tdbb, IndexErrorContext context(relation, idx, index_name); context.raise(tdbb, idx_e_duplicate, error_record); } - - if ((relation->rel_flags & REL_temp_conn) && (relation->getPages(tdbb)->rel_instance_id != 0)) +/* + if ((getPermanent(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); - if (idx_lock) - { - ++idx_lock->idl_count; - if (idx_lock->idl_count == 1) - LCK_lock(tdbb, idx_lock->idl_lock, LCK_SR, LCK_WAIT); - } - } + IndexPermanent* idp = getPermanent(relation)->lookupIndex(tdbb, idx->idx_id); + if (idp) + idp->sharedLock(tdbb); + } */ +} + + +void IDX_activate_index(thread_db* tdbb, Cached::Relation* relation, MetaId id) +{ + BTR_activate_index(tdbb, relation, id); } -IndexBlock* IDX_create_index_block(thread_db* tdbb, jrd_rel* relation, USHORT id) +void IDX_mark_index(thread_db* tdbb, Cached::Relation* relation, MetaId id) { /************************************** * - * I D X _ c r e a t e _ i n d e x _ b l o c k + * I D X _ d e l e t e _ i n d e x * ************************************** * * Functional description - * Create an index block and an associated - * lock block for the specified index. + * Delete a single index. * **************************************/ SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - CHECK_DBB(dbb); - IndexBlock* index_block = FB_NEW_POOL(*relation->rel_pool) IndexBlock(); - index_block->idb_id = id; - - // link the block in with the relation linked list - - index_block->idb_next = relation->rel_index_blocks; - relation->rel_index_blocks = index_block; + signal_index_deletion(tdbb, relation, id); - // create a shared lock for the index, to coordinate - // any modification to the index so that the cached information - // about the index will be discarded + auto* relPages = relation->getBasePages(); + WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); + index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); - 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); + BTR_mark_index_for_delete(tdbb, relation, id, &window, root); - return index_block; +/* ?????????????????? + if ((relation->rel_flags & REL_temp_conn) && (relation->getPages(tdbb)->rel_instance_id != 0) && + tree_exists) + { + IndexPermanent* idx_lock = relation->getIndexLock(tdbb, id); + if (idx_lock) + idx_lock->unlockAll(tdbb); + } +*/ } -void IDX_delete_index(thread_db* tdbb, jrd_rel* relation, USHORT id) +void IDX_delete_indices(thread_db* tdbb, RelationPermanent* relation, RelationPages* relPages) { /************************************** * - * I D X _ d e l e t e _ i n d e x + * I D X _ d e l e t e _ i n d i c e s * ************************************** * * Functional description - * Delete a single index. + * Delete all known indices in preparation for deleting a + * complete relation. * **************************************/ SET_TDBB(tdbb); - signal_index_deletion(tdbb, relation, id); + fb_assert(relPages->rel_index_root); - WIN window(get_root_page(tdbb, relation)); - CCH_FETCH(tdbb, &window, LCK_write, pag_root); + WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); + index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); - const bool tree_exists = BTR_delete_index(tdbb, &window, id); +// const bool is_temp = (relation->rel_flags & REL_temp_conn) && (relPages->rel_instance_id != 0); - if ((relation->rel_flags & REL_temp_conn) && (relation->getPages(tdbb)->rel_instance_id != 0) && - tree_exists) + for (USHORT i = 0; i < root->irt_count; i++) { - IndexLock* idx_lock = CMP_get_index_lock(tdbb, relation, id); - if (idx_lock) + const bool tree_exists = BTR_delete_index(tdbb, &window, i); + root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); +/* !!!!!!!!!!!!!! + if (is_temp && tree_exists) { - if (!--idx_lock->idl_count) { - LCK_release(tdbb, idx_lock->idl_lock); - } - } + IndexPermanent* idx_lock = relation->getIndexLock(tdbb, i); + if (idx_lock) + idx_lock->unlockAll(tdbb); + }*/ } + + CCH_RELEASE(tdbb, &window); } -void IDX_delete_indices(thread_db* tdbb, jrd_rel* relation, RelationPages* relPages) +void IDX_mark_indices(thread_db* tdbb, Cached::Relation* relation) { /************************************** * @@ -1068,28 +1070,25 @@ void IDX_delete_indices(thread_db* tdbb, jrd_rel* relation, RelationPages* relPa **************************************/ SET_TDBB(tdbb); + auto* relPages = relation->getBasePages(); fb_assert(relPages->rel_index_root); WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); - index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_root); + index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); - const bool is_temp = (relation->rel_flags & REL_temp_conn) && (relPages->rel_instance_id != 0); +// const bool is_temp = (relation->rel_flags & REL_temp_conn) && (relPages->rel_instance_id != 0); for (USHORT i = 0; i < root->irt_count; i++) { - const bool tree_exists = BTR_delete_index(tdbb, &window, i); - root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_root); - + BTR_mark_index_for_delete(tdbb, relation, i, &window, root); + root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); +/* !!!!!!!!!!!!!! if (is_temp && tree_exists) { - IndexLock* idx_lock = CMP_get_index_lock(tdbb, relation, i); + IndexPermanent* idx_lock = relation->getIndexLock(tdbb, i); if (idx_lock) - { - if (!--idx_lock->idl_count) { - LCK_release(tdbb, idx_lock->idl_lock); - } - } - } + idx_lock->unlockAll(tdbb); + }*/ } CCH_RELEASE(tdbb, &window); @@ -1118,7 +1117,7 @@ void IDX_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) RelationPages* relPages = rpb->rpb_relation->getPages(tdbb); WIN window(relPages->rel_pg_space_id, -1); - while (BTR_next_index(tdbb, rpb->rpb_relation, transaction, &idx, &window)) + while (BTR_next_index(tdbb, getPermanent(rpb->rpb_relation), transaction, &idx, &window)) { if (idx.idx_flags & (idx_primary | idx_unique)) { @@ -1159,13 +1158,13 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, insertion.iib_relation = rpb->rpb_relation; insertion.iib_btr_level = 0; - WIN window(get_root_page(tdbb, rpb->rpb_relation)); + WIN window(get_root_page(tdbb, getPermanent(rpb->rpb_relation))); - index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root); + auto* root = BTR_fetch_root(FB_FUNCTION, tdbb, &window); for (USHORT i = 0; i < root->irt_count; i++) { - if (BTR_description(tdbb, rpb->rpb_relation, root, &idx, i)) + if (BTR_description(tdbb, getPermanent(rpb->rpb_relation), root, &idx, i)) { IndexErrorContext context(rpb->rpb_relation, &idx); IndexCondition condition(tdbb, &idx); @@ -1239,10 +1238,10 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, insertion.iib_key = key1; BTR_remove(tdbb, &window, &insertion); - root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root); + root = BTR_fetch_root(FB_FUNCTION, tdbb, &window); if (stack1.hasMore(1)) - BTR_description(tdbb, rpb->rpb_relation, root, &idx, i); + BTR_description(tdbb, getPermanent(rpb->rpb_relation), root, &idx, i); } } } @@ -1283,7 +1282,7 @@ void IDX_modify(thread_db* tdbb, RelationPages* relPages = org_rpb->rpb_relation->getPages(tdbb); WIN window(relPages->rel_pg_space_id, -1); - while (BTR_next_index(tdbb, org_rpb->rpb_relation, transaction, &idx, &window)) + while (BTR_next_index(tdbb, getPermanent(org_rpb->rpb_relation), transaction, &idx, &window)) { IndexErrorContext context(new_rpb->rpb_relation, &idx); idx_e error_code = idx_e_ok; @@ -1370,8 +1369,8 @@ void IDX_modify_check_constraints(thread_db* tdbb, // If relation's primary/unique keys have no dependencies by other // relations' foreign keys then don't bother cycling thru all index descriptions. - if (!(org_rpb->rpb_relation->rel_flags & REL_check_partners) && - !(org_rpb->rpb_relation->rel_primary_dpnds.prim_reference_ids)) + if (!(getPermanent(org_rpb->rpb_relation)->rel_flags & REL_check_partners) && + !(getPermanent(org_rpb->rpb_relation)->rel_primary_dpnds)) { return; } @@ -1385,10 +1384,10 @@ void IDX_modify_check_constraints(thread_db* tdbb, // Now check all the foreign key constraints. Referential integrity relation // could be established by primary key/foreign key or unique key/foreign key - while (BTR_next_index(tdbb, org_rpb->rpb_relation, transaction, &idx, &window)) + while (BTR_next_index(tdbb, getPermanent(org_rpb->rpb_relation), transaction, &idx, &window)) { if (!(idx.idx_flags & (idx_primary | idx_unique)) || - !MET_lookup_partner(tdbb, org_rpb->rpb_relation, &idx, 0)) + !MET_lookup_partner(tdbb, getPermanent(org_rpb->rpb_relation), &idx, 0)) { continue; } @@ -1465,10 +1464,10 @@ void IDX_modify_flag_uk_modified(thread_db* tdbb, index_desc idx; idx.idx_id = idx_invalid; - while (BTR_next_index(tdbb, relation, transaction, &idx, &window)) + while (BTR_next_index(tdbb, getPermanent(relation), transaction, &idx, &window)) { if (!(idx.idx_flags & (idx_primary | idx_unique)) || - !MET_lookup_partner(tdbb, relation, &idx, 0)) + !MET_lookup_partner(tdbb, getPermanent(relation), &idx, 0)) { continue; } @@ -1491,7 +1490,7 @@ void IDX_modify_flag_uk_modified(thread_db* tdbb, } -void IDX_statistics(thread_db* tdbb, jrd_rel* relation, USHORT id, SelectivityList& selectivity) +void IDX_statistics(thread_db* tdbb, Cached::Relation* relation, USHORT id, SelectivityList& selectivity) { /************************************** * @@ -1540,7 +1539,7 @@ void IDX_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) RelationPages* relPages = rpb->rpb_relation->getPages(tdbb); WIN window(relPages->rel_pg_space_id, -1); - while (BTR_next_index(tdbb, rpb->rpb_relation, transaction, &idx, &window)) + while (BTR_next_index(tdbb, getPermanent(rpb->rpb_relation), transaction, &idx, &window)) { IndexErrorContext context(rpb->rpb_relation, &idx); idx_e error_code = idx_e_ok; @@ -1792,35 +1791,36 @@ static idx_e check_foreign_key(thread_db* tdbb, idx_e result = idx_e_ok; - if (!MET_lookup_partner(tdbb, relation, idx, 0)) + if (!MET_lookup_partner(tdbb, getPermanent(relation), idx, 0)) return result; - jrd_rel* partner_relation = NULL; + jrd_rel* partner_relation = nullptr; USHORT index_id = 0; if (idx->idx_flags & idx_foreign) { - partner_relation = MET_relation(tdbb, idx->idx_primary_relation); + partner_relation = MetadataCache::lookup_relation_id(tdbb, idx->idx_primary_relation, + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); index_id = idx->idx_primary_index; result = check_partner_index(tdbb, relation, record, transaction, idx, partner_relation, index_id); } - else if (idx->idx_flags & (idx_primary | idx_unique)) + else if ((idx->idx_flags & (idx_primary | idx_unique)) && idx->idx_foreign_deps) { - for (int index_number = 0; - index_number < (int) idx->idx_foreign_primaries->count(); - index_number++) + for (auto& frgn : *(idx->idx_foreign_deps)) { - if (idx->idx_id != (*idx->idx_foreign_primaries)[index_number]) + if (idx->idx_id != frgn.dep_reference_id) continue; - partner_relation = MET_relation(tdbb, (*idx->idx_foreign_relations)[index_number]); - index_id = (*idx->idx_foreign_indexes)[index_number]; + partner_relation = MetadataCache::lookup_relation_id(tdbb, frgn.dep_relation, + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); + index_id = frgn.dep_index; - if ((relation->rel_flags & REL_temp_conn) && (partner_relation->rel_flags & REL_temp_tran)) + if ((getPermanent(relation)->rel_flags & REL_temp_conn) && + (getPermanent(partner_relation)->rel_flags & REL_temp_tran)) { - jrd_rel::RelPagesSnapshot pagesSnapshot(tdbb, partner_relation); - partner_relation->fillPagesSnapshot(pagesSnapshot, true); + RelationPermanent::RelPagesSnapshot pagesSnapshot(tdbb, getPermanent(partner_relation)); + getPermanent(partner_relation)->fillPagesSnapshot(pagesSnapshot, true); for (FB_SIZE_T i = 0; i < pagesSnapshot.getCount(); i++) { @@ -1884,13 +1884,13 @@ static idx_e check_partner_index(thread_db* tdbb, // get the index root page for the partner relation - WIN window(get_root_page(tdbb, partner_relation)); - index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root); + WIN window(get_root_page(tdbb, getPermanent(partner_relation))); + auto* root = BTR_fetch_root(FB_FUNCTION, tdbb, &window); // get the description of the partner index index_desc partner_idx; - if (!BTR_description(tdbb, partner_relation, root, &partner_idx, index_id)) + if (!BTR_description(tdbb, getPermanent(partner_relation), root, &partner_idx, index_id)) { CCH_RELEASE(tdbb, &window); BUGCHECK(175); // msg 175 partner index description not found @@ -2019,7 +2019,7 @@ static bool duplicate_key(const UCHAR* record1, const UCHAR* record2, void* ifl_ } -static PageNumber get_root_page(thread_db* tdbb, jrd_rel* relation) +static PageNumber get_root_page(thread_db* tdbb, Cached::Relation* relation) { /************************************** * @@ -2031,21 +2031,11 @@ static PageNumber get_root_page(thread_db* tdbb, jrd_rel* relation) * Find the root page for a relation. * **************************************/ - SET_TDBB(tdbb); - - RelationPages* relPages = relation->getPages(tdbb); - SLONG page = relPages->rel_index_root; - if (!page) - { - DPM_scan_pages(tdbb); - page = relPages->rel_index_root; - } - - return PageNumber(relPages->rel_pg_space_id, page); + return relation->getIndexRootPage(tdbb); } -static int index_block_flush(void* ast_object) +int IndexPermanent::indexReload(void* ast_object) { /************************************** * @@ -2060,16 +2050,19 @@ static int index_block_flush(void* ast_object) * out and release the lock. * **************************************/ - IndexBlock* const index_block = static_cast(ast_object); + + // AST for index reload lock + + Cached::Index* const idp = static_cast(ast_object); try { - Lock* const lock = index_block->idb_lock; + Lock* const lock = idp->getRescanLock(); Database* const dbb = lock->lck_dbb; AsyncContextHolder tdbb(dbb, FB_FUNCTION, lock); - release_index_block(tdbb, index_block); + idp->resetDependentObject(tdbb, ElementBase::ResetType::Mark); } catch (const Firebird::Exception&) {} // no-op @@ -2136,38 +2129,7 @@ static idx_e insert_key(thread_db* tdbb, } -static void release_index_block(thread_db* tdbb, IndexBlock* index_block) -{ -/************************************** - * - * r e l e a s e _ i n d e x _ b l o c k - * - ************************************** - * - * Functional description - * Release index block structure. - * - **************************************/ - if (index_block->idb_expression_statement) - { - index_block->idb_expression_statement->release(tdbb); - index_block->idb_expression_statement = nullptr; - } - index_block->idb_expression = nullptr; - index_block->idb_expression_desc.clear(); - - if (index_block->idb_condition_statement) - { - index_block->idb_condition_statement->release(tdbb); - index_block->idb_condition_statement = nullptr; - } - index_block->idb_condition = nullptr; - - LCK_release(tdbb, index_block->idb_lock); -} - - -static void signal_index_deletion(thread_db* tdbb, jrd_rel* relation, USHORT id) +static void signal_index_deletion(thread_db* tdbb, RelationPermanent* relation, USHORT id) { /************************************** * @@ -2180,40 +2142,16 @@ static void signal_index_deletion(thread_db* tdbb, jrd_rel* relation, USHORT id) * processes to get rid of index info. * **************************************/ - IndexBlock* index_block; +/* !!!!!!!!!!!!!!!!!!!!!!!! Lock* lock = NULL; - SET_TDBB(tdbb); - // get an exclusive lock on the associated index - // block (if it exists) to make sure that all other - // processes flush their cached information about this index - - for (index_block = relation->rel_index_blocks; index_block; index_block = index_block->idb_next) - { - if (index_block->idb_id == id) - { - lock = index_block->idb_lock; - break; - } - } - - // if one didn't exist, create it - - if (!index_block) - { - index_block = IDX_create_index_block(tdbb, relation, id); - lock = index_block->idb_lock; - } - // signal other processes to clear out the index block - if (lock->lck_physical == LCK_SR) { LCK_convert(tdbb, lock, LCK_EX, LCK_WAIT); } else { LCK_lock(tdbb, lock, LCK_EX, LCK_WAIT); } - - release_index_block(tdbb, index_block); + */ } diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 3fd62251440..dd094798182 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -36,7 +36,7 @@ struct ini_idx_t { UCHAR ini_idx_index_id; UCHAR ini_idx_relid; - UCHAR ini_idx_flags; + USHORT ini_idx_flags; UCHAR ini_idx_segment_count; USHORT ini_idx_ods; struct ini_idx_segment_t diff --git a/src/jrd/idx_proto.h b/src/jrd/idx_proto.h index c45b43d5b08..ed62bb787b2 100644 --- a/src/jrd/idx_proto.h +++ b/src/jrd/idx_proto.h @@ -33,24 +33,25 @@ namespace Jrd class jrd_rel; class jrd_tra; struct record_param; - class IndexBlock; struct index_desc; class CompilerScratch; class thread_db; } -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_activate_index(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId); +void IDX_check_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::Cached::Relation*, Jrd::Cached::Relation*); +bool IDX_check_master_types (Jrd::thread_db*, Jrd::index_desc&, Jrd::Cached::Relation*, int&); +void IDX_create_index(Jrd::thread_db*, Jrd::IdxCreate createMethod, Jrd::jrd_rel*, 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_mark_index(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId); +//void IDX_delete_index(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId); +void IDX_delete_indices(Jrd::thread_db*, Jrd::RelationPermanent*, Jrd::RelationPages*); +void IDX_mark_indices(Jrd::thread_db*, Jrd::Cached::Relation*); 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*); void IDX_modify_check_constraints(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*, Jrd::jrd_tra*); -void IDX_statistics(Jrd::thread_db*, Jrd::jrd_rel*, USHORT, Jrd::SelectivityList&); +void IDX_statistics(Jrd::thread_db*, Jrd::Cached::Relation*, USHORT, Jrd::SelectivityList&); void IDX_store(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*); void IDX_modify_flag_uk_modified(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*, Jrd::jrd_tra*); diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index cdac0eeb5a8..b72220c9319 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -574,7 +574,7 @@ void INF_database_info(thread_db* tdbb, for (TraNumber id = transaction->tra_oldest; id < transaction->tra_number; id++) { if (TRA_snapshot_state(tdbb, transaction, id) == tra_limbo && - TRA_wait(tdbb, transaction, id, jrd_tra::tra_wait) == tra_limbo) + TRA_wait(tdbb, transaction, id, tra_wait) == tra_limbo) { length = INF_convert(id, buffer); if (!(info = INF_put_item(item, length, buffer, info, end))) diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 03413fc8d06..4ff7bf86bef 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -56,11 +56,13 @@ #include "../jrd/acl.h" #include "../jrd/dyn.h" #include "../jrd/irq.h" +#include "../jrd/met.h" #include "../jrd/IntlManager.h" #include "../jrd/PreparedStatement.h" #include "../jrd/constants.h" #include "../jrd/grant_proto.h" #include "../jrd/SystemPackages.h" +#include "../dsql/DdlNodes.h" using namespace Firebird; using namespace Jrd; @@ -81,7 +83,7 @@ namespace unsigned getLatestFormat(thread_db* tdbb, int relId, int maxFieldId) { - const auto relation = MET_relation(tdbb, relId); + const auto* relation = MetadataCache::lookupRelation(tdbb, relId, CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); fb_assert(relation && relation->rel_formats); fb_assert(relation->rel_formats->count()); @@ -594,6 +596,27 @@ namespace AutoRequest handle; }; + class UpdateFormats : public Array + { + public: + void addIfMissing(MetaName relName) + { + if (!exist(relName)) + push(relName); + } + + void make(thread_db* tdbb) + { + for (auto relName : *this) + { + RelationNode::makeVersion(tdbb, tdbb->getTransaction(), relName); + auto* rel = MetadataCache::lookupRelation(tdbb, relName, CacheFlag::AUTOCREATE); + fb_assert(rel); + rel->makeObject(tdbb, CacheFlag::AUTOCREATE); + } + } + }; + }; // namespace static void store_admin_role(thread_db*, const MetaName&, RoleSecurity&); @@ -624,6 +647,9 @@ void INI_format(thread_db* tdbb, const string& charset) const auto ownerName = attachment->getUserName(); fb_assert(ownerName.hasData()); + // Adding trigger(s) changes relation format + UpdateFormats updateFormats; + AutoRequest handle, reqAddSC; { // scope for system relations @@ -637,7 +663,10 @@ 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) - DPM_create_relation(tdbb, MET_relation(tdbb, relfld[RFLD_R_ID])); + { + DPM_create_relation(tdbb, MetadataCache::lookupRelation(tdbb, relfld[RFLD_R_ID], + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN)); + } for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH) ; @@ -677,11 +706,7 @@ void INI_format(thread_db* tdbb, const string& charset) store_relation(tdbb, relId, relName, fieldId, relType, handle, relSec); if (needsRdbRuntime) - { - dsc desc; - desc.makeText(static_cast(strlen(relName)), CS_METADATA, (UCHAR*) relName); - DFW_post_work(transaction, dfw_update_format, &desc, 0); - } + updateFormats.push(relName); } } @@ -780,10 +805,11 @@ void INI_format(thread_db* tdbb, const string& charset) // Create system packages - // Reset nonRelSec for package permissions, it should be its last usage in this function - new(&nonRelSec) NonRelationSecurity(ownerName, reqAddSC, true); + // --- Reset nonRelSec for package permissions, it should be its last usage in this function + //new(&nonRelSec) NonRelationSecurity(ownerName, reqAddSC, true); + NonRelationSecurity nonRelSec2(ownerName, reqAddSC, true); - store_packages(tdbb, nonRelSec); + store_packages(tdbb, nonRelSec2); // Store default publication @@ -814,6 +840,8 @@ void INI_format(thread_db* tdbb, const string& charset) storeGrant(tdbb, buf.c_str(), obj_privilege, "RDB$DB_CREATORS", obj_relation, "SIUDR"); GRANT_privileges(tdbb, "RDB$DB_CREATORS", obj_relation, transaction); + updateFormats.make(tdbb); + DFW_perform_work(tdbb, transaction); tdbb->setTransaction(nullptr); @@ -836,26 +864,42 @@ void INI_init(thread_db* tdbb) { const bool isPersistent = (relfld[RFLD_R_TYPE] == rel_persistent); - jrd_rel* relation = MET_relation(tdbb, relfld[RFLD_R_ID]); + jrd_rel* relVers = MetadataCache::lookup_relation_id(tdbb, relfld[RFLD_R_ID], + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); + auto* relation = getPermanent(relVers); + 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]]; - HalfStaticArray fieldNames; + struct Field + { + const char* name; + const gfld* gfield; + + Field(const char* name, const gfld& gfield) + : name(name), gfield(&gfield) + { } + Field() = default; + }; + HalfStaticArray localFields; for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH) { - fieldNames.add(names[fld[RFLD_F_NAME]]); + localFields.add(Field(names[fld[RFLD_F_NAME]], gfields[fld[RFLD_F_ID]])); } - const auto fields = vec::newVector(*pool, fieldNames.getCount()); - relation->rel_fields = fields; + const auto fields = vec::newVector(*pool, localFields.getCount()); + relVers->rel_fields = fields; ULONG fieldPos = 0; for (auto iter = fields->begin(); iter != fields->end(); ++iter) { const auto field = FB_NEW_POOL(*pool) jrd_fld(*pool); - field->fld_name = fieldNames[fieldPos++]; + field->fld_name = localFields[fieldPos].name; + field->fld_source_name = names[localFields[fieldPos].gfield->gfld_name]; + field->fld_length = localFields[fieldPos].gfield->gfld_length; *iter = field; + ++fieldPos; } relation->rel_formats = vec::newVector(*pool, 1); @@ -915,8 +959,9 @@ void INI_init(thread_db* tdbb) if (desc->isText()) { - if (!getCharsetByTextType(desc->dsc_sub_type, gfield->gfld_sub_type)) - desc->dsc_sub_type = CS_NONE; + CSetId cs = CS_NONE; + getCharsetByTextType(cs, gfield->gfld_sub_type); + desc->setTextType(cs); } else desc->dsc_sub_type = gfield->gfld_sub_type; @@ -937,8 +982,8 @@ void INI_init(thread_db* tdbb) } 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]; } } @@ -950,7 +995,8 @@ void INI_init_sys_relations(thread_db* tdbb) for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1) { - jrd_rel* relation = MET_relation(tdbb, relfld[RFLD_R_ID]); + auto relId = relfld[RFLD_R_ID]; + auto relation = MetadataCache::lookupRelation(tdbb, relId, CacheFlag::AUTOCREATE); bool needsRdbRuntime = false; for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH) @@ -962,7 +1008,7 @@ void INI_init_sys_relations(thread_db* tdbb) } if (needsRdbRuntime) - MET_scan_relation(tdbb, relation); + relation->makeObject(tdbb, 0); } } @@ -977,7 +1023,7 @@ void INI_init_dsql(thread_db* tdbb, dsql_dbb* database) const auto majorVersion = dbb->dbb_ods_version; const auto minorVersion = dbb->dbb_minor_version; - +/* // Load relation and fields. const int* fld; @@ -1044,9 +1090,9 @@ void INI_init_dsql(thread_db* tdbb, dsql_dbb* database) } database->dbb_relations.put(relation->rel_name, relation); - MET_dsql_cache_use(tdbb, SYM_relation, relation->rel_name); + MetadataCache::dsql_cache_use(tdbb, SYM_relation, relation->rel_name); } - +*/ // Load internal character sets and collations, necessary for engine operation. for (const IntlManager::CharSetDefinition* csDef = IntlManager::defaultCharSets; @@ -1058,9 +1104,8 @@ void INI_init_dsql(thread_db* tdbb, dsql_dbb* database) dsql_intlsym* csSymbol = FB_NEW_POOL(database->dbb_pool) dsql_intlsym(database->dbb_pool); csSymbol->intlsym_name = csDef->name; csSymbol->intlsym_charset_id = csDef->id; - csSymbol->intlsym_collate_id = 0; - csSymbol->intlsym_ttype = - INTL_CS_COLL_TO_TTYPE(csSymbol->intlsym_charset_id, csSymbol->intlsym_collate_id); + csSymbol->intlsym_collate_id = CollId(0); + csSymbol->intlsym_ttype = TTypeId(csSymbol->intlsym_charset_id, csSymbol->intlsym_collate_id); csSymbol->intlsym_bytes_per_char = csDef->maxBytes; // Mark the charset as invalid to reload it ASAP. This is done because we cannot know here @@ -1069,7 +1114,7 @@ void INI_init_dsql(thread_db* tdbb, dsql_dbb* database) database->dbb_charsets.put(csDef->name, csSymbol); database->dbb_charsets_by_id.put(csSymbol->intlsym_charset_id, csSymbol); - MET_dsql_cache_use(tdbb, SYM_intlsym_charset, csDef->name); + MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_charset, csDef->name); for (const IntlManager::CollationDefinition* colDef = IntlManager::defaultCollations; colDef->name; ++colDef) @@ -1082,12 +1127,11 @@ void INI_init_dsql(thread_db* tdbb, dsql_dbb* database) colSymbol->intlsym_flags = 0; colSymbol->intlsym_charset_id = csDef->id; colSymbol->intlsym_collate_id = colDef->collationId; - colSymbol->intlsym_ttype = - INTL_CS_COLL_TO_TTYPE(colSymbol->intlsym_charset_id, colSymbol->intlsym_collate_id); + colSymbol->intlsym_ttype = TTypeId(colSymbol->intlsym_charset_id, colSymbol->intlsym_collate_id); colSymbol->intlsym_bytes_per_char = csDef->maxBytes; database->dbb_collations.put(colDef->name, colSymbol); - MET_dsql_cache_use(tdbb, SYM_intlsym_collation, colDef->name); + MetadataCache::dsql_cache_use(tdbb, SYM_intlsym_collation, colDef->name); } } } @@ -1132,6 +1176,10 @@ void INI_upgrade(thread_db* tdbb) try { + // finally we should update formats of system relations + + UpdateFormats updateFormats; + // Disable most of the deferred work processing, // as we do all the underlying work ourselves @@ -1157,7 +1205,10 @@ 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::lookupRelation(tdbb, relfld[RFLD_R_ID], + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN)); + } for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH) ; @@ -1210,12 +1261,8 @@ void INI_upgrade(thread_db* tdbb) } END_FOR - // Schedule metadata cache to be updated at the commit time - - dsc desc; - desc.makeText(static_cast(strlen(relName)), CS_METADATA, - (UCHAR*) relName); - DFW_post_work(transaction, dfw_update_format, &desc, 0); + // Schedule metadata cache to be updated + updateFormats.push(relName); } } } @@ -1252,10 +1299,11 @@ void INI_upgrade(thread_db* tdbb) // Create new system packages // Reset nonRelSec for package permissions, it should be its last usage in this function - new(&nonRelSec) NonRelationSecurity(ownerName, reqAddSC, true); + // new(&nonRelSec) NonRelationSecurity(ownerName, reqAddSC, true); + NonRelationSecurity nonRelSec2(ownerName, reqAddSC, true); context = "packages"; - store_packages(tdbb, nonRelSec, odsVersion); + store_packages(tdbb, nonRelSec2, 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 @@ -1264,6 +1312,8 @@ void INI_upgrade(thread_db* tdbb) // // The same about the new types being introduced in minor ODS versions. + updateFormats.make(tdbb); + TRA_commit(tdbb, transaction, false); } @@ -1277,7 +1327,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], CacheFlag::NOSCAN); if (relation && relation->getBasePages()->rel_pages) DPM_delete_relation(tdbb, relation); } @@ -1318,7 +1368,7 @@ void INI_upgrade(thread_db* tdbb) 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 @@ -1344,12 +1394,12 @@ void INI_upgrade(thread_db* tdbb) 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); } } - } + } */ } @@ -1622,7 +1672,9 @@ static void store_indices(thread_db* tdbb, USHORT odsVersion) for (FB_SIZE_T n = 0; n < SYSTEM_INDEX_COUNT; n++) { const ini_idx_t* index = &indices[n]; - const auto relation = MET_relation(tdbb, index->ini_idx_relid); + auto* relVers = MetadataCache::lookup_relation_id(tdbb, index->ini_idx_relid, + CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); + auto* relation = getPermanent(relVers); if (odsVersion && index->ini_idx_ods <= odsVersion) continue; @@ -1636,7 +1688,7 @@ static void store_indices(thread_db* tdbb, USHORT odsVersion) PAD(relation->rel_name, X.RDB$RELATION_NAME); PAD(indexName, X.RDB$INDEX_NAME); - X.RDB$UNIQUE_FLAG = index->ini_idx_flags & idx_unique; + X.RDB$UNIQUE_FLAG = (index->ini_idx_flags & idx_unique) ? 1 : 0; X.RDB$SEGMENT_COUNT = index->ini_idx_segment_count; if (index->ini_idx_flags & idx_descending) @@ -1661,7 +1713,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); @@ -1677,7 +1729,7 @@ static void store_indices(thread_db* tdbb, USHORT odsVersion) idx.idx_flags = index->ini_idx_flags; SelectivityList selectivity(*tdbb->getDefaultPool()); - IDX_create_index(tdbb, relation, &idx, indexName.c_str(), NULL, + IDX_create_index(tdbb, IdxCreate::AtOnce, relVers, &idx, indexName.c_str(), NULL, transaction, selectivity); X.RDB$INDEX_ID = idx.idx_id + 1; @@ -1773,10 +1825,24 @@ static void store_intlnames(thread_db* tdbb, NonRelationSecurity& security) X.RDB$COLLATION_ATTRIBUTES = collation->attributes; - if (collation->specificAttributes) + string charSetName, modAttributes; + for (const auto* charSet = IntlManager::defaultCharSets; + charSet->name; ++charSet) { - attachment->storeMetaDataBlob(tdbb, transaction, - &X.RDB$SPECIFIC_ATTRIBUTES, collation->specificAttributes); + if (charSet->id == collation->charSetId) + { + charSetName = charSet->name; + break; + } + } + + fb_assert(charSetName.hasData()); + DFW_setupCollationAttributes(collation->baseName ? collation->baseName : collation->name, + charSetName, collation->specificAttributes ? collation->specificAttributes : "", modAttributes); + + if (modAttributes.hasData()) + { + attachment->storeMetaDataBlob(tdbb, transaction, &X.RDB$SPECIFIC_ATTRIBUTES, modAttributes); X.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE; } else @@ -1786,6 +1852,12 @@ static void store_intlnames(thread_db* tdbb, NonRelationSecurity& security) security.storePrivileges(tdbb, collation->name, obj_collation); } + + for (const IntlManager::CharSetDefinition* charSet = IntlManager::defaultCharSets; + charSet->name; ++charSet) + { + MetadataCache::lookup_charset(tdbb, charSet->id, CacheFlag::UPGRADE | CacheFlag::NOCOMMIT); + } } diff --git a/src/jrd/ini.h b/src/jrd/ini.h index 9cc22cf22f0..a06046c797f 100644 --- a/src/jrd/ini.h +++ b/src/jrd/ini.h @@ -36,6 +36,7 @@ #include "../jrd/dflt.h" #include "../jrd/constants.h" #include "../jrd/ods.h" +#include "../common/dsc.h" //****************************** // names.h diff --git a/src/jrd/intl.cpp b/src/jrd/intl.cpp index 463293de4ef..29b8fe39ee8 100644 --- a/src/jrd/intl.cpp +++ b/src/jrd/intl.cpp @@ -93,6 +93,7 @@ #include "firebird.h" #include #include +#include "../jrd/CharSetContainer.h" #include "../jrd/jrd.h" #include "../jrd/req.h" #include "../jrd/val.h" @@ -101,6 +102,7 @@ #include "../jrd/intl_classes.h" #include "../jrd/ods.h" #include "../jrd/btr.h" +#include "../jrd/met.h" #include "../intl/charsets.h" #include "../intl/country_codes.h" #include "../common/gdsassert.h" @@ -114,7 +116,7 @@ #include "../yvalve/gds_proto.h" #include "../jrd/intl_proto.h" #include "../common/isc_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../common/intlobj_new.h" #include "../jrd/Collation.h" @@ -131,57 +133,13 @@ using namespace Firebird; static bool allSpaces(CharSet*, const BYTE*, ULONG, ULONG); -static int blocking_ast_collation(void* ast_object); -static void pad_spaces(thread_db*, CHARSET_ID, BYTE *, ULONG); -static void lookup_texttype(texttype* tt, const SubtypeInfo* info); +//static int blocking_ast_collation(void* ast_object); +static void pad_spaces(thread_db*, CSetId, BYTE *, ULONG); static GlobalPtr createCollationMtx; // Classes and structures used internally to this file and intl implementation -class CharSetContainer -{ -public: - CharSetContainer(MemoryPool& p, USHORT cs_id, const SubtypeInfo* info); - - void release(thread_db* tdbb) - { - for (FB_SIZE_T i = 0; i < charset_collations.getCount(); i++) - { - if (charset_collations[i]) - charset_collations[i]->release(tdbb); - } - } - - void destroy(thread_db* tdbb) - { - cs->destroy(); - for (FB_SIZE_T i = 0; i < charset_collations.getCount(); i++) - { - if (charset_collations[i]) - charset_collations[i]->destroy(tdbb); - } - } - - CharSet* getCharSet() { return cs; } - - Collation* lookupCollation(thread_db* tdbb, USHORT tt_id); - void unloadCollation(thread_db* tdbb, USHORT tt_id); - - 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); - -private: - static bool lookupInternalCharSet(USHORT id, SubtypeInfo* info); - -private: - Firebird::Array charset_collations; - CharSet* cs; -}; - - -CharSetContainer* CharSetContainer::lookupCharset(thread_db* tdbb, USHORT ttype) +CharSetContainer* CharSetContainer::lookupCharset(thread_db* tdbb, TTypeId ttype) { /************************************** * @@ -203,45 +161,23 @@ CharSetContainer* CharSetContainer::lookupCharset(thread_db* tdbb, USHORT ttype) * - if error * **************************************/ - CharSetContainer* cs = NULL; - SET_TDBB(tdbb); - Jrd::Attachment* attachment = tdbb->getAttachment(); - fb_assert(attachment); - USHORT id = TTYPE_TO_CHARSET(ttype); + auto id = CSetId(ttype); if (id == CS_dynamic) id = tdbb->getCharSet(); - if (id >= attachment->att_charsets.getCount()) - attachment->att_charsets.resize(id + 10); - else - cs = attachment->att_charsets[id]; - - // allocate a new character set object if we couldn't find one. - if (!cs) - { - SubtypeInfo info; - - if (lookupInternalCharSet(id, &info) || MET_get_char_coll_subtype_info(tdbb, id, &info)) - { - attachment->att_charsets[id] = cs = - FB_NEW_POOL(*attachment->att_pool) CharSetContainer(*attachment->att_pool, id, &info); - } - else - ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(ttype)); - } - - return cs; + return MetadataCache::getCharSet(tdbb, id, CacheFlag::AUTOCREATE); } // Lookup a system character set without looking in the database. -bool CharSetContainer::lookupInternalCharSet(USHORT id, SubtypeInfo* info) +bool CharSetContainer::lookupInternalCharSet(CSetId id, SubtypeInfo* info) { if (id == CS_UTF16) { - info->charsetName = "UTF16"; + info->charsetName.clear(); + info->charsetName.push("UTF16"); return true; } @@ -261,7 +197,8 @@ bool CharSetContainer::lookupInternalCharSet(USHORT id, SubtypeInfo* info) { if (colDef->charSetId == id && colDef->collationId == 0) { - info->charsetName = csDef->name; + info->charsetName.clear(); + info->charsetName.push(csDef->name); info->collationName = colDef->name; info->attributes = colDef->attributes; info->ignoreAttributes = false; @@ -280,49 +217,43 @@ bool CharSetContainer::lookupInternalCharSet(USHORT id, SubtypeInfo* info) return false; } - -Lock* CharSetContainer::createCollationLock(thread_db* tdbb, USHORT ttype, void* object) +CharSetContainer::CharSetContainer(thread_db* tdbb, MemoryPool& p, MetaId id, MakeLock* makeLock, NoData) + : PermanentStorage(p), + names(p), + cs(NULL), + cs_lock(nullptr) { -/************************************** - * - * c r e a t e C o l l a t i o n L o c k - * - ************************************** - * - * Functional description - * Create a collation lock. - * - **************************************/ - // Could we have an AST on this lock? If yes, it will fail if we don't - // have lck_object to it, so set ast routine to NULL for safety. - - Lock* lock = FB_NEW_RPT(*tdbb->getAttachment()->att_pool, 0) - Lock(tdbb, sizeof(SLONG), LCK_tt_exist, object, (object ? blocking_ast_collation : NULL)); - lock->setKey(ttype); + SubtypeInfo info; + CSetId cs_id(id); - return lock; -} + if (!(lookupInternalCharSet(cs_id, &info) || MET_get_char_coll_subtype_info(tdbb, cs_id, &info))) + ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(cs_id)); -CharSetContainer::CharSetContainer(MemoryPool& p, USHORT cs_id, const SubtypeInfo* info) - : charset_collations(p), - cs(NULL) -{ charset* csL = FB_NEW_POOL(p) charset; - memset(csL, 0, sizeof(charset)); - if (IntlManager::lookupCharSet(info->charsetName.c_str(), csL) && - (csL->charset_flags & CHARSET_ASCII_BASED)) + for (auto& csName : info.charsetName) { - this->cs = CharSet::createInstance(p, cs_id, csL); - } - else - { - delete csL; - ERR_post(Arg::Gds(isc_charset_not_installed) << Arg::Str(info->charsetName)); + memset(csL, 0, sizeof(charset)); + + if (IntlManager::lookupCharSet(csName.c_str(), csL) && + (csL->charset_flags & CHARSET_ASCII_BASED)) + { + cs = CharSet::createInstance(p, cs_id, csL); + + cs_lock = makeLock(tdbb, p); + cs_lock->setKey(cs_id); + cs_lock->lck_object = this; + + return; + } } + + delete csL; + ERR_post(Arg::Gds(isc_charset_not_installed) << + (info.charsetName.hasData() ? Arg::Str(info.charsetName[0]) : Arg::Str(""))); } -CsConvert CharSetContainer::lookupConverter(thread_db* tdbb, CHARSET_ID toCsId) +CsConvert CharSetContainer::lookupConverter(thread_db* tdbb, CSetId toCsId) { if (toCsId == CS_UTF16) return CsConvert(cs->getStruct(), NULL); @@ -334,190 +265,68 @@ 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::getCollation(CollId id) { - const USHORT id = TTYPE_TO_COLLATION(tt_id); - - if (id < charset_collations.getCount() && charset_collations[id] != NULL) - { - if (!charset_collations[id]->obsolete) - return charset_collations[id]; - } - - CheckoutLockGuard guard(tdbb, createCollationMtx, FB_FUNCTION); // do we need it ? - - Collation* to_delete = NULL; - - if (id < charset_collations.getCount() && charset_collations[id] != NULL) - { - if (charset_collations[id]->obsolete) - { - // if obsolete collation is not used delete it immediately, - // else wait until all references are released - if (charset_collations[id]->useCount == 0) - { - charset_collations[id]->destroy(tdbb); - delete charset_collations[id]; - } - else - to_delete = charset_collations[id]; - - charset_collations[id] = NULL; - } - else - return charset_collations[id]; - } - - SubtypeInfo info; - if (MET_get_char_coll_subtype_info(tdbb, tt_id, &info)) - { - CharSet* charset = INTL_charset_lookup(tdbb, TTYPE_TO_CHARSET(tt_id)); - - if (TTYPE_TO_CHARSET(tt_id) != CS_METADATA) - { - Firebird::UCharBuffer specificAttributes; - ULONG size = info.specificAttributes.getCount() * charset->maxBytesPerChar(); - - size = INTL_convert_bytes(tdbb, TTYPE_TO_CHARSET(tt_id), - specificAttributes.getBuffer(size), size, - CS_METADATA, info.specificAttributes.begin(), - info.specificAttributes.getCount(), ERR_post); - specificAttributes.shrink(size); - info.specificAttributes = specificAttributes; - } - - Attachment* const att = tdbb->getAttachment(); - AutoPtr tt(FB_NEW_POOL(*att->att_pool) texttype); - memset(tt, 0, sizeof(texttype)); - - lookup_texttype(tt, &info); - - 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)); - - if (tt->texttype_canonical_width == 0) - { - if (charset->isMultiByte()) - tt->texttype_canonical_width = sizeof(ULONG); // UTF-32 - else - { - tt->texttype_canonical_width = charset->minBytesPerChar(); - // canonical is equal to string, then TEXTTYPE_DIRECT_MATCH can be turned on - tt->texttype_flags |= TEXTTYPE_DIRECT_MATCH; - } - } - - charset_collations[id] = Collation::createInstance(*att->att_pool, tt_id, - tt, info.attributes, charset); - charset_collations[id]->name = info.collationName; - - tt.release(); - - // we don't need a lock in the charset - if (id != 0) - { - Lock* lock = charset_collations[id]->existenceLock = - CharSetContainer::createCollationLock(tdbb, tt_id, charset_collations[id]); - - fb_assert(charset_collations[id]->useCount == 0); - fb_assert(!charset_collations[id]->obsolete); - - LCK_lock(tdbb, lock, 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); - delete to_delete; - } - } - } - else - { - if (to_delete) - { - LCK_lock(tdbb, to_delete->existenceLock, LCK_SR, LCK_WAIT); - to_delete->destroy(tdbb); - delete to_delete; - } - - 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]; } +Collation* CharSetVers::getCollation(MetaName name) +{ + FB_SIZE_T pos; + if (charset_collations.find([name](Collation* col) { return col->name == name; }, pos)) + return charset_collations[pos]; + + ERR_post(Arg::Gds(isc_text_subtype) << name); +} +/* void CharSetContainer::unloadCollation(thread_db* tdbb, USHORT tt_id) { const USHORT id = TTYPE_TO_COLLATION(tt_id); fb_assert(id != 0); - if (id < charset_collations.getCount() && charset_collations[id] != NULL) + Collation* coll(FB_FUNCTION); + if (charset_collations.load(tdbb, id, coll)) { - if (charset_collations[id]->useCount != 0) + MutexLockGuard g(MetadataCache::get(tdbb)->mdc_use_mutex, FB_FUNCTION); + + if (coll->useCount != 0) { ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_obj_in_use) << Arg::Str(charset_collations[id]->name)); + Arg::Gds(isc_obj_in_use) << Arg::Str(coll->name)); } - fb_assert(charset_collations[id]->existenceLock); + fb_assert(coll->existenceLock); - if (!charset_collations[id]->obsolete) + if (!coll->obsolete) { - LCK_convert(tdbb, charset_collations[id]->existenceLock, LCK_EX, LCK_WAIT); - charset_collations[id]->obsolete = true; - LCK_release(tdbb, charset_collations[id]->existenceLock); + LCK_convert(tdbb, coll->existenceLock, LCK_EX, LCK_WAIT); + coll->obsolete = true; + LCK_release(tdbb, coll->existenceLock); } } else { // signal other processes collation is gone - Lock* lock = CharSetContainer::createCollationLock(tdbb, tt_id); + AutoPtr lock(CharSetContainer::createCollationLock(tdbb, tt_id)); LCK_lock(tdbb, lock, LCK_EX, LCK_WAIT); LCK_release(tdbb, lock); - - delete lock; } } + */ - -static void lookup_texttype(texttype* tt, const SubtypeInfo* info) +void INTL_lookup_texttype(texttype* tt, const SubtypeInfo* info) { - IntlManager::lookupCollation(info->baseCollationName.c_str(), info->charsetName.c_str(), + IntlManager::lookupCollation(info->baseCollationName.c_str(), info->charsetName, info->attributes, info->specificAttributes.begin(), info->specificAttributes.getCount(), info->ignoreAttributes, tt); } -void Jrd::Attachment::releaseIntlObjects(thread_db* tdbb) -{ - for (FB_SIZE_T i = 0; i < att_charsets.getCount(); i++) - { - if (att_charsets[i]) - att_charsets[i]->release(tdbb); - } -} - - -void Jrd::Attachment::destroyIntlObjects(thread_db* tdbb) -{ - for (FB_SIZE_T i = 0; i < att_charsets.getCount(); i++) - { - if (att_charsets[i]) - { - att_charsets[i]->destroy(tdbb); - att_charsets[i] = NULL; - } - } -} - - void INTL_adjust_text_descriptor(thread_db* tdbb, dsc* desc) { /************************************** @@ -536,7 +345,7 @@ void INTL_adjust_text_descriptor(thread_db* tdbb, dsc* desc) { SET_TDBB(tdbb); - USHORT ttype = INTL_TTYPE(desc); + auto ttype = desc->getTextType(); CharSet* charSet = INTL_charset_lookup(tdbb, ttype); @@ -552,7 +361,7 @@ void INTL_adjust_text_descriptor(thread_db* tdbb, dsc* desc) } -CHARSET_ID INTL_charset(thread_db* tdbb, USHORT ttype) +CSetId INTL_charset(thread_db* tdbb, TTypeId ttype) { /************************************** * @@ -565,20 +374,13 @@ CHARSET_ID INTL_charset(thread_db* tdbb, USHORT ttype) * **************************************/ - switch (ttype) + if (ttype == ttype_dynamic) { - case ttype_none: - return (CS_NONE); - case ttype_ascii: - return (CS_ASCII); - case ttype_binary: - return (CS_BINARY); - case ttype_dynamic: SET_TDBB(tdbb); - return (tdbb->getCharSet()); - default: - return (TTYPE_TO_CHARSET(ttype)); + return tdbb->getCharSet(); } + + return CSetId(ttype); } @@ -606,23 +408,23 @@ int INTL_compare(thread_db* tdbb, const dsc* pText1, const dsc* pText2, ErrorFun // trailing spaces in strings are ignored for comparision UCHAR* p1; - USHORT t1; + TTypeId t1; ULONG length1 = CVT_get_string_ptr(pText1, &t1, &p1, NULL, 0, tdbb->getAttachment()->att_dec_status, err); UCHAR* p2; - USHORT t2; + TTypeId t2; ULONG length2 = CVT_get_string_ptr(pText2, &t2, &p2, NULL, 0, tdbb->getAttachment()->att_dec_status, err); // YYY - by SQL II compare_type must be explicit in the // SQL statement if there is any doubt - USHORT compare_type = MAX(t1, t2); // YYY + TTypeId compare_type = MAX(t1, t2); // YYY HalfStaticArray buffer; if (t1 != t2) { - CHARSET_ID cs1 = INTL_charset(tdbb, t1); - CHARSET_ID cs2 = INTL_charset(tdbb, t2); + CSetId cs1 = INTL_charset(tdbb, t1); + CSetId cs2 = INTL_charset(tdbb, t2); if (cs1 != cs2) { if (compare_type != t2) @@ -663,10 +465,10 @@ int INTL_compare(thread_db* tdbb, const dsc* pText1, const dsc* pText2, ErrorFun ULONG INTL_convert_bytes(thread_db* tdbb, - CHARSET_ID dest_type, + CSetId dest_type, BYTE* dest_ptr, const ULONG dest_len, - CHARSET_ID src_type, + CSetId src_type, const BYTE* src_ptr, const ULONG src_len, ErrorFunction err) @@ -696,21 +498,21 @@ ULONG INTL_convert_bytes(thread_db* tdbb, fb_assert(src_type != dest_type); fb_assert(err != NULL); - dest_type = INTL_charset(tdbb, dest_type); - src_type = INTL_charset(tdbb, src_type); + auto destCs = INTL_charset(tdbb, dest_type); + auto srcCs = INTL_charset(tdbb, src_type); const UCHAR* const start_dest_ptr = dest_ptr; - if (dest_type == CS_BINARY || dest_type == CS_NONE || - src_type == CS_BINARY || src_type == CS_NONE) + if (destCs == CS_BINARY || destCs == CS_NONE || + srcCs == CS_BINARY || srcCs == CS_NONE) { // See if we just need a length estimate if (dest_ptr == NULL) return (src_len); - if (dest_type != CS_BINARY && dest_type != CS_NONE) + if (destCs != CS_BINARY && destCs != CS_NONE) { - CharSet* toCharSet = INTL_charset_lookup(tdbb, dest_type); + CharSet* toCharSet = INTL_charset_lookup(tdbb, destCs); if (!toCharSet->wellFormed(src_len, src_ptr)) err(Arg::Gds(isc_malformed_string)); @@ -726,7 +528,7 @@ ULONG INTL_convert_bytes(thread_db* tdbb, // See if only space characters are remaining len = src_len - MIN(dest_len, src_len); - if (len == 0 || allSpaces(INTL_charset_lookup(tdbb, src_type), src_ptr, len, 0)) + if (len == 0 || allSpaces(INTL_charset_lookup(tdbb, srcCs), src_ptr, len, 0)) return dest_ptr - start_dest_ptr; err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) << @@ -737,7 +539,7 @@ ULONG INTL_convert_bytes(thread_db* tdbb, // character sets are known to be different // Do we know an object from cs1 to cs2? - CsConvert cs_obj = INTL_convert_lookup(tdbb, dest_type, src_type); + CsConvert cs_obj = INTL_convert_lookup(tdbb, destCs, srcCs); return cs_obj.convert(src_len, src_ptr, dest_len, dest_ptr, NULL, true); } @@ -745,7 +547,7 @@ ULONG INTL_convert_bytes(thread_db* tdbb, } -CsConvert INTL_convert_lookup(thread_db* tdbb, CHARSET_ID to_cs, CHARSET_ID from_cs) +CsConvert INTL_convert_lookup(thread_db* tdbb, CSetId to_cs, CSetId from_cs) { /************************************** * @@ -797,15 +599,15 @@ void INTL_convert_string(dsc* to, const dsc* from, Firebird::Callbacks* cb) fb_assert(from != NULL); fb_assert(IS_TEXT(to) && IS_TEXT(from)); - const CHARSET_ID from_cs = INTL_charset(tdbb, INTL_TTYPE(from)); - const CHARSET_ID to_cs = INTL_charset(tdbb, INTL_TTYPE(to)); + const CSetId from_cs = INTL_charset(tdbb, INTL_TTYPE(from)); + const CSetId to_cs = INTL_charset(tdbb, INTL_TTYPE(to)); UCHAR* p = to->dsc_address; // Must convert dtype(cstring,text,vary) and ttype(ascii,binary,..intl..) UCHAR* from_ptr; - USHORT from_type; + TTypeId from_type; const USHORT from_len = CVT_get_string_ptr(from, &from_type, &from_ptr, NULL, 0, tdbb->getAttachment()->att_dec_status, cb->err); @@ -940,11 +742,11 @@ bool INTL_data_or_binary(const dsc* pText) * **************************************/ - return (INTL_data(pText) || (pText->dsc_ttype() == ttype_binary)); + return (INTL_data(pText) || (pText->getTextType() == ttype_binary)); } -bool INTL_defined_type(thread_db* tdbb, USHORT t_type) +bool INTL_defined_type(thread_db* tdbb, TTypeId t_type) { /************************************** * @@ -997,7 +799,7 @@ USHORT INTL_key_length(thread_db* tdbb, USHORT idxType, USHORT iLength) fb_assert(idxType >= idx_first_intl_string); - const USHORT ttype = INTL_INDEX_TO_TEXT(idxType); + const auto ttype = INTL_INDEX_TO_TEXT(idxType); USHORT key_length; if (ttype <= ttype_last_internal) @@ -1020,7 +822,7 @@ USHORT INTL_key_length(thread_db* tdbb, USHORT idxType, USHORT iLength) } -CharSet* INTL_charset_lookup(thread_db* tdbb, USHORT parm1) +CharSet* INTL_charset_lookup(thread_db* tdbb, CSetId parm1) { /************************************** * @@ -1042,12 +844,12 @@ CharSet* INTL_charset_lookup(thread_db* tdbb, USHORT parm1) * - if error * **************************************/ - CharSetContainer *cs = CharSetContainer::lookupCharset(tdbb, parm1); - return cs->getCharSet(); + CharSetContainer* csc = CharSetContainer::lookupCharset(tdbb, parm1); + return csc->getCharSet(); } -Collation* INTL_texttype_lookup(thread_db* tdbb, USHORT parm1) +Collation* INTL_texttype_lookup(thread_db* tdbb, TTypeId parm1) { /************************************** * @@ -1072,17 +874,24 @@ Collation* INTL_texttype_lookup(thread_db* tdbb, USHORT parm1) SET_TDBB(tdbb); if (parm1 == ttype_dynamic) - parm1 = MAP_CHARSET_TO_TTYPE(tdbb->getCharSet()); + parm1 = tdbb->getCharSet(); - CharSetContainer* csc = CharSetContainer::lookupCharset(tdbb, parm1); + auto* vers = MetadataCache::lookup_charset(tdbb, parm1, CacheFlag::AUTOCREATE); - return csc->lookupCollation(tdbb, parm1); + if (vers) + { + auto* coll = vers->getCollation(parm1); + if (coll) + return coll; + } + + ERR_post(Arg::Gds(isc_text_subtype) << Arg::Num(parm1)); } -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 * @@ -1091,14 +900,14 @@ void INTL_texttype_unload(thread_db* tdbb, USHORT ttype) * Functional description * Unload a collation from memory. * - **************************************/ + ************************************** SET_TDBB(tdbb); CharSetContainer* csc = CharSetContainer::lookupCharset(tdbb, ttype); if (csc) csc->unloadCollation(tdbb, ttype); } - +*/ bool INTL_texttype_validate(Jrd::thread_db* tdbb, const SubtypeInfo* info) { @@ -1119,7 +928,7 @@ bool INTL_texttype_validate(Jrd::thread_db* tdbb, const SubtypeInfo* info) try { - lookup_texttype(&tt, info); + INTL_lookup_texttype(&tt, info); if (tt.texttype_fn_destroy) tt.texttype_fn_destroy(&tt); @@ -1152,7 +961,7 @@ void INTL_pad_spaces(thread_db* tdbb, DSC* type, UCHAR* string, ULONG length) fb_assert(IS_TEXT(type)); fb_assert(string != NULL); - const USHORT charset = INTL_charset(tdbb, type->dsc_ttype()); + const auto charset = INTL_charset(tdbb, type->getTextType()); pad_spaces(tdbb, charset, string, length); } @@ -1188,7 +997,7 @@ USHORT INTL_string_to_key(thread_db* tdbb, fb_assert(pByte->dsc_dtype == dtype_text); UCHAR pad_char; - USHORT ttype; + TTypeId ttype; switch (idxType) { @@ -1240,7 +1049,7 @@ USHORT INTL_string_to_key(thread_db* tdbb, outlen = (dest - pByte->dsc_address); break; default: - TextType* obj = INTL_texttype_lookup(tdbb, ttype); + Collation* obj = INTL_texttype_lookup(tdbb, ttype); fb_assert(key_type != INTL_KEY_MULTI_STARTING || (obj->getFlags() & TEXTTYPE_MULTI_STARTING_KEY)); outlen = obj->string_to_key(len, src, pByte->dsc_length, dest, key_type); break; @@ -1303,10 +1112,10 @@ static bool allSpaces(CharSet* charSet, const BYTE* ptr, ULONG len, ULONG offset return true; } - +/* static int blocking_ast_collation(void* ast_object) { -/************************************** + ************************************** * * b l o c k i n g _ a s t _ c o l l a t i o n * @@ -1319,7 +1128,7 @@ static int blocking_ast_collation(void* ast_object) * Otherwise, mark the collation as obsolete * and release the collation existence lock. * - **************************************/ + ************************************** Collation* const tt = static_cast(ast_object); try @@ -1337,9 +1146,10 @@ static int blocking_ast_collation(void* ast_object) return 0; } +*/ -static void pad_spaces(thread_db* tdbb, CHARSET_ID charset, BYTE* ptr, ULONG len) +static void pad_spaces(thread_db* tdbb, CSetId charset, BYTE* ptr, ULONG len) { /* byte count */ /************************************** * diff --git a/src/jrd/intl.h b/src/jrd/intl.h index ee9e0d57335..5bc5a07964b 100644 --- a/src/jrd/intl.h +++ b/src/jrd/intl.h @@ -24,12 +24,58 @@ #ifndef JRD_INTL_H #define JRD_INTL_H -#include "../common/dsc.h" +// Maps a Character_set_id & collation_id to a text_type (driver ID) + +struct TTypeId; +struct CSetId; +struct CollId; + +struct IdStorage +{ + constexpr explicit IdStorage(USHORT id) : val(id) { } + + constexpr operator USHORT() const { return val; } + bool operator==(const IdStorage& id) const { return val == id.val; } + bool operator!=(const IdStorage& id) const { return val != id.val; } + + USHORT val; +}; + +struct TTypeId : public IdStorage +{ + TTypeId() : IdStorage(0) { } + explicit TTypeId(USHORT id) : IdStorage(id) { } + constexpr TTypeId(CSetId id); + TTypeId(CSetId cs, CollId col); +}; + +struct CSetId : public IdStorage +{ + CSetId() : IdStorage(0) { } + constexpr explicit CSetId(USHORT id) : IdStorage(id & 0xFF) { } + constexpr CSetId(TTypeId tt) : IdStorage(tt.val & 0xFF) { } +}; + +struct CollId : public IdStorage +{ + CollId() : IdStorage(0) { } + constexpr explicit CollId(USHORT id) : IdStorage(id) { } + constexpr CollId(TTypeId tt) : IdStorage(tt.val >> 8) { } +}; + +inline TTypeId::TTypeId(CSetId cs, CollId col) + : IdStorage((col.val << 8) | (cs.val & 0xFF)) +{ } + +inline constexpr TTypeId::TTypeId(CSetId cs) + : IdStorage(cs.val & 0xFF) +{ } + #include "../intl/charsets.h" -#define ASCII_SPACE 32 // ASCII code for space +inline const BYTE ASCII_SPACE = 32; // ASCII code for space -//#define INTL_name_not_found 1 +//#define INTL_name_not_found 1 ????????????????? //#define INTL_subtype_not_implemented 2 /* @@ -49,13 +95,13 @@ // text type definitions -#define ttype_none CS_NONE // 0 -#define ttype_ascii CS_ASCII // 2 -#define ttype_binary CS_BINARY // 1 -#define ttype_utf8 CS_UTF8 // 4 -#define ttype_last_internal CS_UTF8 // 4 +#define ttype_none TTypeId(CS_NONE) // 0 +#define ttype_binary TTypeId(CS_BINARY) // 1 +#define ttype_ascii TTypeId(CS_ASCII) // 2 +#define ttype_utf8 TTypeId(CS_UTF8) // 4 +#define ttype_last_internal ttype_utf8 // 4 -#define ttype_dynamic CS_dynamic // use att_charset +#define ttype_dynamic TTypeId(CS_dynamic) // use att_charset #define ttype_sort_key ttype_binary #define ttype_metadata ttype_utf8 @@ -66,43 +112,23 @@ +#define COLLATE_NONE CollId(0) // No special collation, use codepoint order -#define COLLATE_NONE 0 // No special collation, use codepoint order - -#define INTL_ASSIGN_DSC(dsc, cs, coll) \ - { (dsc)->dsc_sub_type = (SSHORT) ((coll) << 8 | (cs)); } - -#define INTL_GET_TTYPE(dsc) \ - ((dsc)->dsc_sub_type) - -#define INTL_GET_CHARSET(dsc) ((UCHAR)((dsc)->dsc_sub_type & 0x00FF)) -#define INTL_GET_COLLATE(dsc) ((UCHAR)((dsc)->dsc_sub_type >> 8)) +#define INTL_GET_TTYPE(dsc) ((dsc)->getTextType()) +#define INTL_GET_CHARSET(dsc) ((dsc)->getCharSet()) +#define INTL_GET_COLLATE(dsc) ((dsc)->getCollation()) // Define tests for international data -#define INTL_TTYPE(desc) ((desc)->dsc_ttype()) - -#define INTERNAL_TTYPE(d) (((USHORT)((d)->dsc_ttype())) <= ttype_last_internal) +#define INTL_TTYPE(desc) ((desc)->getTextType()) -#define IS_INTL_DATA(d) ((d)->dsc_dtype <= dtype_any_text && \ - (((USHORT)((d)->dsc_ttype())) > ttype_last_internal)) +#define INTL_SET_TTYPE(desc, a) ((desc)->setTextType((a))) -inline USHORT INTL_TEXT_TYPE(const dsc& desc) -{ - if (DTYPE_IS_TEXT(desc.dsc_dtype)) - return INTL_TTYPE(&desc); - - if (desc.dsc_dtype == dtype_blob || desc.dsc_dtype == dtype_quad) - { - if (desc.dsc_sub_type == isc_blob_text) - return desc.dsc_blob_ttype(); - - return ttype_binary; - } +#define INTERNAL_TTYPE(d) (((USHORT)((d)->getTextType())) <= ttype_last_internal) - return ttype_ascii; -} +#define IS_INTL_DATA(d) ((d)->getTextType() <= dtype_any_text && \ + (((USHORT)((d)->getTextType())) > ttype_last_internal)) #define INTL_DYNAMIC_CHARSET(desc) (INTL_GET_CHARSET(desc) == CS_dynamic) @@ -114,7 +140,7 @@ inline USHORT INTL_TEXT_TYPE(const dsc& desc) * 2) As a CHARACTER_SET_ID (when collation isn't relevent, like UDF parms) * 3) As an index type - (btr.h) * 4) As a driver ID (used to lookup the code which implements the locale) - * This is also known as dsc_ttype() (aka text subtype). + * This is also known as dsc::getTextType() (aka text subtype). * * In Descriptors (DSC) the data is encoded as: * dsc_charset overloaded into dsc_scale @@ -134,7 +160,7 @@ inline USHORT INTL_TEXT_TYPE(const dsc& desc) * Index type, which is derived from the datatype of the target. * */ -#define INTL_INDEX_TO_TEXT(idxType) ((USHORT)((idxType) - idx_offset_intl_range)) +#define INTL_INDEX_TO_TEXT(idxType) TTypeId((USHORT)((idxType) - idx_offset_intl_range)) // Maps a text_type to an index ID #define INTL_TEXT_TO_INDEX(tType) ((USHORT)((tType) + idx_offset_intl_range)) @@ -147,10 +173,4 @@ inline USHORT INTL_TEXT_TYPE(const dsc& desc) #define INTL_INDEX_TYPE(desc) INTL_TEXT_TO_INDEX (INTL_RES_TTYPE (desc)) -// Maps a Character_set_id & collation_id to a text_type (driver ID) -#define INTL_CS_COLL_TO_TTYPE(cs, coll) ((USHORT) ((coll) << 8 | ((cs) & 0x00FF))) - -#define TTYPE_TO_CHARSET(tt) ((USHORT)((tt) & 0x00FF)) -#define TTYPE_TO_COLLATION(tt) ((USHORT)((tt) >> 8)) - #endif // JRD_INTL_H diff --git a/src/jrd/intl_proto.h b/src/jrd/intl_proto.h index abde9c3933f..914b6379b59 100644 --- a/src/jrd/intl_proto.h +++ b/src/jrd/intl_proto.h @@ -25,34 +25,41 @@ #define JRD_INTL_PROTO_H #include "../jrd/intl_classes.h" +// ???????????????? #include "../jrd/HazardPtr.h" #include "../common/cvt.h" namespace Jrd { class thread_db; class Lock; class Collation; + struct SubtypeInfo; +} + +namespace Firebird { + class CsConvert; + class CharSet; } struct dsc; -struct SubtypeInfo; +struct texttype; void INTL_adjust_text_descriptor(Jrd::thread_db* tdbb, dsc* desc); -CHARSET_ID INTL_charset(Jrd::thread_db*, USHORT); +CSetId INTL_charset(Jrd::thread_db*, TTypeId); int INTL_compare(Jrd::thread_db*, const dsc*, const dsc*, ErrorFunction); -ULONG INTL_convert_bytes(Jrd::thread_db*, CHARSET_ID, UCHAR*, const ULONG, CHARSET_ID, +ULONG INTL_convert_bytes(Jrd::thread_db*, CSetId, UCHAR*, const ULONG, CSetId, const BYTE*, const ULONG, ErrorFunction); -Firebird::CsConvert INTL_convert_lookup(Jrd::thread_db*, CHARSET_ID, CHARSET_ID); +Firebird::CsConvert INTL_convert_lookup(Jrd::thread_db*, CSetId, CSetId); void INTL_convert_string(dsc*, const dsc*, Firebird::Callbacks* cb); bool INTL_data(const dsc*); bool INTL_data_or_binary(const dsc*); -bool INTL_defined_type(Jrd::thread_db*, USHORT); +bool INTL_defined_type(Jrd::thread_db*, TTypeId); USHORT INTL_key_length(Jrd::thread_db*, USHORT, USHORT); -Firebird::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); -bool INTL_texttype_validate(Jrd::thread_db*, const SubtypeInfo*); +Firebird::CharSet* INTL_charset_lookup(Jrd::thread_db* tdbb, CSetId parm1); +Jrd::Collation* INTL_texttype_lookup(Jrd::thread_db* tdbb, TTypeId parm1); +bool INTL_texttype_validate(Jrd::thread_db*, const Jrd::SubtypeInfo*); void INTL_pad_spaces(Jrd::thread_db*, dsc*, UCHAR*, ULONG); USHORT INTL_string_to_key(Jrd::thread_db*, USHORT, const dsc*, dsc*, USHORT); +void INTL_lookup_texttype(texttype* tt, const Jrd::SubtypeInfo* info); // Built-in charsets/texttypes interface INTL_BOOL INTL_builtin_lookup_charset(charset* cs, const ASCII* charset_name, const ASCII* config_info); diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 2c6d1a92881..2d809bcd929 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -184,6 +184,9 @@ enum irq_type_t irq_proc_param_dep, // check procedure parameter dependency irq_func_param_dep, // check function parameter dependency irq_l_pub_tab_state, // lookup publication state for a table + irq_index_scan, // scan index for caching + irq_index_id_erase, // cleanup index ID + irq_get_index_by_name, // find appropriate index irq_MAX }; diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index dd62554fb09..29ec68bf64c 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -89,8 +89,9 @@ #include "../common/isc_f_proto.h" #include "../common/isc_proto.h" #include "../jrd/jrd_proto.h" +#include "../jrd/dpm_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" @@ -133,6 +134,7 @@ #include "../jrd/DebugInterface.h" #include "../jrd/CryptoManager.h" #include "../jrd/DbCreators.h" +#include "../jrd/met.h" #include "../dsql/dsql.h" #include "../dsql/dsql_proto.h" @@ -669,7 +671,7 @@ namespace if (!statement) status_exception::raise(Arg::Gds(isc_bad_req_handle)); - validateHandle(tdbb, statement->requests[0]->req_attachment); + validateHandle(tdbb, statement->makeRootRequest(tdbb)->req_attachment); // ???????????????? } inline void validateHandle(thread_db* tdbb, DsqlRequest* const statement) @@ -880,100 +882,6 @@ template EngineContextHolder::EngineContextHolder( #define TEXT SCHAR #endif // WIN_NT -bool Trigger::isActive() const -{ - return statement && statement->isActive(); -} - -void Trigger::compile(thread_db* tdbb) -{ - if (extTrigger || statement) - return; - - const auto dbb = tdbb->getDatabase(); - const auto att = tdbb->getAttachment(); - - // Allocate statement memory pool - MemoryPool* new_pool = att->createPool(); - - // Trigger request is not compiled yet. Lets do it now - USHORT par_flags = (USHORT) (flags & TRG_ignore_perm) ? csb_ignore_perm : 0; - - if (type & 1) - par_flags |= csb_pre_trigger; - else - par_flags |= csb_post_trigger; - - try - { - Jrd::ContextPoolHolder context(tdbb, new_pool); - - AutoPtr auto_csb(FB_NEW_POOL(*new_pool) CompilerScratch(*new_pool)); - CompilerScratch* csb = auto_csb; - - csb->csb_g_flags |= par_flags; - - if (engine.isEmpty()) - { - TraceTrigCompile trace(tdbb, this); - - if (debugInfo.hasData()) - { - DBG_parse_debug_info((ULONG) debugInfo.getCount(), debugInfo.begin(), - *csb->csb_dbg_info); - } - - PAR_blr(tdbb, relation, blr.begin(), (ULONG) blr.getCount(), NULL, &csb, &statement, - (relation ? true : false), par_flags); - - trace.finish(statement, ITracePlugin::RESULT_SUCCESS); - } - else - { - dbb->dbb_extManager->makeTrigger(tdbb, csb, this, engine, entryPoint, extBody.c_str(), - (relation ? - (type & 1 ? IExternalTrigger::TYPE_BEFORE : IExternalTrigger::TYPE_AFTER) : - IExternalTrigger::TYPE_DATABASE)); - } - } - catch (const Exception&) - { - if (statement) - { - statement->release(tdbb); - statement = NULL; - } - else - att->deletePool(new_pool); - - throw; - } - - statement->triggerName = name; - - if (ssDefiner.asBool()) - statement->triggerInvoker = att->getUserId(owner); - - if (sysTrigger) - statement->flags |= Statement::FLAG_SYS_TRIGGER | Statement::FLAG_INTERNAL; - - if (flags & TRG_ignore_perm) - statement->flags |= Statement::FLAG_IGNORE_PERM; -} - -void Trigger::release(thread_db* tdbb) -{ - extTrigger.reset(); - - if (!statement || statement->isActive() || releaseInProgress) - return; - - AutoSetRestore autoProgressFlag(&releaseInProgress, true); - - statement->release(tdbb); - statement = NULL; -} - namespace { @@ -1315,7 +1223,6 @@ static bool drop_file(Database*, const jrd_file*); static void find_intl_charset(thread_db*, Jrd::Attachment*, const DatabaseOptions*); static void init_database_lock(thread_db*); static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction); -static Request* verify_request_synchronization(Statement* statement, USHORT level); static void purge_transactions(thread_db*, Jrd::Attachment*, const bool); static void check_single_maintenance(thread_db* tdbb); @@ -1441,7 +1348,7 @@ static ISC_STATUS transliterateException(thread_db* tdbb, const Exception& ex, F void JRD_transliterate(thread_db* tdbb, Firebird::IStatus* vector) noexcept { Jrd::Attachment* attachment = tdbb->getAttachment(); - USHORT charSet; + CSetId charSet; if (!attachment || (charSet = attachment->att_client_charset) == CS_METADATA || charSet == CS_NONE) { @@ -1753,7 +1660,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch lateBlocking.lock(jAtt->getStable()->getBlockingMutex(), jAtt->getStable()); attachment->att_crypt_callback = getDefCryptCallback(cryptCallback); - attachment->att_client_charset = attachment->att_charset = options.dpb_interp; + attachment->att_client_charset = attachment->att_charset = CSetId(options.dpb_interp); if (existingId) attachment->att_flags |= ATT_overwrite_check; @@ -1855,6 +1762,11 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch // Initialize TIP cache. We do this late to give SDW a chance to // work while we read states for all interesting transactions dbb->dbb_tip_cache = TipCache::create(tdbb); + if (dbb->dbb_flags & DBB_rescan_pages) + { + dbb->dbb_flags &= ~DBB_rescan_pages; + DPM_scan_pages(tdbb); + } // linger dbb->dbb_linger_seconds = MET_get_linger(tdbb); @@ -2241,17 +2153,18 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch try { // load all database triggers - MET_load_db_triggers(tdbb, DB_TRIGGER_CONNECT); - MET_load_db_triggers(tdbb, DB_TRIGGER_DISCONNECT); - MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_START); - MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_COMMIT); - MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_ROLLBACK); + MetadataCache* mdc = dbb->dbb_mdc; + mdc->loadDbTriggers(tdbb, DB_TRIGGER_CONNECT); + mdc->loadDbTriggers(tdbb, DB_TRIGGER_DISCONNECT); + mdc->loadDbTriggers(tdbb, DB_TRIGGER_TRANS_START); + mdc->loadDbTriggers(tdbb, DB_TRIGGER_TRANS_COMMIT); + mdc->loadDbTriggers(tdbb, DB_TRIGGER_TRANS_ROLLBACK); // load DDL triggers - MET_load_ddl_triggers(tdbb); + mdc->loadDbTriggers(tdbb, DB_TRIGGER_DDL); - const TrigVector* trig_connect = attachment->att_triggers[DB_TRIGGER_CONNECT]; - if (trig_connect && !trig_connect->isEmpty()) + auto* trig_connect = dbb->dbb_mdc->getTriggers(tdbb, 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. @@ -2717,9 +2630,11 @@ JRequest* JAttachment::compileRequest(CheckStatusWrapper* user_status, stmt = CMP_compile(tdbb, blr, blr_length, false, 0, nullptr); const auto attachment = tdbb->getAttachment(); - const auto rootRequest = stmt->getRequest(tdbb, 0); + const auto rootRequest = stmt->makeRootRequest(tdbb); rootRequest->setAttachment(attachment); attachment->att_requests.add(rootRequest); + bool rt = rootRequest->setUsed(); + fb_assert(rt); trace.finish(stmt, ITracePlugin::RESULT_SUCCESS); } @@ -3003,7 +2918,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch break; } - attachment->att_client_charset = attachment->att_charset = options.dpb_interp; + attachment->att_client_charset = attachment->att_charset = CSetId(options.dpb_interp); if (options.dpb_page_size <= 0) { options.dpb_page_size = DEFAULT_PAGE_SIZE; @@ -3948,7 +3863,7 @@ void JRequest::receive(CheckStatusWrapper* user_status, int level, unsigned int EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - Request* request = verify_request_synchronization(getHandle(), level); + Request* request = getHandle()->verifyRequestSynchronization(level); try { @@ -4048,6 +3963,7 @@ void JRequest::freeEngineData(CheckStatusWrapper* user_status) try { + getHandle()->getUserRequest(tdbb, 0)->setUnused(); getHandle()->release(tdbb); rq = NULL; } @@ -4085,7 +4001,7 @@ void JRequest::getInfo(CheckStatusWrapper* user_status, int level, unsigned int EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - Request* request = verify_request_synchronization(getHandle(), level); + Request* request = getHandle()->verifyRequestSynchronization(level); try { @@ -4288,7 +4204,7 @@ void JRequest::send(CheckStatusWrapper* user_status, int level, unsigned int msg EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - Request* request = verify_request_synchronization(getHandle(), level); + Request* request = getHandle()->verifyRequestSynchronization(level); try { @@ -4538,7 +4454,7 @@ void JRequest::startAndSend(CheckStatusWrapper* user_status, ITransaction* tra, validateHandle(tdbb, transaction); check_database(tdbb); - Request* request = getHandle()->getRequest(tdbb, level); + Request* request = getHandle()->getUserRequest(tdbb, level); try { @@ -4596,7 +4512,7 @@ void JRequest::start(CheckStatusWrapper* user_status, ITransaction* tra, int lev validateHandle(tdbb, transaction); check_database(tdbb); - Request* request = getHandle()->getRequest(tdbb, level); + Request* request = getHandle()->getUserRequest(tdbb, level); try { @@ -4800,7 +4716,7 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* const MessageNode* outMessage = NULL; Request* request = NULL; - MemoryPool* new_pool = att->createPool(); + MemoryPool* new_pool = att->att_database->createPool(ALLOC_ARGS0); try { @@ -4828,7 +4744,7 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* if (request) CMP_release(tdbb, request); else - att->deletePool(new_pool); + att->att_database->deletePool(new_pool); throw; } @@ -5017,7 +4933,7 @@ void JRequest::unwind(CheckStatusWrapper* user_status, int level) EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - Request* request = verify_request_synchronization(getHandle(), level); + Request* request = getHandle()->verifyRequestSynchronization(level); try { @@ -6897,19 +6813,19 @@ static void find_intl_charset(thread_db* tdbb, Jrd::Attachment* attachment, cons return; } - USHORT id; + TTypeId id; const UCHAR* lc_ctype = reinterpret_cast(options->dpb_lc_ctype.c_str()); - if (MET_get_char_coll_subtype(tdbb, &id, lc_ctype, options->dpb_lc_ctype.length()) && - INTL_defined_type(tdbb, id & 0xFF)) + if (MetadataCache::get_char_coll_subtype(tdbb, &id, lc_ctype, options->dpb_lc_ctype.length()) && + INTL_defined_type(tdbb, id)) { - if ((id & 0xFF) == CS_BINARY) + if (CSetId(id) == CS_BINARY) { ERR_post(Arg::Gds(isc_bad_dpb_content) << Arg::Gds(isc_invalid_attachment_charset) << Arg::Str(options->dpb_lc_ctype)); } - attachment->att_client_charset = attachment->att_charset = id & 0xFF; + attachment->att_client_charset = attachment->att_charset = CSetId(id); } else { @@ -7763,7 +7679,7 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment, XThreadEns dbb->dbb_extManager->closeAttachment(tdbb, attachment); if (dbb->dbb_config->getServerMode() == MODE_SUPER) - attachment->releaseGTTs(tdbb); + dbb->dbb_mdc->releaseGTTs(tdbb); if (attachment->att_event_session) dbb->eventManager()->deleteSession(attachment->att_event_session); @@ -7772,23 +7688,19 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment, XThreadEns // CMP_release() changes att_requests. while (attachment->att_requests.hasData()) - CMP_release(tdbb, attachment->att_requests.back()); - - MET_clear_cache(tdbb); + { + auto* req = attachment->att_requests.back(); + req->setUnused(); + CMP_release(tdbb, req); + } attachment->releaseLocks(tdbb); - // Shut down any extern relations - - attachment->releaseRelations(tdbb); - // Release any validation error vector allocated delete attachment->att_validation; attachment->att_validation = NULL; - attachment->destroyIntlObjects(tdbb); - attachment->detachLocks(); LCK_fini(tdbb, LCK_OWNER_attachment); @@ -7799,7 +7711,7 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment, XThreadEns { MemoryPool* const pool = &attachment->att_dsql_instance->dbb_pool; delete attachment->att_dsql_instance; - attachment->deletePool(pool); + dbb->deletePool(pool); } attachment->mergeStats(); @@ -8043,6 +7955,9 @@ bool JRD_shutdown_database(Database* dbb, const unsigned flags) return false; } + // Release the system requests + dbb->releaseSystemRequests(tdbb); + // Reset provider unload delay if needed dbb->dbb_linger_end = 0; setEngineReleaseDelay(dbb); @@ -8084,6 +7999,8 @@ bool JRD_shutdown_database(Database* dbb, const unsigned flags) CCH_shutdown(tdbb); + dbb->dbb_mdc->releaseLocks(tdbb); + if (dbb->dbb_tip_cache) dbb->dbb_tip_cache->finalizeTpc(tdbb); @@ -8111,6 +8028,11 @@ bool JRD_shutdown_database(Database* dbb, const unsigned flags) delete dbb->dbb_crypto_manager; dbb->dbb_crypto_manager = NULL; + MetadataCache::clear(tdbb); + + // Shut down any extern relations + dbb->dbb_mdc->releaseRelations(tdbb); + LCK_fini(tdbb, LCK_OWNER_database); CCH_fini(tdbb); @@ -8132,6 +8054,8 @@ bool JRD_shutdown_database(Database* dbb, const unsigned flags) } } + dbb->dbb_mdc->cleanup(tdbb); + if (flags & SHUT_DBB_RELEASE_POOLS) { tdbb->setDatabase(NULL); @@ -8397,15 +8321,14 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign { try { - const TrigVector* const trig_disconnect = - attachment->att_triggers[DB_TRIGGER_DISCONNECT]; + auto* trig_disconnect = dbb->dbb_mdc->getTriggers(tdbb, DB_TRIGGER_DISCONNECT | TRIGGER_TYPE_DB); // ATT_resetting may be set here only in a case when running on disconnect triggers // in ALTER SESSION RESET already failed and attachment was shut down. // Trying them once again here makes no sense. if (!forcedPurge && !(attachment->att_flags & (ATT_no_db_triggers | ATT_resetting)) && - trig_disconnect && !trig_disconnect->isEmpty()) + trig_disconnect && *trig_disconnect) { ThreadStatusGuard temp_status(tdbb); @@ -8539,6 +8462,7 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign MutexUnlockGuard coutBlocking(*sAtt->getBlockingMutex(), FB_FUNCTION); // Try to close database if there are no attachments + tdbb->setAttachment(nullptr); JRD_shutdown_database(dbb, shutdownFlags); } @@ -8570,29 +8494,6 @@ static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction) } -// verify_request_synchronization -// -// @brief Finds the sub-requests at the given level and replaces it with the -// original passed request (note the pointer by reference). If that specific -// sub-request is not found, throw the dreaded "request synchronization error". -// Notice that at this time, the calling function's "request" pointer has been -// set to null, so remember that if you write a debugging routine. -// This function replaced a chunk of code repeated four times. -// -// @param request The incoming, parent request to be replaced. -// @param level The level of the sub-request we need to find. -static Request* verify_request_synchronization(Statement* statement, USHORT level) -{ - if (level) - { - if (level >= statement->requests.getCount() || !statement->requests[level]) - ERR_post(Arg::Gds(isc_req_sync)); - } - - return statement->requests[level]; -} - - /** verifyDatabaseName @@ -9240,9 +9141,9 @@ void thread_db::setRequest(Request* val) reqStat = val ? &val->req_stats : RuntimeStatistics::getDummy(); } -SSHORT thread_db::getCharSet() const +CSetId thread_db::getCharSet() const { - USHORT charSetId; + CSetId charSetId; if (request && (charSetId = request->getStatement()->charSetId) != CS_dynamic) return charSetId; @@ -9379,6 +9280,13 @@ ULONG thread_db::adjustWait(ULONG wait) const return MIN(wait, adjustedTimeout); } +#ifdef DEB_TDBB_BDBS +void thread_db::bprint(BufferDesc* bdb, const char* text) +{ + bdb->bdb_page.print(text); +} +#endif + // end thread_db methods @@ -9883,39 +9791,3 @@ void JRD_cancel_operation(thread_db* /*tdbb*/, Jrd::Attachment* attachment, int } } - -bool TrigVector::hasActive() const -{ - for (const_iterator iter = begin(); iter != end(); ++iter) - { - if (iter->isActive()) - return true; - } - - return false; -} - - -void TrigVector::decompile(thread_db* tdbb) -{ - for (iterator iter = begin(); iter != end(); ++iter) - iter->release(tdbb); -} - - -void TrigVector::release() -{ - release(JRD_get_thread_data()); -} - - -void TrigVector::release(thread_db* tdbb) -{ - fb_assert(useCount.value() > 0); - - if (--useCount == 0) - { - decompile(tdbb); - delete this; - } -} diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 9bb03c60e8b..f52cef6ee3e 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -32,12 +32,15 @@ #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" #include "../jrd/obj.h" #include "../jrd/val.h" +#include "../jrd/vec.h" #include "../jrd/status.h" +#include "../jrd/Database.h" #include "../common/classes/fb_atomic.h" #include "../common/classes/fb_string.h" @@ -60,9 +63,6 @@ #include "../jrd/Attachment.h" #include "firebird/Interface.h" -#include // 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) @@ -87,7 +87,7 @@ #include "../jrd/pag.h" #include "../jrd/RuntimeStatistics.h" -#include "../jrd/Database.h" +//#include "../jrd/Database.h" #include "../jrd/lck.h" // Error codes @@ -120,8 +120,6 @@ class SparseBitmap; class jrd_rel; class ExternalFile; class ViewContext; -class IndexBlock; -class IndexLock; class ArrayField; struct sort_context; class vcl; @@ -131,186 +129,7 @@ class dsql_dbb; class PreparedStatement; class TraceManager; class MessageNode; - - -// Relation trigger definition - -class Trigger -{ -public: - Firebird::HalfStaticArray blr; // BLR code - Firebird::HalfStaticArray debugInfo; // Debug info - Statement* statement = nullptr; // Compiled statement - bool releaseInProgress = false; - bool sysTrigger = false; - FB_UINT64 type = 0; // Trigger type - USHORT flags = 0; // Flags as they are in RDB$TRIGGERS table - jrd_rel* relation = nullptr; // Trigger parent relation - MetaName name; // Trigger name - MetaName engine; // External engine name - MetaName owner; // Owner for SQL SECURITY - Firebird::string entryPoint; // External trigger entrypoint - Firebird::string extBody; // External trigger body - Firebird::TriState ssDefiner; // SQL SECURITY - std::unique_ptr extTrigger; // External trigger - - bool isActive() const; - - void compile(thread_db*); // Ensure that trigger is compiled - void release(thread_db*); // Try to free trigger request - - explicit Trigger(MemoryPool& p) - : blr(p), debugInfo(p), entryPoint(p), extBody(p) - {} -}; - - -// Array of triggers (suppose separate arrays for triggers of different types) - -class TrigVector : public Firebird::ObjectsArray -{ -public: - explicit TrigVector(Firebird::MemoryPool& pool) - : Firebird::ObjectsArray(pool), - useCount(0) - { } - - TrigVector() - : Firebird::ObjectsArray(), - useCount(0) - { } - - void addRef() - { - ++useCount; - } - - bool hasActive() const; - - void decompile(thread_db* tdbb); - - void release(); - void release(thread_db* tdbb); - - ~TrigVector() - { - fb_assert(useCount.value() == 0); - } - -private: - Firebird::AtomicCounter useCount; -}; - - -// -// Flags to indicate normal internal requests vs. dyn internal requests -// -// IRQ_REQUESTS and DYN_REQUESTS are deprecated -const int IRQ_REQUESTS = 1; -const int DYN_REQUESTS = 2; -const int CACHED_REQUESTS = 3; - - -// Procedure block - -class jrd_prc final : public Routine -{ -public: - const Format* prc_record_format; - prc_t prc_type; // procedure type - - const ExtEngineManager::Procedure* getExternal() const { return prc_external; } - void setExternal(ExtEngineManager::Procedure* value) { prc_external = value; } - -private: - const ExtEngineManager::Procedure* prc_external; - -public: - explicit jrd_prc(MemoryPool& p) - : Routine(p), - prc_record_format(NULL), - prc_type(prc_legacy), - prc_external(NULL) - { - } - -public: - int getObjectType() const override - { - return obj_procedure; - } - - SLONG getSclType() const override - { - return obj_procedures; - } - - void releaseFormat() override - { - delete prc_record_format; - prc_record_format = NULL; - } - - ~jrd_prc() override - { - delete prc_external; - } - - bool checkCache(thread_db* tdbb) const override; - void clearCache(thread_db* tdbb) override; - - void releaseExternal() override - { - delete prc_external; - prc_external = NULL; - } - -protected: - bool reload(thread_db* tdbb) override; // impl is in met.epp -}; - - -// Parameter block - -class Parameter : public pool_alloc -{ -public: - USHORT prm_number; - dsc prm_desc; - NestConst prm_default_value; - bool prm_nullable; - prm_mech_t prm_mechanism; - MetaName prm_name; - MetaName prm_field_source; - MetaName prm_type_of_column; - MetaName prm_type_of_table; - std::optional prm_text_type; - FUN_T prm_fun_mechanism; - -public: - explicit Parameter(MemoryPool& p) - : prm_name(p), - prm_field_source(p), - prm_type_of_column(p), - prm_type_of_table(p) - { - } -}; - -// Index block to cache index information - -class IndexBlock : public pool_alloc -{ -public: - IndexBlock* idb_next; - ValueExprNode* idb_expression; // node tree for index expression - Statement* idb_expression_statement; // statement for index expression evaluation - dsc idb_expression_desc; // descriptor for expression result - BoolExprNode* idb_condition; // node tree for index condition - Statement* idb_condition_statement; // statement for index condition evaluation - Lock* idb_lock; // lock to synchronize changes to index - USHORT idb_id; -}; +class Database; // @@ -369,450 +188,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 > -{ -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 -const ULONG TDBB_async = 32768; // Async context (set in AST) - -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 ; - 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 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); - - if ((tdbb_flags & TDBB_async) && !attachment) - dbbStat->bumpValue(index, delta); - - // else dbbStat is adjusted from attStat, see Attachment::mergeAsyncStats() - } - - 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 m_saveTimer; - }; - -private: - Firebird::RefPtr 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 { @@ -917,68 +292,6 @@ inline bool JRD_reschedule(Jrd::thread_db* tdbb, bool force = false) return false; } -// Threading macros - -/* Define JRD_get_thread_data off the platform specific version. - * If we're in DEV mode, also do consistancy checks on the - * retrieved memory structure. This was originally done to - * track down cases of no "PUT_THREAD_DATA" on the NLM. - * - * This allows for NULL thread data (which might be an error by itself) - * If there is thread data, - * AND it is tagged as being a thread_db. - * AND it has a non-NULL database field, - * THEN we validate that the structure there is a database block. - * Otherwise, we return what we got. - * We can't always validate the database field, as during initialization - * there is no database set up. - */ - -#if defined(DEV_BUILD) -#include "../jrd/err_proto.h" - -inline Jrd::thread_db* JRD_get_thread_data() -{ - Firebird::ThreadData* p1 = Firebird::ThreadData::getSpecific(); - if (p1 && p1->getType() == Firebird::ThreadData::tddDBB) - { - Jrd::thread_db* p2 = (Jrd::thread_db*) p1; - if (p2->getDatabase() && !p2->getDatabase()->checkHandle()) - { - BUGCHECK(147); - } - } - return (Jrd::thread_db*) p1; -} - -inline void CHECK_TDBB(const Jrd::thread_db* tdbb) -{ - fb_assert(tdbb && (tdbb->getType() == Firebird::ThreadData::tddDBB) && - (!tdbb->getDatabase() || tdbb->getDatabase()->checkHandle())); -} - -inline void CHECK_DBB(const Jrd::Database* dbb) -{ - fb_assert(dbb && dbb->checkHandle()); -} - -#else // PROD_BUILD - -inline Jrd::thread_db* JRD_get_thread_data() -{ - return (Jrd::thread_db*) Firebird::ThreadData::getSpecific(); -} - -inline void CHECK_DBB(const Jrd::Database*) -{ -} - -inline void CHECK_TDBB(const Jrd::thread_db*) -{ -} - -#endif - inline Jrd::Database* GET_DBB() { return JRD_get_thread_data()->getDatabase(); @@ -1230,6 +543,7 @@ namespace Jrd { Firebird::Sync m_sync; }; -} + +} // namespace Jrd #endif // JRD_JRD_H diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index aecd56df94d..8adb7451a7e 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -35,10 +35,11 @@ #include "../jrd/err_proto.h" #include "../yvalve/gds_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../common/gdsassert.h" #include "../lock/lock_proto.h" #include "../jrd/Attachment.h" +#include "../jrd/tra.h" #ifdef HAVE_SYS_TYPES_H #include @@ -559,18 +560,21 @@ static lck_owner_t get_owner_type(enum lck_t lock_type) case LCK_tpc_init: case LCK_tpc_block: case LCK_repl_state: - owner_type = LCK_OWNER_database; - break; - case LCK_attachment: case LCK_rel_exist: + case LCK_rel_gc: case LCK_rel_partners: case LCK_rel_rescan: case LCK_idx_exist: case LCK_expression: case LCK_prc_exist: case LCK_fun_exist: - case LCK_tt_exist: + case LCK_cs_exist: + case LCK_dbwide_triggers: + owner_type = LCK_OWNER_database; + break; + + case LCK_attachment: case LCK_page_space: case LCK_relation: case LCK_tra: @@ -580,12 +584,12 @@ static lck_owner_t get_owner_type(enum lck_t lock_type) case LCK_cancel: case LCK_monitor: case LCK_btr_dont_gc: - case LCK_rel_gc: case LCK_record_gc: case LCK_alter_database: case LCK_repl_tables: case LCK_dsql_statement_cache: case LCK_profiler_listener: + case LCK_idx_create: owner_type = LCK_OWNER_attachment; break; @@ -1498,6 +1502,11 @@ Lock::~Lock() } } +Attachment* Lock::getLockAttachment() +{ + return lck_attachment ? lck_attachment->getHandle() : NULL; +} + void Lock::setLockAttachment(Jrd::Attachment* attachment) { if (get_owner_type(lck_type) == LCK_OWNER_database) @@ -1581,3 +1590,4 @@ Lock* Lock::detach() return next; } + diff --git a/src/jrd/lck.h b/src/jrd/lck.h index dc8f9274984..3560385c1b6 100644 --- a/src/jrd/lck.h +++ b/src/jrd/lck.h @@ -33,6 +33,7 @@ #endif #include "../jrd/Attachment.h" +#include "../common/classes/auto.h" namespace Jrd { @@ -61,7 +62,7 @@ enum lck_t { LCK_page_space, // Page space ID lock LCK_dsql_cache, // DSQL cache lock LCK_monitor, // Lock to dump the monitoring data - LCK_tt_exist, // TextType existence lock + LCK_cs_exist, // Charset existence lock LCK_cancel, // Cancellation lock LCK_btr_dont_gc, // Prevent removal of b-tree page from index LCK_rel_gc, // Allow garbage collection for relation @@ -76,7 +77,9 @@ enum lck_t { LCK_repl_state, // Replication state lock LCK_repl_tables, // Replication set lock LCK_dsql_statement_cache, // DSQL statement cache lock - LCK_profiler_listener // Remote profiler listener + LCK_profiler_listener, // Remote profiler listener + LCK_dbwide_triggers, // Database wide triggers existence + LCK_idx_create // Taken during index build process }; // Lock owner types @@ -99,10 +102,7 @@ class Lock : public pool_alloc_rpt return lck_attachment; } - Attachment* getLockAttachment() - { - return lck_attachment ? lck_attachment->getHandle() : NULL; - } + Attachment* getLockAttachment(); void setLockAttachment(Attachment* att); @@ -179,4 +179,71 @@ class Lock : public pool_alloc_rpt } // namespace Jrd +void LCK_assert(Jrd::thread_db*, Jrd::Lock*); +bool LCK_cancel_wait(Jrd::Attachment*); +bool LCK_convert(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); +bool LCK_convert_opt(Jrd::thread_db*, Jrd::Lock*, USHORT); +void LCK_downgrade(Jrd::thread_db*, Jrd::Lock*); +void LCK_fini(Jrd::thread_db*, Jrd::lck_owner_t); +void LCK_init(Jrd::thread_db*, Jrd::lck_owner_t); +bool LCK_lock(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); +bool LCK_lock_opt(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); +LOCK_DATA_T LCK_query_data(Jrd::thread_db*, Jrd::lck_t, USHORT); +LOCK_DATA_T LCK_read_data(Jrd::thread_db*, Jrd::Lock*); +void LCK_release(Jrd::thread_db*, Jrd::Lock*); +void LCK_re_post(Jrd::thread_db*, Jrd::Lock*); +void LCK_write_data(Jrd::thread_db*, Jrd::Lock*, LOCK_DATA_T); + + +namespace Jrd { + +class AutoLock +{ +public: + explicit AutoLock(thread_db* tdbb, Lock* lck = NULL) + : m_tdbb(tdbb), + m_lock(lck) + { + } + + ~AutoLock() + { + release(); + } + + void release() + { + if (m_lock) + { + if (m_lock->lck_id) + LCK_release(m_tdbb, m_lock); + delete m_lock; + m_lock = NULL; + } + } + + Lock* operator-> () + { + return m_lock; + } + + operator Lock* () + { + return m_lock; + } + + Lock* operator= (Lock* lck) + { + release(); + m_lock = lck; + return m_lock; + } + +private: + thread_db* m_tdbb; + Lock* m_lock; +}; + +} // namespace Jrd + #endif // JRD_LCK_H diff --git a/src/jrd/lck_proto.h b/src/jrd/lck_proto.h deleted file mode 100644 index 4d71d472fc4..00000000000 --- a/src/jrd/lck_proto.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: lck_proto.h - * DESCRIPTION: Prototype header file for lck.cpp - * - * 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_LCK_PROTO_H -#define JRD_LCK_PROTO_H - -#include "../jrd/lck.h" - -namespace Jrd { - enum lck_t; -} - -void LCK_assert(Jrd::thread_db*, Jrd::Lock*); -bool LCK_cancel_wait(Jrd::Attachment*); -bool LCK_convert(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); -bool LCK_convert_opt(Jrd::thread_db*, Jrd::Lock*, USHORT); -void LCK_downgrade(Jrd::thread_db*, Jrd::Lock*); -void LCK_fini(Jrd::thread_db*, Jrd::lck_owner_t); -void LCK_init(Jrd::thread_db*, Jrd::lck_owner_t); -bool LCK_lock(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); -bool LCK_lock_opt(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); -LOCK_DATA_T LCK_query_data(Jrd::thread_db*, Jrd::lck_t, USHORT); -LOCK_DATA_T LCK_read_data(Jrd::thread_db*, Jrd::Lock*); -void LCK_release(Jrd::thread_db*, Jrd::Lock*); -void LCK_re_post(Jrd::thread_db*, Jrd::Lock*); -void LCK_write_data(Jrd::thread_db*, Jrd::Lock*, LOCK_DATA_T); - - -class AutoLock -{ -public: - explicit AutoLock(Jrd::thread_db* tdbb, Jrd::Lock* lck = NULL) - : m_tdbb(tdbb), - m_lock(lck) - { - } - - ~AutoLock() - { - release(); - } - - void release() - { - if (m_lock) - { - if (m_lock->lck_id) - LCK_release(m_tdbb, m_lock); - delete m_lock; - m_lock = NULL; - } - } - - Jrd::Lock* operator-> () - { - return m_lock; - } - - operator Jrd::Lock* () - { - return m_lock; - } - - Jrd::Lock* operator= (Jrd::Lock* lck) - { - release(); - m_lock = lck; - return m_lock; - } - -private: - Jrd::thread_db* m_tdbb; - Jrd::Lock* m_lock; -}; - - -#endif // JRD_LCK_PROTO_H diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 8d5efc9f1b0..696f0c6e0c0 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -50,6 +50,7 @@ #include "../jrd/ods.h" #include "../jrd/btr.h" #include "../jrd/req.h" +#include "../jrd/Statement.h" #include "../jrd/exe.h" #include "../jrd/scl.h" #include "../jrd/blb.h" @@ -78,13 +79,15 @@ #include "../jrd/idx_proto.h" #include "../jrd/ini_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/par_proto.h" #include "../jrd/os/pio_proto.h" #include "../jrd/scl_proto.h" #include "../jrd/sdw_proto.h" +#include "../jrd/tra_proto.h" +#include "../jrd/intl_proto.h" #include "../common/utils_proto.h" #include "../jrd/PreparedStatement.h" @@ -108,79 +111,19 @@ DATABASE DB = FILENAME "ODS.RDB"; using namespace Jrd; using namespace Firebird; -static int blocking_ast_dsql_cache(void* ast_object); -static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, sym_type type, const QualifiedName& name); -static int blocking_ast_procedure(void*); -static int blocking_ast_relation(void*); -static int partners_ast_relation(void*); -static int rescan_ast_relation(void*); static ULONG get_rel_flags_from_FLAGS(USHORT); -static void get_trigger(thread_db*, jrd_rel*, bid*, bid*, TrigVector**, const TEXT*, FB_UINT64, bool, +static void get_trigger(thread_db*, jrd_rel*, bid*, bid*, Triggers&, const TEXT*, FB_UINT64, USHORT, const MetaName&, const string&, const bid*, TriState ssDefiner); -static bool get_type(thread_db*, USHORT*, const UCHAR*, const TEXT*); static void lookup_view_contexts(thread_db*, jrd_rel*); static void make_relation_scope_name(const TEXT*, const USHORT, string& str); static ValueExprNode* parse_field_default_blr(thread_db* tdbb, bid* blob_id); static BoolExprNode* parse_field_validation_blr(thread_db* tdbb, bid* blob_id, const MetaName name); -static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*); -static void save_trigger_data(thread_db*, TrigVector**, jrd_rel*, Statement*, blb*, blb*, - const TEXT*, FB_UINT64, bool, USHORT, const MetaName&, const string&, +static void save_trigger_data(thread_db*, Triggers&, jrd_rel*, Statement*, blb*, blb*, + const TEXT*, FB_UINT64, USHORT, const MetaName&, const string&, const bid*, TriState ssDefiner); -static void scan_partners(thread_db*, jrd_rel*); static bool verify_TRG_ignore_perm(thread_db*, const MetaName&); -static void inc_int_use_count(Statement* statement) -{ - // Handle sub-statements - for (Statement** subStatement = statement->subStatements.begin(); - subStatement != statement->subStatements.end(); - ++subStatement) - { - inc_int_use_count(*subStatement); - } - - // Increment int_use_count for all procedures in resource list of request - ResourceList& list = statement->resources; - FB_SIZE_T i; - - for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) - { - Resource& resource = list[i]; - if (resource.rsc_type != Resource::rsc_procedure) - break; - //// FIXME: CORE-4271: fb_assert(resource.rsc_routine->intUseCount >= 0); - ++resource.rsc_routine->intUseCount; - } - - for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) - { - Resource& resource = list[i]; - if (resource.rsc_type != Resource::rsc_function) - break; - //// FIXME: CORE-4271: fb_assert(resource.rsc_routine->intUseCount >= 0); - ++resource.rsc_routine->intUseCount; - } -} - - -// Increment int_use_count for all procedures used by triggers -static void post_used_procedures(TrigVector* vector) -{ - if (!vector) - return; - - for (FB_SIZE_T i = 0; i < vector->getCount(); i++) - { - Statement* stmt = (*vector)[i].statement; - if (stmt && !stmt->isActive()) - inc_int_use_count(stmt); - } -} - - void MET_get_domain(thread_db* tdbb, MemoryPool& csbPool, const MetaName& name, dsc* desc, FieldInfo* fieldInfo) { @@ -208,8 +151,8 @@ void MET_get_domain(thread_db* tdbb, MemoryPool& csbPool, const MetaName& name, FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, - FLD.RDB$CHARACTER_SET_ID, - FLD.RDB$COLLATION_ID)) + CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID))) { found = true; @@ -276,8 +219,8 @@ MetaName MET_get_relation_field(thread_db* tdbb, MemoryPool& csbPool, const Meta FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, - FLD.RDB$CHARACTER_SET_ID, - (RFL.RDB$COLLATION_ID.NULL ? FLD.RDB$COLLATION_ID : RFL.RDB$COLLATION_ID))) + CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(RFL.RDB$COLLATION_ID.NULL ? FLD.RDB$COLLATION_ID : RFL.RDB$COLLATION_ID))) { found = true; sourceName = RFL.RDB$FIELD_SOURCE; @@ -322,7 +265,7 @@ MetaName MET_get_relation_field(thread_db* tdbb, MemoryPool& csbPool, const Meta } -void MET_update_partners(thread_db* tdbb) +void MetadataCache::update_partners(thread_db* tdbb) { /************************************** * @@ -337,14 +280,10 @@ void MET_update_partners(thread_db* tdbb) * **************************************/ SET_TDBB(tdbb); - Attachment* const attachment = tdbb->getAttachment(); - - vec* relations = attachment->att_relations; + Database* const dbb = tdbb->getDatabase(); - vec::iterator ptr = relations->begin(); - for (const vec::const_iterator end = relations->end(); ptr < end; ++ptr) + for (auto relation : dbb->dbb_mdc->mdc_relations) { - jrd_rel* relation = *ptr; if (!relation) continue; @@ -356,61 +295,8 @@ void MET_update_partners(thread_db* tdbb) } -static void adjust_dependencies(Routine* routine) -{ - if (routine->intUseCount == -1) - { - // Already processed - return; - } - - routine->intUseCount = -1; // Mark as undeletable - - if (routine->getStatement()) - { - // Loop over procedures from resource list of request - ResourceList& list = routine->getStatement()->resources; - FB_SIZE_T i; - - for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) - { - Resource& resource = list[i]; - - if (resource.rsc_type != Resource::rsc_procedure) - break; - - routine = resource.rsc_routine; - - if (routine->intUseCount == routine->useCount) - { - // Mark it and all dependent procedures as undeletable - adjust_dependencies(routine); - } - } - - for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) - { - Resource& resource = list[i]; - - if (resource.rsc_type != Resource::rsc_function) - break; - - routine = resource.rsc_routine; - - if (routine->intUseCount == routine->useCount) - { - // Mark it and all dependent functions as undeletable - adjust_dependencies(routine); - } - } - } -} - - -#ifdef DEV_BUILD -void MET_verify_cache(thread_db* tdbb) +#ifdef NEVERDEF +void MetadataCache::verify_cache(thread_db* tdbb) { /************************************** * @@ -423,37 +309,16 @@ void MET_verify_cache(thread_db* tdbb) * **************************************/ SET_TDBB(tdbb); - Attachment* att = tdbb->getAttachment(); - if (!att) + Database* dbb = tdbb->getDatabase(); + if (!dbb) return; - for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter) - { - Routine* routine = *iter; - - if (routine && routine->getStatement() /*&& - !(routine->flags & Routine::FLAG_OBSOLETE)*/ ) - { - fb_assert(routine->intUseCount == 0); - } - } - - for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter) - { - Routine* routine = *iter; - - if (routine && routine->getStatement() /*&& - !(routine->flags & Routine::FLAG_OBSOLETE)*/ ) - { - fb_assert(routine->intUseCount == 0); - } - } + MetadataCache* mdc = dbb->dbb_mdc; + MutexLockGuard guard(mdc->mdc_use_mutex, FB_FUNCTION); // Walk procedures and calculate internal dependencies - for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter) + for (auto routine : mdc->mdc_procedures) { - jrd_prc* routine = *iter; - if (routine && routine->getStatement() /*&& !(routine->flags & Routine::FLAG_OBSOLETE)*/ ) { @@ -461,10 +326,8 @@ void MET_verify_cache(thread_db* tdbb) } } - for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter) + for (auto routine : mdc->mdc_functions) { - Routine* routine = *iter; - if (routine && routine->getStatement() /*&& !(routine->flags & Routine::FLAG_OBSOLETE)*/ ) { @@ -473,176 +336,161 @@ void MET_verify_cache(thread_db* tdbb) } // Walk procedures again and check dependencies - for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter) + for (auto routine : mdc->mdc_procedures) { - Routine* routine = *iter; - if (routine && routine->getStatement() && /* !(routine->flags & Routine::FLAG_OBSOLETE) && */ - routine->useCount < routine->intUseCount) + routine->getUseCount() < routine->intUseCount) { - char buffer[1024], *buf = buffer; - buf += sprintf(buf, "Procedure %d:%s is not properly counted (use count=%d, prc use=%d). Used by: \n", - routine->getId(), routine->getName().toString().c_str(), - routine->useCount, routine->intUseCount); + string buffer; + buffer.printf("Procedure %d:%s is not properly counted (use count=%d, prc use=%d). Used by: \n", + routine->getId(), routine->c_name(), + routine->getUseCount(), routine->intUseCount); - for (jrd_prc** iter2 = att->att_procedures.begin(); iter2 != att->att_procedures.end(); ++iter2) + for (auto routine2 : mdc->mdc_procedures) { - Routine* routine2 = *iter2; - if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ ) { // Loop over procedures from resource list of request - const ResourceList& list = routine2->getStatement()->resources; - FB_SIZE_T i; - - for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) + for (auto resource : routine2->getStatement()->resources.getObjects(Resource::rsc_procedure)) { - const Resource& resource = list[i]; - if (resource.rsc_type != Resource::rsc_procedure) - break; - - if (resource.rsc_routine == routine) + if (resource->rsc_routine == routine) { - // Do not enable this code in production builds unless - // the possible B.O. is fixed here. - buf += sprintf(buf, "%d:%s\n", routine2->getId(), + string buf; + buf.printf("%d:%s\n", routine2->getId(), routine2->getName().toString().c_str()); + buffer += buf; } } } } - for (Function** iter2 = att->att_functions.begin(); iter2 != att->att_functions.end(); ++iter2) + for (auto routine2 : mdc->mdc_functions) { - Routine* routine2 = *iter2; - if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ ) { - // Loop over procedures from resource list of request - const ResourceList& list = routine2->getStatement()->resources; - FB_SIZE_T i; - - for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) + // Loop over functions from resource list of request + for (auto resource : routine2->getStatement()->resources.getObjects(Resource::rsc_function)) { - const Resource& resource = list[i]; - if (resource.rsc_type != Resource::rsc_procedure) + if (resource->rsc_type != Resource::rsc_procedure) break; - if (resource.rsc_routine == routine) + if (resource->rsc_routine == routine) { - // Do not enable this code in production builds unless - // the possible B.O. is fixed here. - buf += sprintf(buf, "%d:%s\n", routine2->getId(), + string buf; + buf.printf("%d:%s\n", routine2->getId(), routine2->getName().toString().c_str()); + buffer += buf; } } } } - gds__log(buffer); + gds__log("%s", buffer.c_str()); fb_assert(false); } } // Walk functions again and check dependencies - for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter) + for (auto routine : mdc->mdc_functions) { - Routine* routine = *iter; - if (routine && routine->getStatement() && /* !(routine->flags & Routine::FLAG_OBSOLETE) && */ - routine->useCount < routine->intUseCount) + routine->getUseCount() < routine->intUseCount) { - char buffer[1024], *buf = buffer; - buf += sprintf(buf, "Function %d:%s is not properly counted (use count=%d, func use=%d). Used by: \n", + string buffer; + buffer.printf("Function %d:%s is not properly counted (use count=%d, func use=%d). Used by: \n", routine->getId(), routine->getName().toString().c_str(), - routine->useCount, routine->intUseCount); + routine->getUseCount(), routine->intUseCount); - for (jrd_prc** iter2 = att->att_procedures.begin(); iter2 != att->att_procedures.end(); ++iter2) + for (auto routine2 : mdc->mdc_procedures) { - Routine* routine2 = *iter2; - if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ ) { // Loop over procedures from resource list of request - const ResourceList& list = routine2->getStatement()->resources; - FB_SIZE_T i; - - for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) + for (auto resource : routine2->getStatement()->resources.getObjects(Resource::rsc_procedure)) { - const Resource& resource = list[i]; - if (resource.rsc_type != Resource::rsc_function) + if (resource->rsc_type != Resource::rsc_function) break; - if (resource.rsc_routine == routine) + if (resource->rsc_routine == routine) { // Do not enable this code in production builds unless // the possible B.O. is fixed here. - buf += sprintf(buf, "%d:%s\n", routine2->getId(), + string buf; + buf.printf("%d:%s\n", routine2->getId(), routine2->getName().toString().c_str()); + buffer += buf; } } } } - for (Function** iter2 = att->att_functions.begin(); iter2 != att->att_functions.end(); ++iter2) + for (auto routine2 : mdc->mdc_functions) { - Routine* routine2 = *iter2; - if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ ) { - // Loop over procedures from resource list of request - const ResourceList& list = routine2->getStatement()->resources; - FB_SIZE_T i; - - for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i); - i < list.getCount(); i++) + // Loop over functions from resource list of request + for (auto resource : routine2->getStatement()->resources.getObjects(Resource::rsc_function)) { - const Resource& resource = list[i]; - if (resource.rsc_type != Resource::rsc_function) + if (resource->rsc_type != Resource::rsc_function) break; - if (resource.rsc_routine == routine) + if (resource->rsc_routine == routine) { // Do not enable this code in production builds unless // the possible B.O. is fixed here. - buf += sprintf(buf, "%d:%s\n", routine2->getId(), + string buf; + buf.printf("%d:%s\n", routine2->getId(), routine2->getName().toString().c_str()); + buffer += buf; } } } } - gds__log(buffer); + gds__log("%s", buffer); fb_assert(false); } } // Fix back int_use_count - for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter) + for (auto routine : mdc->mdc_procedures) { - Routine* routine = *iter; - if (routine) routine->intUseCount = 0; } - for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter) + for (auto routine : mdc->mdc_functions) { - Routine* routine = *iter; - if (routine) routine->intUseCount = 0; } } -#endif +#endif // NEVERDEF -void MET_clear_cache(thread_db* tdbb) +// Done before MDC is deleted +void MetadataCache::cleanup(thread_db* tdbb) +{ + mdc_relations.cleanup(tdbb); + mdc_procedures.cleanup(tdbb); + mdc_functions.cleanup(tdbb); + mdc_charsets.cleanup(tdbb); + + for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i) + { + auto* ptr = mdc_triggers[i].load(atomics::memory_order_relaxed); + if (ptr) + { + Cached::Triggers::cleanup(tdbb, ptr); + mdc_triggers[i].store(nullptr, atomics::memory_order_relaxed); + } + } +} + + +void MetadataCache::clear(thread_db* tdbb) { /************************************** * @@ -655,6 +503,8 @@ void MET_clear_cache(thread_db* tdbb) * release resources they use * **************************************/ +/* !!!!!!!!!!!!!!!!!!! force remove old versions + SET_TDBB(tdbb); #ifdef DEV_BUILD MET_verify_cache(tdbb); @@ -664,11 +514,9 @@ void MET_clear_cache(thread_db* tdbb) // Release global (db-level and DDL) triggers - for (unsigned i = 0; i < DB_TRIGGER_MAX; i++) + for (unsigned i = 0; i < DB_TRIGGERS_COUNT; i++) MET_release_triggers(tdbb, &att->att_triggers[i], false); - MET_release_triggers(tdbb, &att->att_ddl_triggers, false); - // Release relation triggers vec* const relations = att->att_relations; @@ -749,124 +597,8 @@ void MET_clear_cache(thread_db* tdbb) #ifdef DEV_BUILD MET_verify_cache(tdbb); #endif -} - - -bool MET_routine_in_use(thread_db* tdbb, Routine* routine) -{ -/************************************** - * - * M E T _ r o u t i n e _ i n _ u s e - * - ************************************** - * - * Functional description - * Determine if routine is used by any user requests or transactions. - * Return false if routine is used only inside cache or not used at all. - * - **************************************/ - SET_TDBB(tdbb); - -#ifdef DEV_BUILD - MET_verify_cache(tdbb); -#endif - - Attachment* const att = tdbb->getAttachment(); - - vec* relations = att->att_relations; - { // scope - vec::iterator ptr, end; - - for (ptr = relations->begin(), end = relations->end(); ptr < end; ++ptr) - { - jrd_rel* relation = *ptr; - if (!relation) { - continue; - } - post_used_procedures(relation->rel_pre_store); - post_used_procedures(relation->rel_post_store); - post_used_procedures(relation->rel_pre_erase); - post_used_procedures(relation->rel_post_erase); - post_used_procedures(relation->rel_pre_modify); - post_used_procedures(relation->rel_post_modify); - } - } // scope - - // Walk routines and calculate internal dependencies - - for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter) - { - jrd_prc* procedure = *iter; - - if (procedure && procedure->getStatement() && - !(procedure->flags & Routine::FLAG_OBSOLETE)) - { - inc_int_use_count(procedure->getStatement()); - } - } - - for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter) - { - Function* function = *iter; - - if (function && function->getStatement() && - !(function->flags & Routine::FLAG_OBSOLETE)) - { - inc_int_use_count(function->getStatement()); - } - } - - // Walk routines again and adjust dependencies for routines - // which will not be removed. - - for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter) - { - jrd_prc* procedure = *iter; - - if (procedure && procedure->getStatement() && - !(procedure->flags & Routine::FLAG_OBSOLETE) && - procedure->useCount != procedure->intUseCount && procedure != routine) - { - adjust_dependencies(procedure); - } - } - - for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter) - { - Function* function = *iter; - - if (function && function->getStatement() && - !(function->flags & Routine::FLAG_OBSOLETE) && - function->useCount != function->intUseCount && function != routine) - { - adjust_dependencies(function); - } - } - - const bool result = routine->useCount != routine->intUseCount; - // Fix back intUseCount - - for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter) - { - jrd_prc* procedure = *iter; - - if (procedure) - procedure->intUseCount = 0; - } - - for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter) - { - Function* function = *iter; - - if (function) - function->intUseCount = 0; - } - -#ifdef DEV_BUILD - MET_verify_cache(tdbb); -#endif - return result; + */ } @@ -984,13 +716,23 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc AutoCacheRequest request(tdbb, irq_m_fields, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) - X IN RDB$RELATION_FIELDS WITH - X.RDB$FIELD_SOURCE EQ field_source->dsc_address + X IN RDB$RELATION_FIELDS + CROSS R IN RDB$RELATIONS + OVER RDB$RELATION_NAME + WITH X.RDB$FIELD_SOURCE EQ field_source->dsc_address { relation_name.makeText(sizeof(X.RDB$RELATION_NAME) - 1, CS_METADATA, (UCHAR*) X.RDB$RELATION_NAME); SCL_check_relation(tdbb, &relation_name, SCL_alter); - dw = DFW_post_work(transaction, dfw_update_format, &relation_name, 0); + { + AutoSetRestore2 tempTrans(tdbb, + &thread_db::getTransaction, + &thread_db::setTransaction, + transaction); + + MetadataCache::tagForUpdate(tdbb, R.RDB$RELATION_ID); + } + DFW_post_work(transaction, dfw_commit_relation, nullptr, R.RDB$RELATION_ID); AutoCacheRequest request2(tdbb, irq_m_fields4, IRQ_REQUESTS); @@ -1027,16 +769,15 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc DEP.RDB$DEPENDENT_NAME EQ TRG.RDB$TRIGGER_NAME { MetaName trigger_name(TRG.RDB$TRIGGER_NAME); - MetaName trigger_relation_name(TRG.RDB$RELATION_NAME); + MetaName relation_name(TRG.RDB$RELATION_NAME); dsc desc; desc.makeText(trigger_name.length(), CS_METADATA, (UCHAR*) trigger_name.c_str()); - DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_trigger, &desc, 0); - DFW_post_work_arg(transaction, dw2, NULL, TRG.RDB$TRIGGER_TYPE, dfw_arg_trg_type); + DeferredWork* dw2 = + DFW_post_work(transaction, dfw_modify_trigger, &desc, TRG.RDB$TRIGGER_TYPE); - desc.dsc_length = trigger_relation_name.length(); - desc.dsc_address = (UCHAR*) trigger_relation_name.c_str(); + desc.makeText(relation_name.length(), CS_METADATA, (UCHAR*) relation_name.c_str()); DFW_post_work_arg(transaction, dw2, &desc, 0, dfw_arg_check_blr); } END_FOR @@ -1121,16 +862,15 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc DEP.RDB$DEPENDENT_NAME EQ TRG.RDB$TRIGGER_NAME { MetaName trigger_name(TRG.RDB$TRIGGER_NAME); - MetaName trigger_relation_name(TRG.RDB$RELATION_NAME); + MetaName relation_name(TRG.RDB$RELATION_NAME); dsc desc; desc.makeText(trigger_name.length(), CS_METADATA, (UCHAR*) trigger_name.c_str()); - DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_trigger, &desc, 0); - DFW_post_work_arg(transaction, dw2, NULL, TRG.RDB$TRIGGER_TYPE, dfw_arg_trg_type); + DeferredWork* dw2 = + DFW_post_work(transaction, dfw_modify_trigger, &desc, TRG.RDB$TRIGGER_TYPE); - desc.dsc_length = trigger_relation_name.length(); - desc.dsc_address = (UCHAR*) trigger_relation_name.c_str(); + desc.makeText(relation_name.length(), CS_METADATA, (UCHAR*) relation_name.c_str()); DFW_post_work_arg(transaction, dw2, &desc, 0, dfw_arg_check_blr); } END_FOR @@ -1183,60 +923,6 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc } -Format* MET_current(thread_db* tdbb, jrd_rel* relation) -{ -/************************************** - * - * M E T _ c u r r e n t - * - ************************************** - * - * Functional description - * Get the current format for a relation. The current format is the - * format in which new records are to be stored. - * - **************************************/ - - // dimitr: rel_current_format may sometimes get out of sync, - // e.g. after DFW error raised during ALTER TABLE command. - // Thus it makes sense to validate it before usage and - // fetch the proper one if something is suspicious. - - if (relation->rel_current_format && - relation->rel_current_format->fmt_version == relation->rel_current_fmt) - { - return relation->rel_current_format; - } - - SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); - - if (!(relation->rel_flags & REL_scanned)) - { - AutoCacheRequest request(tdbb, irq_l_curr_format, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - REL IN RDB$RELATIONS - WITH REL.RDB$RELATION_ID EQ relation->rel_id - { - relation->rel_current_fmt = REL.RDB$FORMAT; - } - END_FOR - } - - // Usually, format numbers start with one and they are present in RDB$FORMATS. - // However, system tables have zero as their initial format and they don't have - // any related records in RDB$FORMATS, instead their rel_formats[0] is initialized - // directly (see ini.epp). Every other case of zero format number found for an already - // scanned table must be catched here and investigated. - fb_assert(relation->rel_current_fmt || relation->isSystem()); - - relation->rel_current_format = MET_format(tdbb, relation, relation->rel_current_fmt); - - return relation->rel_current_format; -} - - void MET_delete_dependencies(thread_db* tdbb, const MetaName& object_name, int dependency_type, @@ -1255,9 +941,9 @@ void MET_delete_dependencies(thread_db* tdbb, **************************************/ SET_TDBB(tdbb); -// if (!object_name) -// return; -// + if (object_name.isEmpty()) + return; + AutoCacheRequest request(tdbb, irq_d_deps, IRQ_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) @@ -1309,7 +995,7 @@ void MET_delete_shadow(thread_db* tdbb, USHORT shadow_number) } -bool MET_dsql_cache_use(thread_db* tdbb, sym_type type, const MetaName& name, const MetaName& package) +bool MetadataCache::dsql_cache_use(thread_db* tdbb, sym_type type, const MetaName& name, const MetaName& package) { const QualifiedName qualifiedName(name, package); DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, qualifiedName); @@ -1330,7 +1016,7 @@ bool MET_dsql_cache_use(thread_db* tdbb, sym_type type, const MetaName& name, co } -void MET_dsql_cache_release(thread_db* tdbb, sym_type type, const MetaName& name, const MetaName& package) +void MetadataCache::dsql_cache_release(thread_db* tdbb, sym_type type, const MetaName& name, const MetaName& package) { const QualifiedName qualifiedName(name, package); DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, qualifiedName); @@ -1378,7 +1064,7 @@ void MET_error(const TEXT* string, ...) Arg::Gds(isc_random) << Arg::Str(s)); } -Format* MET_format(thread_db* tdbb, jrd_rel* relation, USHORT number) +Format* MET_format(thread_db* tdbb, RelationPermanent* relation, USHORT number) { /************************************** * @@ -1409,7 +1095,7 @@ Format* MET_format(thread_db* tdbb, jrd_rel* relation, USHORT number) AutoCacheRequest request(tdbb, irq_r_format, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) - X IN RDB$FORMATS WITH X.RDB$RELATION_ID EQ relation->rel_id AND + X IN RDB$FORMATS WITH X.RDB$RELATION_ID EQ relation->getId() AND X.RDB$FORMAT EQ number { blb* blob = blb::open(tdbb, attachment->getSysTransaction(), &X.RDB$DESCRIPTOR); @@ -1421,7 +1107,7 @@ Format* MET_format(thread_db* tdbb, jrd_rel* relation, USHORT number) unsigned bufferPos = 2; USHORT count = buffer[0] | (buffer[1] << 8); - format = Format::newFormat(*relation->rel_pool, count); + format = Format::newFormat(relation->getPool(), count); Array odsDescs; Ods::Descriptor* odsDesc = odsDescs.getBuffer(count); @@ -1453,7 +1139,7 @@ Format* MET_format(thread_db* tdbb, jrd_rel* relation, USHORT number) desc.dsc_address = tmpArray.getBuffer(desc.dsc_length, false); memcpy(desc.dsc_address, p, desc.dsc_length); - EVL_make_value(tdbb, &desc, &format->fmt_defaults[offset], relation->rel_pool); + EVL_make_value(tdbb, &desc, &format->fmt_defaults[offset], &relation->getPool()); p += desc.dsc_length; } @@ -1461,21 +1147,21 @@ Format* MET_format(thread_db* tdbb, jrd_rel* relation, USHORT number) END_FOR if (!format) - format = Format::newFormat(*relation->rel_pool); + format = Format::newFormat(relation->getPool()); format->fmt_version = number; // Link the format block into the world formats = relation->rel_formats = - vec::newVector(*relation->rel_pool, relation->rel_formats, number + 1); + vec::newVector(relation->getPool(), relation->rel_formats, number + 1); (*formats)[number] = format; return format; } -bool MET_get_char_coll_subtype(thread_db* tdbb, USHORT* id, const UCHAR* name, USHORT length) +bool MetadataCache::get_texttype(thread_db* tdbb, TTypeId* id, const UCHAR* name, USHORT length) { /************************************** * @@ -1521,20 +1207,22 @@ bool MET_get_char_coll_subtype(thread_db* tdbb, USHORT* id, const UCHAR* name, U } *p = 0; + auto mdc = MetadataCache::get(tdbb); + // Is there a period, separating collation name from character set? if (period) { *period = 0; - return resolve_charset_and_collation(tdbb, id, period + 1, buffer); + return mdc->resolve_charset_and_collation(tdbb, id, period + 1, buffer); } // Is it a character set name (implying charset default collation sequence) - bool res = resolve_charset_and_collation(tdbb, id, buffer, NULL); + bool res = mdc->resolve_charset_and_collation(tdbb, id, buffer, NULL); if (!res) { // Is it a collation name (implying implementation-default character set) - res = resolve_charset_and_collation(tdbb, id, NULL, buffer); + res = mdc->resolve_charset_and_collation(tdbb, id, NULL, buffer); } return res; } @@ -1548,6 +1236,8 @@ bool MET_get_char_coll_subtype_info(thread_db* tdbb, USHORT id, SubtypeInfo* inf * ************************************** * + TODO - lookup in MDC first !!!!!!!!!!!!!!!!! + * Functional description * Get charset and collation informations * for a subtype ID. @@ -1563,6 +1253,7 @@ bool MET_get_char_coll_subtype_info(thread_db* tdbb, USHORT id, SubtypeInfo* inf AutoCacheRequest request(tdbb, irq_l_subtype, IRQ_REQUESTS); bool found = false; + info->charsetName.clear(); FOR(REQUEST_HANDLE request) FIRST 1 CL IN RDB$COLLATIONS CROSS @@ -1573,7 +1264,7 @@ bool MET_get_char_coll_subtype_info(thread_db* tdbb, USHORT id, SubtypeInfo* inf { found = true; - info->charsetName = CS.RDB$CHARACTER_SET_NAME; + info->charsetName.push(CS.RDB$CHARACTER_SET_NAME); info->collationName = CL.RDB$COLLATION_NAME; if (CL.RDB$BASE_COLLATION_NAME.NULL) @@ -1603,7 +1294,7 @@ bool MET_get_char_coll_subtype_info(thread_db* tdbb, USHORT id, SubtypeInfo* inf DmlNode* MET_get_dependencies(thread_db* tdbb, - jrd_rel* relation, + Cached::Relation* relation, const UCHAR* blob, const ULONG blob_length, CompilerScratch* view_csb, @@ -1662,7 +1353,7 @@ DmlNode* MET_get_dependencies(thread_db* tdbb, RLF IN RDB$RELATION_FIELDS CROSS FLD IN RDB$FIELDS WITH RLF.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND - RLF.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND + RLF.RDB$RELATION_NAME EQ relation->c_name() AND RLF.RDB$FIELD_NAME EQ object_name.c_str() { domainName = FLD.RDB$FIELD_NAME; @@ -1822,16 +1513,16 @@ void MET_get_shadow_files(thread_db* tdbb, bool delete_files) } -void MET_load_db_triggers(thread_db* tdbb, int type) +ScanResult DbTriggers::scan(thread_db* tdbb, ObjectBase::Flag flags) { -/************************************** +/******************************************** * - * M E T _ l o a d _ d b _ t r i g g e r s + * D b T r i g g e r s :: s c a n * - ************************************** + ******************************************** * * Functional description - * Load database triggers from RDB$TRIGGERS. + * Load database-wide triggers from RDB$TRIGGERS. * **************************************/ @@ -1840,98 +1531,112 @@ void MET_load_db_triggers(thread_db* tdbb, int type) Database* dbb = tdbb->getDatabase(); CHECK_DBB(dbb); - if ((attachment->att_flags & ATT_no_db_triggers) || - attachment->att_triggers[type] != NULL) - { - return; - } - - attachment->att_triggers[type] = FB_NEW_POOL(*attachment->att_pool) - TrigVector(*attachment->att_pool); - attachment->att_triggers[type]->addRef(); - - AutoRequest trigger_request; - int encoded_type = type | TRIGGER_TYPE_DB; - - FOR(REQUEST_HANDLE trigger_request) - TRG IN RDB$TRIGGERS - WITH TRG.RDB$RELATION_NAME MISSING AND - TRG.RDB$TRIGGER_TYPE EQ encoded_type AND - TRG.RDB$TRIGGER_INACTIVE EQ 0 - SORTED BY TRG.RDB$TRIGGER_SEQUENCE - { - MET_load_trigger(tdbb, NULL, TRG.RDB$TRIGGER_NAME, &attachment->att_triggers[type]); - } - END_FOR -} - - -// Load DDL triggers from RDB$TRIGGERS. -void MET_load_ddl_triggers(thread_db* tdbb) -{ - SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); - Database* dbb = tdbb->getDatabase(); - CHECK_DBB(dbb); - - if ((attachment->att_flags & ATT_no_db_triggers) || - attachment->att_ddl_triggers != NULL) - { - return; - } - - attachment->att_ddl_triggers = FB_NEW_POOL(*attachment->att_pool) - TrigVector(*attachment->att_pool); - attachment->att_ddl_triggers->addRef(); + auto type = perm->getId(); + FB_UINT64 mask = (type == DB_TRIGGER_DDL) ? TRIGGER_TYPE_MASK : ~FB_UINT64(0); + type = (type == DB_TRIGGER_DDL) ? TRIGGER_TYPE_DDL : type | TRIGGER_TYPE_DB; AutoRequest trigger_request; + jrd_tra* trans = tdbb->getTransaction() ? tdbb->getTransaction() : attachment->getSysTransaction(); - FOR(REQUEST_HANDLE trigger_request) + FOR(REQUEST_HANDLE trigger_request TRANSACTION_HANDLE trans) TRG IN RDB$TRIGGERS WITH TRG.RDB$RELATION_NAME MISSING AND TRG.RDB$TRIGGER_INACTIVE EQ 0 SORTED BY TRG.RDB$TRIGGER_SEQUENCE { - if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) + if ((TRG.RDB$TRIGGER_TYPE & mask) == type) { - MET_load_trigger(tdbb, NULL, TRG.RDB$TRIGGER_NAME, - &attachment->att_ddl_triggers); + MET_load_trigger(tdbb, nullptr, TRG.RDB$TRIGGER_NAME, [this](int)->Triggers& {return *this;}); } } END_FOR + + return ScanResult::COMPLETE; } -void MET_load_trigger(thread_db* tdbb, - jrd_rel* relation, - const MetaName& trigger_name, - TrigVector** triggers) +void MetadataCache::loadDbTriggers(thread_db* tdbb, unsigned int type) { /************************************** * - * M E T _ l o a d _ t r i g g e r + * l o a d _ d b _ t r i g g e r s * ************************************** * * Functional description - * Load triggers from RDB$TRIGGERS. If a requested, - * also load triggers from RDB$RELATIONS. + * Load database triggers from RDB$TRIGGERS. * **************************************/ - TEXT errmsg[MAX_ERRMSG_LEN + 1]; SET_TDBB(tdbb); Attachment* attachment = tdbb->getAttachment(); Database* dbb = tdbb->getDatabase(); CHECK_DBB(dbb); - if (relation) + fb_assert(type < DB_TRIGGERS_COUNT); + auto* cacheElement = mdc_triggers[type].load(atomics::memory_order_acquire); + + if (!cacheElement) + { + // actual type will be taken into an account in DbTriggers::scan + auto* newCacheElement = FB_NEW_POOL(getPool()) + Cached::Triggers(tdbb, getPool(), type, DbTriggers::makeLock, NoData()); + if (mdc_triggers[type].compare_exchange_strong(cacheElement, newCacheElement, + atomics::memory_order_release, atomics::memory_order_acquire)) + { + cacheElement = newCacheElement; + } + else + { + delete newCacheElement; + return; + } + } + + DbTriggers* triggers = DbTriggers::create(tdbb, getPool(), cacheElement); + try + { + if (cacheElement->storeObject(tdbb, triggers, 0) == StoreResult::DUP) + DbTriggers::destroy(tdbb, triggers); + } + catch(const Exception&) + { + DbTriggers::destroy(tdbb, triggers); + throw; + } +} + + +void MET_load_trigger(thread_db* tdbb, + jrd_rel* relation, + const MetaName& trigger_name, + std::function triggers) +{ +/************************************** + * + * M E T _ l o a d _ t r i g g e r + * + ************************************** + * + * Functional description + * Load triggers from RDB$TRIGGERS. If a requested, + * also load triggers from RDB$RELATIONS. + * + **************************************/ + TEXT errmsg[MAX_ERRMSG_LEN + 1]; + + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + CHECK_DBB(dbb); + + if (relation) { // No need to load table triggers for ReadOnly databases, // since INSERT/DELETE/UPDATE statements are not going to be allowed // hvlad: GTT with ON COMMIT DELETE ROWS clause is writable - if (dbb->readOnly() && !(relation->rel_flags & REL_temp_tran)) + if (dbb->readOnly() && !(getPermanent(relation)->rel_flags & REL_temp_tran)) return; } @@ -1999,10 +1704,9 @@ void MET_load_trigger(thread_db* tdbb, relation, &TRG.RDB$TRIGGER_BLR, &debug_blob_id, - triggers, + triggers(0), TRG.RDB$TRIGGER_NAME, TRG.RDB$TRIGGER_TYPE, - (bool) TRG.RDB$SYSTEM_FLAG, trig_flags, engine, entryPoint, @@ -2020,10 +1724,9 @@ void MET_load_trigger(thread_db* tdbb, relation, &TRG.RDB$TRIGGER_BLR, &debug_blob_id, - triggers + trigger_action, + triggers(trigger_action), TRG.RDB$TRIGGER_NAME, (UCHAR) trigger_action, - (bool) TRG.RDB$SYSTEM_FLAG, trig_flags, engine, entryPoint, @@ -2230,14 +1933,11 @@ int MET_lookup_field(thread_db* tdbb, jrd_rel* relation, const MetaName& name) int id = -1; - if (relation->rel_flags & REL_deleted) - return id; - AutoCacheRequest request(tdbb, irq_l_field, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) X IN RDB$RELATION_FIELDS WITH - X.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND + X.RDB$RELATION_NAME EQ relation->c_name() AND X.RDB$FIELD_ID NOT MISSING AND X.RDB$FIELD_NAME EQ name.c_str() { @@ -2444,42 +2144,29 @@ void MET_update_generator_increment(thread_db* tdbb, SLONG gen_id, SLONG step) } -void MET_lookup_index(thread_db* tdbb, - MetaName& index_name, - const MetaName& relation_name, - USHORT number) +IndexStatus MetadataCache::getIndexStatus(bool nullFlag, int state) { -/************************************** - * - * M E T _ l o o k u p _ i n d e x - * - ************************************** - * - * Functional description - * Lookup index name from relation and index number. - * Calling routine must pass a buffer of at least 32 bytes. - * - **************************************/ - SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); - - index_name = ""; + if (nullFlag) + return MET_index_active; - AutoCacheRequest request(tdbb, irq_l_index, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - X IN RDB$INDICES WITH X.RDB$RELATION_NAME EQ relation_name.c_str() - AND X.RDB$INDEX_ID EQ number + switch (state) { - index_name = X.RDB$INDEX_NAME; + case MET_index_active: + case MET_index_inactive: + case MET_index_deferred_active: + case MET_index_deferred_drop: + return (IndexStatus)state; + + default: + break; } - END_FOR + + return MET_index_state_unknown; } -SLONG MET_lookup_index_name(thread_db* tdbb, - const MetaName& index_name, - SLONG* relation_id, IndexStatus* status) +ElementBase::ReturnedId MetadataCache::lookup_index_name(thread_db* tdbb, const MetaName& index_name, + IndexStatus* status) { /************************************** * @@ -2498,22 +2185,16 @@ SLONG MET_lookup_index_name(thread_db* tdbb, AutoCacheRequest request(tdbb, irq_l_index_name, IRQ_REQUESTS); - *status = MET_object_unknown; + *status = MET_index_state_unknown; FOR(REQUEST_HANDLE request) X IN RDB$INDICES WITH X.RDB$INDEX_NAME EQ index_name.c_str() { - if (X.RDB$INDEX_INACTIVE == 0) - *status = MET_object_active; - else if (X.RDB$INDEX_INACTIVE == 3) - *status = MET_object_deferred_active; - else - *status = MET_object_inactive; + *status = getIndexStatus(X.RDB$INDEX_INACTIVE.NULL, X.RDB$INDEX_INACTIVE); - id = X.RDB$INDEX_ID - 1; - const jrd_rel* relation = MET_lookup_relation(tdbb, X.RDB$RELATION_NAME); - *relation_id = relation->rel_id; + if ((!X.RDB$INDEX_ID.NULL) && X.RDB$INDEX_ID) + id = X.RDB$INDEX_ID - 1; } END_FOR @@ -2521,188 +2202,32 @@ SLONG MET_lookup_index_name(thread_db* tdbb, } -void MET_lookup_index_condition(thread_db* tdbb, jrd_rel* relation, index_desc* idx) -{ -/************************************** -* -* M E T _ l o o k u p _ i n d e x _ c o n d i t i o n -* -************************************** -* -* Functional description -* Lookup information about an index, in -* the metadata cache if possible. -* -**************************************/ - SET_TDBB(tdbb); - const auto attachment = tdbb->getAttachment(); - - // Check the index blocks for the relation to see if we have a cached block - - IndexBlock* index_block; - for (index_block = relation->rel_index_blocks; index_block; index_block = index_block->idb_next) - { - if (index_block->idb_id == idx->idx_id) - break; - } - - if (index_block && index_block->idb_condition) - { - idx->idx_condition = index_block->idb_condition; - idx->idx_condition_statement = index_block->idb_condition_statement; - return; - } - - const auto dbb = tdbb->getDatabase(); - if (dbb->getEncodedOdsVersion() < ODS_13_1) - return; - - if (!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)) - MET_scan_relation(tdbb, relation); - - CompilerScratch* csb = nullptr; - AutoCacheRequest request(tdbb, irq_l_cond_index, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - IDX IN RDB$INDICES WITH - IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND - IDX.RDB$INDEX_ID EQ idx->idx_id + 1 - { - if (idx->idx_condition_statement) - { - idx->idx_condition_statement->release(tdbb); - idx->idx_condition_statement = nullptr; - } - - // Parse the blr, making sure to create the resulting expression - // tree and request in its own pool so that it may be cached - // with the index block in the "permanent" metadata cache - - { // scope - Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); - - MET_parse_blob(tdbb, relation, &IDX.RDB$CONDITION_BLR, &csb, nullptr, false, false); - - idx->idx_condition_statement = Statement::makeBoolExpression(tdbb, - idx->idx_condition, csb, false); - } // end scope - } - END_FOR - - delete csb; - - // If there is no existing index block for this index, create - // one and link it in with the index blocks for this relation - - if (!index_block) - index_block = IDX_create_index_block(tdbb, relation, idx->idx_id); - - // If we can't get the lock, no big deal: just give up on caching the index info - - if (!LCK_lock(tdbb, index_block->idb_lock, LCK_SR, LCK_NO_WAIT)) - { - // clear lock error from status vector - fb_utils::init_status(tdbb->tdbb_status_vector); - return; - } - - // Fill in the cached information about the index - - index_block->idb_condition = idx->idx_condition; - index_block->idb_condition_statement = idx->idx_condition_statement; -} - - -void MET_lookup_index_expression(thread_db* tdbb, jrd_rel* relation, index_desc* idx) +void MET_lookup_index_code(thread_db* tdbb, Cached::Relation* relation, index_desc* idx) { -/************************************** +/*********************************************** * -* M E T _ l o o k u p _ i n d e x _ e x p r e s s i o n +* M E T _ l o o k u p _ i n d e x _ c o d e * -************************************** +************************************************ * * Functional description -* Lookup information about an index, in -* the metadata cache if possible. +* Lookup information about an index. * **************************************/ SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); - - // Check the index blocks for the relation to see if we have a cached block - - IndexBlock* index_block; - for (index_block = relation->rel_index_blocks; index_block; index_block = index_block->idb_next) - { - if (index_block->idb_id == idx->idx_id) - break; - } - - if (index_block && index_block->idb_expression) - { - idx->idx_expression = index_block->idb_expression; - idx->idx_expression_statement = index_block->idb_expression_statement; - idx->idx_expression_desc = index_block->idb_expression_desc; - return; - } - - if (!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)) - { - MET_scan_relation(tdbb, relation); - } - - CompilerScratch* csb = NULL; - AutoCacheRequest request(tdbb, irq_l_exp_index, IRQ_REQUESTS); - FOR(REQUEST_HANDLE request) - IDX IN RDB$INDICES WITH - IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND - IDX.RDB$INDEX_ID EQ idx->idx_id + 1 + IndexVersion* idv = relation->lookup_index(tdbb, idx->idx_id, CacheFlag::AUTOCREATE); + if (idv) { - if (idx->idx_expression_statement) - { - idx->idx_expression_statement->release(tdbb); - idx->idx_expression_statement = nullptr; - } - - // parse the blr, making sure to create the resulting expression - // tree and request in its own pool so that it may be cached - // with the index block in the "permanent" metadata cache - - { // scope - Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); - - MET_parse_blob(tdbb, relation, &IDX.RDB$EXPRESSION_BLR, &csb, nullptr, false, false); - - idx->idx_expression_statement = Statement::makeValueExpression(tdbb, - idx->idx_expression, idx->idx_expression_desc, csb, false); - } // end scope - } - END_FOR - - delete csb; - - // if there is no existing index block for this index, create - // one and link it in with the index blocks for this relation + idx->idx_condition_node = idv->idv_condition; + idx->idx_condition_statement = idv->idv_condition_statement; - if (!index_block) - index_block = IDX_create_index_block(tdbb, relation, idx->idx_id); + idx->idx_expression_node = idv->idv_expression; + idx->idx_expression_statement = idv->idv_expression_statement; + memcpy(&idx->idx_expression_desc, &idv->idv_expression_desc, sizeof(struct dsc)); - // if we can't get the lock, no big deal: just give up on caching the index info - - if (!LCK_lock(tdbb, index_block->idb_lock, LCK_SR, LCK_NO_WAIT)) - { - // clear lock error from status vector - fb_utils::init_status(tdbb->tdbb_status_vector); return; } - - // whether the index block already existed or was just created, - // fill in the cached information about the index - - index_block->idb_expression = idx->idx_expression; - index_block->idb_expression_statement = idx->idx_expression_statement; - memcpy(&index_block->idb_expression_desc, &idx->idx_expression_desc, sizeof(struct dsc)); } @@ -2741,7 +2266,7 @@ bool MET_lookup_index_expr_cond_blr(thread_db* tdbb, const MetaName& index_name, } -bool MET_lookup_partner(thread_db* tdbb, jrd_rel* relation, index_desc* idx, const TEXT* index_name) +bool MET_lookup_partner(thread_db* tdbb, RelationPermanent* relation, index_desc* idx, const TEXT* index_name) { /************************************** * @@ -2759,7 +2284,7 @@ bool MET_lookup_partner(thread_db* tdbb, jrd_rel* relation, index_desc* idx, con Attachment* attachment = tdbb->getAttachment(); if (relation->rel_flags & REL_check_partners) - scan_partners(tdbb, relation); + relation->scan_partners(tdbb); if (idx->idx_flags & idx_foreign) { @@ -2774,19 +2299,20 @@ bool MET_lookup_partner(thread_db* tdbb, jrd_rel* relation, index_desc* idx, con FOR(REQUEST_HANDLE request) IDX IN RDB$INDICES CROSS IND IN RDB$INDICES WITH - IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND + IDX.RDB$RELATION_NAME EQ relation->c_name() AND (IDX.RDB$INDEX_ID EQ idx->idx_id + 1 OR IDX.RDB$INDEX_NAME EQ index_name) AND IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND IND.RDB$UNIQUE_FLAG = 1 { //// ASF: Hack fix for CORE-4304, until nasty interactions between dfw and met are not resolved. - const jrd_rel* partner_relation = relation->rel_name == IND.RDB$RELATION_NAME ? - relation : MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME); + const RelationPermanent* partner_relation = relation; + if (relation->getName() != IND.RDB$RELATION_NAME) + partner_relation = MetadataCache::lookupRelation(tdbb, IND.RDB$RELATION_NAME, CacheFlag::AUTOCREATE); if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE) { - idx->idx_primary_relation = partner_relation->rel_id; + idx->idx_primary_relation = partner_relation->getId(); idx->idx_primary_index = IND.RDB$INDEX_ID - 1; fb_assert(idx->idx_primary_index != idx_invalid); found = true; @@ -2797,17 +2323,15 @@ bool MET_lookup_partner(thread_db* tdbb, jrd_rel* relation, index_desc* idx, con return found; } - frgn* references = &relation->rel_foreign_refs; - if (references->frgn_reference_ids) + auto* references = relation->rel_foreign_refs; + if (references) { - for (unsigned int index_number = 0; - index_number < references->frgn_reference_ids->count(); - index_number++) + for (auto& dep : *references) { - if (idx->idx_id == (*references->frgn_reference_ids)[index_number]) + if (idx->idx_id == dep.dep_reference_id) { - idx->idx_primary_relation = (*references->frgn_relations)[index_number]; - idx->idx_primary_index = (*references->frgn_indexes)[index_number]; + idx->idx_primary_relation = dep.dep_relation; + idx->idx_primary_index = dep.dep_index; return true; } } @@ -2816,18 +2340,14 @@ bool MET_lookup_partner(thread_db* tdbb, jrd_rel* relation, index_desc* idx, con } else if (idx->idx_flags & (idx_primary | idx_unique)) { - const prim* dependencies = &relation->rel_primary_dpnds; - if (dependencies->prim_reference_ids) + auto* dependencies = relation->rel_primary_dpnds; + if (dependencies) { - for (unsigned int index_number = 0; - index_number < dependencies->prim_reference_ids->count(); - index_number++) + for (auto& dep : *dependencies) { - if (idx->idx_id == (*dependencies->prim_reference_ids)[index_number]) + if (idx->idx_id == dep.dep_reference_id) { - idx->idx_foreign_primaries = relation->rel_primary_dpnds.prim_reference_ids; - idx->idx_foreign_relations = relation->rel_primary_dpnds.prim_relations; - idx->idx_foreign_indexes = relation->rel_primary_dpnds.prim_indexes; + idx->idx_foreign_deps = relation->rel_primary_dpnds; return true; } } @@ -2839,7 +2359,7 @@ bool MET_lookup_partner(thread_db* tdbb, jrd_rel* relation, index_desc* idx, con } -jrd_prc* MET_lookup_procedure(thread_db* tdbb, const QualifiedName& name, bool noscan) +jrd_prc* MetadataCache::lookup_procedure(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) { /************************************** * @@ -2854,36 +2374,23 @@ jrd_prc* MET_lookup_procedure(thread_db* tdbb, const QualifiedName& name, bool n **************************************/ SET_TDBB(tdbb); Attachment* attachment = tdbb->getAttachment(); - jrd_prc* check_procedure = NULL; + MetadataCache* mdc = MetadataCache::get(tdbb); + // See if we already know the procedure by name - for (jrd_prc** iter = attachment->att_procedures.begin(); iter != attachment->att_procedures.end(); ++iter) - { - jrd_prc* procedure = *iter; - if (procedure && !(procedure->flags & Routine::FLAG_OBSOLETE) && - !(procedure->flags & Routine::FLAG_CLEARED) && - ((procedure->flags & Routine::FLAG_SCANNED) || noscan) && - !(procedure->flags & Routine::FLAG_BEING_SCANNED) && - !(procedure->flags & Routine::FLAG_BEING_ALTERED)) - { - if (procedure->getName() == name) - { - if (procedure->flags & Routine::FLAG_CHECK_EXISTENCE) - { - check_procedure = procedure; - LCK_lock(tdbb, check_procedure->existenceLock, LCK_SR, LCK_WAIT); - break; - } + auto* proc = mdc->mdc_procedures.lookup(tdbb, + [name] (Cached::Procedure* proc) { return proc->getName() == name; }, flags); + if (proc) + return proc->getObject(tdbb, flags); + + if (!(flags & CacheFlag::AUTOCREATE)) + return nullptr; - return procedure; - } - } - } // We need to look up the procedure name in RDB$PROCEDURES - jrd_prc* procedure = NULL; + jrd_prc* procedure = nullptr; AutoCacheRequest request(tdbb, irq_l_procedure, IRQ_REQUESTS); @@ -2892,26 +2399,16 @@ jrd_prc* MET_lookup_procedure(thread_db* tdbb, const QualifiedName& name, bool n WITH P.RDB$PROCEDURE_NAME EQ name.identifier.c_str() AND P.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') { - procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, 0); + fb_assert(!procedure); + procedure = mdc->mdc_procedures.getObject(tdbb, P.RDB$PROCEDURE_ID, flags); } END_FOR - if (check_procedure) - { - check_procedure->flags &= ~Routine::FLAG_CHECK_EXISTENCE; - if (check_procedure != procedure) - { - LCK_release(tdbb, check_procedure->existenceLock); - check_procedure->flags |= Routine::FLAG_OBSOLETE; - } - } - return procedure; } -jrd_prc* MET_lookup_procedure_id(thread_db* tdbb, USHORT id, - bool return_deleted, bool noscan, USHORT flags) +jrd_prc* MetadataCache::lookup_procedure_id(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) { /************************************** * @@ -2925,157 +2422,111 @@ jrd_prc* MET_lookup_procedure_id(thread_db* tdbb, USHORT id, **************************************/ SET_TDBB(tdbb); Attachment* attachment = tdbb->getAttachment(); - jrd_prc* check_procedure = NULL; + MetadataCache* mdc = MetadataCache::get(tdbb); - jrd_prc* procedure; - - if (id < (USHORT) attachment->att_procedures.getCount() && (procedure = attachment->att_procedures[id]) && - procedure->getId() == id && - !(procedure->flags & Routine::FLAG_CLEARED) && - !(procedure->flags & Routine::FLAG_BEING_SCANNED) && - ((procedure->flags & Routine::FLAG_SCANNED) || noscan) && - !(procedure->flags & Routine::FLAG_BEING_ALTERED) && - (!(procedure->flags & Routine::FLAG_OBSOLETE) || return_deleted)) - { - if (procedure->flags & Routine::FLAG_CHECK_EXISTENCE) - { - check_procedure = procedure; - LCK_lock(tdbb, check_procedure->existenceLock, LCK_SR, LCK_WAIT); - } - else { - return procedure; - } - } - - // We need to look up the procedure name in RDB$PROCEDURES - - procedure = NULL; - - AutoCacheRequest request(tdbb, irq_l_proc_id, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ id - { - procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, flags); - } - END_FOR - - if (check_procedure) - { - check_procedure->flags &= ~Routine::FLAG_CHECK_EXISTENCE; - if (check_procedure != procedure) - { - LCK_release(tdbb, check_procedure->existenceLock); - check_procedure->flags |= Routine::FLAG_OBSOLETE; - } - } - - return procedure; + return mdc->mdc_procedures.getObject(tdbb, id, flags); } -jrd_rel* MET_lookup_relation(thread_db* tdbb, const MetaName& name) +Function* MetadataCache::lookup_function(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) { -/************************************** +/************************************************ * - * M E T _ l o o k u p _ r e l a t i o n + * M E T _ l o o k u p _ f u n c t i o n * - ************************************** + ************************************************ * * Functional description - * Lookup relation by name. Name passed in is + * Lookup function by name. Name passed in is * ASCIZ name. * **************************************/ SET_TDBB(tdbb); Attachment* attachment = tdbb->getAttachment(); + MetadataCache* mdc = MetadataCache::get(tdbb); - // See if we already know the relation by name - vec* relations = attachment->att_relations; - jrd_rel* check_relation = NULL; + // See if we already know the function by name - vec::iterator ptr = relations->begin(); - for (const vec::const_iterator end = relations->end(); ptr < end; ++ptr) - { - jrd_rel* const relation = *ptr; + auto* func = mdc->mdc_functions.lookup(tdbb, + [name] (Cached::Function* func) { return func->getName() == name; }, flags); + if (func) + return func->getObject(tdbb, flags); - if (relation) - { - if (relation->rel_flags & REL_deleting) - CheckoutLockGuard guard(tdbb, relation->rel_drop_mutex, FB_FUNCTION); + if (!(flags & CacheFlag::AUTOCREATE)) + return nullptr; - if (!(relation->rel_flags & REL_deleted)) - { - // dimitr: for non-system relations we should also check - // REL_scanned and REL_being_scanned flags. Look - // at MET_lookup_procedure for example. - if (!(relation->rel_flags & REL_system) && - (!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned))) - { - continue; - } - if (relation->rel_name == name) - { - if (relation->rel_flags & REL_check_existence) - { - check_relation = relation; - LCK_lock(tdbb, check_relation->rel_existence_lock, LCK_SR, LCK_WAIT); - break; - } + // We need to look up the function in RDB$FUNCTIONS - return relation; - } - } - } + Function* function = nullptr; + + AutoCacheRequest request(tdbb, irq_l_fun_name, IRQ_REQUESTS); + FOR(REQUEST_HANDLE request) + X IN RDB$FUNCTIONS + WITH X.RDB$FUNCTION_NAME EQ name.identifier.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') + { + fb_assert(!function); + function = mdc->getFunction(tdbb, X.RDB$FUNCTION_ID, flags); } + END_FOR + + return function; +} - // We need to look up the relation name in RDB$RELATIONS - jrd_rel* relation = NULL; +Cached::Relation* MetadataCache::lookupRelation(thread_db* tdbb, const MetaName& name, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + Attachment* attachment = tdbb->getAttachment(); + MetadataCache* mdc = MetadataCache::get(tdbb); + // See if we already know the relation by name + auto* rc = mdc->mdc_relations.lookup(tdbb, [name](RelationPermanent* rel) { return rel->rel_name == name; }, flags); + + if (rc || !(flags & CacheFlag::AUTOCREATE)) + return rc; + + // We need to look up the relation name in RDB$RELATIONS AutoCacheRequest request(tdbb, irq_l_relation, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name.c_str() { - relation = MET_relation(tdbb, X.RDB$RELATION_ID); - if (relation->rel_name.length() == 0) { - relation->rel_name = name; - } - - relation->rel_flags |= get_rel_flags_from_FLAGS(X.RDB$FLAGS); - - if (!X.RDB$RELATION_TYPE.NULL) - { - relation->rel_flags |= MET_get_rel_flags_from_TYPE(X.RDB$RELATION_TYPE); - } + if (auto* relVers = mdc->mdc_relations.getObject(tdbb, X.RDB$RELATION_ID, flags)) + rc = relVers->rel_perm; } END_FOR - if (check_relation) - { - check_relation->rel_flags &= ~REL_check_existence; - if (check_relation != relation) - { - LCK_release(tdbb, check_relation->rel_existence_lock); - if (!(check_relation->rel_flags & REL_check_partners)) - { - check_relation->rel_flags |= REL_check_partners; - LCK_release(tdbb, check_relation->rel_partners_lock); - check_relation->rel_flags &= ~REL_check_partners; - } - LCK_release(tdbb, check_relation->rel_rescan_lock); - check_relation->rel_flags |= REL_deleted; - } - } + return rc; +} + +jrd_rel* MetadataCache::lookup_relation(thread_db* tdbb, const MetaName& name, ObjectBase::Flag flags) +{ +/************************************** + * + * M E T _ l o o k u p _ r e l a t i o n + * + ************************************** + * + * Functional description + * Lookup relation by name. Name passed in is + * ASCIZ name. + * + **************************************/ + SET_TDBB(tdbb); + + auto* perm = lookupRelation(tdbb, name, flags); + if (!perm) + return nullptr; - return relation; + return MetadataCache::get(tdbb)->mdc_relations.getObject(tdbb, perm->getId(), flags); } -jrd_rel* MET_lookup_relation_id(thread_db* tdbb, SLONG id, bool return_deleted) +jrd_rel* MetadataCache::lookup_relation_id(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) { /************************************** * @@ -3088,87 +2539,26 @@ jrd_rel* MET_lookup_relation_id(thread_db* tdbb, SLONG id, bool return_deleted) * **************************************/ SET_TDBB(tdbb); - Attachment* const attachment = tdbb->getAttachment(); - // System relations are above suspicion + return MetadataCache::get(tdbb)->mdc_relations.getObject(tdbb, id, flags); +} - if (id < (int) rel_MAX) - { - fb_assert(id < MAX_USHORT); - return MET_relation(tdbb, (USHORT) id); - } - jrd_rel* check_relation = NULL; - jrd_rel* relation; - vec* vector = attachment->att_relations; - if (vector && (id < (SLONG) vector->count()) && (relation = (*vector)[id])) - { - if (relation->rel_flags & REL_deleting) - CheckoutLockGuard guard(tdbb, relation->rel_drop_mutex, FB_FUNCTION); +CharSetVers* MetadataCache::lookup_charset(thread_db* tdbb, CSetId id, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); - if (relation->rel_flags & REL_deleted) - return return_deleted ? relation : NULL; + return MetadataCache::get(tdbb)->mdc_charsets.getObject(tdbb, id, flags); +} - if (relation->rel_flags & REL_check_existence) - { - check_relation = relation; - LCK_lock(tdbb, check_relation->rel_existence_lock, LCK_SR, LCK_WAIT); - } - else - return relation; - } - // We need to look up the relation id in RDB$RELATIONS - - relation = NULL; - - AutoCacheRequest request(tdbb, irq_l_rel_id, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) - X IN RDB$RELATIONS WITH X.RDB$RELATION_ID EQ id - { - relation = MET_relation(tdbb, X.RDB$RELATION_ID); - if (relation->rel_name.length() == 0) { - relation->rel_name = X.RDB$RELATION_NAME; - } - - relation->rel_flags |= get_rel_flags_from_FLAGS(X.RDB$FLAGS); - - if (!X.RDB$RELATION_TYPE.NULL) - { - relation->rel_flags |= MET_get_rel_flags_from_TYPE(X.RDB$RELATION_TYPE); - } - } - END_FOR - - if (check_relation) - { - check_relation->rel_flags &= ~REL_check_existence; - if (check_relation != relation) - { - LCK_release(tdbb, check_relation->rel_existence_lock); - if (!(check_relation->rel_flags & REL_check_partners)) - { - check_relation->rel_flags |= REL_check_partners; - LCK_release(tdbb, check_relation->rel_partners_lock); - check_relation->rel_flags &= ~REL_check_partners; - } - LCK_release(tdbb, check_relation->rel_rescan_lock); - check_relation->rel_flags |= REL_deleted; - } - } - - return relation; -} - - -DmlNode* MET_parse_blob(thread_db* tdbb, - jrd_rel* relation, - bid* blob_id, - CompilerScratch** csb_ptr, - Statement** statementPtr, - const bool trigger, - bool validationExpr) +DmlNode* MET_parse_blob(thread_db* tdbb, + Cached::Relation* relation, + bid* blob_id, + CompilerScratch** csb_ptr, + Statement** statementPtr, + const bool trigger, + bool validationExpr) { /************************************** * @@ -3209,30 +2599,6 @@ DmlNode* MET_parse_blob(thread_db* tdbb, } -void MET_post_existence(thread_db* tdbb, jrd_rel* relation) -{ -/************************************** - * - * M E T _ p o s t _ e x i s t e n c e - * - ************************************** - * - * Functional description - * Post an interest in the existence of a relation. - * - **************************************/ - SET_TDBB(tdbb); - - relation->rel_use_count++; - - if (!MET_lookup_relation_id(tdbb, relation->rel_id, false)) - { - relation->rel_use_count--; - ERR_post(Arg::Gds(isc_relnotdef) << Arg::Str(relation->rel_name)); - } -} - - void MET_prepare(thread_db* tdbb, jrd_tra* transaction, USHORT length, const UCHAR* msg) { /************************************** @@ -3262,7 +2628,7 @@ void MET_prepare(thread_db* tdbb, jrd_tra* transaction, USHORT length, const UCH } -jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) +jrd_prc* MetadataCache::findProcedure(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) { /************************************** * @@ -3275,90 +2641,41 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) * **************************************/ SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); - Database* dbb = tdbb->getDatabase(); - - if (id >= attachment->att_procedures.getCount()) - attachment->att_procedures.resize(id + 10); - - jrd_prc* procedure = attachment->att_procedures[id]; - - if (procedure && !(procedure->flags & Routine::FLAG_OBSOLETE)) - { - // Make sure PRC_being_scanned and PRC_scanned are not set at the same time - - fb_assert(!(procedure->flags & Routine::FLAG_BEING_SCANNED) || - !(procedure->flags & Routine::FLAG_SCANNED)); - - /* To avoid scanning recursive procedure's blr recursively let's - make use of Routine::FLAG_BEING_SCANNED bit. Because this bit is set - later in the code, it is not set when we are here first time. - If (in case of rec. procedure) we get here second time it is - already set and we return half baked procedure. - In case of superserver this code is under the rec. mutex - protection, thus the only guy (thread) who can get here and see - FLAG_BEING_SCANNED bit set is the guy which started procedure scan - and currently holds the mutex. - In case of classic, there is always only one guy and if it - sees FLAG_BEING_SCANNED bit set it is safe to assume it is here - second time. - - If procedure has already been scanned - return. This condition - is for those threads that did not find procedure in cache and - came here to get it from disk. But only one was able to lock - the mutex and do the scanning, others were waiting. As soon as - the first thread releases the mutex another thread gets in and - it would be just unfair to make it do the work again. - */ - - if ((procedure->flags & Routine::FLAG_BEING_SCANNED) || - (procedure->flags & Routine::FLAG_SCANNED)) - { - return procedure; - } - } - - if (!procedure) - { - procedure = FB_NEW_POOL(*attachment->att_pool) jrd_prc(*attachment->att_pool); - } - try { - - procedure->flags |= (Routine::FLAG_BEING_SCANNED | flags); - procedure->flags &= ~(Routine::FLAG_OBSOLETE | Routine::FLAG_CLEARED); - - procedure->setId(id); - attachment->att_procedures[id] = procedure; + return MetadataCache::get(tdbb)->mdc_procedures.getObject(tdbb, id, flags); +} - if (!procedure->existenceLock) - { - Lock* lock = FB_NEW_RPT(*attachment->att_pool, 0) - Lock(tdbb, sizeof(SLONG), LCK_prc_exist, procedure, blocking_ast_procedure); - procedure->existenceLock = lock; - lock->setKey(procedure->getId()); - } +jrd_prc* jrd_prc::create(thread_db* tdbb, MemoryPool&, Cached::Procedure* perm) +{ + return FB_NEW_POOL(perm->getPool()) jrd_prc(perm); +} - LCK_lock(tdbb, procedure->existenceLock, LCK_SR, LCK_WAIT); +ScanResult jrd_prc::scan(thread_db* tdbb, ObjectBase::Flag) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetadataCache* mdc = dbb->dbb_mdc; + bool found = false; - if (!noscan) { AutoCacheRequest request(tdbb, irq_r_procedure, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) - P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ procedure->getId() + P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ getId() { - if (procedure->getName().toString().length() == 0) + found = true; + + if (getName().toString().length() == 0) { - procedure->setName(QualifiedName(P.RDB$PROCEDURE_NAME, - (P.RDB$PACKAGE_NAME.NULL ? "" : P.RDB$PACKAGE_NAME))); + getPermanent()->name = QualifiedName(P.RDB$PROCEDURE_NAME, + (P.RDB$PACKAGE_NAME.NULL ? "" : P.RDB$PACKAGE_NAME)); } - procedure->setId(P.RDB$PROCEDURE_ID); + MetaName secClass; TriState ssDefiner; if (!P.RDB$SECURITY_CLASS.NULL) - procedure->setSecurityName(P.RDB$SECURITY_CLASS); + secClass = P.RDB$SECURITY_CLASS; else if (!P.RDB$PACKAGE_NAME.NULL) { AutoCacheRequest requestHandle(tdbb, irq_l_procedure_pkg_class, IRQ_REQUESTS); @@ -3368,7 +2685,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) WITH PKG.RDB$PACKAGE_NAME EQ P.RDB$PACKAGE_NAME { if (!PKG.RDB$SECURITY_CLASS.NULL) - procedure->setSecurityName(PKG.RDB$SECURITY_CLASS); + secClass = PKG.RDB$SECURITY_CLASS; if (!PKG.RDB$SQL_SECURITY.NULL) ssDefiner = (bool) PKG.RDB$SQL_SECURITY; @@ -3376,6 +2693,9 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) END_FOR } + if (getPermanent()->securityName.length() == 0) + getPermanent()->securityName = secClass; + if (ssDefiner.isUnknown()) { if (!P.RDB$SQL_SECURITY.NULL) @@ -3384,16 +2704,17 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) ssDefiner = MET_get_ss_definer(tdbb); } - procedure->owner = P.RDB$OWNER_NAME; + if (getPermanent()->owner.length() == 0) + getPermanent()->owner = P.RDB$OWNER_NAME; if (ssDefiner.asBool()) - procedure->invoker = attachment->getUserId(procedure->owner); + invoker = attachment->getUserId(getPermanent()->owner); - procedure->setImplemented(true); - procedure->setDefined(true); - procedure->getInputFields().resize(P.RDB$PROCEDURE_INPUTS); - procedure->getOutputFields().resize(P.RDB$PROCEDURE_OUTPUTS); - procedure->setDefaultCount(0); + setImplemented(true); + setDefined(true); + getInputFields().resize(P.RDB$PROCEDURE_INPUTS); + getOutputFields().resize(P.RDB$PROCEDURE_OUTPUTS); + setDefaultCount(0); AutoCacheRequest request2(tdbb, irq_r_params, IRQ_REQUESTS); @@ -3407,15 +2728,15 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) PA.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') { const SSHORT pa_collation_id_null = PA.RDB$COLLATION_ID.NULL; - const SSHORT pa_collation_id = PA.RDB$COLLATION_ID; + const CollId pa_collation_id(PA.RDB$COLLATION_ID); const SSHORT pa_default_value_null = PA.RDB$DEFAULT_VALUE.NULL; bid pa_default_value = pa_default_value_null ? F.RDB$DEFAULT_VALUE : PA.RDB$DEFAULT_VALUE; Array >& paramArray = PA.RDB$PARAMETER_TYPE ? - procedure->getOutputFields() : procedure->getInputFields(); + getOutputFields() : getInputFields(); // should be error if field already exists - Parameter* parameter = FB_NEW_POOL(*attachment->att_pool) Parameter(*attachment->att_pool); + Parameter* parameter = FB_NEW_POOL(mdc->getPool()) Parameter(mdc->getPool()); parameter->prm_number = PA.RDB$PARAMETER_NUMBER; paramArray[parameter->prm_number] = parameter; parameter->prm_name = PA.RDB$PARAMETER_NAME; @@ -3428,8 +2749,11 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) DSC_make_descriptor(¶meter->prm_desc, F.RDB$FIELD_TYPE, F.RDB$FIELD_SCALE, F.RDB$FIELD_LENGTH, - F.RDB$FIELD_SUB_TYPE, F.RDB$CHARACTER_SET_ID, - (pa_collation_id_null ? F.RDB$COLLATION_ID : pa_collation_id)); + F.RDB$FIELD_SUB_TYPE, CSetId(F.RDB$CHARACTER_SET_ID), + (pa_collation_id_null ? CollId(F.RDB$COLLATION_ID) : pa_collation_id)); + + if (F.RDB$FIELD_TYPE == blr_blob) + parameter->prm_seg_length = F.RDB$SEGMENT_LENGTH; if (parameter->prm_desc.isText() && parameter->prm_desc.getTextType() != CS_NONE) { @@ -3447,21 +2771,21 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) (!pa_default_value_null || (fb_utils::implicit_domain(F.RDB$FIELD_NAME) && !F.RDB$DEFAULT_VALUE.NULL))) { - procedure->setDefaultCount(procedure->getDefaultCount() + 1); - MemoryPool* pool = attachment->createPool(); + setDefaultCount(getDefaultCount() + 1); + MemoryPool* pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, pool); try { parameter->prm_default_value = static_cast( - MET_parse_blob(tdbb, NULL, &pa_default_value, NULL, NULL, false, false)); + MET_parse_blob(tdbb, nullptr, &pa_default_value, NULL, NULL, false, false)); } catch (const Exception&) { // Here we lose pools created for previous defaults. // Probably we should use common pool for defaults and procedure itself. - attachment->deletePool(pool); + dbb->deletePool(pool); throw; } } @@ -3470,13 +2794,12 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) const bool external = !P.RDB$ENGINE_NAME.NULL; // ODS_12_0 - Array >& paramArray = procedure->getOutputFields(); + Array >& paramArray = getOutputFields(); if (paramArray.hasData() && paramArray[0]) { - Format* format = Format::newFormat( - *attachment->att_pool, procedure->getOutputFields().getCount()); - procedure->prc_record_format = format; + Format* format = Format::newFormat(mdc->getPool(), getOutputFields().getCount()); + prc_record_format = format; ULONG length = FLAG_BYTES(format->fmt_count); Format::fmt_desc_iterator desc = format->fmt_desc.begin(); Array >::iterator ptr, end; @@ -3498,12 +2821,12 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) format->fmt_length = length; } - procedure->prc_type = P.RDB$PROCEDURE_TYPE.NULL ? + prc_type = P.RDB$PROCEDURE_TYPE.NULL ? prc_legacy : (prc_t) P.RDB$PROCEDURE_TYPE; if (external || !P.RDB$PROCEDURE_BLR.NULL) { - MemoryPool* const csb_pool = attachment->createPool(); + MemoryPool* const csb_pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); try @@ -3525,24 +2848,24 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) else body.getBuffer(1)[0] = '\0'; - dbb->dbb_extManager->makeProcedure(tdbb, csb, procedure, P.RDB$ENGINE_NAME, + dbb->dbb_extManager->makeProcedure(tdbb, csb, this, P.RDB$ENGINE_NAME, (P.RDB$ENTRYPOINT.NULL ? "" : P.RDB$ENTRYPOINT), body.begin()); - if (!procedure->getExternal()) - procedure->setDefined(false); + if (!getExternal()) + setDefined(false); } else { - const string name = procedure->getName().toString(); + const string name = getName().toString(); try { TraceProcCompile trace(tdbb, name); - procedure->parseBlr(tdbb, csb, &P.RDB$PROCEDURE_BLR, + parseBlr(tdbb, csb, &P.RDB$PROCEDURE_BLR, P.RDB$DEBUG_INFO.NULL ? NULL : &P.RDB$DEBUG_INFO); - trace.finish(procedure->getStatement(), ITracePlugin::RESULT_SUCCESS); + trace.finish(getStatement(), ITracePlugin::RESULT_SUCCESS); } catch (const Exception& ex) { @@ -3555,33 +2878,31 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) } catch (const Exception&) { - if (procedure->getStatement()) - procedure->releaseStatement(tdbb); + if (getStatement()) + releaseStatement(tdbb); else - attachment->deletePool(csb_pool); + dbb->deletePool(csb_pool); throw; } - fb_assert(!procedure->isDefined() || procedure->getStatement()->procedure == procedure); + fb_assert(!isDefined() || getStatement()->procedure == this); } else { RefPtr inputMetadata(REF_NO_INCR, - Routine::createMetadata(procedure->getInputFields(), false)); - procedure->setInputFormat( - Routine::createFormat(procedure->getPool(), inputMetadata, false)); + Routine::createMetadata(getInputFields(), false)); + setInputFormat( + Routine::createFormat(getPermanent()->getPool(), inputMetadata, false)); RefPtr outputMetadata(REF_NO_INCR, - Routine::createMetadata(procedure->getOutputFields(), false)); - procedure->setOutputFormat( - Routine::createFormat(procedure->getPool(), outputMetadata, true)); + Routine::createMetadata(getOutputFields(), false)); + setOutputFormat( + Routine::createFormat(getPermanent()->getPool(), outputMetadata, true)); - procedure->setImplemented(false); + setImplemented(false); } - procedure->flags |= Routine::FLAG_SCANNED; - if (!dbb->readOnly() && !P.RDB$PROCEDURE_BLR.NULL && !P.RDB$VALID_BLR.NULL && P.RDB$VALID_BLR == FALSE) @@ -3596,40 +2917,20 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) } } END_FOR - } // if !noscan - - // Make sure that it is really being scanned - fb_assert(procedure->flags & Routine::FLAG_BEING_SCANNED); - - procedure->flags &= ~Routine::FLAG_BEING_SCANNED; - - } // try - catch (const Exception&) - { - procedure->flags &= ~(Routine::FLAG_BEING_SCANNED | Routine::FLAG_SCANNED); - - if (procedure->getExternal()) - { - delete procedure->getExternal(); - procedure->setExternal(NULL); - } - - if (procedure->existenceLock) - { - LCK_release(tdbb, procedure->existenceLock); - delete procedure->existenceLock; - procedure->existenceLock = NULL; - } - - throw; } - return procedure; + return found ? (this->flReload ? ScanResult::REPEAT : ScanResult::COMPLETE) : ScanResult::MISS; } -bool jrd_prc::reload(thread_db* tdbb) +void jrd_prc::checkReload(thread_db* tdbb) const { - fb_assert(this->flags & Routine::FLAG_RELOAD); + if (flReload) + getPermanent()->reload(tdbb, 0); +} + +ScanResult jrd_prc::reload(thread_db* tdbb, ObjectBase::Flag /*unused*/) +{ + fb_assert(flReload); Attachment* attachment = tdbb->getAttachment(); AutoCacheRequest request(tdbb, irq_r_proc_blr, IRQ_REQUESTS); @@ -3637,7 +2938,7 @@ bool jrd_prc::reload(thread_db* tdbb) FOR(REQUEST_HANDLE request) P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ this->getId() { - MemoryPool* const csb_pool = attachment->createPool(); + MemoryPool* const csb_pool = tdbb->getDatabase()->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); try @@ -3649,8 +2950,7 @@ bool jrd_prc::reload(thread_db* tdbb) this->parseBlr(tdbb, csb, &P.RDB$PROCEDURE_BLR, P.RDB$DEBUG_INFO.NULL ? NULL : &P.RDB$DEBUG_INFO); - // parseBlr() above could set FLAG_RELOAD again - return !(this->flags & Routine::FLAG_RELOAD); + return flReload ? ScanResult::REPEAT : ScanResult::COMPLETE; } catch (const Exception& ex) { @@ -3664,107 +2964,13 @@ bool jrd_prc::reload(thread_db* tdbb) } catch (const Exception&) { - attachment->deletePool(csb_pool); + tdbb->getDatabase()->deletePool(csb_pool); throw; } } END_FOR - return false; -} - -jrd_rel* MET_relation(thread_db* tdbb, USHORT id) -{ -/************************************** - * - * M E T _ r e l a t i o n - * - ************************************** - * - * Functional description - * Find or create a relation block for a given relation id. - * - **************************************/ - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - CHECK_DBB(dbb); - - Attachment* attachment = tdbb->getAttachment(); - vec* vector = attachment->att_relations; - MemoryPool* pool = attachment->att_pool; - - if (!vector) - vector = attachment->att_relations = vec::newVector(*pool, id + 10); - else if (id >= vector->count()) - vector->resize(id + 10); - - jrd_rel* relation = (*vector)[id]; - if (relation) - return relation; - - relation = FB_NEW_POOL(*pool) jrd_rel(*pool); - (*vector)[id] = relation; - relation->rel_id = id; - - { // Scope block. - Lock* lock = FB_NEW_RPT(*pool, 0) - Lock(tdbb, sizeof(SLONG), LCK_rel_partners, relation, partners_ast_relation); - relation->rel_partners_lock = lock; - lock->setKey(relation->rel_id); - } - - { // Scope block. - Lock* lock = FB_NEW_RPT(*pool, 0) - Lock(tdbb, sizeof(SLONG), LCK_rel_rescan, relation, rescan_ast_relation); - relation->rel_rescan_lock = lock; - lock->setKey(relation->rel_id); - } - - if (relation->rel_id < rel_MAX) - return relation; - - { // Scope block. - Lock* lock = FB_NEW_RPT(*pool, 0) - Lock(tdbb, sizeof(SLONG), LCK_rel_exist, relation, blocking_ast_relation); - relation->rel_existence_lock = lock; - lock->setKey(relation->rel_id); - } - - relation->rel_flags |= (REL_check_existence | REL_check_partners); - return relation; -} - - -void MET_release_existence(thread_db* tdbb, jrd_rel* relation) -{ -/************************************** - * - * M E T _ r e l e a s e _ e x i s t e n c e - * - ************************************** - * - * Functional description - * Release interest in relation. If no remaining interest - * and we're blocking the drop of the relation then release - * existence lock and mark deleted. - * - **************************************/ - if (!relation->rel_use_count) - return; - - --relation->rel_use_count; - - if (!relation->rel_use_count) - { - if (relation->rel_flags & REL_blocking) - LCK_re_post(tdbb, relation->rel_existence_lock); - - // release trigger requests - relation->releaseTriggers(tdbb, false); - - // close external file - EXT_fini(relation, true); - } + return ScanResult::MISS; } @@ -3819,7 +3025,7 @@ void MET_revoke(thread_db* tdbb, jrd_tra* transaction, const MetaName& relation, } -void MET_scan_partners(thread_db* tdbb, jrd_rel* relation) +void MET_scan_partners(thread_db* tdbb, RelationPermanent* relation) { /************************************** * @@ -3836,11 +3042,11 @@ void MET_scan_partners(thread_db* tdbb, jrd_rel* relation) SET_TDBB(tdbb); if (relation->rel_flags & REL_check_partners) - scan_partners(tdbb, relation); + relation->scan_partners(tdbb); } -void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) +ScanResult jrd_rel::scan(thread_db* tdbb, ObjectBase::Flag flags) { /************************************** * @@ -3854,54 +3060,101 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) * **************************************/ SET_TDBB(tdbb); - TrigVector* triggers[TRIGGER_MAX]; Attachment* attachment = tdbb->getAttachment(); Database* dbb = tdbb->getDatabase(); - Jrd::ContextPoolHolder context(tdbb, attachment->att_pool); - bool dependencies = false; - + Jrd::ContextPoolHolder context(tdbb, dbb->dbb_permanent); blb* blob = NULL; + bool found = false; - jrd_tra* depTrans = tdbb->getTransaction() ? + jrd_tra* trans = tdbb->getTransaction() ? tdbb->getTransaction() : attachment->getSysTransaction(); - // If anything errors, catch it to reset the scan flag. This will - // make sure that the error will be caught if the operation is tried again. + if (flags & CacheFlag::NOCOMMIT) + { + // New version of relation is created currently. + // Perform only very basic scan - may be more changes to come. + // scan() will be called automatically w/o NOCOMMIT + // on any attempt to use modified relation + // or on transaction commit. - try { + static const CachedRequestId requestCacheId; + AutoCacheRequest request(tdbb, requestCacheId); - if (relation->rel_flags & (REL_scanned | REL_deleted)) - return; + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE trans) + REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ getId() + { + rel_perm->rel_flags |= REL_format; + if (getName().isEmpty()) + { + rel_perm->rel_name = REL.RDB$RELATION_NAME; + rel_perm->rel_flags |= get_rel_flags_from_FLAGS(REL.RDB$FLAGS); + if (!REL.RDB$RELATION_TYPE.NULL) + rel_perm->rel_flags |= MET_get_rel_flags_from_TYPE(REL.RDB$RELATION_TYPE); + } + } + END_FOR - relation->rel_flags |= REL_being_scanned; - dependencies = (relation->rel_flags & REL_get_dependencies) ? true : false; - relation->rel_flags &= ~REL_get_dependencies; + fb_assert(rel_perm->rel_flags & REL_format); - for (USHORT itr = 0; itr < TRIGGER_MAX; ++itr) - triggers[itr] = NULL; + // Next time reload() will be called + return ScanResult::REPEAT; + } + + bool dependencies = (rel_perm->rel_flags & REL_get_dependencies) ? true : false; + + // If anything errors, cleanup to reset the flag. + // This will ensure that the error will be caught if the operation is tried again. + Cleanup onError([&] { + if (dependencies) + rel_perm->rel_flags |= REL_get_dependencies; + + if (blob) + blob->BLB_close(tdbb); + }); + + rel_perm->rel_flags &= ~REL_get_dependencies; + + // Do we need new format version? + if (rel_perm->rel_flags & REL_format) + { + RelationNode::makeVersion(tdbb, trans, rel_perm->getName()); + rel_perm->rel_flags &= ~REL_format; + } // Since this can be called recursively, find an inactive clone of the request AutoCacheRequest request(tdbb, irq_r_fields, IRQ_REQUESTS); CompilerScratch* csb = NULL; - FOR(REQUEST_HANDLE request) - REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ relation->rel_id + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE trans) + REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ getId() { + found = true; + + if (getName().isEmpty()) + rel_perm->rel_name = REL.RDB$RELATION_NAME; + + rel_perm->rel_flags |= get_rel_flags_from_FLAGS(REL.RDB$FLAGS); + if (!REL.RDB$RELATION_TYPE.NULL) + rel_perm->rel_flags |= MET_get_rel_flags_from_TYPE(REL.RDB$RELATION_TYPE); + // Pick up relation level stuff - relation->rel_current_fmt = REL.RDB$FORMAT; - vec* vector = relation->rel_fields = - vec::newVector(*relation->rel_pool, relation->rel_fields, REL.RDB$FIELD_ID + 1); - if (!REL.RDB$SECURITY_CLASS.NULL) - relation->rel_security_name = REL.RDB$SECURITY_CLASS; + rel_dbkey_length = REL.RDB$DBKEY_LENGTH; + fb_assert(rel_dbkey_length % 8 == 0); + if (!rel_dbkey_length) + rel_dbkey_length = 8; + + rel_current_fmt = REL.RDB$FORMAT; + rel_fields = vec::newVector(*rel_pool, rel_fields, REL.RDB$FIELD_ID); + if (rel_perm->rel_security_name.isEmpty() && !REL.RDB$SECURITY_CLASS.NULL) + rel_perm->rel_security_name = REL.RDB$SECURITY_CLASS; - relation->rel_name = REL.RDB$RELATION_NAME; - relation->rel_owner_name = REL.RDB$OWNER_NAME; + rel_perm->rel_owner_name = REL.RDB$OWNER_NAME; if (!REL.RDB$SQL_SECURITY.NULL) - relation->rel_ss_definer = (bool) REL.RDB$SQL_SECURITY; + rel_ss_definer = (bool) REL.RDB$SQL_SECURITY; else - relation->rel_ss_definer = MET_get_ss_definer(tdbb); + rel_ss_definer = MET_get_ss_definer(tdbb); if (!REL.RDB$VIEW_BLR.isEmpty()) { @@ -3912,30 +3165,29 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) if (dependencies) { const MetaName depName(REL.RDB$RELATION_NAME); - rseNode = MET_get_dependencies(tdbb, relation, NULL, 0, NULL, &REL.RDB$VIEW_BLR, - NULL, &csb, depName, obj_view, 0, depTrans); + rseNode = MET_get_dependencies(tdbb, getPermanent(), NULL, 0, NULL, &REL.RDB$VIEW_BLR, + NULL, &csb, depName, obj_view, 0, trans); } else - rseNode = MET_parse_blob(tdbb, relation, &REL.RDB$VIEW_BLR, &csb, NULL, false, false); + rseNode = MET_parse_blob(tdbb, rel_perm, &REL.RDB$VIEW_BLR, &csb, NULL, false, false); if (rseNode) { fb_assert(rseNode->getKind() == DmlNode::KIND_REC_SOURCE); - relation->rel_view_rse = nodeAs(static_cast(rseNode)); - fb_assert(relation->rel_view_rse); + rel_view_rse = nodeAs(static_cast(rseNode)); + fb_assert(rel_view_rse); } else - relation->rel_view_rse = NULL; + rel_view_rse = NULL; // retrieve the view context names - lookup_view_contexts(tdbb, relation); + lookup_view_contexts(tdbb, this); } - relation->rel_flags |= REL_scanned; - if (REL.RDB$EXTERNAL_FILE[0]) + if (REL.RDB$EXTERNAL_FILE[0] && !rel_perm->getExtFile()) { - EXT_file(relation, REL.RDB$EXTERNAL_FILE); //, &REL.RDB$EXTERNAL_DESCRIPTION); + rel_perm->setExtFile(ExternalFile::create(getPool(), REL.RDB$EXTERNAL_FILE)); } if (!REL.RDB$RELATION_TYPE.NULL) @@ -3945,24 +3197,24 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) case rel_persistent: break; case rel_external: - fb_assert(relation->rel_file); + fb_assert(getExtFile()); break; case rel_view: - fb_assert(relation->rel_view_rse); - fb_assert(relation->rel_flags & REL_jrd_view); - relation->rel_flags |= REL_jrd_view; + fb_assert(rel_view_rse); + fb_assert(rel_perm->rel_flags & REL_jrd_view); + rel_perm->rel_flags |= REL_jrd_view; break; case rel_virtual: - fb_assert(relation->rel_flags & REL_virtual); - relation->rel_flags |= REL_virtual; + fb_assert(rel_perm->rel_flags & REL_virtual); + rel_perm->rel_flags |= REL_virtual; break; case rel_global_temp_preserve: - fb_assert(relation->rel_flags & REL_temp_conn); - relation->rel_flags |= REL_temp_conn; + fb_assert(rel_perm->rel_flags & REL_temp_conn); + rel_perm->rel_flags |= REL_temp_conn; break; case rel_global_temp_delete: - fb_assert(relation->rel_flags & REL_temp_tran); - relation->rel_flags |= REL_temp_tran; + fb_assert(rel_perm->rel_flags & REL_temp_tran); + rel_perm->rel_flags |= REL_temp_tran; break; default: fb_assert(false); @@ -4004,7 +3256,7 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) field->fld_security_name = REL.RDB$DEFAULT_CLASS; } field_id = n; - field = (*vector)[field_id]; + field = (*rel_fields)[field_id]; if (field) { @@ -4027,19 +3279,17 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) if (field->fld_name == reinterpret_cast(p)) break; - - field->fld_name = reinterpret_cast(p); } else { - field = FB_NEW_POOL(*relation->rel_pool) jrd_fld(*relation->rel_pool); - (*vector)[field_id] = field; - field->fld_name = reinterpret_cast(p); + field = FB_NEW_POOL(*rel_pool) jrd_fld(*rel_pool); + (*rel_fields)[field_id] = field; } + field->fld_name = reinterpret_cast(p); // CVC: Be paranoid and allow the possible trigger(s) to have a // not null security class to work on, even if we only take it - // from the relation itself. + // from the this itself. if (field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL) { field->fld_security_name = REL.RDB$DEFAULT_CLASS; @@ -4047,6 +3297,10 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) break; + case RSR_field_source: + field->fld_source_name = reinterpret_cast(p); + break; + case RSR_view_context: view_context = n; break; @@ -4057,13 +3311,13 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) csb->csb_g_flags |= csb_get_dependencies; field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p); const MetaName depName(REL.RDB$RELATION_NAME); - MET_store_dependencies(tdbb, csb->csb_dependencies, 0, depName, obj_view, depTrans); + MET_store_dependencies(tdbb, csb->csb_dependencies, nullptr, depName, obj_view, trans); } else field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p); { // scope - const ViewContexts& ctx = relation->rel_view_contexts; + const ViewContexts& ctx = rel_view_contexts; FB_SIZE_T pos; if (ctx.find(view_context, pos) && @@ -4080,9 +3334,9 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) AutoSetRestoreFlag flag(&field->fld_flags, FLD_parse_computed, true); DmlNode* nod = dependencies ? - MET_get_dependencies(tdbb, relation, p, length, csb, NULL, NULL, NULL, - field->fld_name, obj_computed, csb_computed_field, depTrans) : - PAR_blr(tdbb, relation, p, length, csb, NULL, NULL, false, csb_computed_field); + MET_get_dependencies(tdbb, getPermanent(), p, length, csb, NULL, NULL, NULL, + field->fld_name, obj_computed, csb_computed_field, trans) : + PAR_blr(tdbb, rel_perm, p, length, csb, NULL, NULL, false, csb_computed_field); field->fld_computation = static_cast(nod); } @@ -4090,12 +3344,12 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) case RSR_missing_value: field->fld_missing_value = static_cast( - PAR_blr(tdbb, relation, p, length, csb, NULL, NULL, false, 0)); + PAR_blr(tdbb, rel_perm, p, length, csb, NULL, NULL, false, 0)); break; case RSR_default_value: field->fld_default_value = static_cast( - PAR_blr(tdbb, relation, p, length, csb, NULL, NULL, false, 0)); + PAR_blr(tdbb, rel_perm, p, length, csb, NULL, NULL, false, 0)); break; case RSR_validation_blr: @@ -4107,13 +3361,13 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) // Because a VIEW can't have a validation section i ignored the whole call. if (!csb) { - field->fld_validation = PAR_validation_blr(tdbb, relation, p, length, csb, + field->fld_validation = PAR_validation_blr(tdbb, rel_perm, p, length, csb, NULL, csb_validation); } break; case RSR_field_not_null: - field->fld_not_null = PAR_validation_blr(tdbb, relation, p, length, csb, + field->fld_not_null = PAR_validation_blr(tdbb, rel_perm, p, length, csb, NULL, csb_validation); break; @@ -4122,11 +3376,11 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) break; case RSR_trigger_name: - MET_load_trigger(tdbb, relation, (const TEXT*) p, triggers); + MET_load_trigger(tdbb, this, (const TEXT*) p, [&](int t)->Triggers& {return rel_triggers[t];}); break; case RSR_dimensions: - field->fld_array = array = FB_NEW_RPT(*relation->rel_pool, n) ArrayField(); + field->fld_array = array = FB_NEW_RPT(*rel_pool, n) ArrayField(); array->arr_desc.iad_dimensions = n; break; @@ -4145,12 +3399,28 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) field->fld_identity_type = static_cast(n); break; + case RSR_field_length: + field->fld_length = n; + break; + + case RSR_segment_length: + field->fld_segment_length = n; + break; + + case RSR_field_pos: + field->fld_pos = n; + break; + + case RSR_character_length: + field->fld_character_length = n; + break; + default: // Shut up compiler warning break; } } blob->BLB_close(tdbb); - blob = 0; + blob = nullptr; if (field && field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL) { @@ -4161,27 +3431,15 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) delete csb; - // We have just loaded the triggers onto the local vector triggers. - // It's now time to place them at their rightful place inside the relation block. - relation->replaceTriggers(tdbb, triggers); - - LCK_lock(tdbb, relation->rel_rescan_lock, LCK_SR, LCK_WAIT); - relation->rel_flags &= ~REL_being_scanned; + rel_current_format = MET_format(tdbb, rel_perm, rel_current_fmt); + dependencies = false; - relation->rel_current_format = NULL; + if (rel_fields) + rel_fields->trimNulls(); - } // try - catch (const Exception&) - { - relation->rel_flags &= ~(REL_being_scanned | REL_scanned); - if (dependencies) { - relation->rel_flags |= REL_get_dependencies; - } - if (blob) - blob->BLB_close(tdbb); + LCK_lock(tdbb, rel_perm->rel_rescan_lock, LCK_SR, LCK_WAIT); - throw; - } + return found ? ScanResult::COMPLETE : ScanResult::MISS; } @@ -4316,10 +3574,11 @@ static int blocking_ast_dsql_cache(void* ast_object) } -static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, sym_type type, const QualifiedName& name) +DSqlCacheItem* MetadataCache::get_dsql_cache_item(thread_db* tdbb, sym_type type, const QualifiedName& name) { Database* dbb = tdbb->getDatabase(); Attachment* attachment = tdbb->getAttachment(); + MetadataCache* mdc = MetadataCache::get(tdbb); fb_assert((int) type <= MAX_UCHAR); UCHAR ucharType = (UCHAR) type; @@ -4347,7 +3606,7 @@ static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, sym_type type, const if (item) { item->key = key; - item->lock = FB_NEW_RPT(*attachment->att_pool, key.length()) + item->lock = FB_NEW_RPT(mdc->getPool(), key.length()) Lock(tdbb, key.length(), LCK_dsql_cache, item, blocking_ast_dsql_cache); memcpy(item->lock->getKeyPtr(), key.c_str(), key.length()); } @@ -4358,77 +3617,20 @@ static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, sym_type type, const } -static int blocking_ast_procedure(void* ast_object) +int RelationPermanent::partners_ast_relation(void* ast_object) { -/************************************** - * - * b l o c k i n g _ a s t _ p r o c e d u r e - * - ************************************** - * - * Functional description - * Someone is trying to drop a proceedure. If there - * are outstanding interests in the existence of - * the relation then just mark as blocking and return. - * Otherwise, mark the procedure block as questionable - * and release the procedure existence lock. - * - **************************************/ - jrd_prc* const procedure = static_cast(ast_object); + auto* const relation = static_cast(ast_object); try { - if (procedure->existenceLock) - { - Database* const dbb = procedure->existenceLock->lck_dbb; - - AsyncContextHolder tdbb(dbb, FB_FUNCTION, procedure->existenceLock); - - LCK_release(tdbb, procedure->existenceLock); - } - procedure->flags |= Routine::FLAG_OBSOLETE; - } - catch (const Exception&) - {} // no-op - - return 0; -} - + Database* const dbb = relation->rel_partners_lock->lck_dbb; -static int blocking_ast_relation(void* ast_object) -{ -/************************************** - * - * b l o c k i n g _ a s t _ r e l a t i o n - * - ************************************** - * - * Functional description - * Someone is trying to drop a relation. If there - * are outstanding interests in the existence of - * the relation then just mark as blocking and return. - * Otherwise, mark the relation block as questionable - * and release the relation existence lock. - * - **************************************/ - jrd_rel* const relation = static_cast(ast_object); + AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_partners_lock); - try - { - if (relation->rel_existence_lock) + if (!(relation->rel_flags & REL_check_partners)) { - Database* const dbb = relation->rel_existence_lock->lck_dbb; - - AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_existence_lock); - - if (relation->rel_use_count) - relation->rel_flags |= REL_blocking; - else if (!(relation->rel_flags & REL_deleting)) - { - relation->rel_flags &= ~REL_blocking; - relation->rel_flags |= REL_check_existence; - LCK_release(tdbb, relation->rel_existence_lock); - } + relation->rel_flags |= REL_check_partners; + LCK_release(tdbb, relation->rel_partners_lock); } } catch (const Exception&) @@ -4438,41 +3640,38 @@ static int blocking_ast_relation(void* ast_object) } -static int partners_ast_relation(void* ast_object) +int RelationPermanent::rescan_ast_relation(void* ast_object) { - jrd_rel* const relation = static_cast(ast_object); + auto* const relation = static_cast(ast_object); try { - Database* const dbb = relation->rel_partners_lock->lck_dbb; + Database* const dbb = relation->rel_rescan_lock->lck_dbb; - AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_partners_lock); + AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_rescan_lock); - if (!(relation->rel_flags & REL_check_partners)) - { - relation->rel_flags |= REL_check_partners; - LCK_release(tdbb, relation->rel_partners_lock); - } + relation->resetDependentObject(tdbb, ElementBase::ResetType::Mark); + LCK_release(tdbb, relation->rel_rescan_lock); } - catch (const Exception&) + catch (const Firebird::Exception&) {} // no-op return 0; } -static int rescan_ast_relation(void* ast_object) +int RelationPermanent::blocking_ast_relation(void* ast_object) { - jrd_rel* const relation = static_cast(ast_object); + auto* const relation = static_cast(ast_object); try { - Database* const dbb = relation->rel_rescan_lock->lck_dbb; + Database* const dbb = relation->rel_existence_lock->lck_dbb; - AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_rescan_lock); + AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_existence_lock); - LCK_release(tdbb, relation->rel_rescan_lock); - relation->rel_flags &= ~REL_scanned; +// ??????????????????????????????? cacheElement->resetDependentObject(tdbb, ElementBase::ResetType::Drop); + LCK_release(tdbb, relation->rel_existence_lock); } catch (const Firebird::Exception&) {} // no-op @@ -4544,9 +3743,8 @@ ULONG MET_get_rel_flags_from_TYPE(USHORT type) static void get_trigger(thread_db* tdbb, jrd_rel* relation, - bid* blob_id, bid* debug_blob_id, TrigVector** ptr, - const TEXT* name, FB_UINT64 type, - bool sys_trigger, USHORT flags, + bid* blob_id, bid* debug_blob_id, Triggers& triggers, + const TEXT* name, FB_UINT64 type, USHORT flags, const MetaName& engine, const string& entryPoint, const bid* body, TriState ssDefiner) { @@ -4575,14 +3773,14 @@ static void get_trigger(thread_db* tdbb, jrd_rel* relation, if (!debug_blob_id->isEmpty()) debugInfoBlob = blb::open(tdbb, attachment->getSysTransaction(), debug_blob_id); - save_trigger_data(tdbb, ptr, relation, NULL, blrBlob, debugInfoBlob, - name, type, sys_trigger, flags, engine, entryPoint, body, ssDefiner); + save_trigger_data(tdbb, triggers, relation, NULL, blrBlob, debugInfoBlob, + name, type, flags, engine, entryPoint, body, ssDefiner); } - +/* !!!!!!!!!!!!!!!!!!!! static bool get_type(thread_db* tdbb, USHORT* id, const UCHAR* name, const TEXT* field) { -/************************************** + ************************************** * * g e t _ t y p e * @@ -4595,7 +3793,7 @@ static bool get_type(thread_db* tdbb, USHORT* id, const UCHAR* name, const TEXT* * * Return (1) if found, (0) otherwise. * - **************************************/ + ************************************** UCHAR buffer[MAX_SQL_IDENTIFIER_SIZE]; // BASED ON RDB$TYPE_NAME SET_TDBB(tdbb); @@ -4631,7 +3829,7 @@ static bool get_type(thread_db* tdbb, USHORT* id, const UCHAR* name, const TEXT* return found; } - +*/ static void lookup_view_contexts( thread_db* tdbb, jrd_rel* view) { @@ -4653,7 +3851,7 @@ static void lookup_view_contexts( thread_db* tdbb, jrd_rel* view) FOR(REQUEST_HANDLE request) V IN RDB$VIEW_RELATIONS WITH - V.RDB$VIEW_NAME EQ view->rel_name.c_str() + V.RDB$VIEW_NAME EQ view->c_name() SORTED BY V.RDB$VIEW_CONTEXT { // trim trailing spaces @@ -4712,7 +3910,7 @@ static ValueExprNode* parse_field_default_blr(thread_db* tdbb, bid* blob_id) length = blob->BLB_get_data(tdbb, temp.getBuffer(length), length); - DmlNode* const node = PAR_blr(tdbb, NULL, temp.begin(), length, NULL, &csb, NULL, false, 0); + DmlNode* const node = PAR_blr(tdbb, nullptr, temp.begin(), length, NULL, &csb, NULL, false, 0); return static_cast(node); } @@ -4735,87 +3933,12 @@ static BoolExprNode* parse_field_validation_blr(thread_db* tdbb, bid* blob_id, c length = blob->BLB_get_data(tdbb, temp.getBuffer(length), length); - return PAR_validation_blr(tdbb, NULL, temp.begin(), length, NULL, &csb, 0); + return PAR_validation_blr(tdbb, nullptr, temp.begin(), length, NULL, &csb, 0); } -void MET_release_trigger(thread_db* tdbb, TrigVector** vector_ptr, const MetaName& name) -{ -/*********************************************** - * - * M E T _ r e l e a s e _ t r i g g e r - * - *********************************************** - * - * Functional description - * Release a specified trigger. - * If trigger are still active let someone - * else do the work. - * - **************************************/ - if (!*vector_ptr) - return; - - TrigVector& vector = **vector_ptr; - - SET_TDBB(tdbb); - - for (FB_SIZE_T i = 0; i < vector.getCount(); ++i) - { - if (vector[i].name == name) - { - Statement* stmt = vector[i].statement; - if (stmt) - { - if (stmt->isActive()) - break; - stmt->release(tdbb); - } - vector.remove(i); - break; - } - } -} - - -void MET_release_triggers(thread_db* tdbb, TrigVector** vector_ptr, bool destroy) -{ -/*********************************************** - * - * 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; - - if (!vector) - return; - - if (!destroy) - { - vector->decompile(tdbb); - return; - } - - *vector_ptr = NULL; - - if (vector->hasActive()) - return; - - vector->release(tdbb); -} - - -static bool resolve_charset_and_collation(thread_db* tdbb, - USHORT* id, - const UCHAR* charset, - const UCHAR* collation) +bool MetadataCache::resolve_charset_and_collation(thread_db* tdbb, TTypeId* id, + const UCHAR* charset, const UCHAR* collation) { /************************************** * @@ -4851,7 +3974,7 @@ static bool resolve_charset_and_collation(thread_db* tdbb, * character set. * **************************************/ - bool found = false; + CharSetVers* csVer = nullptr; SET_TDBB(tdbb); Attachment* attachment = tdbb->getAttachment(); @@ -4860,38 +3983,55 @@ static bool resolve_charset_and_collation(thread_db* tdbb, AutoRequest handle; - if (!collation) - { - if (charset == NULL) - charset = (const UCHAR*) DEFAULT_CHARACTER_SET_NAME; + if ((!collation) && (!charset)) + charset = (const UCHAR*) DEFAULT_CHARACTER_SET_NAME; - if (attachment->att_charset_ids.get((const TEXT*) charset, *id)) - return true; + if (charset) + { + MetaName name = (const char*) charset; + auto* cs = mdc_charsets.lookup(tdbb, + [name](CharSetContainer* csc) + { + return csc->names.exist(name); + }, + 0 + ); - USHORT charset_id = 0; - if (get_type(tdbb, &charset_id, charset, "RDB$CHARACTER_SET_NAME")) + if (!cs) { - attachment->att_charset_ids.put((const TEXT*) charset, charset_id); - *id = charset_id; - return true; - } + FOR(REQUEST_HANDLE handle) + FIRST 1 CS IN RDB$CHARACTER_SETS + CROSS T IN RDB$TYPES + WITH T.RDB$TYPE_NAME EQ charset + AND T.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME" + AND T.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID + { + csVer = mdc_charsets.getObject(tdbb, CS.RDB$CHARACTER_SET_ID, CacheFlag::AUTOCREATE); + cs = csVer->getContainer(); + } + END_FOR - // Charset name not found in the alias table - before giving up - // try the character_set table + if (!cs) + return false; + } - FOR(REQUEST_HANDLE handle) - FIRST 1 CS IN RDB$CHARACTER_SETS - WITH CS.RDB$CHARACTER_SET_NAME EQ charset + if (!collation) { - found = true; - attachment->att_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID); - *id = CS.RDB$CHARACTER_SET_ID; + *id = TTypeId(cs->getId()); + return true; } - END_FOR - return found; + if (!csVer) + csVer = cs->getObject(tdbb, CacheFlag::AUTOCREATE); + Collation* coll = csVer->getCollation((const char*)collation); + + if (!coll) + return false; } + fb_assert(collation); + + bool found = false; if (!charset) { FOR(REQUEST_HANDLE handle) @@ -4899,36 +4039,123 @@ static bool resolve_charset_and_collation(thread_db* tdbb, WITH COL.RDB$COLLATION_NAME EQ collation { found = true; - *id = COL.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8); + *id = TTypeId(CSetId(COL.RDB$CHARACTER_SET_ID), CollId(COL.RDB$COLLATION_ID)); } END_FOR + } - return found; + return found; +} + + +ScanResult CharSetVers::scan(thread_db* tdbb, ObjectBase::Flag flags) +{ + fb_assert(perm->hasData()); + + Attachment* attachment = tdbb->getAttachment(); + + AutoRequest handle, handle2; + + if (perm->names.getCount() == 0) + { + perm->names.push(getName()); + + FOR(REQUEST_HANDLE handle) + T IN RDB$TYPES + WITH T.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME" + AND T.RDB$TYPE EQ getId() + { + if (getName() != T.RDB$TYPE_NAME) + perm->names.push(T.RDB$TYPE_NAME); + } + END_FOR } - FOR(REQUEST_HANDLE handle) - FIRST 1 CS IN RDB$CHARACTER_SETS CROSS - COL IN RDB$COLLATIONS OVER RDB$CHARACTER_SET_ID CROSS - AL1 IN RDB$TYPES - WITH AL1.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME" - AND AL1.RDB$TYPE_NAME EQ charset - AND COL.RDB$COLLATION_NAME EQ collation - AND AL1.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID + FOR(REQUEST_HANDLE handle2) + CS IN RDB$CHARACTER_SETS + CROSS COL IN RDB$COLLATIONS OVER RDB$CHARACTER_SET_ID + WITH CS.RDB$CHARACTER_SET_ID EQ getId() { - found = true; - attachment->att_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID); - *id = CS.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8); + fb_assert(perm->names.exist(CS.RDB$CHARACTER_SET_NAME)); + + SubtypeInfo info; + info.charsetName = perm->names; + info.collationName = COL.RDB$COLLATION_NAME; + info.attributes = (USHORT)COL.RDB$COLLATION_ATTRIBUTES; + info.ignoreAttributes = COL.RDB$COLLATION_ATTRIBUTES.NULL; + + if (COL.RDB$BASE_COLLATION_NAME.NULL) + info.baseCollationName = info.collationName; + else + info.baseCollationName = COL.RDB$BASE_COLLATION_NAME; + + CharSet* charset = perm->getCharSet(); + CollId colId(COL.RDB$COLLATION_ID); + CSetId id(getId()); + + if (COL.RDB$SPECIFIC_ATTRIBUTES.NULL) + info.specificAttributes.clear(); + else + { + blb* blob = blb::open(tdbb, attachment->getSysTransaction(), &COL.RDB$SPECIFIC_ATTRIBUTES); + const ULONG length = blob->blb_length; + + // ASF: Here info.specificAttributes is in UNICODE_FSS charset. + blob->BLB_get_data(tdbb, info.specificAttributes.getBuffer(length), length); + + if (id != CS_METADATA) + { + Firebird::UCharBuffer specificAttributes; + ULONG size = info.specificAttributes.getCount() * charset->maxBytesPerChar(); + + size = INTL_convert_bytes(tdbb, id, + specificAttributes.getBuffer(size), size, + CS_METADATA, info.specificAttributes.begin(), + info.specificAttributes.getCount(), ERR_post); + specificAttributes.shrink(size); + info.specificAttributes = specificAttributes; + } + } + + AutoPtr tt = FB_NEW_POOL(perm->getPool()) texttype; + memset(tt, 0, sizeof(texttype)); + INTL_lookup_texttype(tt, &info); + + if (charset_collations.getCount() <= colId) + charset_collations.grow(colId + 1); + + fb_assert((tt->texttype_canonical_width == 0 && tt->texttype_fn_canonical == NULL) || + (tt->texttype_canonical_width != 0 && tt->texttype_fn_canonical != NULL)); + + if (tt->texttype_canonical_width == 0) + { + if (charset->isMultiByte()) + tt->texttype_canonical_width = sizeof(ULONG); // UTF-32 + else + { + tt->texttype_canonical_width = charset->minBytesPerChar(); + // canonical is equal to string, then TEXTTYPE_DIRECT_MATCH can be turned on + tt->texttype_flags |= TEXTTYPE_DIRECT_MATCH; + } + } + + Collation* collation = Collation::createInstance(perm->getPool(), + TTypeId(id, colId), tt, info.attributes, charset); + collation->name = info.collationName; + + tt.release(); + charset_collations[colId] = collation; } END_FOR - return found; +// return found ? ScanResult::COMPLETE : ScanResult::REPEAT; + return ScanResult::COMPLETE; } -static void save_trigger_data(thread_db* tdbb, TrigVector** ptr, jrd_rel* relation, +static void save_trigger_data(thread_db* tdbb, Triggers& vector, jrd_rel* relation, Statement* statement, blb* blrBlob, blb* debugInfoBlob, - const TEXT* name, FB_UINT64 type, - bool sys_trigger, USHORT flags, + const TEXT* name, FB_UINT64 type, USHORT flags, const MetaName& engine, const string& entryPoint, const bid* body, TriState ssDefiner) { @@ -4943,34 +4170,27 @@ static void save_trigger_data(thread_db* tdbb, TrigVector** ptr, jrd_rel* relati * **************************************/ Attachment* attachment = tdbb->getAttachment(); - TrigVector* vector = *ptr; + Database* database = tdbb->getDatabase(); + MemoryPool& pool(*(relation ? relation->rel_pool : database->dbb_permanent)); - if (!vector) - { - MemoryPool* pool = relation ? relation->rel_pool : attachment->att_pool; - vector = FB_NEW_POOL(*pool) TrigVector(*pool); - vector->addRef(); - *ptr = vector; - } - - Trigger& t = vector->add(); + Trigger* t = FB_NEW_POOL(pool) Trigger(pool); if (blrBlob) { const ULONG length = blrBlob->blb_length + 10; - UCHAR* const data = t.blr.getBuffer(length); - t.blr.resize(blrBlob->BLB_get_data(tdbb, data, length)); + UCHAR* const data = t->blr.getBuffer(length); + t->blr.resize(blrBlob->BLB_get_data(tdbb, data, length)); } if (debugInfoBlob) { const ULONG length = debugInfoBlob->blb_length + 10; - UCHAR* const data = t.debugInfo.getBuffer(length); - t.debugInfo.resize(debugInfoBlob->BLB_get_data(tdbb, data, length)); + UCHAR* const data = t->debugInfo.getBuffer(length); + t->debugInfo.resize(debugInfoBlob->BLB_get_data(tdbb, data, length)); } if (name) - t.name = name; + t->name = name; if (body) { @@ -4980,37 +4200,23 @@ static void save_trigger_data(thread_db* tdbb, TrigVector** ptr, jrd_rel* relati ULONG length = bodyBlob->BLB_get_data(tdbb, (UCHAR*) temp.getBuffer(bodyBlob->blb_length), bodyBlob->blb_length); - t.extBody.assign(temp.begin(), length); + t->extBody.assign(temp.begin(), length); } - t.type = type; - t.flags = flags; - t.sysTrigger = sys_trigger; - t.statement = statement; - t.relation = relation; - t.engine = engine; - t.entryPoint = entryPoint; - t.ssDefiner = ssDefiner; - t.owner = relation ? relation->rel_owner_name : tdbb->getDatabase()->dbb_owner; -} + t->type = type; + t->flags = flags; + t->statement = statement; + t->relation = relation; // trigger can't exist longer than relation + t->engine = engine; + t->entryPoint = entryPoint; + t->ssDefiner = ssDefiner; + t->owner = relation ? relation->getOwnerName() : tdbb->getDatabase()->dbb_owner; - -const Trigger* findTrigger(TrigVector* triggers, const MetaName& trig_name) -{ - if (triggers) - { - for (TrigVector::iterator t = triggers->begin(); t != triggers->end(); ++t) - { - if (t->name.compare(trig_name) == 0) - return &(*t); - } - } - - return NULL; + vector.addTrigger(tdbb, t); } -void scan_partners(thread_db* tdbb, jrd_rel* relation) +void RelationPermanent::scan_partners(thread_db* tdbb) { /************************************** * @@ -5025,72 +4231,41 @@ void scan_partners(thread_db* tdbb, jrd_rel* relation) **************************************/ Attachment* attachment = tdbb->getAttachment(); - while (relation->rel_flags & REL_check_partners) + if (rel_flags & REL_check_partners) { - relation->rel_flags &= ~REL_check_partners; - LCK_lock(tdbb, relation->rel_partners_lock, LCK_SR, LCK_WAIT); - - if (relation->rel_flags & REL_check_partners) - continue; + LCK_lock(tdbb, rel_partners_lock, LCK_SR, LCK_WAIT); AutoCacheRequest request(tdbb, irq_foreign1, IRQ_REQUESTS); - frgn* references = &relation->rel_foreign_refs; int index_number = 0; - if (references->frgn_reference_ids) - { - delete references->frgn_reference_ids; - references->frgn_reference_ids = NULL; - } - if (references->frgn_relations) - { - delete references->frgn_relations; - references->frgn_relations = NULL; - } - if (references->frgn_indexes) - { - delete references->frgn_indexes; - references->frgn_indexes = NULL; - } - FOR(REQUEST_HANDLE request) IDX IN RDB$INDICES CROSS RC IN RDB$RELATION_CONSTRAINTS OVER RDB$INDEX_NAME CROSS IND IN RDB$INDICES WITH RC.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND - IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND + IDX.RDB$RELATION_NAME EQ c_name() AND IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND IDX.RDB$INDEX_ID > 0 AND IND.RDB$INDEX_ID > 0 AND IND.RDB$UNIQUE_FLAG = 1 { //// ASF: Hack fix for CORE-4304, until nasty interactions between dfw and met are not resolved. - const jrd_rel* partner_relation = relation->rel_name == IND.RDB$RELATION_NAME ? - relation : MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME); + const RelationPermanent* partner_relation = this; + if (getName() != IND.RDB$RELATION_NAME) + partner_relation = MetadataCache::lookupRelation(tdbb, IND.RDB$RELATION_NAME, CacheFlag::AUTOCREATE); if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE) { - // This seems a good candidate for vcl. - references->frgn_reference_ids = - vec::newVector(*relation->rel_pool, references->frgn_reference_ids, - index_number + 1); - - (*references->frgn_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1; - - references->frgn_relations = - vec::newVector(*relation->rel_pool, references->frgn_relations, - index_number + 1); - - (*references->frgn_relations)[index_number] = partner_relation->rel_id; + dep deps; + deps.dep_reference_id = IDX.RDB$INDEX_ID - 1; + deps.dep_relation = partner_relation->getId(); + deps.dep_index = IND.RDB$INDEX_ID - 1; - references->frgn_indexes = - vec::newVector(*relation->rel_pool, references->frgn_indexes, - index_number + 1); + if (!rel_foreign_refs) + rel_foreign_refs = FB_NEW_POOL(getPool()) ForeignDeps(getPool()); - (*references->frgn_indexes)[index_number] = IND.RDB$INDEX_ID - 1; - - index_number++; + rel_foreign_refs->push(deps); } } END_FOR @@ -5098,23 +4273,10 @@ void scan_partners(thread_db* tdbb, jrd_rel* relation) // Prepare for rescan of primary dependencies on relation's primary key and stale vectors. request.reset(tdbb, irq_foreign2, IRQ_REQUESTS); - prim* dependencies = &relation->rel_primary_dpnds; - index_number = 0; - - if (dependencies->prim_reference_ids) - { - delete dependencies->prim_reference_ids; - dependencies->prim_reference_ids = NULL; - } - if (dependencies->prim_relations) + if (rel_primary_dpnds) { - delete dependencies->prim_relations; - dependencies->prim_relations = NULL; - } - if (dependencies->prim_indexes) - { - delete dependencies->prim_indexes; - dependencies->prim_indexes = NULL; + delete rel_primary_dpnds; + rel_primary_dpnds = NULL; } FOR(REQUEST_HANDLE request) @@ -5123,34 +4285,25 @@ void scan_partners(thread_db* tdbb, jrd_rel* relation) IDX.RDB$UNIQUE_FLAG = 1 AND IDX.RDB$INDEX_ID > 0 AND IND.RDB$INDEX_ID > 0 AND - IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND + IDX.RDB$RELATION_NAME EQ c_name() AND IND.RDB$FOREIGN_KEY EQ IDX.RDB$INDEX_NAME { //// ASF: Hack fix for CORE-4304, until nasty interactions between dfw and met are not resolved. - const jrd_rel* partner_relation = relation->rel_name == IND.RDB$RELATION_NAME ? - relation : MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME); + const auto* partner_relation = this; + if (getName() != IND.RDB$RELATION_NAME) + partner_relation = MetadataCache::lookupRelation(tdbb, IND.RDB$RELATION_NAME, CacheFlag::AUTOCREATE); if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE) { - dependencies->prim_reference_ids = - vec::newVector(*relation->rel_pool, dependencies->prim_reference_ids, - index_number + 1); - - (*dependencies->prim_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1; - - dependencies->prim_relations = - vec::newVector(*relation->rel_pool, dependencies->prim_relations, - index_number + 1); + dep deps; + deps.dep_reference_id = IDX.RDB$INDEX_ID - 1; + deps.dep_relation = partner_relation->getId(); + deps.dep_index = IND.RDB$INDEX_ID - 1; - (*dependencies->prim_relations)[index_number] = partner_relation->rel_id; + if (!rel_primary_dpnds) + rel_primary_dpnds = FB_NEW_POOL(getPool()) ForeignDeps(getPool()); - dependencies->prim_indexes = - vec::newVector(*relation->rel_pool, dependencies->prim_indexes, - index_number + 1); - - (*dependencies->prim_indexes)[index_number] = IND.RDB$INDEX_ID - 1; - - index_number++; + rel_primary_dpnds->push(deps); } } END_FOR @@ -5158,12 +4311,27 @@ void scan_partners(thread_db* tdbb, jrd_rel* relation) } +const Trigger* jrd_rel::findTrigger(const MetaName trig_name) const +{ + for (int n = TRIGGER_PRE_STORE; n <= TRIGGER_POST_ERASE; ++n) + { + for (auto t : rel_triggers[n]) + { + if (t->name == trig_name) + return t; + } + } + + return nullptr; +} + + void MET_store_dependencies(thread_db* tdbb, - Array& dependencies, - const jrd_rel* dep_rel, - const MetaName& object_name, - int dependency_type, - jrd_tra* transaction) + Array& dependencies, + Cached::Relation* dep_rel, + const MetaName& object_name, + int dependency_type, + jrd_tra* transaction) { /************************************** * @@ -5182,32 +4350,22 @@ void MET_store_dependencies(thread_db* tdbb, SET_TDBB(tdbb); const Trigger* t = nullptr; - const bool checkTableScope = - (dependency_type == obj_computed) || - (dependency_type == obj_trigger) && (dep_rel != 0) && - ( - (t = findTrigger(dep_rel->rel_pre_erase, object_name)) || - (t = findTrigger(dep_rel->rel_pre_modify, object_name)) || - (t = findTrigger(dep_rel->rel_pre_store, object_name)) || - (t = findTrigger(dep_rel->rel_post_erase, object_name)) || - (t = findTrigger(dep_rel->rel_post_modify, object_name)) || - (t = findTrigger(dep_rel->rel_post_store, object_name)) - ) && t && (t->sysTrigger); + const bool checkTableScope = (dependency_type == obj_computed); while (dependencies.hasData()) { - CompilerScratch::Dependency dependency = dependencies.pop(); + Dependency dependency = dependencies.pop(); if (!dependency.relation && !dependency.function && !dependency.procedure && - !dependency.name && !dependency.number) + !dependency.name.hasData() && !dependency.number) { continue; } int dpdo_type = dependency.objType; - jrd_rel* relation = NULL; - const jrd_prc* procedure = NULL; - const MetaName* dpdo_name = NULL; + Cached::Relation* relation = nullptr; + Cached::Procedure* procedure = nullptr; + MetaName dpdo_name; MetaName packageName; SubtypeInfo info; @@ -5215,7 +4373,7 @@ void MET_store_dependencies(thread_db* tdbb, { case obj_relation: relation = dependency.relation; - dpdo_name = &relation->rel_name; + dpdo_name = relation->getName(); fb_assert(dep_rel || !checkTableScope); @@ -5226,14 +4384,14 @@ void MET_store_dependencies(thread_db* tdbb, if ( !( // master is ON COMMIT PRESERVE, detail is ON COMMIT DELETE (dep_rel->rel_flags & REL_temp_tran) && (relation->rel_flags & REL_temp_conn) || // computed field of a view - (dependency_type == obj_computed) && (dep_rel->rel_view_rse != NULL) + (dependency_type == obj_computed) && dep_rel->isView() )) { string sMaster, sChild; - make_relation_scope_name(relation->rel_name.c_str(), + make_relation_scope_name(relation->c_name(), relation->rel_flags, sMaster); - make_relation_scope_name(dep_rel->rel_name.c_str(), + make_relation_scope_name(dep_rel->c_name(), dep_rel->rel_flags, sChild); ERR_post(Arg::Gds(isc_no_meta_update) << @@ -5242,33 +4400,36 @@ void MET_store_dependencies(thread_db* tdbb, } } - MET_scan_relation(tdbb, relation); - if (relation->rel_view_rse) { + if (relation->isView()) dpdo_type = obj_view; - } break; + case obj_procedure: procedure = dependency.procedure; - dpdo_name = &procedure->getName().identifier; + dpdo_name = procedure->getName().identifier; packageName = procedure->getName().package; break; + case obj_collation: { const USHORT number = dependency.number; MET_get_char_coll_subtype_info(tdbb, number, &info); - dpdo_name = &info.collationName; + dpdo_name = info.collationName; } break; + case obj_exception: { const SLONG number = dependency.number; MET_lookup_exception(tdbb, number, name, NULL); - dpdo_name = &name; + dpdo_name = name; } break; + case obj_field: dpdo_name = dependency.name; break; + case obj_generator: { // CVC: Here I'm going to track those pesky things named generators and UDFs. @@ -5277,38 +4438,40 @@ void MET_store_dependencies(thread_db* tdbb, const SLONG number = dependency.number; if (number == 0 || !MET_lookup_generator_id(tdbb, number, name, &sysGen) || sysGen) continue; - dpdo_name = &name; + dpdo_name = name; } break; + case obj_udf: { - const Function* const udf = dependency.function; - dpdo_name = &udf->getName().identifier; + Cached::Function* const udf = dependency.function; + dpdo_name = udf->getName().identifier; packageName = udf->getName().package; } break; + case obj_index: - name = *dependency.name; - dpdo_name = &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) { const SSHORT fld_id = (SSHORT) dependency.subNumber; if (relation) { - const jrd_fld* field = MET_get_field(relation, fld_id); + const jrd_fld* field = MET_get_field(relation->getObject(tdbb, CacheFlag::AUTOCREATE), fld_id); if (field) field_name = field->fld_name; } else if (procedure) { - const Parameter* param = procedure->getOutputFields()[fld_id]; + const Parameter* param = procedure->getObject(tdbb, CacheFlag::AUTOCREATE)->getOutputFields()[fld_id]; // CVC: Setting the field var here didn't make sense alone, // so I thought the missing code was to try to extract // the field name that's in this case an output var from a proc. @@ -5317,18 +4480,18 @@ void MET_store_dependencies(thread_db* tdbb, } } else - field_name = *dependency.subName; + field_name = dependency.subName; } if (field_name.hasData()) { AutoCacheRequest request(tdbb, irq_c_deps_f, IRQ_REQUESTS); bool found = false; - fb_assert(dpdo_name); + fb_assert(dpdo_name.hasData()); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$DEPENDENCIES WITH X.RDB$DEPENDENT_NAME = object_name.c_str() AND - X.RDB$DEPENDED_ON_NAME = dpdo_name->c_str() AND + X.RDB$DEPENDED_ON_NAME = dpdo_name.c_str() AND X.RDB$DEPENDED_ON_TYPE = dpdo_type AND X.RDB$FIELD_NAME = field_name.c_str() AND X.RDB$DEPENDENT_TYPE = dependency_type @@ -5347,7 +4510,7 @@ void MET_store_dependencies(thread_db* tdbb, FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$DEPENDENCIES WITH X.RDB$DEPENDENT_NAME = object_name.c_str() AND - X.RDB$DEPENDED_ON_NAME = dpdo_name->c_str() AND + X.RDB$DEPENDED_ON_NAME = dpdo_name.c_str() AND X.RDB$DEPENDED_ON_TYPE = dpdo_type AND X.RDB$FIELD_NAME MISSING AND X.RDB$DEPENDENT_TYPE = dependency_type AND @@ -5363,13 +4526,13 @@ void MET_store_dependencies(thread_db* tdbb, AutoCacheRequest request(tdbb, irq_s_deps, IRQ_REQUESTS); - fb_assert(dpdo_name); + fb_assert(dpdo_name.hasData()); STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) DEP IN RDB$DEPENDENCIES { strcpy(DEP.RDB$DEPENDENT_NAME, object_name.c_str()); DEP.RDB$DEPENDED_ON_TYPE = dpdo_type; - strcpy(DEP.RDB$DEPENDED_ON_NAME, dpdo_name->c_str()); + strcpy(DEP.RDB$DEPENDED_ON_NAME, dpdo_name.c_str()); if (field_name.hasData()) { @@ -5501,3 +4664,619 @@ TriState MET_get_ss_definer(Jrd::thread_db* tdbb) return r; } + +MetadataCache::~MetadataCache() +{ +/* !!!!!!!!!!!!!!! + for (auto iter = mdc_functions.begin(); iter < mdc_functions.end(); ++iter) + delete *iter; + + for (auto iter = mdc_procedures.begin(); iter < mdc_procedures.end(); ++iter) + delete *iter; + */ +} + +void MetadataCache::releaseGTTs(thread_db* tdbb) +{ + for (auto relation : mdc_relations) + { + if (relation && (relation->rel_flags & REL_temp_conn) && + !(relation->isDropped())) + { + relation->delPages(tdbb); + } + } +} + +void MetadataCache::runDBTriggers(thread_db* tdbb, TriggerAction action) +{ + fb_assert(action == TRIGGER_CONNECT || action == TRIGGER_DISCONNECT); + const MetaId trgKind = (action == TRIGGER_CONNECT) ? DB_TRIGGER_CONNECT : DB_TRIGGER_DISCONNECT; + + auto* element = mdc_triggers[trgKind].load(atomics::memory_order_relaxed); + if (!element) + return; + + auto *triggers = element->getObject(tdbb, CacheFlag::AUTOCREATE); + if ((!triggers) || (!*triggers)) + return; + + ThreadStatusGuard temp_status(tdbb); + jrd_tra* transaction = NULL; + + try + { + transaction = TRA_start(tdbb, 0, NULL); + EXE_execute_db_triggers(tdbb, transaction, action); + TRA_commit(tdbb, transaction, false); + return; + } + catch (const Exception& /*ex*/) + { + Database* dbb = tdbb->getDatabase(); + if (!(dbb->dbb_flags & DBB_bugcheck) && transaction) + { + try + { + TRA_rollback(tdbb, transaction, false, false); + } + catch (const Exception& /*ex2*/) + { + } + } + throw; + } +} + +void MetadataCache::releaseRelations(thread_db* tdbb) +{ + // Shut down any extern relations + + for (auto relation : mdc_relations) + { + if (relation) + { + auto* ext = relation->getExtFile(); + if (ext) + ext->release(); + + // !!!!!!!!!!! + } + } +} + +void MetadataCache::releaseLocks(thread_db* tdbb) +{ + // Go through relations and indices and release + // all existence locks that might have been taken. + + for (auto relation : mdc_relations) + { + if (relation) + { + if (relation->rel_existence_lock) + LCK_release(tdbb, relation->rel_existence_lock); + + if (relation->rel_partners_lock) + LCK_release(tdbb, relation->rel_partners_lock); + + if (relation->rel_rescan_lock) + LCK_release(tdbb, relation->rel_rescan_lock); + + relation->rel_gc_lock.forcedRelease(tdbb); + + for (auto* index : relation->rel_indices) + { + if (index) + index->unlock(tdbb); + } + } + } + + // Release all procedure existence locks that might have been taken + + for (auto procedure : mdc_procedures) + { + if (procedure) + procedure->releaseLocks(tdbb); + } + + // Release all function existence locks that might have been taken + + for (auto function : mdc_functions) + { + if (function) + function->releaseLocks(tdbb); + } + + // Release charset locks + + for (auto charset : mdc_charsets) + { + if (charset) + charset->releaseLocks(tdbb); + } + +} + +void MetadataCache::invalidateReplSet(thread_db* tdbb) +{ + for (auto relation : mdc_relations) + { + if (relation) + relation->rel_repl_state.reset(); + } +} + +Cached::Triggers* MetadataCache::getTriggersSet(thread_db* tdbb, MetaId triggerId) +{ + if ((triggerId & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) + { + triggerId &= ~TRIGGER_TYPE_MASK; + return mdc_triggers[triggerId].load(atomics::memory_order_acquire); + } + + if ((triggerId & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) + return mdc_triggers[DB_TRIGGER_DDL].load(atomics::memory_order_acquire); + + return nullptr; +} + +const Triggers* MetadataCache::getTriggers(thread_db* tdbb, MetaId triggerId) +{ + auto* tset = getTriggersSet(tdbb, triggerId); + return tset ? tset->getObject(tdbb, CacheFlag::AUTOCREATE) : nullptr; +} + +void Trigger::compile(thread_db* tdbb) +{ + if (extTrigger || statement) + return; + + const auto dbb = tdbb->getDatabase(); + const auto att = tdbb->getAttachment(); + + // Allocate statement memory pool + MemoryPool* new_pool = dbb->createPool(ALLOC_ARGS0); + + // Trigger request is not compiled yet. Lets do it now + USHORT par_flags = (USHORT) (flags & TRG_ignore_perm) ? csb_ignore_perm : 0; + + if (type & 1) + par_flags |= csb_pre_trigger; + else + par_flags |= csb_post_trigger; + + try + { + Jrd::ContextPoolHolder context(tdbb, new_pool); + + AutoPtr auto_csb(FB_NEW_POOL(*new_pool) CompilerScratch(*new_pool)); + CompilerScratch* csb = auto_csb; + + csb->csb_g_flags |= par_flags; + + if (engine.isEmpty()) + { + TraceTrigCompile trace(tdbb, this); + + if (debugInfo.hasData()) + { + DBG_parse_debug_info((ULONG) debugInfo.getCount(), debugInfo.begin(), + *csb->csb_dbg_info); + } + + PAR_blr(tdbb, getPermanent(relation), blr.begin(), (ULONG) blr.getCount(), NULL, + &csb, &statement, (relation ? true : false), par_flags); + + trace.finish(statement, ITracePlugin::RESULT_SUCCESS); + } + else + { + dbb->dbb_extManager->makeTrigger(tdbb, csb, this, engine, entryPoint, extBody.c_str(), + (relation ? + (type & 1 ? IExternalTrigger::TYPE_BEFORE : IExternalTrigger::TYPE_AFTER) : + IExternalTrigger::TYPE_DATABASE)); + } + } + catch (const Exception&) + { + if (statement) + { + statement->release(tdbb); + statement = NULL; + } + else + dbb->deletePool(new_pool); + + throw; + } + + statement->triggerName = name; + + if (ssDefiner.asBool()) + statement->triggerInvoker = att->getUserId(owner); + + if (flags & TRG_ignore_perm) + statement->flags |= Statement::FLAG_IGNORE_PERM; +} + +Cached::Relation* MetadataCache::lookupRelation(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + MetadataCache* mdc = get(tdbb); + auto rc = mdc->mdc_relations.getData(tdbb, id, flags); + if (rc || !(flags & CacheFlag::AUTOCREATE)) + return rc; + + if (auto* rel = mdc->mdc_relations.getObject(tdbb, id, flags)) + rc = rel->rel_perm; + + return rc; +} + +Cached::Relation* MetadataCache::lookupRelation(thread_db* tdbb, MetaId id) +{ + return mdc_relations.getData(tdbb, id, 0); +} + +Cached::Relation* MetadataCache::lookupRelationNoChecks(MetaId id) +{ + return mdc_relations.getDataNoChecks(id); +} + +Cached::Procedure* MetadataCache::lookupProcedure(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + Attachment* attachment = tdbb->getAttachment(); + MetadataCache* mdc = get(tdbb); + + // See if we already know the relation by name + auto* rc = mdc->mdc_procedures.lookup(tdbb, [name](RoutinePermanent* proc) { return proc->name == name; }, flags); + + if (rc || !(flags & CacheFlag::AUTOCREATE)) + return rc; + + // We need to look up the procedure name in RDB$PROCEDURES + AutoCacheRequest request(tdbb, irq_l_procedure, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + P IN RDB$PROCEDURES + WITH P.RDB$PROCEDURE_NAME EQ name.identifier.c_str() AND + P.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') + { + if (auto* prc = mdc->mdc_procedures.getObject(tdbb, P.RDB$PROCEDURE_ID, flags)) + rc = prc->getPermanent(); + } + END_FOR + + return rc; +} + +Cached::Function* MetadataCache::lookupFunction(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + MetadataCache* mdc = get(tdbb); + + // See if we already know the function by name + auto* rc = mdc->mdc_functions.lookup(tdbb, [name](RoutinePermanent* func) { return func->name == name; }, flags); + + if (rc || !(flags & CacheFlag::AUTOCREATE)) + return rc; + + auto* fun = Function::lookup(tdbb, name, flags); + return fun ? fun->getPermanent() : nullptr; +} + +Cached::Function* MetadataCache::lookupFunction(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + MetadataCache* mdc = MetadataCache::get(tdbb); + auto* rc = mdc->mdc_functions.getData(tdbb, id, flags); + if (rc || !(flags & CacheFlag::AUTOCREATE)) + return rc; + + if (auto* fun = mdc->mdc_functions.getObject(tdbb, id, flags)) + rc = fun->getPermanent(); + + return rc; +} + +Cached::Procedure* MetadataCache::lookupProcedure(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + MetadataCache* mdc = MetadataCache::get(tdbb); + auto* rc = mdc->mdc_procedures.getData(tdbb, id, flags); + if (rc || !(flags & CacheFlag::AUTOCREATE)) + return rc; + + if (auto* prc = mdc->mdc_procedures.getObject(tdbb, id, flags)) + rc = prc->getPermanent(); + + return rc; +} + +Lock* jrd_prc::makeLock(thread_db* tdbb, MemoryPool& p) +{ + return FB_NEW_RPT(p, 0) Lock(tdbb, sizeof(SLONG), LCK_prc_exist, nullptr, blockingAst); +} + +int jrd_prc::blockingAst(void* ast_object) +{ +/************************************** + * + * b l o c k i n g _ a s t _ p r o c e d u r e + * + ************************************** + * + * Functional description + * Someone is trying to drop a proceedure. If there + * are outstanding interests in the existence of + * the relation then just mark as blocking and return. + * Otherwise, mark the procedure block as questionable + * and release the procedure existence lock. + * + **************************************/ + RoutinePermanent* const procedure = static_cast(ast_object); +/* !!!!!!!!!!!!!!! + try + { + if (procedure->existenceLock) + { + Database* const dbb = procedure->existenceLock->lck_dbb; + + AsyncContextHolder tdbb(dbb, FB_FUNCTION, procedure->existenceLock); + + LCK_release(tdbb, procedure->existenceLock); + } + /// !!!!!!!!!!!!!!!!! + procedure->flags |= Routine::FLAG_OBSOLETE; + } + catch (const Exception&) + {} // no-op +*/ + return 0; +} + +Cached::CharSet* MetadataCache::getCharSet(thread_db* tdbb, CSetId id, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + MetadataCache* mdc = MetadataCache::get(tdbb); + auto* rc = mdc->mdc_charsets.getData(tdbb, id, flags); + if (rc || !(flags & CacheFlag::AUTOCREATE)) + return rc; + + if (auto* cs = mdc->mdc_charsets.getObject(tdbb, id, flags)) + rc = cs->getContainer(); + + return rc; +} + +MetadataCache* MetadataCache::getCache(thread_db* tdbb) +{ + return tdbb->getDatabase()->dbb_mdc; +} + +int jrd_prc::objectType() +{ + return obj_procedure; +} + +MetadataCache::CleanupQueue::CleanupQueue(MemoryPool& p) + : cq_data(p) +{ } + +void MetadataCache::CleanupQueue::enqueue(TraNumber traNum, ElementBase* toClean) +{ + MutexLockGuard g(cq_mutex, FB_FUNCTION); + + if (cq_data.getCount() == 0) + { + cq_traNum = traNum; + fb_assert(cq_pos == 0); + } + cq_data.push(Stored(traNum, toClean)); +} + +void MetadataCache::CleanupQueue::dequeue(thread_db* tdbb, TraNumber oldest) +{ + MutexEnsureUnlock g(cq_mutex, FB_FUNCTION); + + if (!g.tryEnter()) + return; + + while (cq_pos < cq_data.getCount() && oldest > cq_data[cq_pos].t) + { + cq_data[cq_pos++].c->cleanup(tdbb); + } + + if (cq_data.getCount() <= cq_pos) + { + fb_assert(cq_data.getCount() == cq_pos); + + cq_data.clear(); + cq_pos = 0; + cq_traNum = MAX_TRA_NUMBER; + } + else + { + if (cq_pos > cq_data.getCount() / 2) + { + cq_data.removeCount(0, cq_pos); + cq_pos = 0; + } + cq_traNum = cq_data[cq_pos].t; + } +} + +void MetadataCache::objectCleanup(TraNumber traNum, ElementBase* toClean) +{ + mdc_cleanup_queue.enqueue(traNum, toClean); +} + +ScanResult IndexVersion::scan(thread_db* tdbb, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + jrd_tra* transaction = tdbb->getTransaction() ? tdbb->getTransaction() : attachment->getSysTransaction(); + + bid expression, condition; + expression.clear(); + condition.clear(); + MetaId relId = getPermanent()->getRelation()->getId(); + bool found = false; + + Jrd::ContextPoolHolder context(tdbb, dbb->dbb_permanent); + + AutoCacheRequest handle(tdbb, irq_index_scan, IRQ_REQUESTS); + FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) + IND IN RDB$INDICES + CROSS REL IN RDB$RELATIONS + OVER RDB$RELATION_NAME + WITH IND.RDB$INDEX_ID EQ getId() + 1 + AND REL.RDB$RELATION_ID EQ relId + { + found = true; + + idv_name = IND.RDB$INDEX_NAME; + idv_uniqFlag = IND.RDB$UNIQUE_FLAG; + idv_segmentCount = IND.RDB$SEGMENT_COUNT; + idv_type = IND.RDB$INDEX_TYPE; + idv_foreignKey = IND.RDB$FOREIGN_KEY; + + idv_active = MetadataCache::getIndexStatus(IND.RDB$INDEX_INACTIVE.NULL, IND.RDB$INDEX_INACTIVE); + + if (!IND.RDB$EXPRESSION_BLR.NULL) + expression = IND.RDB$EXPRESSION_BLR; + if (!IND.RDB$CONDITION_BLR.NULL) + condition = IND.RDB$CONDITION_BLR; + } + END_FOR + + if (idv_name.isEmpty()) + { + idv_active = MET_index_inactive; + return ScanResult::MISS; + } + perm->idp_name = idv_name; + + auto* relation = (expression.hasData() || condition.hasData()) ? + MetadataCache::lookupRelation(tdbb, relId, CacheFlag::AUTOCREATE) : nullptr; + perm->createLock(tdbb, relId, getId()); + // ?????????????? perm->createLock( caching lock + + if (expression.hasData()) + { + AutoMemoryPool stmtPool(dbb->createPool(ALLOC_ARGS0)); + Jrd::ContextPoolHolder context(tdbb, stmtPool); + + CompilerScratch* csb = nullptr; + Cleanup cc([csb]() {delete csb;}); + MET_parse_blob(tdbb, relation, &expression, &csb, nullptr, false, false); + idv_expression_statement = Statement::makeValueExpression(tdbb, idv_expression, idv_expression_desc, csb, false); + + stmtPool.release(); + } + + if (condition.hasData()) + { + AutoMemoryPool stmtPool(dbb->createPool(ALLOC_ARGS0)); + Jrd::ContextPoolHolder context(tdbb, stmtPool); + + CompilerScratch* csb = nullptr; + Cleanup cc([csb]() {delete csb;}); + MET_parse_blob(tdbb, relation, &condition, &csb, nullptr, false, false); + idv_condition_statement = Statement::makeBoolExpression(tdbb, idv_condition, csb, false); + + stmtPool.release(); + } + + return found ? ScanResult::COMPLETE : ScanResult::MISS; +} + +IndexVersion* RelationPermanent::lookup_index(thread_db* tdbb, MetaName name, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + Attachment* attachment = tdbb->getAttachment(); + + // See if we already know the index by name + auto* idp = rel_indices.lookup(tdbb, + [tdbb, name, flags](Cached::Index* idp)->Cached::Index* + { + if (idp->getName() == name) + { + auto* idv = idp->getObject(tdbb, flags); + if (idv && idv->getName() == name) + return idp; + } + return nullptr; + }, + flags); + + if (idp) + return idp->getObject(tdbb, flags); + if (!(flags & CacheFlag::AUTOCREATE)) + return nullptr; + + // We need to look up the index in RDB$INDICES + AutoCacheRequest request(tdbb, irq_get_index_by_name, IRQ_REQUESTS); + IndexVersion* idv = nullptr; + + FOR(REQUEST_HANDLE request) + IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name.c_str() + AND IDX.RDB$RELATION_NAME EQ c_name() + { + if (!idv) + idv = rel_indices.getObject(tdbb, IDX.RDB$INDEX_ID - 1, flags); + else + fb_assert(false); + } + END_FOR + + return idv; +} + +Cached::Index* RelationPermanent::lookupIndex(thread_db* tdbb, MetaName name, ObjectBase::Flag flags) +{ + SET_TDBB(tdbb); + + Attachment* attachment = tdbb->getAttachment(); + + // See if we already know the relation by name + auto* idp = rel_indices.lookup(tdbb, + [tdbb, name, flags](Cached::Index* idp) + { + return idp->getName() == name ? idp : nullptr; + }, + flags); + + if (idp || !(flags & CacheFlag::AUTOCREATE)) + return idp; + + // We need to look up the index in RDB$INDICES + AutoCacheRequest request(tdbb, irq_get_index_by_name, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name.c_str() + AND IDX.RDB$RELATION_NAME EQ c_name() + { + if (!idp) + { + if (auto* idv = rel_indices.getObject(tdbb, IDX.RDB$INDEX_ID - 1, flags)) + idp = getPermanent(idv); + } + else + fb_assert(false); + } + END_FOR + + return idp; +} diff --git a/src/jrd/met.h b/src/jrd/met.h index 944d20b5532..58c8f0df694 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -24,9 +24,26 @@ #ifndef JRD_MET_H #define JRD_MET_H +#include "../jrd/CacheVector.h" +#include + +#include "../common/StatusArg.h" + +#include "../jrd/Relation.h" +#include "../jrd/Function.h" + +#include "../jrd/val.h" +#include "../jrd/irq.h" +#include "../jrd/drq.h" +#include "../jrd/exe.h" + +#include "../jrd/CharSetContainer.h" + +namespace Jrd { + // Record types for record summary blob records -enum rsr_t { +enum rsr_t : UCHAR { RSR_field_id, RSR_field_name, RSR_view_context, @@ -51,7 +68,12 @@ enum rsr_t { RSR_field_sub_type, RSR_field_not_null, RSR_field_generator_name, - RSR_field_identity_type + RSR_field_identity_type, + + RSR_segment_length, // Needed for DSQL + RSR_field_source, + RSR_character_length, + RSR_field_pos }; // Temporary field block @@ -60,7 +82,7 @@ class TemporaryField : public pool_alloc { public: TemporaryField* tfb_next; // next block in chain - USHORT tfb_id; // id of field in relation + MetaId tfb_id; // id of field in relation USHORT tfb_flags; dsc tfb_desc; Jrd::impure_value tfb_default; @@ -71,60 +93,468 @@ class TemporaryField : public pool_alloc const int TFB_computed = 1; const int TFB_array = 2; +} // namespace Jrd -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; +#include "../jrd/exe_proto.h" +#include "../jrd/obj.h" +#include "../dsql/sym.h" -// trigger type prefixes -const int TRIGGER_PRE = 0; -const int TRIGGER_POST = 1; +namespace Jrd { -// trigger type suffixes -const int TRIGGER_STORE = 1; -const int TRIGGER_MODIFY = 2; -const int TRIGGER_ERASE = 3; +// Procedure block -// 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; +class jrd_prc : public Routine +{ +public: + const Format* prc_record_format; + prc_t prc_type; // procedure type -#include "../jrd/obj.h" + const ExtEngineManager::Procedure* getExternal() const { return prc_external; } + void setExternal(ExtEngineManager::Procedure* value) { prc_external = value; } + +private: + Cached::Procedure* cachedProcedure; + const ExtEngineManager::Procedure* prc_external; + +public: + explicit jrd_prc(Cached::Procedure* perm) + : Routine(perm->getPool()), + prc_record_format(NULL), + prc_type(prc_legacy), + cachedProcedure(perm), + prc_external(NULL) + { + } + + explicit jrd_prc(MemoryPool& p) + : Routine(p), + prc_record_format(NULL), + prc_type(prc_legacy), + cachedProcedure(FB_NEW_POOL(p) Cached::Procedure(p)), + prc_external(NULL) + { + } + +public: + int getObjectType() const override + { + return obj_procedure; + } + + SLONG getSclType() const override + { + return obj_procedures; + } + + void releaseFormat() override + { + delete prc_record_format; + prc_record_format = NULL; + } + +private: + virtual ~jrd_prc() override + { + delete prc_external; + } + + static int blockingAst(void* ast_object); + +public: + static jrd_prc* create(thread_db* tdbb, MemoryPool& p, Cached::Procedure* perm); + static Lock* makeLock(thread_db* tdbb, MemoryPool& p); + ScanResult scan(thread_db* tdbb, ObjectBase::Flag); + + void releaseExternal() override + { + delete prc_external; + prc_external = NULL; + } + + Cached::Procedure* getPermanent() const override + { + return cachedProcedure; + } + + static const char* objectFamily(void*) + { + return "procedure"; + } + + ScanResult reload(thread_db* tdbb, ObjectBase::Flag fl); + void checkReload(thread_db* tdbb) const override; + + static int objectType(); +}; + + +// Parameter block + +class Parameter : public pool_alloc +{ +public: + MetaId prm_number; + dsc prm_desc; + NestConst prm_default_value; + bool prm_nullable; + prm_mech_t prm_mechanism; + MetaName prm_name; + MetaName prm_field_source; + MetaName prm_type_of_column; + MetaName prm_type_of_table; + std::optional prm_text_type; + FUN_T prm_fun_mechanism; + USHORT prm_seg_length = 0; + +public: + explicit Parameter(MemoryPool& p) + : prm_name(p), + prm_field_source(p), + prm_type_of_column(p), + prm_type_of_table(p) + { + } +}; + + +struct index_desc; +struct DSqlCacheItem; + +typedef atomics::atomic TriggersSet; + +class MetadataCache : public Firebird::PermanentStorage +{ + friend class CharSetContainer; + +public: + MetadataCache(MemoryPool& pool) + : Firebird::PermanentStorage(pool), + mdc_generators(getPool()), + mdc_relations(getPool()), + mdc_procedures(getPool()), + mdc_functions(getPool()), + mdc_charsets(getPool()), + mdc_version(0), + mdc_cleanup_queue(pool) + { + memset(mdc_triggers, 0, sizeof(mdc_triggers)); + } + + ~MetadataCache(); + + // Objects are placed here after DROP OBJECT and wait for current OAT >= NEXT when DDL committed + void objectCleanup(TraNumber traNum, ElementBase* toClean); + void checkCleanup(thread_db* tdbb, TraNumber oldest) + { + mdc_cleanup_queue.check(tdbb, oldest); + } + + void releaseRelations(thread_db* tdbb); + void releaseLocks(thread_db* tdbb); + void releaseGTTs(thread_db* tdbb); + void runDBTriggers(thread_db* tdbb, TriggerAction action); + void invalidateReplSet(thread_db* tdbb); + void setRelation(thread_db* tdbb, ULONG rel_id, jrd_rel* rel); + void releaseTrigger(thread_db* tdbb, MetaId triggerId, const MetaName& name); + Cached::Triggers* getTriggersSet(thread_db* tdbb, MetaId triggerId); + const Triggers* getTriggers(thread_db* tdbb, MetaId tType); + + MetaId relCount() + { + return mdc_relations.getCount(); + } + + Function* getFunction(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) + { + return mdc_functions.getObject(tdbb, id, flags); + } + + jrd_prc* getProcedure(thread_db* tdbb, MetaId id) + { + return mdc_procedures.getObject(tdbb, id, CacheFlag::AUTOCREATE); + } + + static Cached::CharSet* getCharSet(thread_db* tdbb, CSetId id, ObjectBase::Flag flags); + + void cleanup(Jrd::thread_db*); + + // former met_proto.h +#ifdef NEVERDEF + static void verify_cache(thread_db* tdbb); +#else + static void verify_cache(thread_db* tdbb) { } +#endif + static void clear(thread_db* tdbb); + static void update_partners(thread_db* tdbb); + void loadDbTriggers(thread_db* tdbb, unsigned int type); + static jrd_prc* lookup_procedure(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags); + static jrd_prc* lookup_procedure_id(thread_db* tdbb, MetaId id, ObjectBase::Flag flags); + static Function* lookup_function(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags); + 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); + 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&, ObjectBase::Flag flags); + static jrd_rel* lookup_relation_id(thread_db*, MetaId, ObjectBase::Flag flags); + static Cached::Relation* lookupRelation(thread_db* tdbb, const MetaName& name, ObjectBase::Flag flags); + static Cached::Relation* lookupRelation(thread_db* tdbb, MetaId id, ObjectBase::Flag flags); + Cached::Relation* lookupRelation(thread_db* tdbb, MetaId id); + Cached::Relation* lookupRelationNoChecks(MetaId id); + static ElementBase::ReturnedId lookup_index_name(thread_db* tdbb, const MetaName& index_name, IndexStatus* status); + static void post_existence(thread_db* tdbb, jrd_rel* relation); + static jrd_prc* findProcedure(thread_db* tdbb, MetaId id, ObjectBase::Flag flags); + static IndexStatus getIndexStatus(bool nullFlag, int inactive); + static MetadataCache* get(thread_db* tdbb) + { + return getCache(tdbb); + } + + template + static bool get_char_coll_subtype(thread_db* tdbb, ID* id, const UCHAR* name, USHORT length) + { + fb_assert(id); + + TTypeId ttId; + bool rc = get_texttype(tdbb, &ttId, name, length); + *id = ttId; + return rc; + } + +private: + static bool get_texttype(thread_db* tdbb, TTypeId* id, const UCHAR* name, USHORT length); + +public: + bool resolve_charset_and_collation(thread_db* tdbb, TTypeId* id, + const UCHAR* charset, const UCHAR* collation); + static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, sym_type type, const QualifiedName& name); + static void dsql_cache_release(thread_db* tdbb, sym_type type, const MetaName& name, const MetaName& package = ""); + static bool dsql_cache_use(thread_db* tdbb, sym_type type, const MetaName& name, const MetaName& package = ""); + // end of former met_proto.h + + static CharSetVers* lookup_charset(thread_db* tdbb, CSetId id, ObjectBase::Flag flags); + + static void release_temp_tables(thread_db* tdbb, jrd_tra* transaction); + static void retain_temp_tables(thread_db* tdbb, jrd_tra* transaction, TraNumber new_number); + + MdcVersion getVersion() + { + return mdc_version.load(std::memory_order_relaxed); + } + + MdcVersion nextVersion() + { + return ++mdc_version; + } + + SLONG lookupSequence(thread_db*, const MetaName& genName) + { + return mdc_generators.lookup(genName); + } + + void setSequence(thread_db*, SLONG id, const MetaName& name) + { + mdc_generators.store(id, name); + } + + bool getSequence(thread_db*, SLONG id, MetaName& name) + { + return mdc_generators.lookup(id, name); + } + + template + static C* oldVersion(thread_db* tdbb, MetaId id) + { + auto& vector = Vector::get(getCache(tdbb)); + auto* vrsn = vector.getObject(tdbb, id, CacheFlag::AUTOCREATE); + return vrsn ? getPermanent(vrsn) : nullptr; + } + + template + static C* newVersion(thread_db* tdbb, MetaId id) + { + auto& vector = Vector::get(getCache(tdbb)); + auto* vrsn = vector.makeObject(tdbb, id, CacheFlag::NOCOMMIT); + return vrsn ? getPermanent(vrsn) : nullptr; + } + + template + static void tagForUpdate(thread_db* tdbb, MetaId id) + { + auto& vector = Vector::get(getCache(tdbb)); + vector.tagForUpdate(tdbb, id); + } + + template + static C* erase(thread_db* tdbb, MetaId id) + { + auto& vector = Vector::get(getCache(tdbb)); + return vector.erase(tdbb, id); + } + +private: + // Hack with "typename Dummy" is needed to avoid incomplete support of c++ standard in gcc14. + // Hack changes explicit specialization to partial. + // When in-class explicit specializations are implemented - to be cleaned up. + template + class Vector + { + public: + static CacheVector& get(MetadataCache*); + }; + + // specialization + template + class Vector + { + public: + static CacheVector& get(MetadataCache* mdc) + { + return mdc->mdc_relations; + } + }; + + template + class Vector + { + public: + static CacheVector& get(MetadataCache* mdc) + { + return mdc->mdc_procedures; + } + }; + + template + class Vector + { + public: + static CacheVector& get(MetadataCache* mdc) + { + return mdc->mdc_charsets; + } + }; + + template + class Vector + { + public: + static CacheVector& get(MetadataCache* mdc) + { + return mdc->mdc_functions; + } + }; + + static MetadataCache* getCache(thread_db* tdbb); + + class GeneratorFinder + { + typedef Firebird::MutexLockGuard Guard; + + public: + explicit GeneratorFinder(MemoryPool& pool) + : m_objects(pool) + {} + + void store(SLONG id, const MetaName& name) + { + fb_assert(id >= 0); + fb_assert(name.hasData()); + + Guard g(m_tx, FB_FUNCTION); + + if (id < (int) m_objects.getCount()) + { + fb_assert(m_objects[id].isEmpty()); + m_objects[id] = name; + } + else + { + m_objects.resize(id + 1); + m_objects[id] = name; + } + } + + bool lookup(SLONG id, MetaName& name) + { + Guard g(m_tx, FB_FUNCTION); + + if (id < (int) m_objects.getCount() && m_objects[id].hasData()) + { + name = m_objects[id]; + return true; + } + + return false; + } + + SLONG lookup(const MetaName& name) + { + Guard g(m_tx, FB_FUNCTION); + + FB_SIZE_T pos; + + if (m_objects.find(name, pos)) + return (SLONG) pos; + + return -1; + } + + private: + Firebird::Array m_objects; + Firebird::Mutex m_tx; + }; + + class CleanupQueue + { + public: + CleanupQueue(MemoryPool& p); + + void enqueue(TraNumber traNum, ElementBase* toClean); + + void check(thread_db* tdbb, TraNumber oldest) + { + // We check transaction number w/o lock - that's OK here cause even in + // hardly imaginable case when correctly alligned memory read is not de-facto atomic + // the worst result we get is skipped check (will be corrected by next transaction) + // or taken extra lock for precise check. Not tragical. + + if (oldest > cq_traNum) + dequeue(tdbb, oldest); + } + + private: + struct Stored + { + TraNumber t; + ElementBase* c; + + Stored(TraNumber traNum, ElementBase* toClean) + : t(traNum), c(toClean) + { } + + Stored() // let HalfStatic work + { } + }; + + Firebird::Mutex cq_mutex; + Firebird::HalfStaticArray cq_data; + TraNumber cq_traNum = MAX_TRA_NUMBER; + FB_SIZE_T cq_pos = 0; + + void dequeue(thread_db* tdbb, TraNumber oldest); + }; + + GeneratorFinder mdc_generators; + CacheVector mdc_relations; + CacheVector mdc_procedures; + CacheVector mdc_functions; // User defined functions + CacheVector mdc_charsets; // intl character set descriptions + TriggersSet mdc_triggers[DB_TRIGGERS_COUNT]; + + std::atomic mdc_version; // Current version of metadata cache (should have 2 nums???????????????) + CleanupQueue mdc_cleanup_queue; +}; + +} // namespace Jrd #endif // JRD_MET_H diff --git a/src/jrd/met_proto.h b/src/jrd/met_proto.h index 1182c814496..e5cd60a4c67 100644 --- a/src/jrd/met_proto.h +++ b/src/jrd/met_proto.h @@ -24,7 +24,12 @@ #ifndef JRD_MET_PROTO_H #define JRD_MET_PROTO_H +#include "../common/classes/array.h" +#include "../common/classes/TriState.h" #include "../jrd/MetaName.h" +#include "../jrd/Resources.h" + +#include struct dsc; @@ -37,6 +42,7 @@ namespace Jrd class Format; class jrd_rel; class CompilerScratch; + struct Dependency; class DmlNode; class Database; struct bid; @@ -46,46 +52,40 @@ namespace Jrd class DeferredWork; struct FieldInfo; class ExceptionItem; + class GeneratorItem; + class BlobFilter; + class RelationPermanent; + class Triggers; + class TrigArray; - // index status - enum IndexStatus - { - MET_object_active, - MET_object_deferred_active, - MET_object_inactive, - MET_object_unknown - }; -} + typedef Firebird::HalfStaticArray CharsetVariants; -struct SubtypeInfo -{ - SubtypeInfo() - : attributes(0), - ignoreAttributes(true) + struct SubtypeInfo { - } + SubtypeInfo() + : attributes(0), + ignoreAttributes(true) + { + } - Jrd::MetaName charsetName; - Jrd::MetaName collationName; - Jrd::MetaName baseCollationName; - USHORT attributes; - bool ignoreAttributes; - Firebird::UCharBuffer specificAttributes; -}; + CharsetVariants charsetName; + MetaName collationName; + MetaName baseCollationName; + USHORT attributes; + bool ignoreAttributes; + Firebird::UCharBuffer specificAttributes; + }; +} void MET_activate_shadow(Jrd::thread_db*); ULONG MET_align(const dsc*, ULONG); Jrd::DeferredWork* MET_change_fields(Jrd::thread_db*, Jrd::jrd_tra*, const dsc*); -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); -bool MET_dsql_cache_use(Jrd::thread_db* tdbb, Jrd::sym_type type, const Jrd::MetaName& name, const Jrd::MetaName& package = ""); -void MET_dsql_cache_release(Jrd::thread_db* tdbb, Jrd::sym_type type, const Jrd::MetaName& name, const Jrd::MetaName& package = ""); void MET_error(const TEXT*, ...); -Jrd::Format* MET_format(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); -bool MET_get_char_coll_subtype(Jrd::thread_db*, USHORT*, const UCHAR*, 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::Format* MET_format(Jrd::thread_db*, Jrd::RelationPermanent*, USHORT); +bool MET_get_char_coll_subtype_info(Jrd::thread_db*, USHORT, Jrd::SubtypeInfo* info); +Jrd::DmlNode* MET_get_dependencies(Jrd::thread_db*, Jrd::Cached::Relation*, const UCHAR*, const ULONG, Jrd::CompilerScratch*, Jrd::bid*, Jrd::Statement**, Jrd::CompilerScratch**, const Jrd::MetaName&, int, USHORT, Jrd::jrd_tra*, const Jrd::MetaName& = Jrd::MetaName()); @@ -93,10 +93,8 @@ Jrd::jrd_fld* MET_get_field(const Jrd::jrd_rel*, USHORT); 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); -void MET_load_db_triggers(Jrd::thread_db*, int); -void MET_load_ddl_triggers(Jrd::thread_db* tdbb); bool MET_load_exception(Jrd::thread_db*, Jrd::ExceptionItem&); -void MET_load_trigger(Jrd::thread_db*, Jrd::jrd_rel*, const Jrd::MetaName&, Jrd::TrigVector**); +void MET_load_trigger(Jrd::thread_db*, Jrd::jrd_rel*, const Jrd::MetaName&, std::function); 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*); @@ -106,34 +104,19 @@ 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(Jrd::thread_db*, Jrd::MetaName&, const Jrd::MetaName&, USHORT); -void MET_lookup_index_condition(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); -void MET_lookup_index_expression(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); +void MET_lookup_index_code(Jrd::thread_db* tdbb, Jrd::Cached::Relation* relation, Jrd::index_desc* idx); bool MET_lookup_index_expr_cond_blr(Jrd::thread_db* tdbb, const Jrd::MetaName& index_name, Jrd::bid& expr_blob_id, Jrd::bid& cond_blob_id); -SLONG MET_lookup_index_name(Jrd::thread_db*, const Jrd::MetaName&, SLONG*, Jrd::IndexStatus* status); -bool MET_lookup_partner(Jrd::thread_db*, Jrd::jrd_rel*, struct Jrd::index_desc*, const TEXT*); -Jrd::jrd_prc* MET_lookup_procedure(Jrd::thread_db*, const Jrd::QualifiedName&, bool); -Jrd::jrd_prc* MET_lookup_procedure_id(Jrd::thread_db*, USHORT, bool, bool, USHORT); -Jrd::jrd_rel* MET_lookup_relation(Jrd::thread_db*, const Jrd::MetaName&); -Jrd::jrd_rel* MET_lookup_relation_id(Jrd::thread_db*, SLONG, bool); -Jrd::DmlNode* MET_parse_blob(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::bid*, Jrd::CompilerScratch**, +bool MET_lookup_partner(Jrd::thread_db* tdbb, Jrd::RelationPermanent* relation, Jrd::index_desc* idx, + const TEXT* index_name); +Jrd::DmlNode* MET_parse_blob(Jrd::thread_db*, Jrd::Cached::Relation*, Jrd::bid*, Jrd::CompilerScratch**, Jrd::Statement**, bool, bool); -void MET_post_existence(Jrd::thread_db*, Jrd::jrd_rel*); void MET_prepare(Jrd::thread_db*, Jrd::jrd_tra*, USHORT, const UCHAR*); -Jrd::jrd_prc* MET_procedure(Jrd::thread_db*, USHORT, bool, USHORT); -Jrd::jrd_rel* MET_relation(Jrd::thread_db*, USHORT); void MET_release_existence(Jrd::thread_db*, Jrd::jrd_rel*); -void MET_release_trigger(Jrd::thread_db*, Jrd::TrigVector**, const Jrd::MetaName&); -void MET_release_triggers(Jrd::thread_db*, Jrd::TrigVector**, bool); -#ifdef DEV_BUILD -void MET_verify_cache(Jrd::thread_db*); -#endif -void MET_clear_cache(Jrd::thread_db*); -bool MET_routine_in_use(Jrd::thread_db*, Jrd::Routine*); 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_scan_partners(Jrd::thread_db*, Jrd::RelationPermanent*); +void MET_store_dependencies(Jrd::thread_db*, Firebird::Array&, Jrd::Cached::Relation*, + 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); @@ -141,11 +124,6 @@ void MET_get_domain(Jrd::thread_db*, MemoryPool& csbPool, const Jrd::MetaName&, Jrd::FieldInfo*); Jrd::MetaName MET_get_relation_field(Jrd::thread_db*, MemoryPool& csbPool, const Jrd::MetaName&, const Jrd::MetaName&, dsc*, Jrd::FieldInfo*); -void MET_update_partners(Jrd::thread_db*); - -void MET_store_dependencies(Jrd::thread_db*, Firebird::Array& dependencies, - const Jrd::jrd_rel*, const Jrd::MetaName&, int, Jrd::jrd_tra*); - int MET_get_linger(Jrd::thread_db*); Firebird::TriState MET_get_ss_definer(Jrd::thread_db*); diff --git a/src/jrd/misc/evl_string_test.cpp b/src/jrd/misc/evl_string_test.cpp index faf9fd5f734..798e9059bb0 100644 --- a/src/jrd/misc/evl_string_test.cpp +++ b/src/jrd/misc/evl_string_test.cpp @@ -89,7 +89,7 @@ class StringContainsEvaluator : public ContainsEvaluator int main() { - MemoryPool *p = MemoryPool::createPool(); + MemoryPool *p = MemoryPool::createPool(ALLOC_ARGS0); // The tests below attempt to do full code coverage for the LikeEvaluator // Every line of evl_string.h code is covered by the tests diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index 3f03c8f79cd..b4c53d061ff 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -201,7 +201,7 @@ SQUAD MOV_get_quad(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) int MOV_get_string_ptr(Jrd::thread_db* tdbb, const dsc* desc, - USHORT* ttype, + TTypeId* ttype, UCHAR** address, vary* temp, USHORT length) { /************************************** @@ -236,7 +236,7 @@ int MOV_get_string(Jrd::thread_db* tdbb, const dsc* desc, UCHAR** address, vary* * Functional description * **************************************/ - USHORT ttype; + TTypeId ttype; return MOV_get_string_ptr(tdbb, desc, &ttype, address, temp, length); } @@ -338,7 +338,7 @@ ISC_TIMESTAMP_TZ MOV_get_timestamp_tz(const dsc* desc) USHORT MOV_make_string(Jrd::thread_db* tdbb, const dsc* desc, - USHORT ttype, + TTypeId ttype, const char** address, vary* temp, USHORT length) @@ -368,7 +368,7 @@ USHORT MOV_make_string(Jrd::thread_db* tdbb, ULONG MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, - USHORT ttype, + TTypeId ttype, UCHAR** address, Jrd::MoveBuffer& buffer, bool limit) @@ -428,7 +428,7 @@ ULONG MOV_make_string2(Jrd::thread_db* tdbb, } -Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype, bool limit) +Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, TTypeId ttype, bool limit) { Jrd::MoveBuffer buffer; UCHAR* ptr; @@ -497,7 +497,7 @@ Int128 MOV_get_int128(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) namespace Jrd { -DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, USHORT charSetId) +DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, TTypeId charSetId) : maxLen(mLen) { const char* const NULL_KEY_STRING = "NULL"; @@ -511,7 +511,7 @@ DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, USHOR fb_assert(!desc->isBlob()); const bool isBinary = (desc->isText() && desc->getCharSet() == CS_BINARY); - value = MOV_make_string2(tdbb, desc, isBinary ? CS_BINARY : charSetId); + value = MOV_make_string2(tdbb, desc, isBinary ? TTypeId(CS_BINARY) : charSetId); const char* const str = value.c_str(); diff --git a/src/jrd/mov_proto.h b/src/jrd/mov_proto.h index d2200f6f6d6..9e98ef35a9a 100644 --- a/src/jrd/mov_proto.h +++ b/src/jrd/mov_proto.h @@ -40,7 +40,7 @@ SLONG MOV_get_long(Jrd::thread_db*, const dsc*, SSHORT); void MOV_get_metaname(Jrd::thread_db*, const dsc*, Jrd::MetaName&); SQUAD MOV_get_quad(Jrd::thread_db*, const dsc*, SSHORT); SINT64 MOV_get_int64(Jrd::thread_db*, const dsc*, SSHORT); -int MOV_get_string_ptr(Jrd::thread_db*, const dsc*, USHORT*, UCHAR**, vary*, USHORT); +int MOV_get_string_ptr(Jrd::thread_db*, const dsc*, TTypeId*, UCHAR**, vary*, USHORT); int MOV_get_string(Jrd::thread_db*, const dsc*, UCHAR**, vary*, USHORT); void MOV_get_string(Jrd::thread_db* tdbb, const dsc* desc, Firebird::string& str); GDS_DATE MOV_get_sql_date(const dsc*); @@ -48,9 +48,9 @@ GDS_TIME MOV_get_sql_time(const dsc*); ISC_TIME_TZ MOV_get_sql_time_tz(const dsc*); GDS_TIMESTAMP MOV_get_timestamp(const dsc*); ISC_TIMESTAMP_TZ MOV_get_timestamp_tz(const dsc*); -USHORT MOV_make_string(Jrd::thread_db*, const dsc*, USHORT, const char**, vary*, USHORT); -ULONG MOV_make_string2(Jrd::thread_db*, const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&, bool = true); -Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype, +USHORT MOV_make_string(Jrd::thread_db*, const dsc*, TTypeId, const char**, vary*, USHORT); +ULONG MOV_make_string2(Jrd::thread_db*, const dsc*, TTypeId, UCHAR**, Jrd::MoveBuffer&, bool = true); +Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, TTypeId ttype, bool limit = true); void MOV_move(Jrd::thread_db*, /*const*/ dsc*, dsc*); Firebird::Decimal64 MOV_get_dec64(Jrd::thread_db*, const dsc*); @@ -63,7 +63,7 @@ namespace Jrd class DescPrinter { public: - DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, USHORT charSetId); + DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, TTypeId charSetId); const Firebird::string& get() const { diff --git a/src/jrd/nbak.cpp b/src/jrd/nbak.cpp index c78072489ed..627f5d33bd9 100644 --- a/src/jrd/nbak.cpp +++ b/src/jrd/nbak.cpp @@ -34,7 +34,7 @@ #include "ods.h" #include "lck.h" #include "cch.h" -#include "lck_proto.h" +#include "lck.h" #include "pag_proto.h" #include "err_proto.h" #include "cch_proto.h" diff --git a/src/jrd/ods.h b/src/jrd/ods.h index 18ad4824e6f..9dd2dda4123 100644 --- a/src/jrd/ods.h +++ b/src/jrd/ods.h @@ -365,42 +365,61 @@ struct index_root_page { pag irt_header; USHORT irt_relation; // relation id (for consistency) - USHORT irt_count; // Number of indices + USHORT irt_count; // number of indices + ULONG irt_dummy; // so far used as a padding to ensure the same + // alignment between 32-bit and 64-bit builds struct irt_repeat { + friend class index_root_page; // to allow offset check for private members + private: - friend struct index_root_page; // to allow offset check for private members - ULONG irt_root; // page number of index root if irt_in_progress is NOT set, or - // highest 32 bit of transaction if irt_in_progress is set - ULONG irt_transaction; // transaction in progress (lowest 32 bits) + FB_UINT64 irt_transaction; // transaction in progress + ULONG irt_page_num; // page number + ULONG irt_page_space_id; // page space public: - USHORT irt_desc; // offset to key descriptions - UCHAR irt_keys; // number of keys in index - UCHAR irt_flags; + USHORT irt_desc; // offset to key descriptions + USHORT irt_flags; // index flags + UCHAR irt_state; // index state + UCHAR irt_keys; // number of keys in index + private: + USHORT irt_dummy; // alignment to 8-byte boundary - ULONG getRoot() const; - void setRoot(ULONG root_page); + private: + void setState(UCHAR newState); + public: + ULONG getRoot() const; TraNumber getTransaction() const; - void setTransaction(TraNumber traNumber); - bool isUsed() const; - + UCHAR getState() const; + + void setRoot(ULONG rootPage/*, ULONG pageSpaceId*/); + void setProgress(TraNumber traNumber); + void setRollback(ULONG rootPage/*, ULONG pageSpaceId*/, TraNumber traNumber); + void setRollback(TraNumber traNumber); + void setKill(TraNumber traNumber); + void setNormal(); + void setNormal(ULONG rootPage/*, ULONG pageSpaceId*/); + void setCommit(TraNumber traNumber); + void setDrop(TraNumber traNumber); + void setEmpty(); } irt_rpt[1]; - static_assert(sizeof(struct irt_repeat) == 12, "struct irt_repeat size mismatch"); - static_assert(offsetof(struct irt_repeat, irt_root) == 0, "irt_root offset mismatch"); - static_assert(offsetof(struct irt_repeat, irt_transaction) == 4, "irt_transaction offset mismatch"); - static_assert(offsetof(struct irt_repeat, irt_desc) == 8, "irt_desc offset mismatch"); - static_assert(offsetof(struct irt_repeat, irt_keys) == 10, "irt_keys offset mismatch"); - static_assert(offsetof(struct irt_repeat, irt_flags) == 11, "irt_flags offset mismatch"); + static_assert(sizeof(struct irt_repeat) == 24, "struct irt_repeat size mismatch"); + static_assert(offsetof(struct irt_repeat, irt_transaction) == 0, "irt_transaction offset mismatch"); + static_assert(offsetof(struct irt_repeat, irt_page_num) == 8, "irt_root offset mismatch"); + static_assert(offsetof(struct irt_repeat, irt_page_space_id) == 12, "irt_root offset mismatch"); + static_assert(offsetof(struct irt_repeat, irt_desc) == 16, "irt_desc offset mismatch"); + static_assert(offsetof(struct irt_repeat, irt_flags) == 18, "irt_flags offset mismatch"); + static_assert(offsetof(struct irt_repeat, irt_state) == 20, "irt_state offset mismatch"); + static_assert(offsetof(struct irt_repeat, irt_keys) == 21, "irt_keys offset mismatch"); }; -static_assert(sizeof(struct index_root_page) == 32, "struct index_root_page size mismatch"); +static_assert(sizeof(struct index_root_page) == 48, "struct index_root_page size mismatch"); static_assert(offsetof(struct index_root_page, irt_header) == 0, "irt_header offset mismatch"); static_assert(offsetof(struct index_root_page, irt_relation) == 16, "irt_relation offset mismatch"); static_assert(offsetof(struct index_root_page, irt_count) == 18, "irt_count offset mismatch"); -static_assert(offsetof(struct index_root_page, irt_rpt) == 20, "irt_rpt offset mismatch"); +static_assert(offsetof(struct index_root_page, irt_rpt) == 24, "irt_rpt offset mismatch"); // key descriptor @@ -416,49 +435,193 @@ static_assert(offsetof(struct irtd, irtd_field) == 0, "irtd_field offset mismatc static_assert(offsetof(struct irtd, irtd_itype) == 2, "irtd_itype offset mismatch"); static_assert(offsetof(struct irtd, irtd_selectivity) == 4, "irtd_selectivity offset mismatch"); +// possible index states +inline constexpr UCHAR irt_unused = 0; // empty slot +inline constexpr UCHAR irt_in_progress = 1; // under construction - sort & merge +inline constexpr UCHAR irt_rollback = 2; // index to be removed when irt_transaction dead (rolled back) +inline constexpr UCHAR irt_normal = 3; // normal working state of index +inline constexpr UCHAR irt_kill = 4; // index to be removed when irt_transaction ended (both commit/rollback) +inline constexpr UCHAR irt_commit = 5; // start index removal (switch to irt_drop) when irt_transaction committed +inline constexpr UCHAR irt_drop = 6; // index to be removed when OAT > irt_transaction + // irt_flags, must match the idx_flags (see btr.h) inline constexpr USHORT irt_unique = 1; inline constexpr USHORT irt_descending = 2; -inline constexpr USHORT irt_in_progress = 4; -inline constexpr USHORT irt_foreign = 8; -inline constexpr USHORT irt_primary = 16; -inline constexpr USHORT irt_expression = 32; -inline constexpr USHORT irt_condition = 64; +inline constexpr USHORT irt_foreign = 4; +inline constexpr USHORT irt_primary = 8; +inline constexpr USHORT irt_expression = 16; +inline constexpr USHORT irt_condition = 32; -inline ULONG index_root_page::irt_repeat::getRoot() const +/* + How does index state change: + +states for just created index: + irt_in_progress + create index / alter index active => irt_rollback + irt_rollback + on commit => irt_normal + on rollback delete index + alter index inactive => irt_kill + irt_kill + on commit / on rollback delete index + alter index active => irt_rollback + +states for existing index: + irt_normal + alter index inactive / drop index => irt_commit + on commit / on rollback no action + irt_commit + on commit => irt_drop + on rollback => irt_normal + alter index active => irt_normal + irt_drop + OAT > irt_transaction delete index + alter index active => irt_normal + +access in SELECTs: + irt_normal - all + irt_rollback - self transaction + irt_commit - other transactions + */ + +inline void index_root_page::irt_repeat::setState(UCHAR newState) { - return (irt_flags & irt_in_progress) ? 0 : irt_root; + fb_assert(newState <= irt_drop); + irt_state = newState; } -inline void index_root_page::irt_repeat::setRoot(ULONG root_page) +inline bool index_root_page::irt_repeat::isUsed() const { - irt_root = root_page; - irt_flags &= ~irt_in_progress; + return getState() != irt_unused; } -inline TraNumber index_root_page::irt_repeat::getTransaction() const +inline void index_root_page::irt_repeat::setEmpty() { - return (irt_flags & irt_in_progress) ? ((TraNumber) irt_root << BITS_PER_LONG) | irt_transaction : 0; + setState(irt_unused); + irt_transaction = 0; + irt_page_num = 0; + irt_page_space_id = 0; + irt_flags = 0; + irt_state = irt_unused; } -inline void index_root_page::irt_repeat::setTransaction(TraNumber traNumber) +inline void index_root_page::irt_repeat::setRoot(ULONG rootPage) { - irt_root = ULONG(traNumber >> BITS_PER_LONG); - irt_transaction = ULONG(traNumber); - irt_flags |= irt_in_progress; + fb_assert(irt_page_num); + fb_assert(rootPage); + + irt_page_num = rootPage; + irt_page_space_id = 0; } -inline bool index_root_page::irt_repeat::isUsed() const +inline void index_root_page::irt_repeat::setProgress(TraNumber traNumber) { - return (irt_flags & irt_in_progress) || (irt_root != 0); + fb_assert(getState() == irt_unused); + + irt_page_num = 0; + irt_page_space_id = 0; + irt_transaction = traNumber; + setState(irt_in_progress); } +inline void index_root_page::irt_repeat::setRollback(ULONG rootPage, TraNumber traNumber) +{ + fb_assert(getState() == irt_in_progress); + fb_assert(traNumber == irt_transaction); + fb_assert(!irt_page_num); + + irt_page_num = rootPage; + irt_page_space_id = 0; + irt_transaction = traNumber; + setState(irt_rollback); +} + +inline void index_root_page::irt_repeat::setRollback(TraNumber traNumber) +{ + fb_assert(getState() == irt_kill); + fb_assert(traNumber == irt_transaction); + fb_assert(irt_page_num); + + irt_transaction = traNumber; + setState(irt_rollback); +} + +inline void index_root_page::irt_repeat::setKill(TraNumber traNumber) +{ + fb_assert(getState() == irt_rollback); + fb_assert(irt_page_num); + fb_assert(traNumber == irt_transaction); + + irt_transaction = traNumber; + setState(irt_kill); +} + +inline void index_root_page::irt_repeat::setNormal() +{ + //create index mode ForRollback + fb_assert(getState() == irt_rollback + // deleted by current tra + || getState() == irt_commit + // deleted not long ago + || getState() == irt_drop); + fb_assert(irt_page_num); + fb_assert(irt_transaction); + + irt_transaction = 0; + setState(irt_normal); +} + +inline void index_root_page::irt_repeat::setNormal(ULONG rootPage) +{ + //create index mode AtOnce + fb_assert(getState() == irt_in_progress); + fb_assert(!irt_page_num); + fb_assert(rootPage); + + irt_transaction = 0; + irt_page_num = rootPage; + irt_page_space_id = 0; + setState(irt_normal); +} + +inline void index_root_page::irt_repeat::setCommit(TraNumber traNumber) +{ + fb_assert(getState() == irt_normal); + fb_assert(irt_page_num); + + irt_transaction = traNumber; + setState(irt_commit); +} + +inline void index_root_page::irt_repeat::setDrop(TraNumber traNumber) +{ + fb_assert(getState() == irt_commit); + fb_assert(irt_page_num); + irt_transaction = traNumber; + setState(irt_drop); +} + +inline UCHAR index_root_page::irt_repeat::getState() const +{ + return irt_state; +} + +inline ULONG index_root_page::irt_repeat::getRoot() const +{ + return getState() == irt_unused ? 0 : irt_page_num; +} + +inline TraNumber index_root_page::irt_repeat::getTransaction() const +{ + return irt_transaction; +} inline constexpr int STUFF_COUNT = 4; inline constexpr ULONG END_LEVEL = ~0; inline constexpr ULONG END_BUCKET = (~0u) << 1; + // Header page struct header_page diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index f40713f4638..ce42a8190d5 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -50,6 +50,7 @@ #include "../jrd/btr.h" #include "../jrd/sort.h" #include "../jrd/ini.h" +#include "../jrd/met.h" #include "../jrd/intl.h" #include "../jrd/Collation.h" #include "../common/gdsassert.h" @@ -62,7 +63,7 @@ #include "../jrd/err_proto.h" #include "../jrd/ext_proto.h" #include "../jrd/intl_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/par_proto.h" @@ -376,20 +377,16 @@ namespace return false; } - double getCardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) + double getCardinality(thread_db* tdbb, const Rsc::Rel& relation, const Format* format) { // Return the estimated cardinality for the given relation double cardinality = DEFAULT_CARDINALITY; - if (relation->rel_file) - cardinality = EXT_cardinality(tdbb, relation); - else if (!relation->isVirtual()) - { - MET_post_existence(tdbb, relation); - cardinality = DPM_cardinality(tdbb, relation, format); - MET_release_existence(tdbb, relation); - } + if (auto* extFile = relation()->getExtFile()) + cardinality = extFile->getCardinality(tdbb, relation(tdbb)); + else if (!relation()->isVirtual()) + cardinality = DPM_cardinality(tdbb, relation(tdbb), format); return MAX(cardinality, MINIMUM_CARDINALITY); } @@ -802,7 +799,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) for (auto iter = getConjuncts(); iter.hasData(); ++iter) { if (!(iter & CONJUNCT_USED) && - iter->deterministic() && + iter->deterministic(tdbb) && iter->computable(csb, INVALID_STREAM, false)) { compose(getPool(), &invariantBoolean, iter); @@ -1068,9 +1065,9 @@ 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); + CMP_post_access(tdbb, csb, tail->csb_relation()->getSecurityName(), + tail->csb_view ? tail->csb_view()->getId() : 0, + SCL_update, obj_relations, tail->csb_relation()->getName()); } rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb); @@ -1118,15 +1115,26 @@ void Optimizer::compileRelation(StreamType stream) tail->csb_idx = nullptr; - if (needIndices && !relation->rel_file && !relation->isVirtual()) + if (needIndices && !relation()->getExtFile() && !relation()->isVirtual()) { - const auto relPages = relation->getPages(tdbb); + const auto relPages = relation()->getPages(tdbb); IndexDescList idxList; - BTR_all(tdbb, relation, idxList, relPages); + BTR_all(tdbb, relation(), idxList, relPages); + + MetaId n = idxList.getCount(); + while (n--) + { + auto id = idxList[n].idx_id; + auto* idv = relation()->lookup_index(tdbb, id, CacheFlag::AUTOCREATE); + if (idv && idv->getActive() != MET_index_active) + idv = nullptr; + if (!idv) + idxList.remove(n); + } // if index stats is empty, update it for non-empty and not too big system relations - const bool updateStats = (relation->isSystem() && idxList.hasData() && + const bool updateStats = (relation()->isSystem() && idxList.hasData() && !tdbb->getDatabase()->readOnly() && (relPages->rel_data_pages > 0) && (relPages->rel_data_pages < 100)); @@ -1138,7 +1146,7 @@ void Optimizer::compileRelation(StreamType stream) if (idx.idx_selectivity <= 0.0f) { SelectivityList selectivity; - BTR_selectivity(tdbb, relation, idx.idx_id, selectivity); + BTR_selectivity(tdbb, relation(), idx.idx_id, selectivity); if (selectivity[0] > 0.0f) updated = true; } @@ -1147,7 +1155,7 @@ void Optimizer::compileRelation(StreamType stream) if (updated) { idxList.clear(); - BTR_all(tdbb, relation, idxList, relPages); + BTR_all(tdbb, relation(), idxList, relPages); } } @@ -1155,7 +1163,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()); } } @@ -1434,9 +1442,9 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, const auto relation = csb->csb_rpt[item.stream].csb_relation; if (relation && - !relation->rel_file && - !relation->rel_view_rse && - !relation->isVirtual()) + !relation()->getExtFile() && + !relation()->isView() && + !relation()->isVirtual()) { item.desc = nullptr; --fieldCount; @@ -1528,7 +1536,7 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, if (sort_key->skd_dtype == SKD_varying || sort_key->skd_dtype == SKD_cstring) { - if (desc->dsc_ttype() == ttype_binary) + if (desc->getTextType() == ttype_binary) sort_key->skd_flags |= SKD_binary; } @@ -1689,17 +1697,20 @@ void Optimizer::checkIndices() // Check to make sure that all indices are either used or marked not to be used, // and that there are no unused navigational indices - MetaName index_name; - for (const auto& idx : *tail->csb_idx) { if (!(idx.idx_runtime_flags & (idx_plan_dont_use | idx_used)) || ((idx.idx_runtime_flags & idx_plan_navigate) && !(idx.idx_runtime_flags & idx_navigate))) { + MetaName index_name; if (relation) - MET_lookup_index(tdbb, index_name, relation->rel_name, (USHORT) (idx.idx_id + 1)); - else - index_name = ""; + { + auto* idp = relation()->lookupIndex(tdbb, idx.idx_id, CacheFlag::AUTOCREATE); + if (idp) + index_name = idp->getName(); + } + if (!index_name.hasData()) + index_name = "***unknown***"; // index %s cannot be used in the specified plan if (isGbak) @@ -2581,15 +2592,15 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, Array dbkeyRanges; double scanSelectivity = MAXIMUM_SELECTIVITY; - if (relation->rel_file) + if (relation()->getExtFile()) { // External table rsb = FB_NEW_POOL(getPool()) ExternalTableScan(csb, alias, stream, relation); } - else if (relation->isVirtual()) + 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); @@ -2890,9 +2901,9 @@ string Optimizer::getStreamName(StreamType stream) string name; if (relation) - name = relation->rel_name.c_str(); + name = relation()->c_name(); else if (procedure) - name = procedure->getName().toString(); + name = procedure()->c_name(); if (alias && alias->hasData()) { @@ -2925,7 +2936,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()->c_name()); if (!csb_tail->csb_view) break; @@ -2942,9 +2953,9 @@ 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()->c_name(); else if (csb_tail->csb_procedure) - alias = csb_tail->csb_procedure->getName().toString(); + alias = csb_tail->csb_procedure()->c_name(); //// TODO: LocalTableSourceNode else fb_assert(false); diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index a8c78b1ab9e..939047f32f2 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -40,6 +40,7 @@ #include "../dsql/ExprNodes.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/exe.h" +#include "../jrd/Statement.h" #include "../jrd/recsrc/RecordSource.h" namespace Jrd { @@ -722,7 +723,7 @@ class Retrieval : private Firebird::PermanentStorage const bool innerFlag; const bool outerFlag; SortNode* const sort; - jrd_rel* relation; + Rsc::Rel relation; const bool createIndexScanNodes; const bool setConjunctionsMatched; Firebird::string alias; diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index ef6b11c6da5..399a93b82c7 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -31,6 +31,7 @@ #include "../jrd/intl.h" #include "../jrd/Collation.h" #include "../jrd/ods.h" +#include "../jrd/met.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/recsrc/RecordSource.h" #include "../dsql/BoolNodes.h" @@ -157,7 +158,6 @@ Retrieval::Retrieval(thread_db* aTdbb, Optimizer* opt, StreamType streamNumber, const auto tail = &csb->csb_rpt[stream]; relation = tail->csb_relation; - fb_assert(relation); if (!tail->csb_idx) return; @@ -173,7 +173,7 @@ Retrieval::Retrieval(thread_db* aTdbb, Optimizer* opt, StreamType streamNumber, if ((index.idx_flags & idx_condition) && !checkIndexCondition(index, matches)) continue; - const auto length = ROUNDUP(BTR_key_length(tdbb, relation, &index), sizeof(SLONG)); + const auto length = ROUNDUP(BTR_key_length(tdbb, relation(tdbb), &index), sizeof(SLONG)); // AB: Calculate the cardinality which should reflect the total number // of index pages for this index. @@ -249,7 +249,7 @@ InversionCandidate* Retrieval::getInversion() InversionCandidate* invCandidate = nullptr; - if (relation && !relation->rel_file && !relation->isVirtual()) + if (relation && !relation()->getExtFile() && !relation()->isVirtual()) { InversionCandidateList inversions; @@ -461,7 +461,7 @@ IndexTableScan* Retrieval::getNavigation(const InversionCandidate* candidate) const auto indexNode = makeIndexScanNode(scratch); const USHORT keyLength = - ROUNDUP(BTR_key_length(tdbb, relation, scratch->index), sizeof(SLONG)); + ROUNDUP(BTR_key_length(tdbb, relation(tdbb), scratch->index), sizeof(SLONG)); return FB_NEW_POOL(getPool()) IndexTableScan(csb, getAlias(), stream, relation, indexNode, keyLength, @@ -594,12 +594,12 @@ void Retrieval::analyzeNavigation(const InversionCandidateList& inversions) dsc desc; node->getDesc(tdbb, csb, &desc); - // ASF: "desc.dsc_ttype() > ttype_last_internal" is to avoid recursion + // ASF: "desc.getTextType() > ttype_last_internal" is to avoid recursion // when looking for charsets/collations - if (DTYPE_IS_TEXT(desc.dsc_dtype) && desc.dsc_ttype() > ttype_last_internal) + if (DTYPE_IS_TEXT(desc.dsc_dtype) && desc.getTextType() > ttype_last_internal) { - const TextType* const tt = INTL_texttype_lookup(tdbb, desc.dsc_ttype()); + auto tt = INTL_texttype_lookup(tdbb, desc.getTextType()); if (idx->idx_flags & idx_unique) { @@ -802,9 +802,9 @@ bool Retrieval::betterInversion(const InversionCandidate* inv1, bool Retrieval::checkIndexCondition(index_desc& idx, MatchedBooleanList& matches) const { - fb_assert(idx.idx_condition); + fb_assert(idx.idx_condition_node); - if (!idx.idx_condition->containsStream(0, true)) + if (!idx.idx_condition_node->containsStream(0, true)) return false; fb_assert(matches.isEmpty()); @@ -812,7 +812,7 @@ bool Retrieval::checkIndexCondition(index_desc& idx, MatchedBooleanList& matches auto iter = optimizer->getConjuncts(outerFlag, innerFlag); BoolExprNodeStack idxConjuncts; - const auto conjunctCount = optimizer->decomposeBoolean(idx.idx_condition, idxConjuncts); + const auto conjunctCount = optimizer->decomposeBoolean(idx.idx_condition_node, idxConjuncts); fb_assert(conjunctCount); idx.idx_fraction = MAXIMUM_SELECTIVITY; @@ -879,11 +879,11 @@ bool Retrieval::checkIndexCondition(index_desc& idx, MatchedBooleanList& matches bool Retrieval::checkIndexExpression(const index_desc* idx, ValueExprNode* node) const { - fb_assert(idx && idx->idx_expression); + fb_assert(idx && idx->idx_expression_node); // The desired expression can be hidden inside a derived expression node, // so try to recover it (see CORE-4118). - while (!idx->idx_expression->sameAs(node, true)) + while (!idx->idx_expression_node->sameAs(node, true)) { const auto derivedExpr = nodeAs(node); const auto cast = nodeAs(node); @@ -898,7 +898,7 @@ bool Retrieval::checkIndexExpression(const index_desc* idx, ValueExprNode* node) // Check the index for matching both the given stream and the given expression tree - return idx->idx_expression->containsStream(0, true) && + return idx->idx_expression_node->containsStream(0, true) && node->containsStream(stream, true); } @@ -1209,19 +1209,16 @@ InversionNode* Retrieval::makeIndexScanNode(IndexScratch* indexScratch) const const auto idx = indexScratch->index; auto& segments = indexScratch->segments; - // Check whether this is during a compile or during a SET INDEX operation - if (csb) - CMP_post_resource(&csb->csb_resources, relation, Resource::rsc_index, idx->idx_id); - else - { - CMP_post_resource(&tdbb->getRequest()->getStatement()->resources, relation, - Resource::rsc_index, idx->idx_id); - } + fb_assert(csb); // For external requests, determine index name (to be reported in plans) MetaName indexName; - if (!(csb->csb_g_flags & csb_internal)) - MET_lookup_index(tdbb, indexName, relation->rel_name, idx->idx_id + 1); + if (relation && !(csb->csb_g_flags & csb_internal)) + { + auto* idp = relation()->lookupIndex(tdbb, idx->idx_id, CacheFlag::AUTOCREATE); + if (idp) + indexName = idp->getName(); + } const auto retrieval = FB_NEW_POOL(getPool()) IndexRetrieval(getPool(), relation, idx, indexName); @@ -1439,7 +1436,7 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) for (auto otherInversion : inversions) { if (otherInversion->boolean && - idx->idx_condition->sameAs(otherInversion->boolean, true)) + idx->idx_condition_node->sameAs(otherInversion->boolean, true)) { otherInversion->used = true; } @@ -1784,7 +1781,7 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch, { // If index condition matches the boolean, this should not be // considered a match. Full index scan will be used instead. - if (idx->idx_condition->sameAs(boolean, true)) + if (idx->idx_condition_node->sameAs(boolean, true)) return false; } diff --git a/src/jrd/os/posix/unix.cpp b/src/jrd/os/posix/unix.cpp index 76071238def..9ebe112097e 100644 --- a/src/jrd/os/posix/unix.cpp +++ b/src/jrd/os/posix/unix.cpp @@ -70,7 +70,7 @@ #include "../common/isc_proto.h" #include "../common/isc_f_proto.h" #include "../common/os/isc_i_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/mov_proto.h" #include "../jrd/ods_proto.h" #include "../jrd/os/pio_proto.h" diff --git a/src/jrd/os/win32/winnt.cpp b/src/jrd/os/win32/winnt.cpp index fa9ea5bb1ea..aff9f4cd3ac 100644 --- a/src/jrd/os/win32/winnt.cpp +++ b/src/jrd/os/win32/winnt.cpp @@ -46,7 +46,7 @@ #include "../common/isc_proto.h" #include "../common/isc_f_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/mov_proto.h" #include "../jrd/os/pio_proto.h" #include "../common/classes/init.h" diff --git a/src/jrd/pag.cpp b/src/jrd/pag.cpp index 3c7c6835171..a88a716ec70 100644 --- a/src/jrd/pag.cpp +++ b/src/jrd/pag.cpp @@ -78,12 +78,13 @@ #include "../jrd/cch.h" #include "../jrd/nbak.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "../jrd/vio_debug.h" #include "../jrd/cch_proto.h" #include "../jrd/dpm_proto.h" #include "../jrd/err_proto.h" #include "../yvalve/gds_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/ods_proto.h" @@ -968,13 +969,13 @@ void PAG_header(thread_db* tdbb, bool info, const TriState newForceWrite) if (header->hdr_flags & hdr_SQL_dialect_3) dbb->dbb_flags |= DBB_DB_SQL_dialect_3; - jrd_rel* relation = MET_relation(tdbb, 0); + auto* relation = MetadataCache::lookupRelation(tdbb, 0, CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); RelationPages* relPages = relation->getBasePages(); if (!relPages->rel_pages) { // NS: There's no need to reassign first page for RDB$PAGES relation since // current code cannot change its location after database creation. - vcl* vector = vcl::newVector(*relation->rel_pool, 1); + vcl* vector = vcl::newVector(relation->getPool(), 1); relPages->rel_pages = vector; (*vector)[0] = header->hdr_PAGES; } @@ -2238,3 +2239,10 @@ void PAG_set_page_scn(thread_db* tdbb, win* window) CCH_precedence(tdbb, window, scn_page); } +#ifdef DEB_TDBB_BDBS +void PageNumber::print(const char* text) const +{ + printf("%s %d %d\n", text, pageSpaceID, pageNum); +} +#endif + diff --git a/src/jrd/pag.h b/src/jrd/pag.h index dc786782fad..4a8c9eda3a7 100644 --- a/src/jrd/pag.h +++ b/src/jrd/pag.h @@ -214,9 +214,7 @@ class PageNumber : pageNum(0), pageSpaceID(INVALID_PAGE_SPACE) { } - inline PageNumber(const PageNumber& from) - : pageNum(from.pageNum), pageSpaceID(from.pageSpaceID) - { } + inline PageNumber(const PageNumber& from) = default; inline ULONG getPageNum() const { @@ -259,13 +257,7 @@ class PageNumber memcpy(str, &val, sizeof(ULONG)); } - inline PageNumber& operator=(const PageNumber& from) - { - pageSpaceID = from.pageSpaceID; - // fb_assert(pageSpaceID != INVALID_PAGE_SPACE); - pageNum = from.pageNum; - return *this; - } + inline PageNumber& operator=(const PageNumber& from) = default; inline ULONG operator=(const ULONG from) { @@ -317,6 +309,10 @@ class PageNumber } */ +#ifdef DEB_TDBB_BDBS + void print(const char* text) const; +#endif + private: ULONG pageNum; USHORT pageSpaceID; diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 3d7442d49d9..7f9e21cfcf5 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -48,6 +48,7 @@ #include "../jrd/lls.h" #include "../jrd/scl.h" #include "../jrd/req.h" +#include "../jrd/Statement.h" #include "../jrd/blb.h" #include "../jrd/intl.h" #include "../jrd/met.h" @@ -68,6 +69,7 @@ #include "../dsql/BoolNodes.h" #include "../dsql/ExprNodes.h" #include "../dsql/StmtNodes.h" +#include "../jrd/intl_proto.h" using namespace Jrd; @@ -76,20 +78,20 @@ using namespace Firebird; static NodeParseFunc blr_parsers[256] = {NULL}; - static void par_error(BlrReader& blrReader, const Arg::StatusVector& v, bool isSyntaxError = true); static PlanNode* par_plan(thread_db*, CompilerScratch*); static void getBlrVersion(CompilerScratch* csb); static void parseSubRoutines(thread_db* tdbb, CompilerScratch* csb); static void setNodeLineColumn(CompilerScratch* csb, DmlNode* node, ULONG blrOffset); - +static void checkIndexStatus(CompilerScratch* csb, bool isGbak, IndexStatus idx_status, MetaName name, + Cached::Relation* relation); namespace { class BlrParseWrapper { public: - BlrParseWrapper(MemoryPool& pool, jrd_rel* relation, CompilerScratch* view_csb, + BlrParseWrapper(thread_db* tdbb, MemoryPool& pool, Cached::Relation* relation, CompilerScratch* view_csb, CompilerScratch** csb_ptr, const bool trigger, USHORT flags) : m_csbPtr(csb_ptr) { @@ -107,20 +109,20 @@ namespace StreamType stream = m_csb->nextStream(); CompilerScratch::csb_repeat* t1 = CMP_csb_element(m_csb, 0); t1->csb_flags |= csb_used | csb_active | csb_trigger; - t1->csb_relation = relation; + t1->csb_relation = m_csb->csb_resources->relations.registerResource(relation); 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 = relation; + t1->csb_relation = m_csb->csb_resources->relations.registerResource(relation); t1->csb_stream = stream; } else if (relation) { CompilerScratch::csb_repeat* t1 = CMP_csb_element(m_csb, 0); t1->csb_stream = m_csb->nextStream(); - t1->csb_relation = relation; + t1->csb_relation = m_csb->csb_resources->relations.registerResource(relation); t1->csb_flags = csb_used | csb_active; } @@ -163,12 +165,33 @@ namespace AutoPtr 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 // Parse blr, returning a compiler scratch block with the results. // Caller must do pool handling. -DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr_length, +DmlNode* PAR_blr(thread_db* tdbb, Cached::Relation* relation, const UCHAR* blr, ULONG blr_length, CompilerScratch* view_csb, CompilerScratch** csb_ptr, Statement** statementPtr, const bool trigger, USHORT flags) { @@ -178,7 +201,7 @@ DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr fb_print_blr(blr, blr_length, gds__trace_printer, 0, 0); #endif - BlrParseWrapper csb(*tdbb->getDefaultPool(), relation, view_csb, csb_ptr, trigger, flags); + BlrParseWrapper csb(tdbb, *tdbb->getDefaultPool(), relation, view_csb, csb_ptr, trigger, flags); csb->csb_blr_reader = BlrReader(blr, blr_length); @@ -200,11 +223,12 @@ DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr // Finish parse of memory nodes, returning a compiler scratch block with the results. // Caller must do pool handling. -void PAR_preparsed_node(thread_db* tdbb, jrd_rel* relation, DmlNode* node, +// !!!!!!!!!!!!!!! FixMe - pool handling in ExtEngineManager.cpp +void PAR_preparsed_node(thread_db* tdbb, Cached::Relation* relation, DmlNode* node, CompilerScratch* view_csb, CompilerScratch** csb_ptr, Statement** statementPtr, const bool trigger, USHORT flags) { - BlrParseWrapper csb(*tdbb->getDefaultPool(), relation, view_csb, csb_ptr, trigger, flags); + BlrParseWrapper csb(tdbb, *tdbb->getDefaultPool(), relation, view_csb, csb_ptr, trigger, flags); csb->blrVersion = 5; // blr_version5 csb->csb_node = node; @@ -216,7 +240,7 @@ void PAR_preparsed_node(thread_db* tdbb, jrd_rel* relation, DmlNode* node, // PAR_blr equivalent for validation expressions. // Validation expressions are boolean expressions, but may be prefixed with a blr_stmt_expr. -BoolExprNode* PAR_validation_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr_length, +BoolExprNode* PAR_validation_blr(thread_db* tdbb, Cached::Relation* relation, const UCHAR* blr, ULONG blr_length, CompilerScratch* view_csb, CompilerScratch** csb_ptr, USHORT flags) { SET_TDBB(tdbb); @@ -227,7 +251,7 @@ BoolExprNode* PAR_validation_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR fb_print_blr(blr, blr_length, gds__trace_printer, 0, 0); #endif - BlrParseWrapper csb(*tdbb->getDefaultPool(), relation, view_csb, csb_ptr, false, flags); + BlrParseWrapper csb(tdbb, *tdbb->getDefaultPool(), relation, view_csb, csb_ptr, false, flags); csb->csb_blr_reader = BlrReader(blr, blr_length); @@ -250,12 +274,12 @@ BoolExprNode* PAR_validation_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR // 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(); const USHORT dtype = blrReader.getByte(); - USHORT textType; + TTypeId textType; switch (dtype) { @@ -277,18 +301,18 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc) break; case blr_text2: - textType = blrReader.getWord(); + textType = VldTTypeId(tdbb, blrReader.getWord()); desc->makeText(blrReader.getWord(), textType); break; case blr_cstring2: desc->dsc_dtype = dtype_cstring; - desc->setTextType(blrReader.getWord()); + desc->setTextType(VldTTypeId(tdbb, blrReader.getWord())); desc->dsc_length = blrReader.getWord(); break; case blr_varying2: - textType = blrReader.getWord(); + textType = VldTTypeId(tdbb, blrReader.getWord()); desc->makeVarying(blrReader.getWord(), textType); break; @@ -382,7 +406,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 = blrReader.getWord(); + textType = VldTTypeId(tdbb, blrReader.getWord()); desc->dsc_scale = textType & 0xFF; // BLOB character set desc->dsc_flags = textType & 0xFF00; // BLOB collation break; @@ -425,14 +449,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); @@ -452,19 +476,15 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item if (dtype == blr_domain_name2) { - const USHORT ttype = csb->csb_blr_reader.getWord(); + const auto ttype = VldTTypeId(tdbb, csb->csb_blr_reader.getWord()); switch (desc->dsc_dtype) { case dtype_cstring: case dtype_text: case dtype_varying: - desc->setTextType(ttype); - break; - case dtype_blob: - desc->dsc_scale = ttype & 0xFF; // BLOB character set - desc->dsc_flags = ttype & 0xFF00; // BLOB collation + desc->setTextType(ttype); break; default: @@ -475,7 +495,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); } @@ -489,14 +509,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) @@ -517,19 +537,15 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item if (dtype == blr_column_name2) { - const USHORT ttype = csb->csb_blr_reader.getWord(); + const auto ttype = VldTTypeId(tdbb, csb->csb_blr_reader.getWord()); switch (desc->dsc_dtype) { case dtype_cstring: case dtype_text: case dtype_varying: - desc->setTextType(ttype); - break; - case dtype_blob: - desc->dsc_scale = ttype & 0xFF; // BLOB character set - desc->dsc_flags = ttype & 0xFF00; // BLOB collation + desc->setTextType(ttype); break; default: @@ -540,8 +556,11 @@ USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* item if (csb->collectingDependencies()) { - CompilerScratch::Dependency dependency(obj_relation); - dependency.relation = MET_lookup_relation(tdbb, *relationName); + Dependency dependency(obj_relation); + auto* rel = MetadataCache::lookupRelation(tdbb, *relationName, CacheFlag::AUTOCREATE); + if (!rel) + fatal_exception::raiseFmt("Unexpectedly lost relation %s\n", relationName->c_str()); + dependency.relation = rel; dependency.subName = fieldName; csb->addDependency(dependency); } @@ -551,14 +570,14 @@ 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; } if (csb->collectingDependencies() && desc->getTextType() != CS_NONE) { - CompilerScratch::Dependency dependency(obj_collation); - dependency.number = INTL_TEXT_TYPE(*desc); + Dependency dependency(obj_collation); + dependency.number = INTL_GET_TTYPE(desc); csb->addDependency(dependency); } @@ -604,7 +623,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); @@ -614,8 +633,8 @@ ValueExprNode* PAR_make_field(thread_db* tdbb, CompilerScratch* csb, USHORT cont const StreamType stream = csb->csb_rpt[context].csb_stream; - jrd_rel* const relation = csb->csb_rpt[stream].csb_relation; - jrd_prc* const procedure = csb->csb_rpt[stream].csb_procedure; + auto* const relation = csb->csb_rpt[stream].csb_relation(tdbb); + auto* const procedure = csb->csb_rpt[stream].csb_procedure(tdbb); const SSHORT id = relation ? MET_lookup_field(tdbb, relation, base_field) : @@ -897,13 +916,14 @@ 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) { - dependency.relation = csb->csb_rpt[stream].csb_relation; + dependency.relation = csb->csb_rpt[stream].csb_relation(); // How do I determine reliably this is a view? // At this time, rel_view_rse is still null. + // Add new flag to RelationPermanent ??????????? //if (is_view) // dependency.objType = obj_view; //else @@ -911,15 +931,15 @@ void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SS } else if (csb->csb_rpt[stream].csb_procedure) { - if (csb->csb_rpt[stream].csb_procedure->isSubRoutine()) + if (csb->csb_rpt[stream].csb_procedure()->isSubRoutine()) return; - dependency.procedure = csb->csb_rpt[stream].csb_procedure; + dependency.procedure = csb->csb_rpt[stream].csb_procedure(); dependency.objType = obj_procedure; } 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; @@ -927,6 +947,40 @@ void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SS } +static void checkIndexStatus(CompilerScratch* csb, bool isGbak, IndexStatus idx_status, MetaName name, + Cached::Relation* relation) +{ + switch(idx_status) + { + case MET_index_state_unknown: + case MET_index_inactive: + case MET_index_deferred_drop: + if (isGbak) + { + PAR_warning(Arg::Warning(isc_indexname) << Arg::Str(name) << + Arg::Str(relation->getName())); + } + else + { + PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) << + Arg::Str(relation->getName())); + } + break; + + case MET_index_deferred_active: + if (!isGbak) + { + PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) << + Arg::Str(relation->getName())); + } + break; + + case MET_index_active: + break; + } +} + + static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb) { /************************************** @@ -962,8 +1016,8 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb) // we have hit a stream; parse the context number and access type - jrd_rel* relation = nullptr; - jrd_prc* procedure = nullptr; + Cached::Relation* relation = nullptr; + Cached::Procedure* procedure = nullptr; if (blrOp == blr_retrieve) { @@ -988,7 +1042,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb) { const auto relationNode = RelationSourceNode::parse(tdbb, csb, blrOp, false); plan->recordSourceNode = relationNode; - relation = relationNode->relation; + relation = relationNode->relation(); break; } @@ -1003,7 +1057,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb) { const auto procedureNode = ProcedureSourceNode::parse(tdbb, csb, blrOp, false); plan->recordSourceNode = procedureNode; - procedure = procedureNode->procedure; + procedure = procedureNode->procedure(); break; } @@ -1051,45 +1105,24 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb) MetaName name; csb->csb_blr_reader.getMetaName(name); - SLONG relation_id; IndexStatus idx_status; - const SLONG index_id = MET_lookup_index_name(tdbb, name, &relation_id, &idx_status); - - if (idx_status == MET_object_unknown || idx_status == MET_object_inactive) - { - if (isGbak) - { - PAR_warning(Arg::Warning(isc_indexname) << Arg::Str(name) << - Arg::Str(relation->rel_name)); - } - else - { - PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) << - Arg::Str(relation->rel_name)); - } - } - else if (idx_status == MET_object_deferred_active) - { - if (!isGbak) - { - PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) << - Arg::Str(relation->rel_name)); - } - } + const ElementBase::ReturnedId index_id = + MetadataCache::lookup_index_name(tdbb, name, &idx_status); + checkIndexStatus(csb, isGbak, idx_status, name, relation); // save both the relation id and the index id, since // the relation could be a base relation of a view; // save the index name also, for convenience PlanNode::AccessItem& item = plan->accessType->items.add(); - item.relationId = relation_id; + item.relationId = relation->getId(); item.indexId = index_id; item.indexName = name; if (csb->collectingDependencies()) { - CompilerScratch::Dependency dependency(obj_index); - dependency.name = &item.indexName; + Dependency dependency(obj_index); + dependency.name = item.indexName; csb->addDependency(dependency); } @@ -1120,45 +1153,24 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb) MetaName name; csb->csb_blr_reader.getMetaName(name); - SLONG relation_id; IndexStatus idx_status; - const SLONG index_id = MET_lookup_index_name(tdbb, name, &relation_id, &idx_status); - - if (idx_status == MET_object_unknown || idx_status == MET_object_inactive) - { - if (isGbak) - { - PAR_warning(Arg::Warning(isc_indexname) << Arg::Str(name) << - Arg::Str(relation->rel_name)); - } - else - { - PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) << - Arg::Str(relation->rel_name)); - } - } - else if (idx_status == MET_object_deferred_active) - { - if (!isGbak) - { - PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) << - Arg::Str(relation->rel_name)); - } - } + const ElementBase::ReturnedId index_id = + MetadataCache::lookup_index_name(tdbb, name, &idx_status); + checkIndexStatus(csb, isGbak, idx_status, name, relation); // save both the relation id and the index id, since // the relation could be a base relation of a view; // save the index name also, for convenience PlanNode::AccessItem& item = plan->accessType->items.add(); - item.relationId = relation_id; + item.relationId = relation->getId(); item.indexId = index_id; item.indexName = name; if (csb->collectingDependencies()) { - CompilerScratch::Dependency dependency(obj_index); - dependency.name = &item.indexName; + Dependency dependency(obj_index); + dependency.name = item.indexName; csb->addDependency(dependency); } } @@ -1302,21 +1314,20 @@ 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(rse->rse_relations[iter]); if (!subNode) continue; - const RelationSourceNode* relNode = static_cast(subNode); - const jrd_rel* relation = relNode->relation; + const RelationSourceNode* relNode = static_cast(subNode); // ????????????????????????? + const auto* 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; diff --git a/src/jrd/par_proto.h b/src/jrd/par_proto.h index c72d71861c8..32a7da4e96b 100644 --- a/src/jrd/par_proto.h +++ b/src/jrd/par_proto.h @@ -47,17 +47,17 @@ struct dsc; Jrd::ValueListNode* PAR_args(Jrd::thread_db*, Jrd::CompilerScratch*, USHORT, USHORT); Jrd::ValueListNode* PAR_args(Jrd::thread_db*, Jrd::CompilerScratch*); -Jrd::DmlNode* PAR_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR*, ULONG blr_length, +Jrd::DmlNode* PAR_blr(Jrd::thread_db*, Jrd::Cached::Relation*, const UCHAR*, ULONG blr_length, Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::Statement**, const bool, USHORT); -void PAR_preparsed_node(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::DmlNode*, +void PAR_preparsed_node(Jrd::thread_db*, Jrd::Cached::Relation*, Jrd::DmlNode*, Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::Statement**, const bool, USHORT); -Jrd::BoolExprNode* PAR_validation_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR* blr, +Jrd::BoolExprNode* PAR_validation_blr(Jrd::thread_db*, Jrd::Cached::Relation*, const UCHAR* blr, ULONG blr_length, Jrd::CompilerScratch*, Jrd::CompilerScratch**, USHORT); StreamType PAR_context(Jrd::CompilerScratch*, SSHORT*); StreamType PAR_context2(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&); diff --git a/src/jrd/recsrc/BitmapTableScan.cpp b/src/jrd/recsrc/BitmapTableScan.cpp index df80b4e2ef5..1da09cdbfa7 100644 --- a/src/jrd/recsrc/BitmapTableScan.cpp +++ b/src/jrd/recsrc/BitmapTableScan.cpp @@ -36,7 +36,7 @@ using namespace Jrd; // --------------------------------------------- BitmapTableScan::BitmapTableScan(CompilerScratch* csb, const string& alias, - StreamType stream, jrd_rel* relation, + StreamType stream, Rsc::Rel relation, InversionNode* inversion, double selectivity) : RecordStream(csb, stream), m_alias(csb->csb_pool, alias), m_relation(relation), m_inversion(inversion) @@ -56,7 +56,7 @@ void BitmapTableScan::internalOpen(thread_db* tdbb) const impure->irsb_bitmap = EVL_bitmap(tdbb, m_inversion, NULL); record_param* const rpb = &request->req_rpb[m_stream]; - RLCK_reserve_relation(tdbb, request->req_transaction, m_relation, false); + RLCK_reserve_relation(tdbb, request->req_transaction, m_relation(), false); rpb->rpb_number.setValue(BOF_NUMBER); } @@ -140,14 +140,14 @@ void BitmapTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, uns { planEntry.className = "BitmapTableScan"; - planEntry.lines.add().text = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; + planEntry.lines.add().text = "Table " + printName(tdbb, m_relation()->c_name(), m_alias) + " Access By ID"; printOptInfo(planEntry.lines); printInversion(tdbb, m_inversion, planEntry.lines, true, 1, false); - planEntry.objectType = m_relation->getObjectType(); - planEntry.objectName = m_relation->rel_name; + planEntry.objectType = m_relation()->getObjectType(); + planEntry.objectName = m_relation()->getName(); - if (m_alias.hasData() && m_relation->rel_name != m_alias) + if (m_alias.hasData() && m_relation()->getName() != m_alias) planEntry.alias = m_alias; } diff --git a/src/jrd/recsrc/BufferedStream.cpp b/src/jrd/recsrc/BufferedStream.cpp index 06b95c57f4c..7837331a959 100644 --- a/src/jrd/recsrc/BufferedStream.cpp +++ b/src/jrd/recsrc/BufferedStream.cpp @@ -241,8 +241,8 @@ bool BufferedStream::internalGetRecord(thread_db* tdbb) const rpb->rpb_runtime_flags &= ~RPB_CLEAR_FLAGS; if (relation && - !relation->rel_file && - !relation->rel_view_rse && + !relation->getExtFile() && + !relation->isView() && !relation->isVirtual()) { rpb->rpb_runtime_flags |= RPB_refetch; @@ -256,7 +256,7 @@ bool BufferedStream::internalGetRecord(thread_db* tdbb) const // to upgrade the record format if (relation && !rpb->rpb_number.isValid()) - VIO_record(tdbb, rpb, MET_current(tdbb, relation), tdbb->getDefaultPool()); + VIO_record(tdbb, rpb, relation->currentFormat(), tdbb->getDefaultPool()); } const bool isNull = !EVL_field(relation, buffer_record, (USHORT) i, &from); diff --git a/src/jrd/recsrc/ExternalTableScan.cpp b/src/jrd/recsrc/ExternalTableScan.cpp index 47d5330955f..b98cdf964d8 100644 --- a/src/jrd/recsrc/ExternalTableScan.cpp +++ b/src/jrd/recsrc/ExternalTableScan.cpp @@ -38,7 +38,7 @@ using namespace Jrd; // -------------------------------- ExternalTableScan::ExternalTableScan(CompilerScratch* csb, const string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : RecordStream(csb, stream), m_relation(relation), m_alias(csb->csb_pool, alias) { m_impure = csb->allocImpure(); @@ -56,9 +56,7 @@ void ExternalTableScan::internalOpen(thread_db* tdbb) const record_param* const rpb = &request->req_rpb[m_stream]; rpb->getWindow(tdbb).win_flags = 0; - EXT_open(dbb, m_relation->rel_file); - - VIO_record(tdbb, rpb, MET_current(tdbb, m_relation), request->req_pool); + VIO_record(tdbb, rpb, m_relation(request->getResources())->currentFormat(), request->req_pool); impure->irsb_position = 0; rpb->rpb_number.setValue(BOF_NUMBER); @@ -92,7 +90,7 @@ bool ExternalTableScan::internalGetRecord(thread_db* tdbb) const rpb->rpb_runtime_flags &= ~RPB_CLEAR_FLAGS; - if (EXT_get(tdbb, rpb, impure->irsb_position)) + if (rpb->rpb_relation->getExtFile()->get(tdbb, rpb, impure->irsb_position)) { rpb->rpb_number.increment(); rpb->rpb_number.setValid(true); @@ -130,12 +128,12 @@ void ExternalTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, u { planEntry.className = "ExternalTableScan"; - planEntry.lines.add().text = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; + planEntry.lines.add().text = "Table " + printName(tdbb, m_relation()->c_name(), m_alias) + " Full Scan"; printOptInfo(planEntry.lines); - planEntry.objectType = m_relation->getObjectType(); - planEntry.objectName = m_relation->rel_name; + planEntry.objectType = m_relation()->getObjectType(); + planEntry.objectName = m_relation()->getName(); - if (m_alias.hasData() && m_relation->rel_name != m_alias) + if (m_alias.hasData() && m_relation()->getName() != m_alias) planEntry.alias = m_alias; } diff --git a/src/jrd/recsrc/FullTableScan.cpp b/src/jrd/recsrc/FullTableScan.cpp index dcc8c6a0416..78124fd528e 100644 --- a/src/jrd/recsrc/FullTableScan.cpp +++ b/src/jrd/recsrc/FullTableScan.cpp @@ -37,7 +37,7 @@ using namespace Jrd; // ------------------------------------------- FullTableScan::FullTableScan(CompilerScratch* csb, const string& alias, - StreamType stream, jrd_rel* relation, + StreamType stream, Rsc::Rel relation, const Array& dbkeyRanges) : RecordStream(csb, stream), m_alias(csb->csb_pool, alias), @@ -57,7 +57,7 @@ void FullTableScan::internalOpen(thread_db* tdbb) const impure->irsb_flags = irsb_open; - RLCK_reserve_relation(tdbb, request->req_transaction, m_relation, false); + RLCK_reserve_relation(tdbb, request->req_transaction, m_relation(), false); record_param* const rpb = &request->req_rpb[m_stream]; rpb->getWindow(tdbb).win_flags = 0; @@ -79,10 +79,10 @@ void FullTableScan::internalOpen(thread_db* tdbb) const BufferControl* const bcb = dbb->dbb_bcb; - if (attachment->isGbak() || DPM_data_pages(tdbb, m_relation) > bcb->bcb_count) + if (attachment->isGbak() || DPM_data_pages(tdbb, m_relation()) > bcb->bcb_count) { rpb->getWindow(tdbb).win_flags = WIN_large_scan; - rpb->rpb_org_scans = m_relation->rel_scan_count++; + rpb->rpb_org_scans = m_relation()->rel_scan_count++; } } @@ -125,9 +125,9 @@ void FullTableScan::close(thread_db* tdbb) const record_param* const rpb = &request->req_rpb[m_stream]; if ((rpb->getWindow(tdbb).win_flags & WIN_large_scan) && - m_relation->rel_scan_count) + m_relation()->rel_scan_count) { - m_relation->rel_scan_count--; + m_relation()->rel_scan_count--; } } } @@ -191,12 +191,12 @@ void FullTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsig else if (upperBounds) bounds += " (upper bound)"; - planEntry.lines.add().text = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds; + planEntry.lines.add().text = "Table " + printName(tdbb, m_relation()->c_name(), m_alias) + " Full Scan" + bounds; printOptInfo(planEntry.lines); - planEntry.objectType = m_relation->getObjectType(); - planEntry.objectName = m_relation->rel_name; + planEntry.objectType = m_relation()->getObjectType(); + planEntry.objectName = m_relation()->getName(); - if (m_alias.hasData() && m_relation->rel_name != m_alias) + if (m_alias.hasData() && m_relation()->getName() != m_alias) planEntry.alias = m_alias; } diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index 7319e4dd832..a35e15a2e2e 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -40,7 +40,7 @@ using namespace Jrd; // ------------------------------------ IndexTableScan::IndexTableScan(CompilerScratch* csb, const string& alias, - StreamType stream, jrd_rel* relation, + StreamType stream, Rsc::Rel relation, InversionNode* index, USHORT length, double selectivity) : RecordStream(csb, stream), @@ -69,7 +69,7 @@ void IndexTableScan::internalOpen(thread_db* tdbb) const impure->irsb_flags = irsb_first | irsb_open; record_param* const rpb = &request->req_rpb[m_stream]; - RLCK_reserve_relation(tdbb, request->req_transaction, m_relation, false); + RLCK_reserve_relation(tdbb, request->req_transaction, m_relation(), false); rpb->rpb_number.setValue(BOF_NUMBER); @@ -200,7 +200,7 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset); // find the last fetched position from the index - const USHORT pageSpaceID = m_relation->getPages(tdbb)->rel_pg_space_id; + const USHORT pageSpaceID = m_relation()->getPages(tdbb)->rel_pg_space_id; win window(pageSpaceID, impure->irsb_nav_page); const IndexRetrieval* const retrieval = m_index->retrieval; @@ -226,7 +226,7 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const memcpy(upper.key_data, impure->irsb_nav_data + m_length, upper.key_length); } - IndexKey recordKey(tdbb, m_relation, idx); + IndexKey recordKey(tdbb, m_relation(tdbb), idx); // Find the next interesting node. If necessary, skip to the next page. RecordNumber number; @@ -321,7 +321,7 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const { if (const auto result = recordKey.compose(rpb->rpb_record)) { - IndexErrorContext context(m_relation, idx); + IndexErrorContext context(m_relation(tdbb), idx); context.raise(tdbb, result, rpb->rpb_record); } @@ -380,15 +380,15 @@ void IndexTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsi { planEntry.className = "IndexTableScan"; - planEntry.lines.add().text = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; + planEntry.lines.add().text = "Table " + printName(tdbb, m_relation()->c_name(), m_alias) + " Access By ID"; printOptInfo(planEntry.lines); printInversion(tdbb, m_index, planEntry.lines, true, 1, true); - planEntry.objectType = m_relation->getObjectType(); - planEntry.objectName = m_relation->rel_name; + planEntry.objectType = m_relation()->getObjectType(); + planEntry.objectName = m_relation()->rel_name; - if (m_alias.hasData() && m_relation->rel_name != m_alias) + if (m_alias.hasData() && m_relation()->rel_name != m_alias) planEntry.alias = m_alias; if (m_inversion) diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index a351d4a99d9..ad9035aff48 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -19,6 +19,7 @@ #include "firebird.h" #include "../jrd/jrd.h" +#include "../jrd/met.h" #include "../jrd/intl.h" #include "../dsql/ExprNodes.h" #include "../dsql/StmtNodes.h" @@ -40,10 +41,10 @@ using namespace Jrd; // Data access: procedure scan // --------------------------- -ProcedureScan::ProcedureScan(CompilerScratch* csb, const string& alias, StreamType stream, - const jrd_prc* procedure, const ValueListNode* sourceList, +ProcedureScan::ProcedureScan(thread_db* tdbb, CompilerScratch* csb, const string& alias, StreamType stream, + const SubRoutine& procedure, const ValueListNode* sourceList, const ValueListNode* targetList, MessageNode* message) - : RecordStream(csb, stream, procedure->prc_record_format), m_alias(csb->csb_pool, alias), + : RecordStream(csb, stream, procedure(tdbb)->prc_record_format), m_alias(csb->csb_pool, alias), m_procedure(procedure), m_sourceList(sourceList), m_targetList(targetList), m_message(message) { m_impure = csb->allocImpure(); @@ -57,22 +58,25 @@ ProcedureScan::ProcedureScan(CompilerScratch* csb, const string& alias, StreamTy void ProcedureScan::internalOpen(thread_db* tdbb) const { - if (!m_procedure->isImplemented()) + Request* const request = tdbb->getRequest(); + + const jrd_prc* proc = m_procedure(request->getResources()); + + if (!proc->isImplemented()) { status_exception::raise( Arg::Gds(isc_proc_pack_not_implemented) << - Arg::Str(m_procedure->getName().identifier) << Arg::Str(m_procedure->getName().package)); + Arg::Str(m_procedure()->getName().identifier) << Arg::Str(m_procedure()->getName().package)); } - else if (!m_procedure->isDefined()) + else if (!proc->isDefined()) { status_exception::raise( - Arg::Gds(isc_prcnotdef) << Arg::Str(m_procedure->getName().toString()) << + Arg::Gds(isc_prcnotdef) << Arg::Str(m_procedure()->getName().toString()) << Arg::Gds(isc_modnotfound)); } - const_cast(m_procedure)->checkReload(tdbb); + proc->checkReload(tdbb); - Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -106,7 +110,7 @@ void ProcedureScan::internalOpen(thread_db* tdbb) const im = NULL; } - Request* const proc_request = m_procedure->getStatement()->findRequest(tdbb); + Request* const proc_request = proc->getStatement()->findRequest(tdbb); impure->irsb_req_handle = proc_request; // req_proc_fetch flag used only when fetching rows, so @@ -157,9 +161,10 @@ void ProcedureScan::close(thread_db* tdbb) const if (proc_request) { EXE_unwind(tdbb, proc_request); - proc_request->req_flags &= ~req_in_use; impure->irsb_req_handle = NULL; proc_request->req_attachment = NULL; + + proc_request->setUnused(); } delete [] impure->irsb_message; @@ -171,10 +176,12 @@ bool ProcedureScan::internalGetRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - UserId* invoker = m_procedure->invoker ? m_procedure->invoker : tdbb->getAttachment()->att_ss_user; + Request* const request = tdbb->getRequest(); + const jrd_prc* proc = m_procedure(request->getResources()); + + UserId* invoker = proc->invoker ? proc->invoker : tdbb->getAttachment()->att_ss_user; AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); - Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -184,7 +191,7 @@ bool ProcedureScan::internalGetRecord(thread_db* tdbb) const return false; } - const Format* const msg_format = m_procedure->getOutputFormat(); + const Format* const msg_format = proc->getOutputFormat(); const ULONG oml = msg_format->fmt_length; UCHAR* om = impure->irsb_message; @@ -264,14 +271,14 @@ void ProcedureScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsig { planEntry.className = "ProcedureScan"; - planEntry.lines.add().text = "Procedure " + printName(tdbb, m_procedure->getName().toString(), m_alias) + " Scan"; + planEntry.lines.add().text = "Procedure " + printName(tdbb, m_procedure()->getName().toString(), m_alias) + " Scan"; printOptInfo(planEntry.lines); planEntry.objectType = obj_procedure; - planEntry.packageName = m_procedure->getName().package; - planEntry.objectName = m_procedure->getName().identifier; + planEntry.packageName = m_procedure()->getName().package; + planEntry.objectName = m_procedure()->getName().identifier; - if (m_alias.hasData() && m_procedure->getName().toString() != m_alias) + if (m_alias.hasData() && m_procedure()->getName().toString() != m_alias) planEntry.alias = m_alias; } @@ -304,11 +311,11 @@ void ProcedureScan::assignParams(thread_db* tdbb, /* YYY for text formats that don't have trailing spaces */ if (len) { - const CHARSET_ID chid = DSC_GET_CHARSET(to_desc); + const CSetId chid = DSC_GET_CHARSET(to_desc); /* CVC: I don't know if we have to check for dynamic-127 charset here. If that is needed, the line above should be replaced by the commented code here. - CHARSET_ID chid = INTL_TTYPE(to_desc); + CSetId chid = INTL_TTYPE(to_desc); if (chid == ttype_dynamic) chid = INTL_charset(tdbb, chid); */ diff --git a/src/jrd/recsrc/RecordSource.cpp b/src/jrd/recsrc/RecordSource.cpp index 8cf68d9de07..53d13762a92 100644 --- a/src/jrd/recsrc/RecordSource.cpp +++ b/src/jrd/recsrc/RecordSource.cpp @@ -325,7 +325,7 @@ WriteLockResult RecordStream::lockRecord(thread_db* tdbb) const fb_assert(relation && !relation->rel_view_rse); - RLCK_reserve_relation(tdbb, transaction, relation, true); + RLCK_reserve_relation(tdbb, transaction, relation->rel_perm, true); return VIO_writelock(tdbb, rpb, transaction); } diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index c6a1b7bf023..757d0d7d2f2 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -235,7 +235,7 @@ namespace Jrd public: FullTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation, + StreamType stream, Rsc::Rel relation, const Firebird::Array& dbkeyRanges); void close(thread_db* tdbb) const override; @@ -249,7 +249,7 @@ namespace Jrd private: const Firebird::string m_alias; - jrd_rel* const m_relation; + const Rsc::Rel m_relation; Firebird::Array m_dbkeyRanges; }; @@ -262,7 +262,7 @@ namespace Jrd public: BitmapTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation, + StreamType stream, Rsc::Rel relation, InversionNode* inversion, double selectivity); void close(thread_db* tdbb) const override; @@ -276,7 +276,7 @@ namespace Jrd private: const Firebird::string m_alias; - jrd_rel* const m_relation; + const Rsc::Rel m_relation; NestConst const m_inversion; }; @@ -303,7 +303,7 @@ namespace Jrd public: IndexTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation, + StreamType stream, Rsc::Rel relation, InversionNode* index, USHORT keyLength, double selectivity); @@ -336,7 +336,7 @@ namespace Jrd bool setupBitmaps(thread_db* tdbb, Impure* impure) const; const Firebird::string m_alias; - jrd_rel* const m_relation; + const Rsc::Rel m_relation; NestConst const m_index; NestConst m_inversion; NestConst m_condition; @@ -353,7 +353,7 @@ namespace Jrd public: ExternalTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation); + StreamType stream, Rsc::Rel relation); void close(thread_db* tdbb) const override; @@ -368,7 +368,7 @@ namespace Jrd void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override; private: - jrd_rel* const m_relation; + const Rsc::Rel m_relation; const Firebird::string m_alias; }; @@ -376,7 +376,7 @@ namespace Jrd { public: VirtualTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation); + StreamType stream, Rsc::Rel relation); void close(thread_db* tdbb) const override; @@ -390,12 +390,12 @@ namespace Jrd void internalOpen(thread_db* tdbb) const override; bool internalGetRecord(thread_db* tdbb) const override; - virtual const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const = 0; + virtual const Format* getFormat(thread_db* tdbb, RelationPermanent* relation) const = 0; virtual bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const = 0; private: - jrd_rel* const m_relation; + const Rsc::Rel m_relation; const Firebird::string m_alias; }; @@ -408,8 +408,8 @@ namespace Jrd }; public: - ProcedureScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, - const jrd_prc* procedure, const ValueListNode* sourceList, + ProcedureScan(thread_db* tdbb, CompilerScratch* csb, const Firebird::string& alias, + StreamType stream, const SubRoutine& procedure, const ValueListNode* sourceList, const ValueListNode* targetList, MessageNode* message); void close(thread_db* tdbb) const override; @@ -429,7 +429,7 @@ namespace Jrd const UCHAR* msg, const dsc* to_desc, SSHORT to_id, Record* record) const; const Firebird::string m_alias; - const jrd_prc* const m_procedure; + const SubRoutine m_procedure; const ValueListNode* m_sourceList; const ValueListNode* m_targetList; NestConst const m_message; diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index 4576f9cdbe1..e4b9817b073 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -388,8 +388,8 @@ void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const const auto refetch = (id == ID_TRANS); if (refetch && relation && - !relation->rel_file && - !relation->rel_view_rse && + !relation->getExtFile() && + !relation->isView() && !relation->isVirtual()) { if (m_map->flags & FLAG_REFETCH) @@ -422,7 +422,7 @@ void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const // BEWARE: This check depends on the fact that ID_DBKEY_VALID flags are stored // *after* real fields and ID_TRANS / ID_DBKEY values. if (relation && !rpb->rpb_number.isValid()) - VIO_record(tdbb, rpb, MET_current(tdbb, relation), tdbb->getDefaultPool()); + VIO_record(tdbb, rpb, relation->currentFormat(), tdbb->getDefaultPool()); } const auto record = rpb->rpb_record; @@ -451,7 +451,7 @@ void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const // Ensure the record is still in the most recent format const auto record = - VIO_record(tdbb, rpb, MET_current(tdbb, relation), tdbb->getDefaultPool()); + VIO_record(tdbb, rpb, relation->currentFormat(), tdbb->getDefaultPool()); // Set all fields to NULL if the stream was originally marked as invalid if (!rpb->rpb_number.isValid()) @@ -482,7 +482,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)) { diff --git a/src/jrd/recsrc/VirtualTableScan.cpp b/src/jrd/recsrc/VirtualTableScan.cpp index 8b7200f77b8..ce2c28cc0f2 100644 --- a/src/jrd/recsrc/VirtualTableScan.cpp +++ b/src/jrd/recsrc/VirtualTableScan.cpp @@ -37,7 +37,7 @@ using namespace Jrd; // ------------------------------- VirtualTableScan::VirtualTableScan(CompilerScratch* csb, const string& alias, - StreamType stream, jrd_rel* relation) + StreamType stream, Rsc::Rel relation) : RecordStream(csb, stream), m_relation(relation), m_alias(csb->csb_pool, alias) { m_impure = csb->allocImpure(); @@ -54,7 +54,7 @@ void VirtualTableScan::internalOpen(thread_db* tdbb) const record_param* const rpb = &request->req_rpb[m_stream]; rpb->getWindow(tdbb).win_flags = 0; - VIO_record(tdbb, rpb, getFormat(tdbb, m_relation), request->req_pool); + VIO_record(tdbb, rpb, getFormat(tdbb, m_relation()), request->req_pool); rpb->rpb_number.setValue(BOF_NUMBER); } @@ -89,7 +89,7 @@ bool VirtualTableScan::internalGetRecord(thread_db* tdbb) const rpb->rpb_number.increment(); - if (retrieveRecord(tdbb, m_relation, rpb->rpb_number.getValue(), rpb->rpb_record)) + if (retrieveRecord(tdbb, m_relation(request->getResources()), rpb->rpb_number.getValue(), rpb->rpb_record)) { rpb->rpb_number.setValid(true); return true; @@ -124,12 +124,12 @@ void VirtualTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, un { planEntry.className = "VirtualTableScan"; - planEntry.lines.add().text = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; + planEntry.lines.add().text = "Table " + printName(tdbb, m_relation()->c_name(), m_alias) + " Full Scan"; printOptInfo(planEntry.lines); - planEntry.objectType = m_relation->getObjectType(); - planEntry.objectName = m_relation->rel_name; + planEntry.objectType = m_relation()->getObjectType(); + planEntry.objectName = m_relation()->getName(); - if (m_alias.hasData() && m_relation->rel_name != m_alias) + if (m_alias.hasData() && m_relation()->getName() != m_alias) planEntry.alias = m_alias; } diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index c9fae3bf2ea..d4ac06aa1a6 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -25,7 +25,9 @@ #include "../jrd/jrd.h" #include "../jrd/blb.h" #include "../jrd/req.h" +#include "../jrd/Statement.h" #include "../jrd/ini.h" +#include "../jrd/met.h" #include "ibase.h" #include "../jrd/btr_proto.h" #include "../jrd/cch_proto.h" @@ -33,7 +35,7 @@ #include "../jrd/dpm_proto.h" #include "../jrd/idx_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/rlck_proto.h" @@ -238,7 +240,7 @@ Applier* Applier::create(thread_db* tdbb) status_exception::raise(Arg::Gds(isc_miss_prvlg) << "REPLICATE_INTO_DATABASE"); Request* request = nullptr; - const auto req_pool = attachment->createPool(); + const auto req_pool = dbb->createPool(ALLOC_ARGS0); try { @@ -254,7 +256,7 @@ Applier* Applier::create(thread_db* tdbb) if (request) CMP_release(tdbb, request); else - attachment->deletePool(req_pool); + dbb->deletePool(req_pool); throw; } @@ -408,8 +410,8 @@ void Applier::process(thread_db* tdbb, ULONG length, const UCHAR* data) case opExecuteSqlIntl: { const auto ownerName = reader.getMetaName(); - const unsigned charset = - (op == opExecuteSql) ? CS_UTF8 : reader.getByte(); + const auto charset = + (op == opExecuteSql) ? CS_UTF8 : CSetId(reader.getByte()); const string sql = reader.getString(); executeSql(tdbb, traNum, charset, sql, ownerName); } @@ -552,13 +554,10 @@ void Applier::insertRecord(thread_db* tdbb, TraNumber traNum, TRA_attach_request(transaction, m_request); - const auto relation = MET_lookup_relation(tdbb, relName); + const auto relation = MetadataCache::lookup_relation(tdbb, relName, CacheFlag::AUTOCREATE); if (!relation) raiseError("Table %s is not found", relName.c_str()); - if (!(relation->rel_flags & REL_scanned)) - MET_scan_relation(tdbb, relation); - const auto format = findFormat(tdbb, relation, length); record_param rpb; @@ -703,13 +702,10 @@ void Applier::updateRecord(thread_db* tdbb, TraNumber traNum, TRA_attach_request(transaction, m_request); - const auto relation = MET_lookup_relation(tdbb, relName); + const auto relation = MetadataCache::lookup_relation(tdbb, relName, CacheFlag::AUTOCREATE); if (!relation) raiseError("Table %s is not found", relName.c_str()); - if (!(relation->rel_flags & REL_scanned)) - MET_scan_relation(tdbb, relation); - const auto orgFormat = findFormat(tdbb, relation, orgLength); record_param orgRpb; @@ -844,13 +840,10 @@ void Applier::deleteRecord(thread_db* tdbb, TraNumber traNum, TRA_attach_request(transaction, m_request); - const auto relation = MET_lookup_relation(tdbb, relName); + const auto relation = MetadataCache::lookup_relation(tdbb, relName, CacheFlag::AUTOCREATE); if (!relation) raiseError("Table %s is not found", relName.c_str()); - if (!(relation->rel_flags & REL_scanned)) - MET_scan_relation(tdbb, relation); - const auto format = findFormat(tdbb, relation, length); record_param rpb; @@ -909,9 +902,9 @@ void Applier::deleteRecord(thread_db* tdbb, TraNumber traNum, void Applier::setSequence(thread_db* tdbb, const MetaName& genName, SINT64 value) { - const auto attachment = tdbb->getAttachment(); + const auto dbb = tdbb->getDatabase(); - auto gen_id = attachment->att_generators.lookup(genName); + auto gen_id = dbb->dbb_mdc->lookupSequence(tdbb, genName); if (gen_id < 0) { @@ -920,7 +913,7 @@ void Applier::setSequence(thread_db* tdbb, const MetaName& genName, SINT64 value if (gen_id < 0) raiseError("Generator %s is not found", genName.c_str()); - attachment->att_generators.store(gen_id, genName); + dbb->dbb_mdc->setSequence(tdbb, gen_id, genName); } AutoSetRestoreFlag noCascade(&tdbb->tdbb_flags, TDBB_repl_in_progress, !m_enableCascade); @@ -971,7 +964,7 @@ void Applier::storeBlob(thread_db* tdbb, TraNumber traNum, bid* blobId, void Applier::executeSql(thread_db* tdbb, TraNumber traNum, - unsigned charset, + CSetId charset, const string& sql, const MetaName& ownerName) { @@ -987,7 +980,7 @@ void Applier::executeSql(thread_db* tdbb, const auto dialect = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; - AutoSetRestore autoCharset(&attachment->att_charset, charset); + AutoSetRestore autoCharset(&attachment->att_charset, charset); UserId* const owner = attachment->getUserId(ownerName); AutoSetRestore autoOwner(&attachment->att_ss_user, owner); @@ -999,7 +992,7 @@ void Applier::executeSql(thread_db* tdbb, NULL, NULL, NULL, NULL, false); } -bool Applier::lookupKey(thread_db* tdbb, jrd_rel* relation, index_desc& key) +bool Applier::lookupKey(thread_db* tdbb, Cached::Relation* relation, index_desc& key) { RelationPages* const relPages = relation->getPages(tdbb); auto page = relPages->rel_index_root; @@ -1092,7 +1085,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; @@ -1102,18 +1095,20 @@ bool Applier::lookupRecord(thread_db* tdbb, if (idxName) { SLONG foundRelId; - IndexStatus idxStatus; - SLONG idx_id = MET_lookup_index_name(tdbb, idxName, &foundRelId, &idxStatus); + auto* idv = relation->getPermanent()->lookup_index(tdbb, idxName, CacheFlag::AUTOCREATE); - fb_assert(idxStatus == MET_object_active); - fb_assert(foundRelId == relation->rel_id); + if (idv) + { + auto idxStatus = idv->getActive(); + fb_assert(idxStatus == MET_index_active); - haveIdx = (idxStatus == MET_object_active) && (foundRelId == relation->rel_id) && - BTR_lookup(tdbb, relation, idx_id, &idx, relation->getPages(tdbb)); + haveIdx = (idxStatus == MET_index_active) && + BTR_lookup(tdbb, relation->getPermanent(), idv->getId(), &idx, relation->getPages(tdbb)); + } } if (!haveIdx) - haveIdx = lookupKey(tdbb, relation, idx); + haveIdx = lookupKey(tdbb, relation->getPermanent(), idx); if (haveIdx) { @@ -1137,7 +1132,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; @@ -1145,11 +1140,11 @@ 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(); - RLCK_reserve_relation(tdbb, transaction, relation, false); + RLCK_reserve_relation(tdbb, transaction, relation->getPermanent(), false); record_param rpb; rpb.rpb_relation = relation; @@ -1191,15 +1186,15 @@ bool Applier::lookupRecord(thread_db* tdbb, const Format* Applier::findFormat(thread_db* tdbb, jrd_rel* relation, ULONG length) { - auto format = MET_current(tdbb, relation); + auto format = relation->currentFormat(); while (format->fmt_length != length && format->fmt_version) - format = MET_format(tdbb, relation, format->fmt_version - 1); + format = MET_format(tdbb, relation->rel_perm, format->fmt_version - 1); 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; @@ -1213,7 +1208,7 @@ void Applier::doInsert(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) const auto format = record->getFormat(); const auto relation = rpb->rpb_relation; - RLCK_reserve_relation(tdbb, transaction, relation, true); + RLCK_reserve_relation(tdbb, transaction, relation->rel_perm, true); for (USHORT id = 0; id < format->fmt_count; id++) { @@ -1239,10 +1234,9 @@ void Applier::doInsert(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { const auto blob = current->bli_blob_object; fb_assert(blob); - blob->blb_relation = relation; blob->blb_sub_type = desc.getBlobSubType(); blob->blb_charset = desc.getCharSet(); - blobId->set_permanent(relation->rel_id, DPM_store_blob(tdbb, blob, 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(); @@ -1255,7 +1249,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()); } } } @@ -1297,7 +1291,7 @@ void Applier::doUpdate(thread_db* tdbb, record_param* orgRpb, record_param* newR const auto format = newRecord->getFormat(); const auto relation = newRpb->rpb_relation; - RLCK_reserve_relation(tdbb, transaction, relation, true); + RLCK_reserve_relation(tdbb, transaction, relation->rel_perm, true); for (USHORT id = 0; id < format->fmt_count; id++) { @@ -1335,10 +1329,9 @@ void Applier::doUpdate(thread_db* tdbb, record_param* orgRpb, record_param* newR { const auto blob = current->bli_blob_object; fb_assert(blob); - blob->blb_relation = relation; blob->blb_sub_type = desc.getBlobSubType(); blob->blb_charset = desc.getCharSet(); - dstBlobId->set_permanent(relation->rel_id, DPM_store_blob(tdbb, blob, 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(); @@ -1351,7 +1344,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()); } } } @@ -1388,7 +1381,7 @@ void Applier::doDelete(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { fb_assert(!(transaction->tra_flags & TRA_system)); - RLCK_reserve_relation(tdbb, transaction, rpb->rpb_relation, true); + RLCK_reserve_relation(tdbb, transaction, getPermanent(rpb->rpb_relation), true); Savepoint::ChangeMarker marker(transaction->tra_save_point); diff --git a/src/jrd/replication/Applier.h b/src/jrd/replication/Applier.h index f549ca6e72a..3af31a3cad6 100644 --- a/src/jrd/replication/Applier.h +++ b/src/jrd/replication/Applier.h @@ -181,11 +181,11 @@ namespace Jrd ULONG length, const UCHAR* data); void executeSql(thread_db* tdbb, TraNumber traNum, - unsigned charset, + CSetId charset, const Firebird::string& sql, const MetaName& owner); - bool lookupKey(thread_db* tdbb, jrd_rel* relation, index_desc& idx); + bool lookupKey(thread_db* tdbb, Cached::Relation* relation, index_desc& idx); bool compareKey(thread_db* tdbb, jrd_rel* relation, const index_desc& idx, Record* record1, Record* record2); diff --git a/src/jrd/replication/Publisher.cpp b/src/jrd/replication/Publisher.cpp index dd141d55dc5..58c850f6f5c 100644 --- a/src/jrd/replication/Publisher.cpp +++ b/src/jrd/replication/Publisher.cpp @@ -26,6 +26,7 @@ #include "../jrd/ods.h" #include "../jrd/req.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "firebird/impl/blr.h" #include "../jrd/trig.h" #include "../jrd/Database.h" @@ -253,11 +254,11 @@ namespace const auto attachment = tdbb->getAttachment(); const auto matcher = attachment->att_repl_matcher.get(); - if (matcher && !matcher->matchTable(relation->rel_name)) + if (matcher && !matcher->matchTable(relation->getName())) return false; } // Do not replicate RDB$BACKUP_HISTORY as it describes physical-level things - else if (relation->rel_id == rel_backup_history) + else if (relation->getId() == rel_backup_history) return false; return true; @@ -265,7 +266,7 @@ namespace Record* upgradeRecord(thread_db* tdbb, jrd_rel* relation, Record* record) { - const auto format = MET_current(tdbb, relation); + const auto format = relation->currentFormat(); if (record->getFormat()->fmt_version == format->fmt_version) return record; @@ -528,7 +529,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); @@ -578,7 +579,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); @@ -612,7 +613,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); @@ -637,13 +638,13 @@ void REPL_gen_id(thread_db* tdbb, SLONG genId, SINT64 value) if (!replicator) return; - const auto attachment = tdbb->getAttachment(); + const auto database = tdbb->getDatabase(); MetaName genName; - if (!attachment->att_generators.lookup(genId, genName)) + if (!database->dbb_mdc->getSequence(tdbb, genId, genName)) { MET_lookup_generator_id(tdbb, genId, genName, nullptr); - attachment->att_generators.store(genId, genName); + database->dbb_mdc->setSequence(tdbb, genId, genName); } fb_assert(genName.hasData()); diff --git a/src/jrd/req.h b/src/jrd/req.h index 23f6303d1f1..21ff7223d91 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -31,7 +31,6 @@ #include "../jrd/exe.h" #include "../jrd/sort.h" #include "../jrd/Attachment.h" -#include "../jrd/Statement.h" #include "../jrd/Record.h" #include "../jrd/RecordNumber.h" #include "../common/classes/timestamp.h" @@ -52,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), @@ -70,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 @@ -92,17 +91,50 @@ struct record_param USHORT rpb_runtime_flags; // runtime flags SSHORT rpb_org_scans; // relation scan count at stream open + RecordParameterBase& operator=(const RecordParameterBase&) = default; + void assign(const RecordParameterBase& from) + { + *this = from; + } + +protected: + struct win rpb_window; +}; + +struct RecordParameter : public RecordParameterBase +{ + RecordParameter() + : RecordParameterBase(), rpb_relation() + { } + + WIN& getWindow(thread_db* tdbb); // in Statement.cpp + + Rsc::Rel rpb_relation; // relation of record +}; + +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(getPermanent(rpb_relation)->getPages(tdbb)->rel_pg_space_id); } return rpb_window; } -private: - struct win rpb_window; + jrd_rel* rpb_relation; // relation of record + + // rpb_relation is not assigned here!!! + record_param& operator=(const RecordParameter& from) + { + assign(from); + return *this; + } }; // Record flags must be an exact replica of ODS record header flags @@ -163,6 +195,7 @@ class AffectedRows int modifiedRows; }; + // request block class Request : public pool_alloc @@ -311,31 +344,7 @@ class Request : public pool_alloc }; public: - 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, attachment->att_database), - 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(); - } + Request(Firebird::AutoMemoryPool& pool, Database* dbb, /*const*/ Statement* aStatement); Statement* getStatement() { @@ -347,52 +356,38 @@ class Request : public pool_alloc return statement; } - bool hasInternalStatement() const - { - return statement->flags & Statement::FLAG_INTERNAL; - } + bool hasInternalStatement() const; - bool hasPowerfulStatement() const - { - return statement->flags & Statement::FLAG_POWERFUL; - } + bool hasPowerfulStatement() const; void setAttachment(Attachment* newAttachment) { req_attachment = newAttachment; } - bool isRoot() const - { - return statement->requests.hasData() && this == statement->requests[0]; - } + bool isRoot() const; bool isRequestIdUnassigned() const { return req_id == 0; } - StmtNumber getRequestId() const - { - if (!req_id) - { - req_id = isRoot() ? - statement->getStatementId() : - JRD_get_thread_data()->getDatabase()->generateStatementId(); - } - - return req_id; - } + StmtNumber getRequestId() const; void setRequestId(StmtNumber id) { req_id = id; } + bool setUsed(); + void setUnused(); + bool isUsed() const; + private: Statement* const statement; mutable StmtNumber req_id; // request identifier TimeStampCache req_timeStampCache; // time stamp cache + std::atomic req_inUse; public: MemoryPool* req_pool; @@ -451,6 +446,20 @@ class Request : public pool_alloc StatusXcp req_last_xcp; // last known exception bool req_batch_mode; +private: + Firebird::RefPtr req_resources; + +public: + const Firebird::RefPtr& getResources() + { + return req_resources; + } + + void setResources(Firebird::RefPtr& r) + { + req_resources = r; + } + enum req_s { req_evaluate, req_return, @@ -461,6 +470,7 @@ class Request : public pool_alloc req_unwind } req_operation; // operation for next node + template T* getImpure(unsigned offset) { return reinterpret_cast(&impureArea[offset]); @@ -542,7 +552,7 @@ const ULONG req_null = 0x8L; const ULONG req_abort = 0x10L; const ULONG req_error_handler = 0x20L; // looper is called to handle error const ULONG req_warning = 0x40L; -const ULONG req_in_use = 0x80L; +//const ULONG req_in_use = 0x80L; const ULONG req_continue_loop = 0x100L; // PSQL continue statement const ULONG req_proc_fetch = 0x200L; // Fetch from procedure in progress const ULONG req_proc_select = 0x400L; // Select from procedure in progress @@ -551,19 +561,16 @@ const ULONG req_reserved = 0x1000L; // Request reserved for client const ULONG req_update_conflict = 0x2000L; // We need to restart request due to update conflict const ULONG req_restart_ready = 0x4000L; // Request is ready to restart in case of update conflict +} //namespace Jrd -// Index lock block - -class IndexLock : public pool_alloc +namespace Firebird { -public: - IndexLock* idl_next; // Next index lock block for relation - Lock* idl_lock; // Lock block - jrd_rel* idl_relation; // Parent relation - USHORT idl_id; // Index id - USHORT idl_count; // Use count -}; +template <> +inline void SimpleDelete::clear(Jrd::Request* req) +{ + req->setUnused(); +} -} //namespace Jrd +} #endif // JRD_REQ_H diff --git a/src/jrd/rlck.cpp b/src/jrd/rlck.cpp index deb912b5246..df0de82dccf 100644 --- a/src/jrd/rlck.cpp +++ b/src/jrd/rlck.cpp @@ -29,13 +29,13 @@ #include "../jrd/tra.h" #include "../jrd/lck.h" #include "../jrd/err_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/rlck_proto.h" using namespace Jrd; using namespace Firebird; -Lock* RLCK_reserve_relation(thread_db* tdbb, jrd_tra* transaction, jrd_rel* relation, bool write_flag) +Lock* RLCK_reserve_relation(thread_db* tdbb, jrd_tra* transaction, Cached::Relation* relation, bool write_flag) { /************************************** * @@ -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(); @@ -133,7 +133,7 @@ Lock* RLCK_reserve_relation(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rela } -Lock* RLCK_transaction_relation_lock(thread_db* tdbb, jrd_tra* transaction, jrd_rel* relation) +Lock* RLCK_transaction_relation_lock(thread_db* tdbb, jrd_tra* transaction, Cached::Relation* relation) { /************************************** * @@ -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* vector = transaction->tra_relation_locks; @@ -159,7 +159,7 @@ Lock* RLCK_transaction_relation_lock(thread_db* tdbb, jrd_tra* transaction, jrd_ vector = transaction->tra_relation_locks = vec::newVector(*transaction->tra_pool, transaction->tra_relation_locks, relId + 1); - lock = jrd_rel::createLock(tdbb, transaction->tra_pool, relation, LCK_relation, true); + lock = relation->createLock(tdbb, *transaction->tra_pool, LCK_relation, true); // enter all relation locks into the intra-process lock manager and treat // them as compatible within the attachment according to IPLM rules diff --git a/src/jrd/rlck_proto.h b/src/jrd/rlck_proto.h index 819795b9075..69486b4997b 100644 --- a/src/jrd/rlck_proto.h +++ b/src/jrd/rlck_proto.h @@ -26,15 +26,16 @@ namespace Jrd { class Lock; - class jrd_rel; class jrd_tra; struct record_param; class Attachment; class thread_db; } -Jrd::Lock* RLCK_reserve_relation(Jrd::thread_db*, Jrd::jrd_tra*, Jrd::jrd_rel*, bool); -Jrd::Lock* RLCK_transaction_relation_lock(Jrd::thread_db*, Jrd::jrd_tra*, Jrd::jrd_rel*); +#include "../jrd/Resources.h" + +Jrd::Lock* RLCK_reserve_relation(Jrd::thread_db*, Jrd::jrd_tra*, Jrd::Cached::Relation*, bool); +Jrd::Lock* RLCK_transaction_relation_lock(Jrd::thread_db*, Jrd::jrd_tra*, Jrd::Cached::Relation*); #endif // JRD_RLCK_PROTO_H diff --git a/src/jrd/rpb_chain.cpp b/src/jrd/rpb_chain.cpp index 8e204d7a86e..c7214481815 100644 --- a/src/jrd/rpb_chain.cpp +++ b/src/jrd/rpb_chain.cpp @@ -35,10 +35,10 @@ using namespace Jrd; int traRpbList::PushRpb(record_param* value) { - if (value->rpb_relation->rel_view_rse || // this is view - value->rpb_relation->rel_file || // this is external file - value->rpb_relation->isVirtual() || // this is virtual table - value->rpb_number.isBof()) // recno is a BOF marker + if (value->rpb_relation->rel_view_rse || // this is view + value->rpb_relation->getExtFile() || // this is external file + value->rpb_relation->isVirtual() || // this is virtual table + value->rpb_number.isBof()) // recno is a BOF marker { return -1; } @@ -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 diff --git a/src/jrd/rpb_chain.h b/src/jrd/rpb_chain.h index 6a53af7c773..468d6aaeae4 100644 --- a/src/jrd/rpb_chain.h +++ b/src/jrd/rpb_chain.h @@ -46,8 +46,8 @@ class traRpbListElement 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 getPermanent(i1.lr_rpb->rpb_relation)->rel_id != getPermanent(i2.lr_rpb->rpb_relation)->rel_id ? + getPermanent(i1.lr_rpb->rpb_relation)->rel_id > getPermanent(i2.lr_rpb->rpb_relation)->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; diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 7b48dd78af6..f5227676fb4 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -39,6 +39,7 @@ #include "../jrd/obj.h" #include "../jrd/req.h" #include "../jrd/tra.h" +#include "../jrd/met.h" #include "../common/gdsassert.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" @@ -906,10 +907,9 @@ SecurityClass::flags_t SCL_get_mask(thread_db* tdbb, const TEXT* relation_name, SecurityClass::flags_t access = ~0; // If there's a relation, track it down - jrd_rel* relation; - if (relation_name && (relation = MET_lookup_relation(tdbb, relation_name))) + Cached::Relation* relation; + if (relation_name && (relation = MetadataCache::lookupRelation(tdbb, relation_name, CacheFlag::AUTOCREATE))) { - MET_scan_relation(tdbb, relation); const SecurityClass* s_class; if ( (s_class = SCL_get_class(tdbb, relation->rel_security_name.c_str())) ) { @@ -918,12 +918,16 @@ SecurityClass::flags_t SCL_get_mask(thread_db* tdbb, const TEXT* relation_name, const jrd_fld* field; SSHORT id; - if (field_name && - (id = MET_lookup_field(tdbb, relation, field_name)) >= 0 && - (field = MET_get_field(relation, id)) && - (s_class = SCL_get_class(tdbb, field->fld_security_name.c_str()))) + if (field_name) { - access &= s_class->scl_flags; + auto* rel = relation->getObject(tdbb, CacheFlag::AUTOCREATE); + if (rel && + (id = MET_lookup_field(tdbb, rel, field_name)) >= 0 && + (field = MET_get_field(rel, id)) && + (s_class = SCL_get_class(tdbb, field->fld_security_name.c_str()))) + { + access &= s_class->scl_flags; + } } } diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 40d0394130f..a68014ca3d1 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -26,6 +26,7 @@ #include "../jrd/MetaName.h" #include "../common/classes/tree.h" +#include "../common/classes/Bits.h" #include "../common/security.h" #include "../jrd/obj.h" #include "../jrd/SystemPrivileges.h" @@ -94,107 +95,7 @@ const USHORT USR_sysdba = 4; // User detected as SYSDBA class UserId { public: - // Arbitrary size bitmask - template - class Bits - { - static const unsigned shift = 3; - static const unsigned bitmask = (1 << shift) - 1; - - static const unsigned L = (N >> shift) + (N & bitmask ? 1 : 0); - - public: - static const unsigned BYTES_COUNT = L; - - Bits() - { - clearAll(); - } - - Bits(const Bits& b) - { - assign(b); - } - - Bits& operator=(const Bits& b) - { - assign(b); - return *this; - } - - Bits& set(unsigned i) - { - fb_assert(i < N); - if (i < N) - data[index(i)] |= mask(i); - return *this; - } - - Bits& setAll() - { - memset(data, ~0, sizeof data); - return *this; - } - - Bits& clear(unsigned i) - { - fb_assert(i < N); - if (i < N) - data[index(i)] &= ~mask(i); - return *this; - } - - Bits& clearAll() - { - memset(data, 0, sizeof data); - return *this; - } - - bool test(unsigned int i) const - { - fb_assert(i < N); - if (i >= N) - return false; - return data[index(i)] & mask(i); - } - - void load(const void* from) - { - memcpy(data, from, sizeof data); - } - - void store(void* to) const - { - memcpy(to, data, sizeof data); - } - - Bits& operator|=(const Bits& b) - { - for (unsigned n = 0; n < L; ++n) - data[n] |= b.data[n]; - return *this; - } - - private: - UCHAR data[L]; - - void assign(const Bits& b) - { - memcpy(data, b.data, sizeof data); - } - - static unsigned index(unsigned i) - { - return i >> shift; - } - - static UCHAR mask(unsigned i) - { - return 1U << (i & bitmask); - } - }; - - typedef Bits Privileges; + typedef Firebird::Bits Privileges; private: Firebird::MetaString usr_user_name; // User name diff --git a/src/jrd/sdw.cpp b/src/jrd/sdw.cpp index 4e78682f33a..b00135addd0 100644 --- a/src/jrd/sdw.cpp +++ b/src/jrd/sdw.cpp @@ -42,7 +42,7 @@ #include "../common/isc_proto.h" #include "../common/isc_f_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/os/pio_proto.h" diff --git a/src/jrd/shut.cpp b/src/jrd/shut.cpp index 3801e7055db..5d571f27b80 100644 --- a/src/jrd/shut.cpp +++ b/src/jrd/shut.cpp @@ -31,7 +31,7 @@ #include "../jrd/cmp_proto.h" #include "../jrd/err_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/rlck_proto.h" #include "../jrd/shut_proto.h" #include "../jrd/tra_proto.h" diff --git a/src/jrd/sort.cpp b/src/jrd/sort.cpp index 348a375c0aa..eb712ae6fac 100644 --- a/src/jrd/sort.cpp +++ b/src/jrd/sort.cpp @@ -2187,7 +2187,6 @@ void SortOwner::releaseBuffer(UCHAR* memory) buffers.push(memory); } - void SortOwner::unlinkAll() { while (sorts.getCount()) @@ -2441,3 +2440,4 @@ sort_record* PartitionedSort::getMerge() return eof ? NULL : record; } + diff --git a/src/jrd/tdbb.h b/src/jrd/tdbb.h new file mode 100644 index 00000000000..5474e3f2960 --- /dev/null +++ b/src/jrd/tdbb.h @@ -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::Manager + +#include "../common/gdsassert.h" + +#include "../common/classes/Synchronize.h" + +#include "../jrd/RuntimeStatistics.h" +#include "../jrd/status.h" +#include "../jrd/err_proto.h" +#include "../jrd/intl.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; + +#ifdef USE_ITIMER +class TimeoutTimer final : + public Firebird::RefCntIface > +{ +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 +const ULONG TDBB_async = 32768; // Async context (set in AST) + +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 ; + 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 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); + + CSetId 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); + + if ((tdbb_flags & TDBB_async) && !attachment) + dbbStat->bumpValue(index, delta); + + // else dbbStat is adjusted from attStat, see Attachment::mergeAsyncStats() + } + + 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 m_saveTimer; + }; + +private: + Firebird::RefPtr 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 diff --git a/src/jrd/tpc.cpp b/src/jrd/tpc.cpp index 79a5115055b..f6b53aa77c3 100644 --- a/src/jrd/tpc.cpp +++ b/src/jrd/tpc.cpp @@ -27,7 +27,7 @@ #include "../jrd/tra.h" #include "../jrd/pag.h" #include "../jrd/cch_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/ods_proto.h" #include "../jrd/tpc_proto.h" #include "../jrd/tra_proto.h" diff --git a/src/jrd/tpc_proto.h b/src/jrd/tpc_proto.h index 2a3f476e696..f4d8f7bcb96 100644 --- a/src/jrd/tpc_proto.h +++ b/src/jrd/tpc_proto.h @@ -28,6 +28,7 @@ #include "../common/classes/array.h" #include "../common/classes/fb_string.h" #include "../common/classes/SyncObject.h" +#include "../jrd/tra.h" namespace Ods { @@ -159,6 +160,36 @@ class TipCache return m_tpcHeader->getHeader()->monitor_generation++ + 1; } + static int cacheState(thread_db* tdbb, TraNumber number) + { + CommitNumber stateCn = tdbb->getDatabase()->dbb_tip_cache->cacheState(number); + + switch (stateCn) + { + case CN_ACTIVE: return tra_active; + case CN_LIMBO: return tra_limbo; + case CN_DEAD: return tra_dead; + default: return tra_committed; + } + } + + // check state of transaction in which some object was created or dropped + template + static int traState(thread_db* tdbb, TraNumber traNum, PRESENCE&& objPresenceFunc, bool created) + { + int rc = cacheState(tdbb, traNum); + if (rc == tra_committed) // too old dead transaction may be reported as committed + { + // check presence of record for an object created in traNum + // if object really created => record should be present + // if object really dropped => record should be missing + // otherwise transaction is dead, not committed + if (objPresenceFunc() != created) + return tra_dead; + } + return rc; + } + private: class GlobalTpcHeader : public Firebird::MemoryHeader { @@ -336,14 +367,7 @@ class TipCache inline int TPC_cache_state(thread_db* tdbb, TraNumber number) { - CommitNumber stateCn = tdbb->getDatabase()->dbb_tip_cache->cacheState(number); - switch (stateCn) - { - case CN_ACTIVE: return tra_active; - case CN_LIMBO: return tra_limbo; - case CN_DEAD: return tra_dead; - default: return tra_committed; - } + return TipCache::cacheState(tdbb, number); } inline TraNumber TPC_find_states(thread_db* tdbb, TraNumber minNumber, TraNumber maxNumber, diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 61921fb1c99..3231e6a60e9 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -37,6 +37,7 @@ #include "../jrd/req.h" #include "../jrd/exe.h" #include "../jrd/extds/ExtDS.h" +#include "../jrd/met.h" #include "../jrd/intl_classes.h" #include "../common/ThreadStart.h" #include "../jrd/TimeZone.h" @@ -52,7 +53,7 @@ #include "../jrd/idx_proto.h" #include "../yvalve/gds_proto.h" #include "../common/isc_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" @@ -76,6 +77,7 @@ #include "../jrd/Mapping.h" #include "../jrd/DbCreators.h" #include "../common/os/fbsyslog.h" +#include "../jrd/Resources.h" const int DYN_MSG_FAC = 8; @@ -99,8 +101,6 @@ static tx_inv_page* fetch_inventory_page(thread_db*, WIN* window, ULONG sequence static const char* get_lockname_v3(const UCHAR lock); static ULONG inventory_page(thread_db*, ULONG); static int limbo_transaction(thread_db*, TraNumber id); -static void release_temp_tables(thread_db*, jrd_tra*); -static void retain_temp_tables(thread_db*, jrd_tra*, TraNumber); static void restart_requests(thread_db*, jrd_tra*); static void start_sweeper(thread_db*); //static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM); @@ -210,6 +210,7 @@ void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::Request* request) TRA_detach_request(request); } + fb_assert(request->isUsed()); fb_assert(request->req_transaction == NULL); fb_assert(request->req_tra_next == NULL); fb_assert(request->req_tra_prev == NULL); @@ -948,7 +949,7 @@ void TRA_update_counters(thread_db* tdbb, Database* dbb) } -void TRA_post_resources(thread_db* tdbb, jrd_tra* transaction, ResourceList& resources) +void jrd_tra::postResources(thread_db* tdbb, const Resources* resources) { /************************************** * @@ -957,53 +958,20 @@ void TRA_post_resources(thread_db* tdbb, jrd_tra* transaction, ResourceList& res ************************************** * * Functional description - * Post interest in relation/procedure/collation existence to transaction. - * This guarantees that the relation/procedure/collation won't be dropped - * out from under the transaction. + * Post interest in external relation existence to transaction. * **************************************/ SET_TDBB(tdbb); - Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool); - - for (Resource* rsc = resources.begin(); rsc < resources.end(); rsc++) + for (auto& rel : resources->relations) { - if (rsc->rsc_type == Resource::rsc_relation || - rsc->rsc_type == Resource::rsc_procedure || - rsc->rsc_type == Resource::rsc_function || - rsc->rsc_type == Resource::rsc_collation) + if (auto* ext = rel()->getExtFile()) { - FB_SIZE_T i; - if (!transaction->tra_resources.find(*rsc, i)) + FB_SIZE_T pos; + if (!traExtRel.find(ext, pos)) { - transaction->tra_resources.insert(i, *rsc); - switch (rsc->rsc_type) - { - case Resource::rsc_relation: - MET_post_existence(tdbb, rsc->rsc_rel); - if (rsc->rsc_rel->rel_file) { - EXT_tra_attach(rsc->rsc_rel->rel_file, transaction); - } - break; - case Resource::rsc_procedure: - case Resource::rsc_function: - rsc->rsc_routine->addRef(); -#ifdef DEBUG_PROCS - { - char buffer[256]; - sprintf(buffer, - "Called from TRA_post_resources():\n\t Incrementing use count of %s\n", - rsc->rsc_routine->prc_name->c_str()); - JRD_print_procedure_info(tdbb, buffer); - } -#endif - break; - case Resource::rsc_collation: - rsc->rsc_coll->incUseCount(tdbb); - break; - default: // shut up compiler warning - break; - } + ext->traAttach(tdbb); + traExtRel.insert(pos, ext); } } } @@ -1185,7 +1153,7 @@ jrd_tra* TRA_reconnect(thread_db* tdbb, const UCHAR* id, USHORT length) Arg::Gds(isc_tra_state) << Arg::Int64(number) << Arg::Str(text)); } - MemoryPool* const pool = attachment->createPool(); + MemoryPool* const pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, pool); jrd_tra* const trans = jrd_tra::create(pool, attachment, NULL); trans->tra_number = number; @@ -1262,32 +1230,11 @@ void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr TRA_detach_request(transaction->tra_requests); } - // Release interest in relation/procedure existence for transaction - - for (Resource* rsc = transaction->tra_resources.begin(); - rsc < transaction->tra_resources.end(); rsc++) - { - switch (rsc->rsc_type) - { - case Resource::rsc_relation: - MET_release_existence(tdbb, rsc->rsc_rel); - if (rsc->rsc_rel->rel_file) { - EXT_tra_detach(rsc->rsc_rel->rel_file, transaction); - } - break; - case Resource::rsc_procedure: - case Resource::rsc_function: - rsc->rsc_routine->release(tdbb); - break; - case Resource::rsc_collation: - rsc->rsc_coll->decUseCount(tdbb); - break; - default: - fb_assert(false); - } - } + // Care about used external files + while (transaction->traExtRel.hasData()) + transaction->traExtRel.pop()->traDetach(); - release_temp_tables(tdbb, transaction); + MetadataCache::release_temp_tables(tdbb, transaction); // Release the locks associated with the transaction @@ -1382,6 +1329,8 @@ void TRA_rollback(thread_db* tdbb, jrd_tra* transaction, const bool retaining_fl EDS::Transaction::jrdTransactionEnd(tdbb, transaction, false, retaining_flag, false /*force_flag ?*/); +// ?????????? transaction->rollbackCleanup(tdbb); + Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool); if (transaction->tra_flags & (TRA_prepare2 | TRA_reconnected)) @@ -1722,7 +1671,7 @@ jrd_tra* TRA_start(thread_db* tdbb, ULONG flags, SSHORT lock_timeout, Jrd::jrd_t // To handle the problems of relation locks, allocate a temporary // transaction block first, seize relation locks, then go ahead and // make up the real transaction block. - MemoryPool* const pool = outer ? outer->getAutonomousPool() : attachment->createPool(); + MemoryPool* const pool = outer ? outer->getAutonomousPool() : dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, pool); jrd_tra* const transaction = jrd_tra::create(pool, attachment, outer); @@ -1779,7 +1728,7 @@ jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb, Jrd::jrd_t // To handle the problems of relation locks, allocate a temporary // transaction block first, seize relation locks, then go ahead and // make up the real transaction block. - MemoryPool* const pool = outer ? outer->getAutonomousPool() : attachment->createPool(); + MemoryPool* const pool = outer ? outer->getAutonomousPool() : dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, pool); jrd_tra* const transaction = jrd_tra::create(pool, attachment, outer); @@ -1963,7 +1912,7 @@ void TRA_sweep(thread_db* tdbb) } -int TRA_wait(thread_db* tdbb, jrd_tra* trans, TraNumber number, jrd_tra::wait_t wait) +int TRA_wait(thread_db* tdbb, jrd_tra* trans, TraNumber number, tra_wait_t wait) { /************************************** * @@ -1989,12 +1938,12 @@ int TRA_wait(thread_db* tdbb, jrd_tra* trans, TraNumber number, jrd_tra::wait_t // Create, wait on, and release lock on target transaction. If // we can't get the lock due to deadlock - if (wait != jrd_tra::tra_no_wait) + if (wait != tra_no_wait) { Lock temp_lock(tdbb, sizeof(TraNumber), LCK_tra); temp_lock.setKey(number); - const SSHORT timeout = (wait == jrd_tra::tra_wait) ? trans->getLockWait() : 0; + const SSHORT timeout = (wait == tra_wait) ? trans->getLockWait() : 0; if (!LCK_lock(tdbb, &temp_lock, LCK_read, timeout)) { @@ -2007,7 +1956,7 @@ int TRA_wait(thread_db* tdbb, jrd_tra* trans, TraNumber number, jrd_tra::wait_t int state = TRA_get_state(tdbb, number); - if (wait != jrd_tra::tra_no_wait && state == tra_committed) + if (wait != tra_no_wait && state == tra_committed) return state; if (state == tra_precommitted) @@ -2190,11 +2139,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) { @@ -2265,11 +2214,11 @@ static void expand_view_lock(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rel } // set up the lock on the relation/view - Lock* lock = RLCK_transaction_relation_lock(tdbb, transaction, relation); + Lock* lock = RLCK_transaction_relation_lock(tdbb, transaction, getPermanent(relation)); 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; @@ -2278,7 +2227,7 @@ static void expand_view_lock(thread_db* tdbb, jrd_tra* transaction, jrd_rel* rel if (ctx[i]->vcx_type == VCT_PROCEDURE) continue; - jrd_rel* base_rel = MET_lookup_relation(tdbb, ctx[i]->vcx_relation_name); + jrd_rel* base_rel = MetadataCache::lookup_relation(tdbb, ctx[i]->vcx_relation_name, CacheFlag::AUTOCREATE); if (!base_rel) { // should be a BUGCHECK @@ -2288,9 +2237,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); } } @@ -2487,7 +2433,7 @@ void jrd_tra::tra_abort(const char* reason) } -static void release_temp_tables(thread_db* tdbb, jrd_tra* transaction) +void MetadataCache::release_temp_tables(thread_db* tdbb, jrd_tra* transaction) { /************************************** * @@ -2499,20 +2445,15 @@ static void release_temp_tables(thread_db* tdbb, jrd_tra* transaction) * Release data of temporary tables with transaction lifetime * **************************************/ - Attachment* att = tdbb->getAttachment(); - vec& rels = *att->att_relations; - - for (FB_SIZE_T i = 0; i < rels.count(); i++) + for (auto* relation : MetadataCache::get(tdbb)->mdc_relations) { - jrd_rel* relation = rels[i]; - - if (relation && (relation->rel_flags & REL_temp_tran)) + if (relation->rel_flags & REL_temp_tran) relation->delPages(tdbb, transaction->tra_number); } } -static void retain_temp_tables(thread_db* tdbb, jrd_tra* transaction, TraNumber new_number) +void MetadataCache::retain_temp_tables(thread_db* tdbb, jrd_tra* transaction, TraNumber new_number) { /************************************** * @@ -2525,14 +2466,9 @@ static void retain_temp_tables(thread_db* tdbb, jrd_tra* transaction, TraNumber * transaction number (see retain_context). * **************************************/ - Attachment* att = tdbb->getAttachment(); - vec& rels = *att->att_relations; - - for (FB_SIZE_T i = 0; i < rels.count(); i++) + for (auto* relation : MetadataCache::get(tdbb)->mdc_relations) { - jrd_rel* relation = rels[i]; - - if (relation && (relation->rel_flags & REL_temp_tran)) + if (relation->rel_flags & REL_temp_tran) relation->retainPages(tdbb, transaction->tra_number, new_number); } } @@ -2558,18 +2494,8 @@ static void restart_requests(thread_db* tdbb, jrd_tra* trans) i != trans->tra_attachment->att_requests.end(); ++i) { - Array& requests = (*i)->getStatement()->requests; - - for (Request** j = requests.begin(); j != requests.end(); ++j) - { - Request* request = *j; - - if (request && request->req_transaction) - { - EXE_unwind(tdbb, request); - EXE_start(tdbb, request, trans); - } - } + auto* statement = (*i)->getStatement(); + statement->restartRequests(tdbb, trans); } } @@ -2667,7 +2593,7 @@ static void retain_context(thread_db* tdbb, jrd_tra* transaction, bool commit, i TRA_set_state(tdbb, transaction, old_number, state); } - retain_temp_tables(tdbb, transaction, new_number); + MetadataCache::retain_temp_tables(tdbb, transaction, new_number); transaction->tra_number = new_number; @@ -3219,7 +3145,7 @@ static void transaction_options(thread_db* tdbb, const MetaName metaName = attachment->nameToMetaCharSet(tdbb, orgName); tpb += len; - jrd_rel* relation = MET_lookup_relation(tdbb, metaName); + jrd_rel* relation = MetadataCache::lookup_relation(tdbb, metaName, CacheFlag::AUTOCREATE); if (!relation) { ERR_post(Arg::Gds(isc_bad_tpb_content) << @@ -3227,9 +3153,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) { @@ -3634,7 +3557,7 @@ static void transaction_start(thread_db* tdbb, jrd_tra* trans) { if (cleanup) { - if (TRA_wait(tdbb, trans, active, jrd_tra::tra_no_wait) == tra_committed) + if (TRA_wait(tdbb, trans, active, tra_no_wait) == tra_committed) cleanup = false; continue; } @@ -3734,6 +3657,10 @@ static void transaction_start(thread_db* tdbb, jrd_tra* trans) dbb->dbb_tip_cache->updateOldestTransaction(tdbb, dbb->dbb_oldest_transaction, dbb->dbb_oldest_snapshot); + // Plumb remove really old objects from metadata cache + + dbb->dbb_mdc->checkCleanup(tdbb, oldest); + // If the transaction block is getting out of hand, force a sweep if (dbb->dbb_sweep_interval && @@ -3815,6 +3742,8 @@ jrd_tra::~jrd_tra() MemoryPool::deletePool(tra_autonomous_pool); delete tra_sec_db_context; + +// !!!!!!!!!!!!!!!!!!!! tra_resources.releaseResources(nullptr, this); } @@ -3895,7 +3824,7 @@ MemoryPool* jrd_tra::getAutonomousPool() pool = outer->tra_pool; outer = outer->tra_outer; } - tra_autonomous_pool = MemoryPool::createPool(pool, tra_memory_stats); + tra_autonomous_pool = MemoryPool::createPool(ALLOC_ARGS1 pool, tra_memory_stats); tra_autonomous_cnt = 0; } @@ -4085,23 +4014,16 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool if (!tra_blobs->locate(blob_id->bid_temp_id()) && !tra_fetched_blobs.locate(*blob_id)) { - vec* vector = tra_attachment->att_relations; - jrd_rel* blb_relation; - - if ((rel_id < vector->count() && (blb_relation = (*vector)[rel_id])) || - (blb_relation = MET_relation(tdbb, rel_id))) + MetadataCache* mdc = MetadataCache::get(tdbb); + auto* blobRelation = mdc->lookupRelationNoChecks(rel_id); // optimization with NoChecks + // correct rel definitely present + if (blobRelation) { - MetaName security_name = (fld && fld->fld_security_name.hasData()) ? - fld->fld_security_name : blb_relation->rel_security_name; - - if (security_name.isEmpty()) - { - MET_scan_relation(tdbb, blb_relation); - security_name = blb_relation->rel_security_name; - } + const MetaName security_name = (fld && fld->fld_security_name.hasData()) ? + fld->fld_security_name : blobRelation->rel_security_name; + fb_assert(security_name.hasData()); SecurityClass* s_class = SCL_get_class(tdbb, security_name.c_str()); - if (!s_class) return; @@ -4116,12 +4038,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, blb_relation->rel_name); + false, fld->fld_name, blobRelation->getName()); } else { SCL_check_access(tdbb, s_class, 0, 0, SCL_select, obj_relations, - false, blb_relation->rel_name); + false, blobRelation->getName()); } s_class->scl_blb_access = SecurityClass::BA_SUCCESS; @@ -4151,7 +4073,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(blb_relation->rel_name))); + (fld ? Arg::Str(fld->fld_name) : Arg::Str(blobRelation->getName()))); } else tra_fetched_blobs.add(*blob_id); @@ -4165,6 +4087,11 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool fb_assert(false); } } + else if (punt) + { + fatal_exception::raiseFmt("Invalid blob ID %x:%x", + blob_id->bid_quad.bid_quad_high, blob_id->bid_quad.bid_quad_low); + } } } @@ -4212,15 +4139,15 @@ TraceSweepEvent::~TraceSweepEvent() } -void TraceSweepEvent::beginSweepRelation(jrd_rel* relation) +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 - MET_lookup_relation_id(m_tdbb, relation->rel_id, false); + MetadataCache::lookup_relation_id(m_tdbb, relation->getId(), CacheFlag::AUTOCREATE); } m_relation_clock = fb_utils::query_performance_counter(); @@ -4228,7 +4155,7 @@ void TraceSweepEvent::beginSweepRelation(jrd_rel* relation) } -void TraceSweepEvent::endSweepRelation(jrd_rel* relation) +void TraceSweepEvent::endSweepRelation() { if (!m_need_trace) return; @@ -4348,3 +4275,21 @@ void jrd_tra::eraseSecDbContext() delete tra_sec_db_context; tra_sec_db_context = NULL; } + +/* ????????????? +void jrd_tra::RollbackCleanup::unlink(RollbackCleanup** from, ULONG id) +{ + for(; *from; from = &((*from)->rb_next)) + { + RollbackCleanup* target = *from; + fb_assert(target->rb_id); + if (target->rb_id == id) + { + *from = target->rb_next; + delete target; + return; + } + } + fb_assert(false); +} +*/ diff --git a/src/jrd/tra.h b/src/jrd/tra.h index c1b128f7f5d..d96de0d9a45 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -46,6 +46,7 @@ #include "../jrd/obj.h" #include "../jrd/EngineInterface.h" #include "../jrd/Savepoint.h" +#include "../jrd/tra_proto.h" namespace EDS { class Transaction; @@ -67,6 +68,7 @@ class UserManagement; class MappingList; class DbCreatorsList; class thread_db; +class Resources; class SecDbContext { @@ -160,12 +162,6 @@ class jrd_tra : public pool_alloc typedef Firebird::HalfStaticArray UndoRecordList; public: - enum wait_t { - tra_no_wait, - tra_probe, - tra_wait - }; - jrd_tra(MemoryPool* p, Firebird::MemoryStats* parent_stats, Attachment* attachment, jrd_tra* outer) : tra_attachment(attachment), @@ -178,7 +174,7 @@ class jrd_tra : public pool_alloc tra_blob_util_map(*p), tra_arrays(NULL), tra_deferred_job(NULL), - tra_resources(*p), + traExtRel(*p), tra_context_vars(*p), tra_lock_timeout(DEFAULT_LOCK_TIMEOUT), tra_timestamp(Firebird::TimeZoneUtil::getCurrentSystemTimeStamp()), @@ -238,7 +234,7 @@ class jrd_tra : public pool_alloc Firebird::MemoryStats temp_stats; pool->setStatsGroup(temp_stats); delete transaction; - attachment->deletePool(pool); + attachment->att_database->deletePool(pool); } } } @@ -283,8 +279,8 @@ class jrd_tra : public pool_alloc SavNumber tra_save_point_number; // next save point number to use ULONG tra_flags; DeferredJob* tra_deferred_job; // work deferred to commit time - ResourceList tra_resources; // resource existence list - Firebird::StringMap tra_context_vars; // Context variables for the transaction + Firebird::SortedArray traExtRel; // extfile access list + Firebird::StringMap tra_context_vars; // Context variables for the transaction traRpbList* tra_rpblist; // active record_param's of given transaction UCHAR tra_use_count; // use count for safe AST delivery UCHAR tra_callback_count; // callback count for 'execute statement' @@ -323,6 +319,8 @@ class jrd_tra : public pool_alloc MemoryPool* tra_autonomous_pool; USHORT tra_autonomous_cnt; static const USHORT TRA_AUTONOMOUS_PER_POOL = 64; +// ??????????????? RollbackCleanup* tra_rb_cleanup = nullptr; +// ????????????? ULONG tra_next_rb_id = 0; public: MemoryPool* getAutonomousPool(); @@ -406,40 +404,30 @@ class jrd_tra : public pool_alloc return tra_gen_ids; } -}; -// System transaction is always transaction 0. -const TraNumber TRA_system_transaction = 0; - -// Flag definitions for tra_flags. - -const ULONG TRA_system = 0x1L; // system transaction -const ULONG TRA_prepared = 0x2L; // transaction is in limbo -const ULONG TRA_reconnected = 0x4L; // reconnect in progress -const ULONG TRA_degree3 = 0x8L; // serializeable transaction -const ULONG TRA_write = 0x10L; // transaction has written -const ULONG TRA_readonly = 0x20L; // transaction is readonly -const ULONG TRA_prepare2 = 0x40L; // transaction has updated RDB$TRANSACTIONS -const ULONG TRA_ignore_limbo = 0x80L; // ignore transactions in limbo -const ULONG TRA_invalidated = 0x100L; // transaction invalidated by failed write -const ULONG TRA_deferred_meta = 0x200L; // deferred meta work posted -const ULONG TRA_read_committed = 0x400L; // can see latest committed records -const ULONG TRA_autocommit = 0x800L; // autocommits all updates -const ULONG TRA_perform_autocommit = 0x1000L; // indicates autocommit is necessary -const ULONG TRA_rec_version = 0x2000L; // don't wait for uncommitted versions -const ULONG TRA_restart_requests = 0x4000L; // restart all requests in attachment -const ULONG TRA_no_auto_undo = 0x8000L; // don't start a savepoint in TRA_start -const ULONG TRA_precommitted = 0x10000L; // transaction committed at startup -const ULONG TRA_own_interface = 0x20000L; // tra_interface was created for internal needs -const ULONG TRA_read_consistency = 0x40000L; // ensure read consistency in this transaction -const ULONG TRA_ex_restart = 0x80000L; // Exception was raised to restart request -const ULONG TRA_replicating = 0x100000L; // transaction is allowed to be replicated -const ULONG TRA_no_blob_check = 0x200000L; // disable blob access checking -const ULONG TRA_auto_release_temp_blobid = 0x400000L; // remove temp ids of materialized user blobs from tra_blobs - -// flags derived from TPB, see also transaction_options() at tra.cpp -const ULONG TRA_OPTIONS_MASK = (TRA_degree3 | TRA_readonly | TRA_ignore_limbo | TRA_read_committed | - TRA_autocommit | TRA_rec_version | TRA_read_consistency | TRA_no_auto_undo | TRA_restart_requests | TRA_auto_release_temp_blobid); + void postResources(thread_db* tdbb, const Resources* resources); + +/* ?????????????? + template + void postRollbackCleanup(P... args) + { + RollbackCleanup* newCleanup = FB_NEW_POOL(*tra_pool) C(args...); + newCleanup->link(++tra_next_rb_id, &tra_rb_cleanup); + } + + void rollbackCleanup(thread_db* tdbb) + { + auto* tc = tra_rb_cleanup; + while (tc) + tc = tc->performCleanup(tdbb, this); + } + + void unlinkCleanup(ULONG id) + { + RollbackCleanup::unlink(&tra_rb_cleanup, id); + } +*/ +}; const int TRA_MASK = 3; //const int TRA_BITS_PER_TRANS = 2; @@ -472,9 +460,9 @@ const int tra_precommitted = 5; // Transaction is precommitted enum dfw_t { dfw_null, + dfw_commit_relation, dfw_create_relation, dfw_delete_relation, - dfw_update_format, dfw_create_index, dfw_delete_index, dfw_compute_security, @@ -495,8 +483,6 @@ enum dfw_t { //dfw_load_triggers, dfw_grant, dfw_revoke, - dfw_scan_relation, - dfw_create_expression_index, dfw_create_procedure, dfw_modify_procedure, dfw_delete_procedure, @@ -528,8 +514,6 @@ enum dfw_t { dfw_arg_proc_name, // procedure name for dfw_delete_prm, mandatory dfw_arg_force_computed, // we need to drop dependencies from a field that WAS computed dfw_arg_check_blr, // check if BLR is still compilable - dfw_arg_rel_name, // relation name of a trigger - dfw_arg_trg_type, // trigger type dfw_arg_new_name, // new name dfw_arg_field_not_null, // set domain to not nullable dfw_db_crypt, // change database encryption status diff --git a/src/jrd/tra_proto.h b/src/jrd/tra_proto.h index 3c2d4b0ebba..886fc451832 100644 --- a/src/jrd/tra_proto.h +++ b/src/jrd/tra_proto.h @@ -24,13 +24,56 @@ #ifndef JRD_TRA_PROTO_H #define JRD_TRA_PROTO_H -#include "../jrd/tra.h" - namespace Jrd { - class Attachment; - class Database; - class TraceTransactionEnd; -} +class Attachment; +class Database; +class TraceTransactionEnd; +class DsqlCursor; +class Request; + +class Resources; +class thread_db; +class jrd_tra; + +// System transaction is always transaction 0. +const TraNumber TRA_system_transaction = 0; + +// Flag definitions for tra_flags. + +const ULONG TRA_system = 0x1L; // system transaction +const ULONG TRA_prepared = 0x2L; // transaction is in limbo +const ULONG TRA_reconnected = 0x4L; // reconnect in progress +const ULONG TRA_degree3 = 0x8L; // serializeable transaction +const ULONG TRA_write = 0x10L; // transaction has written +const ULONG TRA_readonly = 0x20L; // transaction is readonly +const ULONG TRA_prepare2 = 0x40L; // transaction has updated RDB$TRANSACTIONS +const ULONG TRA_ignore_limbo = 0x80L; // ignore transactions in limbo +const ULONG TRA_invalidated = 0x100L; // transaction invalidated by failed write +const ULONG TRA_deferred_meta = 0x200L; // deferred meta work posted +const ULONG TRA_read_committed = 0x400L; // can see latest committed records +const ULONG TRA_autocommit = 0x800L; // autocommits all updates +const ULONG TRA_perform_autocommit = 0x1000L; // indicates autocommit is necessary +const ULONG TRA_rec_version = 0x2000L; // don't wait for uncommitted versions +const ULONG TRA_restart_requests = 0x4000L; // restart all requests in attachment +const ULONG TRA_no_auto_undo = 0x8000L; // don't start a savepoint in TRA_start +const ULONG TRA_precommitted = 0x10000L; // transaction committed at startup +const ULONG TRA_own_interface = 0x20000L; // tra_interface was created for internal needs +const ULONG TRA_read_consistency = 0x40000L; // ensure read consistency in this transaction +const ULONG TRA_ex_restart = 0x80000L; // Exception was raised to restart request +const ULONG TRA_replicating = 0x100000L; // transaction is allowed to be replicated +const ULONG TRA_no_blob_check = 0x200000L; // disable blob access checking +const ULONG TRA_auto_release_temp_blobid = 0x400000L; // remove temp ids of materialized user blobs from tra_blobs + +// flags derived from TPB, see also transaction_options() at tra.cpp +const ULONG TRA_OPTIONS_MASK = (TRA_degree3 | TRA_readonly | TRA_ignore_limbo | TRA_read_committed | + TRA_autocommit | TRA_rec_version | TRA_read_consistency | TRA_no_auto_undo | TRA_restart_requests | TRA_auto_release_temp_blobid); + +enum tra_wait_t { + tra_no_wait, + tra_probe, + tra_wait +}; +} // namespace Jrd bool TRA_active_transactions(Jrd::thread_db* tdbb, Jrd::Database*); bool TRA_cleanup(Jrd::thread_db*); @@ -47,7 +90,9 @@ void TRA_init(Jrd::Attachment*); void TRA_invalidate(Jrd::thread_db* tdbb, ULONG); void TRA_link_cursor(Jrd::jrd_tra*, Jrd::DsqlCursor*); void TRA_unlink_cursor(Jrd::jrd_tra*, Jrd::DsqlCursor*); -void TRA_post_resources(Jrd::thread_db* tdbb, Jrd::jrd_tra*, Jrd::ResourceList&); + +void TRA_post_resources(Jrd::thread_db* tdbb, Jrd::jrd_tra*, Jrd::Resources&); + bool TRA_is_active(Jrd::thread_db*, TraNumber); void TRA_prepare(Jrd::thread_db* tdbb, Jrd::jrd_tra*, USHORT, const UCHAR*); Jrd::jrd_tra* TRA_reconnect(Jrd::thread_db* tdbb, const UCHAR*, USHORT); @@ -60,7 +105,7 @@ Jrd::jrd_tra* TRA_start(Jrd::thread_db* tdbb, int, const UCHAR*, Jrd::jrd_tra* o int TRA_state(const UCHAR*, TraNumber oldest, TraNumber number); void TRA_sweep(Jrd::thread_db* tdbb); void TRA_update_counters(Jrd::thread_db*, Jrd::Database*); -int TRA_wait(Jrd::thread_db* tdbb, Jrd::jrd_tra* trans, TraNumber number, Jrd::jrd_tra::wait_t wait); +int TRA_wait(Jrd::thread_db* tdbb, Jrd::jrd_tra* trans, TraNumber number, Jrd::tra_wait_t wait); void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::Request* request); void TRA_detach_request(Jrd::Request* request); void TRA_setup_request_snapshot(Jrd::thread_db*, Jrd::Request* request); diff --git a/src/jrd/trace/TraceJrdHelpers.h b/src/jrd/trace/TraceJrdHelpers.h index 3a4614c4622..0396640700b 100644 --- a/src/jrd/trace/TraceJrdHelpers.h +++ b/src/jrd/trace/TraceJrdHelpers.h @@ -431,14 +431,13 @@ class TraceTrigCompile const auto attachment = m_tdbb->getAttachment(); const auto trace_mgr = attachment->att_trace_manager; - m_need_trace = !trigger->sysTrigger && - trace_mgr->needs(ITraceFactory::TRACE_EVENT_TRIGGER_COMPILE); + m_need_trace = trace_mgr->needs(ITraceFactory::TRACE_EVENT_TRIGGER_COMPILE); if (!m_need_trace) return; m_name = trigger->name.c_str(); - m_relationName = trigger->relation ? trigger->relation->rel_name.c_str() : ""; + m_relationName = trigger->relation ? trigger->relation->c_name() : ""; const auto type = (trigger->type & ~TRIGGER_TYPE_MASK); @@ -458,7 +457,7 @@ class TraceTrigCompile case TRIGGER_TYPE_DB: { - m_action = type + DB_TRIGGER_MAX - 1; + m_action = type + TRIGGER_CONNECT; fb_assert(m_action == TRIGGER_CONNECT || m_action == TRIGGER_DISCONNECT || @@ -722,8 +721,8 @@ class TraceSweepEvent // implementation is in tra.cpp m_sweep_info.update(header); } - void beginSweepRelation(jrd_rel* relation); - void endSweepRelation(jrd_rel* relation); + void beginSweepRelation(const jrd_rel* relation); + void endSweepRelation(); void finish() { diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index ef0db31db11..8376e25158c 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -36,6 +36,7 @@ #include "../../common/isc_s_proto.h" #include "../../jrd/jrd.h" #include "../../jrd/tra.h" +#include "../../jrd/met.h" #include "../../jrd/DataTypeUtil.h" #include "../../dsql/ExprNodes.h" #include "../../dsql/StmtNodes.h" @@ -104,7 +105,7 @@ bool descToUTF8(const dsc* param, string& result) try { - if (!Jrd::DataTypeUtil::convertToUTF8(src, result, param->dsc_sub_type, status_exception::raise)) + if (!Jrd::DataTypeUtil::convertToUTF8(src, result, param->getCharSet(), status_exception::raise)) result = src; } catch (const Firebird::Exception&) @@ -131,6 +132,26 @@ const char* StatementHolder::ensurePlan(bool explained) } +/// StatementHolder + +Firebird::string StatementHolder::getName() const +{ + if (m_statement) + { + if (m_statement->procedure) + return m_statement->procedure->getName().toString(); + + if (m_statement->function) + return m_statement->function->getName().toString(); + + if (m_statement->triggerName.hasData()) + return m_statement->triggerName.c_str(); + } + + return ""; +} + + /// TraceConnectionImpl unsigned TraceConnectionImpl::getKind() @@ -684,5 +705,13 @@ const char* TraceStatusVectorImpl::getText() return m_error.c_str(); } +/* +TraceProcedureImpl::TraceProcedureImpl(Request* request, Firebird::PerformanceInfo* perf) : + m_request(request), + m_perf(perf), + m_inputs(*getDefaultMemoryPool(), request->req_proc_caller, request->req_proc_inputs), + m_name(m_request->getStatement()->procedure->getName().toString()) +{} +???????????????????????? */ } // namespace Jrd diff --git a/src/jrd/trace/TraceObjects.h b/src/jrd/trace/TraceObjects.h index cbbefa8e14e..343159eb9a5 100644 --- a/src/jrd/trace/TraceObjects.h +++ b/src/jrd/trace/TraceObjects.h @@ -42,6 +42,7 @@ #include "../../jrd/status.h" #include "../../jrd/Function.h" #include "../../jrd/RuntimeStatistics.h" +#include "../../jrd/Statement.h" #include "../../jrd/trace/TraceSession.h" #include "../../common/classes/ImplementHelper.h" #include "../../common/prett_proto.h" @@ -70,23 +71,7 @@ class StatementHolder return m_statement ? m_statement->getStatementId() : 0; } - Firebird::string getName() const - { - if (m_statement) - { - if (m_statement->procedure) - return m_statement->procedure->getName().toString(); - - if (m_statement->function) - return m_statement->function->getName().toString(); - - if (m_statement->triggerName.hasData()) - return m_statement->triggerName.c_str(); - } - - return ""; - } - + Firebird::string getName() const; const char* ensurePlan(bool explained); private: @@ -580,7 +565,7 @@ class TraceTriggerImpl : StatementHolder(request), m_name(getName()), m_relationName((request->req_rpb.hasData() && request->req_rpb[0].rpb_relation) ? - request->req_rpb[0].rpb_relation->rel_name : ""), + request->req_rpb[0].rpb_relation->c_name() : ""), m_which(which), m_action(request->req_trigger_action), m_perf(perf) diff --git a/src/jrd/val.h b/src/jrd/val.h index c0056fa9406..cbb6ce49dda 100644 --- a/src/jrd/val.h +++ b/src/jrd/val.h @@ -265,6 +265,14 @@ class Format : public pool_alloc return FB_NEW_POOL(p) Format(p, len); } + bool operator==(const Format& v) const + { + if ((fmt_length != v.fmt_length) || (fmt_count != v.fmt_count)) + return false; + + return fmt_desc == v.fmt_desc; + } + ULONG fmt_length; USHORT fmt_count; USHORT fmt_version; diff --git a/src/jrd/validation.cpp b/src/jrd/validation.cpp index ec0f4981771..2f57e51cd75 100644 --- a/src/jrd/validation.cpp +++ b/src/jrd/validation.cpp @@ -553,6 +553,7 @@ VI. ADDITIONAL NOTES #include "../jrd/tra.h" #include "../jrd/sqz.h" #include "../jrd/svc.h" +#include "../jrd/met.h" #include "../jrd/btr_proto.h" #include "../jrd/cch_proto.h" #include "../jrd/dpm_proto.h" @@ -569,7 +570,7 @@ VI. ADDITIONAL NOTES #include "../common/classes/ClumpletWriter.h" #include "../common/db_alias.h" #include "../jrd/intl_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #ifdef DEBUG_VAL_VERBOSE #include "../jrd/dmp_proto.h" @@ -758,7 +759,7 @@ static int validate(Firebird::UtilSvc* svc) att->att_use_count++; - val_pool = dbb->createPool(); + val_pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, val_pool); Validation control(tdbb, svc); @@ -1016,7 +1017,7 @@ bool Validation::run(thread_db* tdbb, USHORT flags) try { - val_pool = dbb->createPool(); + val_pool = dbb->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, val_pool); vdr_flags = flags; @@ -1136,7 +1137,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()); @@ -1156,7 +1157,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()); @@ -1560,7 +1561,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); @@ -1599,7 +1600,7 @@ void Validation::walk_database() * Functional description * **************************************/ - Jrd::Attachment* attachment = vdr_tdbb->getAttachment(); + Jrd::Database* const dbb = vdr_tdbb->getDatabase(); #ifdef DEBUG_VAL_VERBOSE if (VAL_debug_level) @@ -1629,19 +1630,16 @@ void Validation::walk_database() walk_generators(); } - vec* vector; - for (USHORT i = 0; (vector = attachment->att_relations) && i < vector->count(); i++) + MetadataCache* mdc = dbb->dbb_mdc; + for (USHORT i = 0; i < mdc->relCount(); i++) { #ifdef DEBUG_VAL_VERBOSE if (i > dbb->dbb_max_sys_rel) // Why not system flag instead? VAL_debug_level = 2; #endif - jrd_rel* relation = (*vector)[i]; + auto* relation = mdc->lookup_relation_id(vdr_tdbb, i, CacheFlag::AUTOCREATE); - if (relation && relation->rel_flags & REL_check_existence) - relation = MET_lookup_relation_id(vdr_tdbb, i, false); - - if (relation) + if (relation && relation->hasData()) { // Can't validate system relations online as they could be modified // by system transaction which not acquires relation locks @@ -1650,13 +1648,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; } @@ -1666,7 +1664,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; @@ -1716,7 +1714,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); @@ -1956,7 +1954,7 @@ void Validation::walk_generators() } } -Validation::RTN Validation::walk_index(jrd_rel* relation, index_root_page& root_page, USHORT id) +Validation::RTN Validation::walk_index(jrd_rel* relation, index_root_page* root_page, USHORT id) { /************************************** * @@ -1976,25 +1974,24 @@ Validation::RTN Validation::walk_index(jrd_rel* relation, index_root_page& root_ **************************************/ Database* dbb = vdr_tdbb->getDatabase(); - const ULONG page_number = root_page.irt_rpt[id].getRoot(); - if (!page_number) { + const ULONG page_number = root_page->irt_rpt[id].getRoot(); + if (!page_number) return rtn_ok; - } - const bool unique = (root_page.irt_rpt[id].irt_flags & (irt_unique | idx_primary)); - const bool descending = (root_page.irt_rpt[id].irt_flags & irt_descending); - const bool condition = (root_page.irt_rpt[id].irt_flags & irt_condition); + const bool unique = (root_page->irt_rpt[id].irt_flags & (irt_unique | idx_primary)); + const bool descending = (root_page->irt_rpt[id].irt_flags & irt_descending); + const bool condition = (root_page->irt_rpt[id].irt_flags & irt_condition); - temporary_key nullKey, *null_key = 0; + temporary_key nullKey, *null_key = nullptr; if (unique) { index_desc idx; { // No need to evaluate index expression and/or condition - AutoSetRestoreFlag flags(&root_page.irt_rpt[id].irt_flags, + AutoSetRestoreFlag flags(&root_page->irt_rpt[id].irt_flags, irt_expression | irt_condition, false); - BTR_description(vdr_tdbb, relation, &root_page, &idx, id); + BTR_description(vdr_tdbb, getPermanent(relation), root_page, &idx, id); } null_key = &nullKey; @@ -2046,7 +2043,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__); @@ -2550,7 +2547,7 @@ Validation::RTN Validation::walk_pointer_page(jrd_rel* relation, ULONG sequence) **************************************/ Database* dbb = vdr_tdbb->getDatabase(); - const vcl* vector = relation->getBasePages()->rel_pages; + const vcl* vector = getPermanent(relation)->getBasePages()->rel_pages; if (!vector || sequence >= vector->count()) return corrupt(VAL_P_PAGE_LOST, relation, sequence); @@ -2565,13 +2562,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); @@ -2669,7 +2666,7 @@ Validation::RTN Validation::walk_pointer_page(jrd_rel* relation, ULONG sequence) DPM_scan_pages(vdr_tdbb); - vector = relation->getBasePages()->rel_pages; + vector = getPermanent(relation)->getBasePages()->rel_pages; --sequence; if (!vector || sequence >= vector->count()) { @@ -2772,7 +2769,7 @@ Validation::RTN Validation::walk_record(jrd_rel* relation, const rhd* header, US length -= RHD_SIZE; } - const auto format = MET_format(vdr_tdbb, relation, header->rhd_format); + const auto format = MET_format(vdr_tdbb, getPermanent(relation), header->rhd_format); auto remainingLength = format->fmt_length; auto calculateLength = [remainingLength](ULONG length, const UCHAR* data, bool notPacked) @@ -2815,7 +2812,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()); @@ -2924,7 +2921,7 @@ void Validation::checkDPinPP(jrd_rel* relation, ULONG page_number) Database* dbb = vdr_tdbb->getDatabase(); DECOMPOSE(sequence, dbb->dbb_dp_per_pp, pp_sequence, slot); - const vcl* vector = relation->getBasePages()->rel_pages; + const vcl* vector = getPermanent(relation)->getBasePages()->rel_pages; pointer_page* ppage = 0; if (pp_sequence < vector->count()) { @@ -3024,36 +3021,29 @@ Validation::RTN Validation::walk_relation(jrd_rel* relation) try { - // If relation hasn't been scanned, do so now - - if (!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)) - { - MET_scan_relation(vdr_tdbb, relation); - } - // skip deleted relations - if (relation->rel_flags & (REL_deleted | REL_deleting)) { + if (getPermanent(relation)->isDropped()) { return rtn_ok; } #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 - if (relation->rel_view_rse || relation->rel_file || relation->isVirtual()) { + if (relation->isView() || relation->getExtFile() || relation->isVirtual()) { return rtn_ok; } AutoLock lckRead(vdr_tdbb); - jrd_rel::GCExclusive lckGC(vdr_tdbb, relation); + GCLock::Exclusive lckGC(vdr_tdbb, getPermanent(relation)); if (vdr_flags & VDR_online) { - lckRead = jrd_rel::createLock(vdr_tdbb, NULL, relation, LCK_relation, false); + lckRead = getPermanent(relation)->createLock(vdr_tdbb, LCK_relation, false); if (!LCK_lock(vdr_tdbb, lckRead, LCK_PR, vdr_lock_tout)) { output("Acquire relation lock failed\n"); @@ -3099,7 +3089,7 @@ Validation::RTN Validation::walk_relation(jrd_rel* relation) for (ULONG sequence = 0; true; sequence++) { - const vcl* vector = relation->getBasePages()->rel_pages; + const vcl* vector = getPermanent(relation)->getBasePages()->rel_pages; const int ppCnt = vector ? vector->count() : 0; output(" process pointer page %4d of %4d\n", sequence, ppCnt); @@ -3170,16 +3160,16 @@ Validation::RTN Validation::walk_relation(jrd_rel* relation) { if (!(vdr_flags & VDR_online)) { - const char* msg = relation->rel_name.length() > 0 ? + const char* msg = relation->getName().hasData() ? "bugcheck during scan of table %d (%s)" : "bugcheck during scan of table %d"; - gds__log(msg, relation->rel_id, relation->rel_name.c_str()); + gds__log(msg, relation->getId(), relation->getName().c_str()); } #ifdef DEBUG_VAL_VERBOSE if (VAL_debug_level) { char s[256]; - SNPRINTF(s, sizeof(s), msg, relation->rel_id, relation->rel_name.c_str()); + SNPRINTF(s, sizeof(s), msg, relation->getId(), relation->getName().c_str()); fprintf(stdout, "LOG:\t%s\n", s); } #endif @@ -3205,24 +3195,26 @@ Validation::RTN Validation::walk_root(jrd_rel* relation, bool getInfo) **************************************/ // If the relation has an index root, walk it - RelationPages* relPages = relation->getBasePages(); + RelationPages* relPages = getPermanent(relation)->getBasePages(); if (!relPages->rel_index_root) return corrupt(VAL_INDEX_ROOT_MISSING, relation); - index_root_page* page = 0; + index_root_page* page = nullptr; WIN window(DB_PAGE_SPACE, -1); fetch_page(!getInfo, relPages->rel_index_root, pag_root, &window, &page); for (USHORT i = 0; i < page->irt_count; i++) { - if (page->irt_rpt[i].getRoot() == 0) + if (!page->irt_rpt[i].getRoot()) continue; - MetaName index; - release_page(&window); - MET_lookup_index(vdr_tdbb, index, relation->rel_name, i + 1); + + auto* idx = getPermanent(relation)->lookupIndex(vdr_tdbb, i, CacheFlag::AUTOCREATE); + MetaName index; + if (idx) + index = idx->getName(); fetch_page(false, relPages->rel_index_root, pag_root, &window, &page); if (vdr_idx_incl) @@ -3242,17 +3234,17 @@ Validation::RTN Validation::walk_root(jrd_rel* relation, bool getInfo) if (page->irt_rpt[i].irt_flags & irt_condition) { // No need to evaluate index expression - AutoSetRestoreFlag flag(&page->irt_rpt[i].irt_flags, irt_expression, false); + AutoSetRestoreFlag flag(&page->irt_rpt[i].irt_flags, irt_expression, false); IdxInfo info; - if (BTR_description(vdr_tdbb, relation, page, &info.m_desc, i)) + if (BTR_description(vdr_tdbb, getPermanent(relation), page, &info.m_desc, i)) vdr_cond_idx.add(info); } continue; } output("Index %d (%s)\n", i + 1, index.c_str()); - walk_index(relation, *page, i); + walk_index(relation, page, i); } release_page(&window); diff --git a/src/jrd/validation.h b/src/jrd/validation.h index 971e0617948..3a9e0e51ed3 100644 --- a/src/jrd/validation.h +++ b/src/jrd/validation.h @@ -213,7 +213,7 @@ class Validation RTN walk_data_page(jrd_rel*, ULONG, ULONG, UCHAR&); void walk_database(); void walk_generators(); - RTN walk_index(jrd_rel*, Ods::index_root_page&, USHORT); + RTN walk_index(jrd_rel*, Ods::index_root_page*, USHORT); void walk_pip(); RTN walk_pointer_page(jrd_rel*, ULONG); RTN walk_record(jrd_rel*, const Ods::rhd*, USHORT, RecordNumber, bool); diff --git a/src/jrd/vec.cpp b/src/jrd/vec.cpp new file mode 100644 index 00000000000..08b58133ec9 --- /dev/null +++ b/src/jrd/vec.cpp @@ -0,0 +1,60 @@ +/* + * PROGRAM: JRD access method + * MODULE: vec.cpp + * DESCRIPTION: Misc helpers + * + * 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): ______________________________________. + * + */ + +#include "firebird.h" +#include "../jrd/vec.h" +#include "../jrd/jrd.h" +#include "../jrd/err_proto.h" + + +#if defined(DEV_BUILD) + +using namespace Jrd; + +thread_db* JRD_get_thread_data() +{ + Firebird::ThreadData* p1 = Firebird::ThreadData::getSpecific(); + if (p1 && p1->getType() == Firebird::ThreadData::tddDBB) + { + Jrd::thread_db* p2 = (Jrd::thread_db*) p1; + if (p2->getDatabase() && !p2->getDatabase()->checkHandle()) + { + BUGCHECK(147); + } + } + return (Jrd::thread_db*) p1; +} + +void CHECK_TDBB(const Jrd::thread_db* tdbb) +{ + fb_assert(tdbb && (tdbb->getType() == Firebird::ThreadData::tddDBB) && + (!tdbb->getDatabase() || tdbb->getDatabase()->checkHandle())); +} + +void CHECK_DBB(const Database* dbb) +{ + fb_assert(dbb && dbb->checkHandle()); +} + +#endif // DEV_BUILD diff --git a/src/jrd/vec.h b/src/jrd/vec.h new file mode 100644 index 00000000000..ec83b598b71 --- /dev/null +++ b/src/jrd/vec.h @@ -0,0 +1,206 @@ +/* + * PROGRAM: JRD access method + * MODULE: vec.h + * DESCRIPTION: General purpose vector + * + * 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_VEC_H +#define JRD_VEC_H + +#include "fb_blk.h" +#include "../common/ThreadData.h" +#include "../common/classes/array.h" + +namespace Jrd { + +// general purpose vector +template +class vec_base : protected pool_alloc +{ +public: + typedef typename Firebird::Array::iterator iterator; + typedef typename Firebird::Array::const_iterator const_iterator; + + /* + static vec_base* newVector(MemoryPool& p, int len) + { + return FB_NEW_POOL(p) vec_base(p, len); + } + + static vec_base* newVector(MemoryPool& p, const vec_base& base) + { + return FB_NEW_POOL(p) vec_base(p, base); + } + */ + + FB_SIZE_T count() const { return v.getCount(); } + T& operator[](FB_SIZE_T index) { return v[index]; } + const T& operator[](FB_SIZE_T index) const { return v[index]; } + + iterator begin() { return v.begin(); } + iterator end() { return v.end(); } + + const_iterator begin() const { return v.begin(); } + const_iterator end() const { return v.end(); } + + void clear() { v.clear(); } + + T* memPtr() { return &v[0]; } + + void resize(FB_SIZE_T n, T val = T()) { v.resize(n, val); } + + void operator delete(void* mem) { MemoryPool::globalFree(mem); } + + // strip unused nulls in the end + void trimNulls() + { + auto* e = v.end(); + for (; e != v.begin(); --e) + { + if (e[-1]) + break; + } + + if (e != v.end()) + v.resize(e - v.begin()); + } + +protected: + vec_base(MemoryPool& p, int len) + : v(p, len) + { + v.resize(len); + } + + vec_base(MemoryPool& p, const vec_base& base) + : v(p) + { + v = base.v; + } + +private: + Firebird::Array v; +}; + +template +class vec : public vec_base +{ +public: + static vec* newVector(MemoryPool& p, int len) + { + return FB_NEW_POOL(p) vec(p, len); + } + + static vec* newVector(MemoryPool& p, const vec& base) + { + return FB_NEW_POOL(p) vec(p, base); + } + + static vec* newVector(MemoryPool& p, vec* base, int len) + { + if (!base) + base = FB_NEW_POOL(p) vec(p, len); + else if (len > (int) base->count()) + base->resize(len); + return base; + } + +private: + vec(MemoryPool& p, int len) : vec_base(p, len) {} + vec(MemoryPool& p, const vec& base) : vec_base(p, base) {} +}; + +class vcl : public vec_base +{ +public: + static vcl* newVector(MemoryPool& p, int len) + { + return FB_NEW_POOL(p) vcl(p, len); + } + + static vcl* newVector(MemoryPool& p, const vcl& base) + { + return FB_NEW_POOL(p) vcl(p, base); + } + + static vcl* newVector(MemoryPool& p, vcl* base, int len) + { + if (!base) + base = FB_NEW_POOL(p) vcl(p, len); + else if (len > (int) base->count()) + base->resize(len); + return base; + } + +private: + vcl(MemoryPool& p, int len) : vec_base(p, len) {} + vcl(MemoryPool& p, const vcl& base) : vec_base(p, base) {} +}; + +typedef vec TransactionsVector; + +// Threading macros + +class Database; +class thread_db; + +} // namespace Jrd + +/* Define JRD_get_thread_data off the platform specific version. + * If we're in DEV mode, also do consistancy checks on the + * retrieved memory structure. This was originally done to + * track down cases of no "PUT_THREAD_DATA" on the NLM. + * + * This allows for NULL thread data (which might be an error by itself) + * If there is thread data, + * AND it is tagged as being a thread_db. + * AND it has a non-NULL database field, + * THEN we validate that the structure there is a database block. + * Otherwise, we return what we got. + * We can't always validate the database field, as during initialization + * there is no database set up. + */ + +#if defined(DEV_BUILD) + +Jrd::thread_db* JRD_get_thread_data(); +void CHECK_TDBB(const Jrd::thread_db* tdbb); +void CHECK_DBB(const Jrd::Database* dbb); + +#else // PROD_BUILD + +inline Jrd::thread_db* JRD_get_thread_data() +{ + return (Jrd::thread_db*) Firebird::ThreadData::getSpecific(); +} + +inline void CHECK_DBB(const Jrd::Database*) +{ +} + +inline void CHECK_TDBB(const Jrd::thread_db*) +{ +} + +#endif + +#endif // JRD_VEC_H + diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 8433d0d9243..73de0d57da0 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -59,6 +59,7 @@ #include "../jrd/btr.h" #include "../jrd/exe.h" #include "../jrd/scl.h" +#include "../jrd/met.h" #include "../common/classes/alloc.h" #include "../common/ThreadStart.h" #include "../jrd/vio_debug.h" @@ -75,7 +76,7 @@ #include "../common/isc_proto.h" #include "../jrd/jrd_proto.h" #include "../jrd/ini_proto.h" -#include "../jrd/lck_proto.h" +#include "../jrd/lck.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" @@ -215,7 +216,7 @@ class SweepTask : public Task m_relInfo.grow(m_items.getCount()); - m_lastRelID = att->att_relations->count(); + m_lastRelID = MetadataCache::get(tdbb)->relCount(); }; virtual ~SweepTask() @@ -428,20 +429,18 @@ bool SweepTask::handler(WorkItem& _item) Database* dbb = tdbb->getDatabase(); Attachment* att = tdbb->getAttachment(); - /*relation = (*att->att_relations)[relInfo->rel_id]; - if (relation)*/ - relation = MET_lookup_relation_id(tdbb, relInfo->rel_id, false); + relation = MetadataCache::lookup_relation_id(tdbb, relInfo->rel_id, CacheFlag::AUTOCREATE); if (relation && - !(relation->rel_flags & (REL_deleted | REL_deleting)) && + !getPermanent(relation)->isDropped() && !relation->isTemporary() && relation->getPages(tdbb)->rel_pages) { - jrd_rel::GCShared gcGuard(tdbb, relation); + GCLock::Shared gcGuard(tdbb, getPermanent(relation)); 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)); } @@ -451,7 +450,7 @@ bool SweepTask::handler(WorkItem& _item) relInfo->countPP = relation->getPages(tdbb)->rel_pages->count(); rpb.rpb_relation = relation; - rpb.rpb_org_scans = relation->rel_scan_count++; + rpb.rpb_org_scans = getPermanent(relation)->rel_scan_count++; rpb.rpb_record = NULL; rpb.rpb_stream_flags = RPB_s_no_data | RPB_s_sweeper; rpb.getWindow(tdbb).win_flags = WIN_large_scan; @@ -467,7 +466,7 @@ bool SweepTask::handler(WorkItem& _item) { CCH_RELEASE(tdbb, &rpb.getWindow(tdbb)); - if (relation->rel_flags & REL_deleting) + if (getPermanent(relation)->isDropped()) break; if (rpb.rpb_number >= lastRecNo) @@ -482,7 +481,7 @@ bool SweepTask::handler(WorkItem& _item) } delete rpb.rpb_record; - --relation->rel_scan_count; + --getPermanent(relation)->rel_scan_count; } return !m_stop; @@ -494,8 +493,8 @@ bool SweepTask::handler(WorkItem& _item) delete rpb.rpb_record; if (relation) { - if (relation->rel_scan_count) { - --relation->rel_scan_count; + if (getPermanent(relation)->rel_scan_count) { + --getPermanent(relation)->rel_scan_count; } } } @@ -616,7 +615,7 @@ static bool assert_gc_enabled(const jrd_tra* transaction, const jrd_rel* relatio * * Notes * System and temporary relations are not validated online. - * Non-zero rel_sweep_count is possible only under GCShared control when + * Non-zero sweep count is possible only under GCShared control when * garbage collection is enabled. * * VIO_backout is more complex as it could run without GCShared control. @@ -624,17 +623,17 @@ static bool assert_gc_enabled(const jrd_tra* transaction, const jrd_rel* relatio * in this case online validation is not run against given relation. * **************************************/ - if (relation->rel_sweep_count || relation->isSystem() || relation->isTemporary()) + if (getPermanent(relation)->rel_gc_lock.getSweepCount() || relation->isSystem() || relation->isTemporary()) return true; - if (relation->rel_flags & REL_gc_disabled) + if (getPermanent(relation)->rel_gc_lock.flags & GCLock::GC_disabled) return false; vec* 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; @@ -656,7 +655,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())); } } @@ -676,7 +675,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: @@ -691,10 +690,10 @@ 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, bool probe) { if (!probe && 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, - probe ? jrd_tra::tra_probe : jrd_tra::tra_wait); + probe ? tra_probe : tra_wait); } inline bool checkGCActive(thread_db* tdbb, record_param* rpb, int& state) @@ -831,7 +830,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 @@ -1016,7 +1015,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; } @@ -1104,7 +1103,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()); } @@ -1140,7 +1139,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); @@ -1245,7 +1244,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb, ((tdbb->tdbb_flags & TDBB_sweeper) && state == tra_committed && rpb->rpb_b_page != 0 && rpb->rpb_transaction_nr >= oldest_snapshot))) { - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); int_gc_done = true; if (gcGuard.gcEnabled()) @@ -1312,7 +1311,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) << @@ -1357,7 +1356,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb, case tra_precommitted: { // scope - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); if ((attachment->att_flags & ATT_NO_CLEANUP) || !gcGuard.gcEnabled() || (rpb->rpb_flags & (rpb_chained | rpb_gc_active))) @@ -1613,7 +1612,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb, { CCH_RELEASE(tdbb, &rpb->getWindow(tdbb)); - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); if (!gcGuard.gcEnabled()) return false; @@ -1661,7 +1660,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb, } { // scope - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); if (!gcGuard.gcEnabled()) return true; @@ -1779,7 +1778,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, @@ -1972,7 +1971,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 @@ -2014,12 +2013,12 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (needDfw(tdbb, transaction)) { - jrd_rel* r2; - const jrd_prc* procedure; + Cached::Relation* r2; + jrd_prc* procedure; USHORT id; DeferredWork* work; - switch ((RIDS) relation->rel_id) + switch ((RIDS) relation->getId()) { case rel_database: case rel_log: @@ -2071,9 +2070,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 = MET_lookup_relation_id(tdbb, id, false); - if (rel_drop) - MET_scan_relation(tdbb, rel_drop); + MetadataCache::lookup_relation_id(tdbb, id, CacheFlag::AUTOCREATE); } break; @@ -2088,7 +2085,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_prc_name, &desc); DFW_post_work(transaction, dfw_delete_procedure, &desc, id, package_name); - MET_lookup_procedure_id(tdbb, id, false, true, 0); + MetadataCache::lookup_procedure_id(tdbb, id, CacheFlag::AUTOCREATE | CacheFlag::NOSCAN); break; case rel_collations: @@ -2097,7 +2094,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) id = MOV_get_long(tdbb, &desc2, 0); EVL_field(0, rpb->rpb_record, f_coll_id, &desc2); - id = INTL_CS_COLL_TO_TTYPE(id, MOV_get_long(tdbb, &desc2, 0)); + id = TTypeId(CSetId(id), CollId(MOV_get_long(tdbb, &desc2, 0))); EVL_field(0, rpb->rpb_record, f_coll_name, &desc); DFW_post_work(transaction, dfw_delete_collation, &desc, id); @@ -2126,71 +2123,24 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) id = MOV_get_long(tdbb, &desc2, 0); DFW_post_work(transaction, dfw_delete_function, &desc, id, package_name); - Function::lookup(tdbb, id, false, true, 0); + Function::lookup(tdbb, id, 0); break; case rel_indices: protect_system_table_delupd(tdbb, relation, "DELETE"); - EVL_field(0, rpb->rpb_record, f_idx_relation, &desc); - EVL_field(0, rpb->rpb_record, f_idx_id, &desc2); - if ( (id = MOV_get_long(tdbb, &desc2, 0)) ) - { - MetaName relation_name; - MOV_get_metaname(tdbb, &desc, relation_name); - r2 = MET_lookup_relation(tdbb, relation_name); - fb_assert(r2); - - DSC idx_name; - EVL_field(0, rpb->rpb_record, f_idx_name, &idx_name); - - // 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); - - // 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); - - // get partner relation for FK index - if (EVL_field(0, rpb->rpb_record, f_idx_foreign, &desc2)) - { - DSC desc3; - EVL_field(0, rpb->rpb_record, f_idx_name, &desc3); - - MetaName index_name; - MOV_get_metaname(tdbb, &desc3, index_name); - - jrd_rel *partner; - index_desc idx; - - if ((BTR_lookup(tdbb, r2, id - 1, &idx, r2->getBasePages())) && - MET_lookup_partner(tdbb, r2, &idx, index_name.nullStr()) && - (partner = MET_lookup_relation_id(tdbb, idx.idx_primary_relation, false)) ) - { - DFW_post_work_arg(transaction, work, 0, partner->rel_id, - dfw_arg_partner_rel_id); - } - else - { - // can't find partner relation - impossible ? - // add empty argument to let DFW know dropping - // index was bound with FK - DFW_post_work_arg(transaction, work, 0, 0, dfw_arg_partner_rel_id); - } - } - } break; case rel_rfr: protect_system_table_delupd(tdbb, relation, "DELETE"); EVL_field(0, rpb->rpb_record, f_rfr_rname, &desc); - DFW_post_work(transaction, dfw_update_format, &desc, 0); - - EVL_field(0, rpb->rpb_record, f_rfr_fname, &desc2); MOV_get_metaname(tdbb, &desc, object_name); - if ( (r2 = MET_lookup_relation(tdbb, object_name)) ) - DFW_post_work(transaction, dfw_delete_rfr, &desc2, r2->rel_id); + RelationPermanent::tagForUpdate(tdbb, object_name); + if ( (r2 = MetadataCache::lookupRelation(tdbb, object_name, CacheFlag::AUTOCREATE)) ) + { + EVL_field(0, rpb->rpb_record, f_rfr_fname, &desc2); + 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); @@ -2212,8 +2162,8 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_prm_name, &desc2); - if ( (procedure = MET_lookup_procedure(tdbb, - QualifiedName(object_name, package_name), true)) ) + if ( (procedure = MetadataCache::lookup_procedure(tdbb, + QualifiedName(object_name, package_name), CacheFlag::AUTOCREATE | CacheFlag::NOSCAN)) ) { work = DFW_post_work(transaction, dfw_delete_prm, &desc2, procedure->getId(), package_name); @@ -2277,20 +2227,26 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_triggers: protect_system_table_delupd(tdbb, relation, "DELETE"); - EVL_field(0, rpb->rpb_record, f_trg_rname, &desc2); - DFW_post_work(transaction, dfw_update_format, &desc2, 0); - EVL_field(0, rpb->rpb_record, f_trg_name, &desc); - work = DFW_post_work(transaction, dfw_delete_trigger, &desc, 0); - if (!(desc2.dsc_flags & DSC_null)) - DFW_post_work_arg(transaction, work, &desc2, 0, dfw_arg_rel_name); - - if (EVL_field(0, rpb->rpb_record, f_trg_type, &desc2)) { - DFW_post_work_arg(transaction, work, &desc2, - (USHORT) MOV_get_int64(tdbb, &desc2, 0), dfw_arg_trg_type); - } + USHORT trg_type = EVL_field(0, rpb->rpb_record, f_trg_type, &desc2) ? + (USHORT) MOV_get_int64(tdbb, &desc2, 0) : 0; + if (EVL_field(0, rpb->rpb_record, f_trg_rname, &desc2)) + { + MOV_get_metaname(tdbb, &desc2, object_name); + RelationPermanent::tagForUpdate(tdbb, object_name); + } + else + { + auto* tSet = MetadataCache::get(tdbb)->getTriggersSet(tdbb, trg_type); + if (tSet) + tSet->tagForUpdate(tdbb); + } + + EVL_field(0, rpb->rpb_record, f_trg_name, &desc); + DFW_post_work(transaction, dfw_delete_trigger, &desc, trg_type); + } break; case rel_priv: @@ -2326,7 +2282,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { MetaName relation_name; MOV_get_metaname(tdbb, &desc, relation_name); - r2 = MET_lookup_relation(tdbb, relation_name); + r2 = MetadataCache::lookupRelation(tdbb, relation_name, CacheFlag::AUTOCREATE); fb_assert(r2); if (r2) @@ -2381,7 +2337,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; } @@ -2419,7 +2375,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); @@ -2438,7 +2394,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 @@ -2450,7 +2406,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (backVersion && !(tdbb->getAttachment()->att_flags & ATT_no_cleanup) && (dbb->dbb_flags & DBB_gc_cooperative)) { - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); if (gcGuard.gcEnabled()) { temp = *rpb; @@ -2777,7 +2733,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) @@ -2804,7 +2760,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 @@ -2814,7 +2770,7 @@ bool VIO_garbage_collect(thread_db* tdbb, record_param* rpb, jrd_tra* transactio rpb->rpb_f_page, rpb->rpb_f_line); #endif - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); if ((attachment->att_flags & ATT_no_cleanup) || !gcGuard.gcEnabled()) return true; @@ -2893,12 +2849,12 @@ Record* VIO_gc_record(thread_db* tdbb, jrd_rel* relation) Database* dbb = tdbb->getDatabase(); CHECK_DBB(dbb); - const Format* const format = MET_current(tdbb, relation); + const Format* const format = relation->currentFormat(); // Set the active flag on an inactive garbage collect record block and return it - for (Record** iter = relation->rel_gc_records.begin(); - iter != relation->rel_gc_records.end(); + for (Record** iter = getPermanent(relation)->rel_gc_records.begin(); + iter != getPermanent(relation)->rel_gc_records.end(); ++iter) { Record* const record = *iter; @@ -2917,7 +2873,7 @@ Record* VIO_gc_record(thread_db* tdbb, jrd_rel* relation) Record* const record = FB_NEW_POOL(*relation->rel_pool) Record(*relation->rel_pool, format, true); - relation->rel_gc_records.add(record); + getPermanent(relation)->rel_gc_records.add(record); return record; } @@ -2940,7 +2896,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 @@ -2982,7 +2938,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; } @@ -3018,7 +2974,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 @@ -3057,7 +3013,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; } @@ -3102,7 +3058,7 @@ bool VIO_get_current(thread_db* tdbb, // return !foreign_key; { - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); if (!gcGuard.gcEnabled()) return !foreign_key; @@ -3200,7 +3156,7 @@ bool VIO_get_current(thread_db* tdbb, // return !foreign_key; { - jrd_rel::GCShared gcGuard(tdbb, rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(rpb->rpb_relation)); if (!gcGuard.gcEnabled()) return !foreign_key; @@ -3296,7 +3252,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, @@ -3340,7 +3296,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; } @@ -3355,7 +3311,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: @@ -3415,7 +3371,8 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j SCL_check_relation(tdbb, &desc1, SCL_alter); check_class(tdbb, transaction, org_rpb, new_rpb, f_rel_class); check_owner(tdbb, transaction, org_rpb, new_rpb, f_rel_owner); - DFW_post_work(transaction, dfw_update_format, &desc1, 0); + MOV_get_metaname(tdbb, &desc1, object_name); + RelationPermanent::tagForUpdate(tdbb, object_name); break; case rel_packages: @@ -3573,48 +3530,70 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_indices: protect_system_table_delupd(tdbb, relation, "UPDATE"); - EVL_field(0, new_rpb->rpb_record, f_idx_relation, &desc1); if (dfw_should_know(tdbb, org_rpb, new_rpb, f_idx_desc, true)) { EVL_field(0, new_rpb->rpb_record, f_idx_name, &desc1); - if (EVL_field(0, new_rpb->rpb_record, f_idx_exp_blr, &desc2)) - { - DFW_post_work(transaction, dfw_create_expression_index, - &desc1, tdbb->getDatabase()->dbb_max_idx); - } - else + EVL_field(0, new_rpb->rpb_record, f_idx_relation, &desc2); + MetaName relation_name; + MOV_get_metaname(tdbb, &desc2, relation_name); + auto* irel = MetadataCache::lookupRelation(tdbb, relation_name, CacheFlag::AUTOCREATE); + fb_assert(irel); + + // AP: In index-related DFW dfw_id is relation id, dfw_name is index name + DFW_post_work(transaction, dfw_create_index, &desc1, irel->getId()); + + bool nullFl = !EVL_field(0, new_rpb->rpb_record, f_idx_inactive, &desc2); + auto newStat = nullFl ? 0 : MOV_get_long(tdbb, &desc2, 0); + if (newStat == MET_index_deferred_drop) { - DFW_post_work(transaction, dfw_create_index, &desc1, - tdbb->getDatabase()->dbb_max_idx); + nullFl = !EVL_field(0, org_rpb->rpb_record, f_idx_inactive, &desc2); + auto oldStat = nullFl ? 0 : MOV_get_long(tdbb, &desc2, 0); + if (newStat != oldStat) + { + // AP: In index-related DFW dfw_id is relation id, dfw_name is index name + DFW_post_work(transaction, dfw_delete_index, &desc1, irel->getId()); + } } } break; case rel_triggers: - EVL_field(0, new_rpb->rpb_record, f_trg_rname, &desc1); - if (!check_nullify_source(tdbb, org_rpb, new_rpb, f_trg_source)) - protect_system_table_delupd(tdbb, relation, "UPDATE"); - else - SCL_check_relation(tdbb, &desc1, SCL_control | SCL_alter); - - if (dfw_should_know(tdbb, org_rpb, new_rpb, f_trg_desc, true)) { - EVL_field(0, new_rpb->rpb_record, f_trg_rname, &desc1); - DFW_post_work(transaction, dfw_update_format, &desc1, 0); - EVL_field(0, org_rpb->rpb_record, f_trg_rname, &desc1); - DFW_post_work(transaction, dfw_update_format, &desc1, 0); - EVL_field(0, org_rpb->rpb_record, f_trg_name, &desc1); - DeferredWork* dw = DFW_post_work(transaction, dfw_modify_trigger, &desc1, 0); + dsc rname, tname; + + bool onRelation = EVL_field(0, org_rpb->rpb_record, f_trg_rname, &rname); - if (EVL_field(0, new_rpb->rpb_record, f_trg_rname, &desc2)) - DFW_post_work_arg(transaction, dw, &desc2, 0, dfw_arg_rel_name); + if (!check_nullify_source(tdbb, org_rpb, new_rpb, f_trg_source)) + protect_system_table_delupd(tdbb, relation, "UPDATE"); + else if (onRelation) + SCL_check_relation(tdbb, &rname, SCL_control | SCL_alter); - if (EVL_field(0, new_rpb->rpb_record, f_trg_type, &desc2)) + if (dfw_should_know(tdbb, org_rpb, new_rpb, f_trg_desc, true)) { - DFW_post_work_arg(transaction, dw, &desc2, - (USHORT) MOV_get_int64(tdbb, &desc2, 0), dfw_arg_trg_type); + USHORT trg_type = EVL_field(0, org_rpb->rpb_record, f_trg_type, &desc2) ? + (USHORT) MOV_get_int64(tdbb, &desc2, 0) : 0; + + EVL_field(0, org_rpb->rpb_record, f_trg_name, &tname); + DFW_post_work(transaction, dfw_modify_trigger, &tname, trg_type); + + if (onRelation) + { + MOV_get_metaname(tdbb, &rname, object_name); + RelationPermanent::tagForUpdate(tdbb, object_name); + + USHORT new_trg_type = EVL_field(0, new_rpb->rpb_record, f_trg_type, &desc2) ? + (USHORT) MOV_get_int64(tdbb, &desc2, 0) : 0; + if (new_trg_type != trg_type) + DFW_post_work(transaction, dfw_modify_trigger, &tname, new_trg_type); + } + else + { + auto* tSet = MetadataCache::get(tdbb)->getTriggersSet(tdbb, trg_type); + if (tSet) + tSet->tagForUpdate(tdbb); + } } } break; @@ -3682,7 +3661,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j // if the same page should be fetched for read. // Explicit scan of relation's partners allows to avoid possible deadlock. - MET_scan_partners(tdbb, org_rpb->rpb_relation); + MET_scan_partners(tdbb, getPermanent(org_rpb->rpb_relation)); /* We're almost ready to go. To modify the record, we must first make a copy of the old record someplace else. Then we must re-fetch @@ -3707,7 +3686,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; } @@ -3742,7 +3721,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 @@ -3756,7 +3735,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j if (backVersion && !(tdbb->getAttachment()->att_flags & ATT_no_cleanup) && (dbb->dbb_flags & DBB_gc_cooperative)) { - jrd_rel::GCShared gcGuard(tdbb, org_rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(org_rpb->rpb_relation)); if (gcGuard.gcEnabled()) { temp.rpb_number = org_rpb->rpb_number; @@ -3801,7 +3780,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, @@ -3851,7 +3830,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; } @@ -3874,14 +3853,14 @@ 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 // If format wasn't given, look one up if (!format) - format = MET_format(tdbb, rpb->rpb_relation, rpb->rpb_format_number); + format = MET_format(tdbb, getPermanent(rpb->rpb_relation), rpb->rpb_format_number); Record* record = rpb->rpb_record; @@ -3917,7 +3896,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; @@ -3943,7 +3922,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 @@ -3958,7 +3937,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) << @@ -3994,7 +3973,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 @@ -4005,7 +3984,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: @@ -4062,7 +4041,6 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) protect_system_table_insert(tdbb, request, relation); EVL_field(0, rpb->rpb_record, f_rel_name, &desc); DFW_post_work(transaction, dfw_create_relation, &desc, 0); - DFW_post_work(transaction, dfw_update_format, &desc, 0); set_system_flag(tdbb, rpb->rpb_record, f_rel_sys_flag); set_owner_name(tdbb, rpb->rpb_record, f_rel_owner); if (set_security_class(tdbb, rpb->rpb_record, f_rel_class)) @@ -4140,22 +4118,29 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_indices: protect_system_table_insert(tdbb, request, relation); - EVL_field(0, rpb->rpb_record, f_idx_name, &desc); - if (EVL_field(0, rpb->rpb_record, f_idx_exp_blr, &desc2)) + { - DFW_post_work(transaction, dfw_create_expression_index, &desc, - tdbb->getDatabase()->dbb_max_idx); - } - else { - DFW_post_work(transaction, dfw_create_index, &desc, tdbb->getDatabase()->dbb_max_idx); + EVL_field(0, rpb->rpb_record, f_idx_relation, &desc); + MetaName relation_name; + MOV_get_metaname(tdbb, &desc, relation_name); + auto* irel = MetadataCache::lookupRelation(tdbb, relation_name, CacheFlag::AUTOCREATE); + fb_assert(irel); + + DSC idx_name; + EVL_field(0, rpb->rpb_record, f_idx_name, &idx_name); + + // AP: In index-related DFW dfw_id is relation id, dfw_name is index name + work = DFW_post_work(transaction, dfw_create_index, &idx_name, irel->getId()); } + set_system_flag(tdbb, rpb->rpb_record, f_idx_sys_flag); break; case rel_rfr: protect_system_table_insert(tdbb, request, relation); EVL_field(0, rpb->rpb_record, f_rfr_rname, &desc); - DFW_post_work(transaction, dfw_update_format, &desc, 0); + MOV_get_metaname(tdbb, &desc, object_name); + RelationPermanent::tagForUpdate(tdbb, object_name); set_system_flag(tdbb, rpb->rpb_record, f_rfr_sys_flag); break; @@ -4215,27 +4200,31 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) break; case rel_triggers: - EVL_field(0, rpb->rpb_record, f_trg_rname, &desc); - - // check if this request go through without checking permissions - if (!(request->getStatement()->flags & (Statement::FLAG_IGNORE_PERM | Statement::FLAG_INTERNAL))) - SCL_check_relation(tdbb, &desc, SCL_control | SCL_alter); + { + bool onRelation = EVL_field(0, rpb->rpb_record, f_trg_rname, &desc); - if (EVL_field(0, rpb->rpb_record, f_trg_rname, &desc2)) - DFW_post_work(transaction, dfw_update_format, &desc2, 0); + USHORT trg_type = EVL_field(0, rpb->rpb_record, f_trg_type, &desc2) ? + (USHORT) MOV_get_int64(tdbb, &desc2, 0) : 0; - EVL_field(0, rpb->rpb_record, f_trg_name, &desc); - work = DFW_post_work(transaction, dfw_create_trigger, &desc, 0); + if (onRelation) + { + // check if this request go through without checking permissions + if (!(request->getStatement()->flags & (Statement::FLAG_IGNORE_PERM | Statement::FLAG_INTERNAL))) + SCL_check_relation(tdbb, &desc, SCL_control | SCL_alter); - if (!(desc2.dsc_flags & DSC_null)) - DFW_post_work_arg(transaction, work, &desc2, 0, dfw_arg_rel_name); + MOV_get_metaname(tdbb, &desc, object_name); + RelationPermanent::tagForUpdate(tdbb, object_name); + } + else + { + auto* tSet = MetadataCache::get(tdbb)->getTriggersSet(tdbb, trg_type); + if (tSet) + tSet->tagForUpdate(tdbb); + } - if (EVL_field(0, rpb->rpb_record, f_trg_type, &desc2)) - { - DFW_post_work_arg(transaction, work, &desc2, - (USHORT) MOV_get_int64(tdbb, &desc2, 0), dfw_arg_trg_type); + EVL_field(0, rpb->rpb_record, f_trg_name, &desc); + DFW_post_work(transaction, dfw_create_trigger, &desc, trg_type); } - set_system_flag(tdbb, rpb->rpb_record, f_trg_sys_flag); break; case rel_priv: @@ -4327,7 +4316,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: { @@ -4335,7 +4324,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) USHORT id = MOV_get_long(tdbb, &desc, 0); EVL_field(0, rpb->rpb_record, f_coll_id, &desc); - id = INTL_CS_COLL_TO_TTYPE(id, MOV_get_long(tdbb, &desc, 0)); + id = TTypeId(CSetId(id), CollId(MOV_get_long(tdbb, &desc, 0))); EVL_field(0, rpb->rpb_record, f_coll_name, &desc); DFW_post_work(transaction, dfw_create_collation, &desc, id); @@ -4369,7 +4358,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 @@ -4426,31 +4415,28 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee // hvlad: restore tdbb->transaction since it can be used later tdbb->setTransaction(transaction); + jrd_rel* relation; + record_param rpb; rpb.rpb_record = NULL; rpb.rpb_stream_flags = RPB_s_no_data | RPB_s_sweeper; rpb.getWindow(tdbb).win_flags = WIN_large_scan; - jrd_rel* relation = NULL; // wasn't initialized: memory problem in catch () part. - vec* vector = NULL; - GarbageCollector* gc = dbb->dbb_garbage_collector; bool ret = true; try { - - for (FB_SIZE_T i = 1; (vector = attachment->att_relations) && i < vector->count(); i++) + MetadataCache* mdc = MetadataCache::get(tdbb); + for (FB_SIZE_T i = 1; i < mdc->relCount(); i++) { - relation = (*vector)[i]; - if (relation) - relation = MET_lookup_relation_id(tdbb, i, false); + relation = MetadataCache::lookup_relation_id(tdbb, i, CacheFlag::AUTOCREATE); if (relation && - !(relation->rel_flags & (REL_deleted | REL_deleting)) && + !(relation->getPermanent()->isDropped()) && !relation->isTemporary() && - relation->getPages(tdbb)->rel_pages) + relation->getPermanent()->getPages(tdbb)->rel_pages) { - jrd_rel::GCShared gcGuard(tdbb, relation); + GCLock::Shared gcGuard(tdbb, getPermanent(relation)); if (!gcGuard.gcEnabled()) { ret = false; @@ -4459,19 +4445,19 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee rpb.rpb_relation = relation; rpb.rpb_number.setValue(BOF_NUMBER); - rpb.rpb_org_scans = relation->rel_scan_count++; + rpb.rpb_org_scans = relation->getPermanent()->rel_scan_count++; 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)) { CCH_RELEASE(tdbb, &rpb.getWindow(tdbb)); - if (relation->rel_flags & REL_deleting) + if (relation->getPermanent()->isDropped()) break; JRD_reschedule(tdbb); @@ -4481,9 +4467,9 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee cache->updateActiveSnapshots(tdbb, &attachment->att_active_snapshots); } - traceSweep->endSweepRelation(relation); + traceSweep->endSweepRelation(); - --relation->rel_scan_count; + relation->getPermanent()->rel_scan_count--; } } @@ -4496,8 +4482,8 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee if (relation) { - if (relation->rel_scan_count) - --relation->rel_scan_count; + if (getPermanent(relation)->rel_scan_count) + --getPermanent(relation)->rel_scan_count; } ERR_punt(); @@ -4525,7 +4511,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 @@ -4576,7 +4562,7 @@ WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* t new_rpb.rpb_transaction_nr = transaction->tra_number; AutoPtr new_record; - const Format* const new_format = MET_current(tdbb, relation); + const Format* const new_format = relation->currentFormat(); // If the fetched record is not in the latest format, upgrade it. // To do that, allocate new record buffer and make the new record @@ -4666,14 +4652,14 @@ 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(); if (backVersion && !(tdbb->getAttachment()->att_flags & ATT_no_cleanup) && (dbb->dbb_flags & DBB_gc_cooperative)) { - jrd_rel::GCShared gcGuard(tdbb, org_rpb->rpb_relation); + GCLock::Shared gcGuard(tdbb, getPermanent(org_rpb->rpb_relation)); if (gcGuard.gcEnabled()) { temp.rpb_number = org_rpb->rpb_number; @@ -4742,7 +4728,9 @@ static void check_rel_field_class(thread_db* tdbb, DSC desc; EVL_field(0, rpb->rpb_record, f_rfr_rname, &desc); - DFW_post_work(transaction, dfw_update_format, &desc, 0); + MetaName object_name; + MOV_get_metaname(tdbb, &desc, object_name); + RelationPermanent::tagForUpdate(tdbb, object_name); } static void check_class(thread_db* tdbb, @@ -4931,7 +4919,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 @@ -5006,7 +4994,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 @@ -5105,7 +5093,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 @@ -5161,7 +5149,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()); } @@ -5190,7 +5178,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 @@ -5313,11 +5301,11 @@ void Database::garbage_collector(Database* dbb) Jrd::Attachment::UseCountHolder use(attachment); tdbb->markAsSweeper(); + jrd_rel* relation; record_param rpb; rpb.getWindow(tdbb).win_flags = WIN_garbage_collector; rpb.rpb_stream_flags = RPB_s_no_data | RPB_s_sweeper; - jrd_rel* relation = NULL; jrd_tra* transaction = NULL; AutoPtr gc(FB_NEW_POOL(*attachment->att_pool) GarbageCollector( @@ -5326,7 +5314,6 @@ void Database::garbage_collector(Database* dbb) try { LCK_init(tdbb, LCK_OWNER_attachment); - INI_init(tdbb); PAG_header(tdbb, true); PAG_attachment_id(tdbb); TRA_init(attachment); @@ -5368,7 +5355,6 @@ void Database::garbage_collector(Database* dbb) // out from under us while garbage collection is in-progress. bool found = false, gc_exit = false; - relation = NULL; USHORT relID; PageBitmap* gc_bitmap = NULL; @@ -5376,8 +5362,8 @@ void Database::garbage_collector(Database* dbb) if ((dbb->dbb_flags & DBB_gc_pending) && (gc_bitmap = gc->getPages(dbb->dbb_oldest_snapshot, relID))) { - relation = MET_lookup_relation_id(tdbb, relID, false); - if (!relation || (relation->rel_flags & (REL_deleted | REL_deleting))) + relation = MetadataCache::lookup_relation_id(tdbb, relID, CacheFlag::AUTOCREATE); + if (!relation || getPermanent(relation)->isDropped()) { delete gc_bitmap; gc_bitmap = NULL; @@ -5386,7 +5372,7 @@ void Database::garbage_collector(Database* dbb) if (gc_bitmap) { - jrd_rel::GCShared gcGuard(tdbb, relation); + GCLock::Shared gcGuard(tdbb, getPermanent(relation)); if (!gcGuard.gcEnabled()) continue; @@ -5436,13 +5422,13 @@ void Database::garbage_collector(Database* dbb) break; } - if (relation->rel_flags & REL_deleting) + if (getPermanent(relation)->isDropped()) { rel_exit = true; break; } - if (relation->rel_flags & REL_gc_disabled) + if (getPermanent(relation)->rel_gc_lock.flags & GCLock::GC_disabled) { rel_exit = true; break; @@ -5524,8 +5510,6 @@ void Database::garbage_collector(Database* dbb) Monitoring::cleanupAttachment(tdbb); attachment->releaseLocks(tdbb); LCK_fini(tdbb, LCK_OWNER_attachment); - - attachment->releaseRelations(tdbb); } // try catch (const Firebird::Exception& ex) { @@ -5662,7 +5646,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; @@ -5783,7 +5767,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) { @@ -6030,7 +6014,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; @@ -6071,7 +6055,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, @@ -6196,7 +6180,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; } } @@ -6241,7 +6225,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu if (writelock || skipLocked || (transaction->tra_flags & TRA_read_consistency)) { - tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id); + tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->getId()); return PrepareResult::DELETED; } @@ -6262,7 +6246,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; } @@ -6365,7 +6349,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 (skipLocked) return PrepareResult::SKIP_LOCKED; @@ -6440,7 +6424,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())); } @@ -6472,7 +6456,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())); } @@ -6502,7 +6486,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 @@ -6554,7 +6538,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; } @@ -6581,7 +6565,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 @@ -6619,15 +6603,15 @@ static void refresh_fk_fields(thread_db* tdbb, Record* old_rec, record_param* cu * new_rpb - new record evaluated by modify statement and before-triggers * **************************************/ - jrd_rel* relation = cur_rpb->rpb_relation; + auto* relation = cur_rpb->rpb_relation->rel_perm; MET_scan_partners(tdbb, relation); - if (!(relation->rel_foreign_refs.frgn_relations)) + const auto* frgn = relation->rel_foreign_refs; + if (!frgn) return; - const FB_SIZE_T frgnCount = relation->rel_foreign_refs.frgn_relations->count(); - if (!frgnCount) + if (!frgn->getCount()) return; RelationPages* relPages = cur_rpb->rpb_relation->getPages(tdbb); @@ -6635,16 +6619,15 @@ static void refresh_fk_fields(thread_db* tdbb, Record* old_rec, record_param* cu // Collect all fields of all foreign keys SortedArray > fields; - for (FB_SIZE_T i = 0; i < frgnCount; i++) + for (auto& dep : *frgn) { // We need self-referenced FK's only - if ((*relation->rel_foreign_refs.frgn_relations)[i] == relation->rel_id) + if (dep.dep_relation == relation->getId()) { index_desc idx; idx.idx_id = idx_invalid; - if (BTR_lookup(tdbb, relation, (*relation->rel_foreign_refs.frgn_reference_ids)[i], - &idx, relPages)) + if (BTR_lookup(tdbb, relation, dep.dep_reference_id, &idx, relPages)) { fb_assert(idx.idx_flags & idx_foreign); @@ -6666,15 +6649,15 @@ static void refresh_fk_fields(thread_db* tdbb, Record* old_rec, record_param* cu { // Detect if user changed FK field by himself. const int fld = fields[idx]; - const bool flag_old = EVL_field(relation, old_rec, fld, &desc1); - const bool flag_new = EVL_field(relation, new_rpb->rpb_record, fld, &desc2); + const bool flag_old = EVL_field(cur_rpb->rpb_relation, old_rec, fld, &desc1); + const bool flag_new = EVL_field(cur_rpb->rpb_relation, new_rpb->rpb_record, fld, &desc2); // If field was not changed by user - pick up possible modification by // system cascade trigger if (flag_old == flag_new && (!flag_old || (flag_old && !MOV_compare(tdbb, &desc1, &desc2)))) { - const bool flag_tmp = EVL_field(relation, cur_rpb->rpb_record, fld, &desc1); + const bool flag_tmp = EVL_field(cur_rpb->rpb_relation, cur_rpb->rpb_record, fld, &desc1); if (flag_tmp) MOV_move(tdbb, &desc1, &desc2); else @@ -6838,7 +6821,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, diff --git a/src/remote/parser.cpp b/src/remote/parser.cpp index eabc92907a7..3d6ed50f54f 100644 --- a/src/remote/parser.cpp +++ b/src/remote/parser.cpp @@ -314,7 +314,7 @@ static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length) USHORT textType = *blr++; textType += (*blr++) << 8; - desc->setTextType(textType); + desc->setTextType(TTypeId(textType)); format->fmt_blob_idx.add(desc - begin); } diff --git a/src/remote/server/ReplServer.cpp b/src/remote/server/ReplServer.cpp index 3b12feee393..a177275a8a7 100644 --- a/src/remote/server/ReplServer.cpp +++ b/src/remote/server/ReplServer.cpp @@ -987,7 +987,7 @@ namespace const auto config = target->getConfig(); const auto dbName = config->dbName.c_str(); - AutoMemoryPool workingPool(MemoryPool::createPool()); + AutoMemoryPool workingPool(MemoryPool::createPool(ALLOC_ARGS0)); ContextPoolHolder threadContext(workingPool); target->verbose("Started replication for database %s", dbName); diff --git a/src/remote/server/os/win32/srvr_w32.cpp b/src/remote/server/os/win32/srvr_w32.cpp index 6650fcc000e..c1658460cba 100644 --- a/src/remote/server/os/win32/srvr_w32.cpp +++ b/src/remote/server/os/win32/srvr_w32.cpp @@ -708,8 +708,6 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) break; case 'Z': - // CVC: printf doesn't work because we don't have a console attached. - //printf("Firebird remote server version %s\n", FB_VERSION); MessageBox(NULL, FB_VERSION, "Firebird server version", MB_OK | MB_ICONINFORMATION | MB_TOPMOST | MB_DEFAULT_DESKTOP_ONLY); exit(FINI_OK); diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index 59726abf89a..b80ff9a010d 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -204,7 +204,7 @@ static void db_error(int); static USHORT get_format_length(ISC_STATUS*, isc_db_handle, isc_tr_handle, ISC_QUAD&); static dba_fil* db_open(const char*, USHORT); -static const pag* db_read(SLONG page_number, bool ok_enc = false); +static const pag* db_read(ULONG page_number, bool ok_enc = false); #ifdef WIN_NT static void db_close(void* file_desc); #else @@ -262,7 +262,7 @@ public: USHORT page_size; USHORT dp_per_pp; USHORT max_records; - SLONG page_number; + ULONG page_number; pag* buffer1; pag* buffer2; pag* global_buffer; @@ -616,7 +616,7 @@ int gstat(Firebird::UtilSvc* uSvc) tddba->page_size = MAX(RAW_HEADER_SIZE, DIRECT_IO_BLOCK_SIZE); tddba->global_buffer = (pag*) temp; tddba->page_number = -1; - const header_page* header = (const header_page*) db_read((SLONG) 0); + const header_page* header = (const header_page*) db_read(HEADER_PAGE); if (!Ods::isSupported(header)) { @@ -1315,7 +1315,7 @@ static void analyze_data( dba_rel* relation, bool sw_record) pointer_page* ptr_page = (pointer_page*) tddba->buffer1; - for (SLONG next_pp = relation->rel_pointer_page; next_pp; next_pp = ptr_page->ppg_next) + for (ULONG next_pp = relation->rel_pointer_page; next_pp; next_pp = ptr_page->ppg_next) { ++relation->rel_pointer_pages; memcpy(ptr_page, (const SCHAR*) db_read(next_pp), tddba->page_size); @@ -1452,18 +1452,18 @@ static void analyze_blob(dba_rel* relation, const blh* blob, int length) if (blob->blh_level != 0) { - const int slots = (length - BLH_SIZE) / static_cast(sizeof(SLONG)); + const unsigned slots = (length - BLH_SIZE) / static_cast(sizeof(ULONG)); ULONG blobPages = slots; if (blob->blh_level == 2) { - SLONG pages[MAX_PAGE_SIZE / sizeof(SLONG)]; - memcpy(pages, blob->blh_page, slots * sizeof(SLONG)); + ULONG pages[MAX_PAGE_SIZE / sizeof(ULONG)]; + memcpy(pages, blob->blh_page, slots * sizeof(ULONG)); - for (int i = 0; i < slots; i++) + for (unsigned i = 0; i < slots; i++) { - const blob_page* bpage = (const blob_page*) db_read(pages[i]); - blobPages += bpage->blp_length / sizeof(SLONG); + const auto bpage = (const blob_page*) db_read(pages[i]); + blobPages += bpage->blp_length / sizeof(ULONG); } } @@ -1490,7 +1490,7 @@ static ULONG analyze_fragments(dba_rel* relation, const rhdf* header) while (header->rhdf_flags & rhd_incomplete) { - const SLONG f_page = header->rhdf_f_page; + const ULONG f_page = header->rhdf_f_page; const USHORT f_line = header->rhdf_f_line; const data_page* page = (const data_page*) db_read(f_page); if (page->dpg_header.pag_type != pag_data || page->dpg_relation != relation->rel_id || @@ -1538,12 +1538,12 @@ static void analyze_index( const dba_rel* relation, dba_idx* index) const index_root_page* index_root = (const index_root_page*) db_read(relation->rel_index_root); - SLONG page; - if (index_root->irt_count <= index->idx_id || - !(page = index_root->irt_rpt[index->idx_id].getRoot())) - { + if (index_root->irt_count <= index->idx_id) + return; + + ULONG page = index_root->irt_rpt[index->idx_id].getRoot(); + if (!page) return; - } // CVC: The two const_cast's for bucket can go away if BTreeNode's functions // are overloaded for constness. They don't modify bucket and pointer's contents. @@ -1561,7 +1561,7 @@ static void analyze_index( const dba_rel* relation, dba_idx* index) } bool firstLeafNode = true; - SLONG number; + ULONG number; FB_UINT64 duplicates = 0; // Maximum key length is 1/4 of the used page-size @@ -1684,7 +1684,7 @@ static ULONG analyze_versions( dba_rel* relation, const rhdf* header) **************************************/ //tdba* tddba = tdba::getSpecific(); ULONG space = 0, versions = 0; - SLONG b_page = header->rhdf_b_page; + ULONG b_page = header->rhdf_b_page; USHORT b_line = header->rhdf_b_line; while (b_page) @@ -1862,7 +1862,7 @@ static dba_fil* db_open(const char* file_name, USHORT file_length) } -static const pag* db_read( SLONG page_number, bool ok_enc) +static const pag* db_read( ULONG page_number, bool ok_enc) { /************************************** * @@ -2005,7 +2005,7 @@ static dba_fil* db_open(const char* file_name, USHORT file_length) } -static const pag* db_read( SLONG page_number, bool ok_enc) +static const pag* db_read( ULONG page_number, bool ok_enc) { /************************************** *