diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 73f1ed906da..6079836fbed 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,6 +1,6 @@
name: CI
-on: [push, pull_request]
+on: [push]
jobs:
diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj
index bc729abfe2a..1d3047a90bf 100644
--- a/builds/win32/msvc15/engine_static.vcxproj
+++ b/builds/win32/msvc15/engine_static.vcxproj
@@ -190,6 +190,9 @@
+
+
+
diff --git a/builds/win32/msvc15/engine_static.vcxproj.filters b/builds/win32/msvc15/engine_static.vcxproj.filters
index f108b854e44..6bd3f61f56d 100644
--- a/builds/win32/msvc15/engine_static.vcxproj.filters
+++ b/builds/win32/msvc15/engine_static.vcxproj.filters
@@ -549,6 +549,15 @@
JRD files
+
+ JRD files
+
+
+ JRD files
+
+
+ JRD files
+
diff --git a/src/alice/exe.cpp b/src/alice/exe.cpp
index 6592c9c9545..3b1b179956a 100644
--- a/src/alice/exe.cpp
+++ b/src/alice/exe.cpp
@@ -73,7 +73,7 @@ int EXE_action(const TEXT* database, const SINT64 switches)
bool error = false;
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
{
- Firebird::AutoMemoryPool newPool(MemoryPool::createPool());
+ Firebird::AutoMemoryPool newPool(MemoryPool::createPool(ALLOC_ARGS0));
AliceContextPoolHolder context(tdgbl, newPool);
for (USHORT i = 0; i < MAX_VAL_ERRORS; i++)
@@ -145,7 +145,7 @@ int EXE_two_phase(const TEXT* database, const SINT64 switches)
bool error = false;
AliceGlobals* tdgbl = AliceGlobals::getSpecific();
{
- Firebird::AutoMemoryPool newPool(MemoryPool::createPool());
+ Firebird::AutoMemoryPool newPool(MemoryPool::createPool(ALLOC_ARGS0));
AliceContextPoolHolder context(tdgbl, newPool);
for (USHORT i = 0; i < MAX_VAL_ERRORS; i++)
diff --git a/src/burp/backup.epp b/src/burp/backup.epp
index b69e1b55f72..de9addbf229 100644
--- a/src/burp/backup.epp
+++ b/src/burp/backup.epp
@@ -767,13 +767,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;
}
@@ -886,13 +886,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;
}
@@ -1647,7 +1647,8 @@ void put_index( burp_rel* relation)
FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1)
X IN RDB$INDICES WITH
X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
- X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str()
+ X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str() AND
+ X.RDB$INDEX_NAME NOT STARTING TEMP_DEPEND
{
count = 0;
FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle2)
diff --git a/src/burp/burp.h b/src/burp/burp.h
index d21317e0b18..71680b43b03 100644
--- a/src/burp/burp.h
+++ b/src/burp/burp.h
@@ -49,6 +49,7 @@
#include "../common/status.h"
#include "../common/sha.h"
#include "../common/classes/ImplementHelper.h"
+#include "../jrd/intl.h"
#ifdef HAVE_UNISTD_H
#include
@@ -767,8 +768,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;
};
@@ -984,7 +985,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 b78afb757e6..0da166f6f57 100644
--- a/src/burp/restore.epp
+++ b/src/burp/restore.epp
@@ -298,13 +298,19 @@ void activateIndex(BurpGlobals* tdgbl, const QualifiedMetaString& indexName)
{
MODIFY IND1 USING
IND1.RDB$INDEX_INACTIVE = FALSE;
- END_MODIFY
+ END_MODIFY;
+ ON_ERROR
+ fError = true;
+ fb_utils::copyStatus(&local_status_vector, isc_status);
+ END_ERROR;
}
- END_FOR
+ END_FOR;
+
ON_ERROR
+ if (!fError)
+ fb_utils::copyStatus(&local_status_vector, isc_status);
fError = true;
- fb_utils::copyStatus(&local_status_vector, isc_status);
- END_ERROR
+ END_ERROR;
if (!fError)
{
@@ -4029,11 +4035,11 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation, USHORT id)
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;
@@ -4236,11 +4242,11 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation, USHORT id)
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;
@@ -8575,9 +8581,8 @@ bool get_schema(BurpGlobals* tdgbl)
QualifiedMetaString name;
bool securityClass = false;
- ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
- STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_schema_req_handle1)
+ STORE (REQUEST_HANDLE tdgbl->handles_get_schema_req_handle1)
X IN RDB$SCHEMAS
{
X.RDB$SCHEMA_NAME.NULL = TRUE;
@@ -13385,13 +13390,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 21e834cece5..6476f39da57 100644
--- a/src/common/CvtFormat.cpp
+++ b/src/common/CvtFormat.cpp
@@ -1849,7 +1849,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/StdHelper.h b/src/common/StdHelper.h
index 11449cb8481..d875b1ae6a4 100644
--- a/src/common/StdHelper.h
+++ b/src/common/StdHelper.h
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include "boost/type_traits/copy_cv.hpp"
namespace Firebird {
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 420d3ed2929..f952c9624ac 100644
--- a/src/common/TextType.h
+++ b/src/common/TextType.h
@@ -31,6 +31,7 @@
#define COMMON_TEXTTYPE_H
#include "../common/classes/QualifiedMetaString.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;
}
@@ -103,7 +104,7 @@ class TextType
CharSet* cs;
private:
- TTYPE_ID type;
+ TTypeId type;
USHORT attributes;
public:
diff --git a/src/common/classes/ImplementHelper.h b/src/common/classes/ImplementHelper.h
index 78dee170ab4..75b5a7b80eb 100644
--- a/src/common/classes/ImplementHelper.h
+++ b/src/common/classes/ImplementHelper.h
@@ -124,7 +124,7 @@ class RefCntIface : public VersionedIface, public GlobalStorage
protected:
void refCntDPrt(char f) noexcept
{
-#ifdef DEV_BUILD
+#ifdef NEVERDEF //DEV_BUILD
if (mark)
fprintf(stderr, "%s %p %c %d\n", mark, this, f, int(refCounter));
#endif
diff --git a/src/common/classes/MetaString.h b/src/common/classes/MetaString.h
index 376f5a28bfa..7a8f568f1ab 100644
--- a/src/common/classes/MetaString.h
+++ b/src/common/classes/MetaString.h
@@ -85,7 +85,7 @@ class MetaString
MetaString() noexcept { init(); count = 0; }
MetaString(const char* s) noexcept { assign(s); }
MetaString(const char* s, FB_SIZE_T l) noexcept { assign(s, l); }
- MetaString(const MetaString& m) noexcept { set(m); }
+ MetaString(const MetaString& m) noexcept = default;
MetaString(const AbstractString& s) noexcept { assign(s.c_str(), s.length()); }
explicit MetaString(MemoryPool&) noexcept { init(); count = 0; }
MetaString(MemoryPool&, const char* s) noexcept { assign(s); }
@@ -214,7 +214,7 @@ class MetaString
MetaString& clear() noexcept { return assign(nullptr, 0); }
MetaString& operator=(const char* s) noexcept { return assign(s); }
MetaString& operator=(const AbstractString& s) noexcept { return assign(s.c_str(), s.length()); }
- MetaString& operator=(const MetaString& m) noexcept { return set(m); }
+ MetaString& operator=(const MetaString& m) noexcept = default;
char* getBuffer(const FB_SIZE_T l) noexcept;
FB_SIZE_T length() const noexcept { 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/QualifiedMetaString.h b/src/common/classes/QualifiedMetaString.h
index ddac5e089fd..01ee26a5a2f 100644
--- a/src/common/classes/QualifiedMetaString.h
+++ b/src/common/classes/QualifiedMetaString.h
@@ -66,13 +66,7 @@ class BaseQualifiedName
{
}
- BaseQualifiedName(const BaseQualifiedName& src)
- : object(src.object),
- schema(src.schema),
- package(src.package),
- unambiguous(src.isUnambiguous())
- {
- }
+ BaseQualifiedName(const BaseQualifiedName& src) = default;
template
BaseQualifiedName(const BaseQualifiedName& src)
@@ -336,6 +330,16 @@ class BaseQualifiedName
package = {};
}
+ bool hasData() const
+ {
+ return object.hasData();
+ }
+
+ bool isEmpty() const
+ {
+ return object.isEmpty();
+ }
+
Firebird::string toQuotedString() const
{
Firebird::string s;
diff --git a/src/common/classes/alloc.cpp b/src/common/classes/alloc.cpp
index 8a1cfcfc1f1..892fd617072 100644
--- a/src/common/classes/alloc.cpp
+++ b/src/common/classes/alloc.cpp
@@ -202,6 +202,12 @@ inline size_t get_map_page_size()
} // anonymous namespace
+#ifdef DEBUG_LOST_POOLS
+namespace Jrd {
+ void checkPool(MemoryPool* pool);
+}
+#endif
+
namespace Firebird {
namespace SemiDoubleLink
@@ -1671,8 +1677,17 @@ class MemPool
};
#endif // VALIDATE_POOL
- MemBlock* allocateInternal(size_t from, size_t& length, bool flagRedirect);
- void releaseBlock(MemBlock *block, bool flagDecr) noexcept;
+ MemBlock* allocateInternal2(size_t from, size_t& length, bool flagRedirect);
+ MemBlock* allocateInternal(size_t from, size_t& length, bool flagRedirect)
+ {
+ auto* block = allocateInternal2(from, length, flagRedirect);
+ block->pool = this;
+ return block;
+ }
+
+ void releaseBlock(MemBlock *block, int flag) noexcept;
+ static constexpr int RELEASE_DECR = 0x1; // Decrement memory usage
+ static constexpr int RELEASE_RED = 0x2; // Perform red zone checks (MEM_DEBUG only)
public:
void* allocate(size_t size ALLOC_PARAMS);
@@ -1707,9 +1722,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;
@@ -1815,9 +1827,16 @@ class MemPool
private:
MemPool* next;
MemPool* child;
+
+#ifdef DEBUG_LOST_POOLS
+ const char* fileName;
+ int lineNum;
+
+ int seq = -1;
#endif
+#endif // MEM_DEBUG
-friend class MemoryPool;
+ friend class MemoryPool;
};
@@ -2052,11 +2071,18 @@ MemPool::~MemPool(void)
block->resetExtent();
#endif
- parent->releaseBlock(block, false);
+ parent->releaseBlock(block, RELEASE_RED);
}
}
#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);
@@ -2107,13 +2133,23 @@ 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);
- return FB_NEW_POOL(*parentPool) MemoryPool(p);
+ MemPool* p = new(*parentPool ALLOC_PASS_ARGS) MemPool(*(parentPool->pool), stats, &defaultExtentsCache);
+#ifdef MEM_DEBUG
+#ifdef DEBUG_LOST_POOLS
+ p->fileName = file;
+ p->lineNum = line;
+
+ static std::atomic seqGen = 0;
+ p->seq = ++seqGen;
+#endif
+#endif
+
+ return new(*parentPool ALLOC_PASS_ARGS) MemoryPool(p);
}
void MemPool::setStatsGroup(MemoryStats& newStats) noexcept
@@ -2142,9 +2178,9 @@ void MemoryPool::setStatsGroup(MemoryStats& newStats) noexcept
pool->setStatsGroup(newStats);
}
-MemBlock* MemPool::allocateInternal(size_t from, size_t& length, bool flagRedirect)
+MemBlock* MemPool::allocateInternal2(size_t from, size_t& length, bool flagRedirect)
{
- MutexEnsureUnlock guard(mutex, "MemPool::allocateInternal");
+ MutexEnsureUnlock guard(mutex, "MemPool::allocateInternal2");
guard.enter();
++blocksAllocated;
@@ -2179,7 +2215,7 @@ MemBlock* MemPool::allocateInternal(size_t from, size_t& length, bool flagRedire
else // worst case - very low possibility
{
guard.leave();
- parent->releaseBlock(block, false);
+ parent->releaseBlock(block, 0);
guard.enter();
}
}
@@ -2207,7 +2243,6 @@ MemBlock* MemPool::allocateRange(size_t from, size_t& size ALLOC_PARAMS)
size_t length = from ? size : ROUNDUP(size + VALGRIND_REDZONE, roundingSize) + GUARD_BYTES;
MemBlock* memory = allocateInternal(from, length, true);
size = length - (VALGRIND_REDZONE + GUARD_BYTES);
- memory->pool = this;
#ifdef USE_VALGRIND
VALGRIND_MEMPOOL_ALLOC(this, &memory->body, size);
@@ -2312,11 +2347,11 @@ void MemPool::releaseMemory(void* object, bool flagExtent) noexcept
// Finally delete it
block->resetExtent();
- pool->releaseBlock(block, !flagExtent);
+ pool->releaseBlock(block, RELEASE_RED | (flagExtent ? 0 : RELEASE_DECR));
}
}
-void MemPool::releaseBlock(MemBlock* block, bool decrUsage) noexcept
+void MemPool::releaseBlock(MemBlock* block, int flags) noexcept
{
#ifdef DELAYED_FREE
fb_assert(!block->isActive());
@@ -2329,10 +2364,13 @@ void MemPool::releaseBlock(MemBlock* block, bool decrUsage) noexcept
}
#ifdef MEM_DEBUG
- for (const UCHAR* end = (UCHAR*) block + block->getSize(), *p = end - GUARD_BYTES; p < end;)
+ if (flags & RELEASE_RED)
{
- if (*p++ != GUARD_BYTE)
- corrupt("guard bytes overwritten");
+ for (const UCHAR* end = (UCHAR*) block + block->getSize(), *p = end - GUARD_BYTES; p < end;)
+ {
+ if (*p++ != GUARD_BYTE)
+ corrupt("guard bytes overwritten");
+ }
}
#endif
@@ -2343,9 +2381,9 @@ void MemPool::releaseBlock(MemBlock* block, bool decrUsage) noexcept
--blocksActive;
- const Validator vld(decrUsage ? this : NULL);
+ const Validator vld(flags & RELEASE_DECR ? this : NULL);
- if (decrUsage)
+ if (flags & RELEASE_DECR)
decrement_usage(length);
// If length is less than threshold, this is a small block
@@ -2364,7 +2402,7 @@ void MemPool::releaseBlock(MemBlock* block, bool decrUsage) noexcept
MutexLockGuard guard(parent->mutex, "MemPool::releaseBlock /parent");
#endif
block->resetRedirect(parent);
- parent->releaseBlock(block, false);
+ parent->releaseBlock(block, RELEASE_RED);
return;
}
@@ -2749,6 +2787,10 @@ void MemoryPool::deallocate(void* block) noexcept
void MemoryPool::deletePool(MemoryPool* pool)
{
+#ifdef DEBUG_LOST_POOLS
+ Jrd::checkPool(pool);
+#endif
+
while (pool->finalizers)
{
auto finalizer = pool->finalizers;
diff --git a/src/common/classes/alloc.h b/src/common/classes/alloc.h
index 142ef1c3434..44ec635e4ff 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);
}
diff --git a/src/common/classes/array.h b/src/common/classes/array.h
index 9261b3fb562..122cbf6137d 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,12 @@ class EmptyStorage : public AutoStorage
// Dynamic array of simple types
template >
-class Array : protected Storage
+class Array : public Storage
{
+#ifndef _MSC_VER
+ static_assert(std::is_trivially_copyable(), "Only simple (trivially copyable) types supported in array");
+#endif
+
public:
typedef FB_SIZE_T size_type;
typedef FB_SSIZE_T difference_type;
@@ -82,6 +87,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),
@@ -197,6 +204,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);
@@ -451,7 +467,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
{
@@ -466,6 +482,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;
@@ -488,7 +517,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.
@@ -622,6 +663,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 a6935c82460..fffbb61c736 100644
--- a/src/common/classes/auto.h
+++ b/src/common/classes/auto.h
@@ -234,11 +234,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)
@@ -255,7 +255,7 @@ class AutoSetRestoreFlag
*value |= oldValue;
}
- void release(T cleanBit)
+ void release(T2 cleanBit)
{
bit &= ~cleanBit;
oldValue &= ~cleanBit;
@@ -267,7 +267,7 @@ class AutoSetRestoreFlag
private:
T* value;
- T bit;
+ T2 bit;
T oldValue;
};
@@ -303,6 +303,35 @@ class AutoSetRestore2
T oldValue;
};
+template
+class AutoSave2
+{
+private:
+ typedef T (T2::*Getter)();
+ typedef void (T2::*Setter)(T);
+
+public:
+ AutoSave2(T2* aPointer, Getter aGetter, Setter aSetter)
+ : pointer(aPointer),
+ setter(aSetter),
+ oldValue((aPointer->*aGetter)())
+ { }
+
+ ~AutoSave2()
+ {
+ (pointer->*setter)(oldValue);
+ }
+
+ // copying is prohibited
+ AutoSave2(const AutoSave2&) = delete;
+ AutoSave2& operator =(const AutoSave2&) = delete;
+
+private:
+ T2* pointer;
+ Setter setter;
+ T oldValue;
+};
+
template
class Cleanup
{
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/misc/class_perf.cpp b/src/common/classes/misc/class_perf.cpp
index 142bfde3262..bde62fcd1ae 100644
--- a/src/common/classes/misc/class_perf.cpp
+++ b/src/common/classes/misc/class_perf.cpp
@@ -204,7 +204,7 @@ static void testAllocatorMemoryPool()
{
printf("Test run for Firebird::MemoryPool...\n");
start();
- Firebird::MemoryPool* pool = Firebird::MemoryPool::createPool();
+ Firebird::MemoryPool* pool = Firebird::MemoryPool::createPool(ALLOC_ARGS0);
MallocAllocator allocator;
BePlusTree, AllocItem> items(&allocator),
bigItems(&allocator);
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 161fdd7e8db..799dfb0f023 100644
--- a/src/common/common.h
+++ b/src/common/common.h
@@ -570,6 +570,7 @@
#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)
@@ -852,4 +853,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 75d2067b91f..7f0aed7e78c 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 = static_cast(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 = static_cast(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); // : to->getCharSet();
const char pad = chid == ttype_binary ? '\0' : ' ';
memset(q, pad, trailing);
@@ -1687,11 +1687,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);
@@ -1985,7 +1985,7 @@ 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;
@@ -1993,7 +1993,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
// Because of this we can freely use `toCharset` against `from`.
{ // 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;
@@ -2098,7 +2098,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));
@@ -2169,7 +2169,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);
@@ -2452,7 +2452,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
MOVE_CLEAR(&desc, sizeof(desc));
desc.dsc_address = (UCHAR*) temp.c_str();
desc.dsc_dtype = dtype_text;
- desc.dsc_ttype() = ttype_ascii;
+ desc.setTextType(ttype_ascii);
desc.dsc_length = static_cast(temp.length());
if (from->isTimeStamp() && version4)
@@ -2472,7 +2472,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,
@@ -2520,7 +2520,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,
@@ -2656,7 +2656,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));
@@ -2922,7 +2922,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)
{
/**************************************
@@ -3685,11 +3685,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();
@@ -3697,13 +3697,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;
}
@@ -3712,7 +3712,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)
@@ -3740,7 +3740,7 @@ namespace
return MIN(length, size);
}
- CHARSET_ID CommonCallbacks::getChid(const dsc* d)
+ CSetId CommonCallbacks::getChid(const dsc* d)
{
return INTL_TTYPE(d);
}
@@ -3769,7 +3769,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 0a4198fc215..5974c6b26da 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*, bool trustedSource = false);
void CVT_move(const dsc*, dsc*, Firebird::DecimalStatus, ErrorFunction, bool trustedSource = false);
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 86cf77f92b0..f08c96b0661 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 81e6bdbc561..27727ca24d7 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,9 +106,10 @@ 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 noexcept { return dsc_scale | (dsc_flags & 0xFF00);}
- SSHORT& dsc_ttype() noexcept { return dsc_sub_type;}
- SSHORT dsc_ttype() const noexcept { return dsc_sub_type;}
+ TTypeId dsc_blob_ttype() const noexcept
+ {
+ return TTypeId(dsc_scale | (dsc_flags & 0xFF00));
+ }
bool isNullable() const noexcept
{
@@ -228,12 +230,9 @@ typedef struct dsc
return dsc_dtype == dtype_unknown;
}
- SSHORT getBlobSubType() const noexcept
+ UCHAR getType() const noexcept
{
- if (isBlob())
- return dsc_sub_type;
-
- return isc_blob_text;
+ return dsc_dtype;
}
SSHORT getSubType() const noexcept
@@ -244,21 +243,29 @@ typedef struct dsc
return 0;
}
+ SSHORT getBlobSubType() const noexcept
+ {
+ if (isBlob())
+ return dsc_sub_type;
+
+ return isc_blob_text;
+ }
+
void setBlobSubType(SSHORT subType) noexcept
{
if (isBlob())
dsc_sub_type = subType;
}
- UCHAR getCharSet() const noexcept
+ CSetId getCharSet() const noexcept
{
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 +276,49 @@ typedef struct dsc
return CS_NONE;
}
- USHORT getTextType() const noexcept
+ TTypeId getTextType() const noexcept
{
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) noexcept
+ void setTextType(TTypeId ttype) noexcept
{
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 noexcept
+ CollId getCollation() const noexcept
+ {
+ return CollId(getTextType());
+ }
+
+ FLD_LENGTH getLength() const
{
- return getTextType() >> 8;
+ return dsc_length;
+ }
+
+ SCHAR getScale() const
+ {
+ return dsc_scale;
}
void clear() noexcept
@@ -317,7 +334,7 @@ typedef struct dsc
dsc_flags = 0;
}
- void makeBlob(SSHORT subType, USHORT ttype, ISC_QUAD* address = NULL) noexcept
+ void makeBlob(SSHORT subType, TTypeId ttype, ISC_QUAD* address = NULL) noexcept
{
clear();
dsc_dtype = dtype_blob;
@@ -422,7 +439,7 @@ typedef struct dsc
dsc_address = (UCHAR*) address;
}
- void makeText(USHORT length, USHORT ttype, UCHAR* address = NULL) noexcept
+ void makeText(USHORT length, TTypeId ttype, UCHAR* address = NULL) noexcept
{
clear();
dsc_dtype = dtype_text;
@@ -485,7 +502,7 @@ typedef struct dsc
dsc_address = (UCHAR*) address;
}
- void makeVarying(USHORT length, USHORT ttype, UCHAR* address = NULL) noexcept
+ void makeVarying(USHORT length, TTypeId ttype, UCHAR* address = NULL) noexcept
{
clear();
dsc_dtype = dtype_varying;
@@ -499,6 +516,16 @@ typedef struct dsc
dsc_address = address;
}
+ bool operator==(const dsc& v) const noexcept
+ {
+ 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 noexcept;
operator Ods::Descriptor() const
@@ -537,16 +564,6 @@ typedef struct dsc
#endif // __cpluplus
} DSC;
-inline SSHORT DSC_GET_CHARSET(const dsc* desc) noexcept
-{
- return (desc->dsc_sub_type & 0x00FF);
-}
-
-inline SSHORT DSC_GET_COLLATE(const dsc* desc) noexcept
-{
- return (desc->dsc_sub_type >> 8);
-}
-
struct alt_dsc
{
SLONG dsc_combined_type;
diff --git a/src/common/dsc_proto.h b/src/common/dsc_proto.h
index cbf265175a6..633e454b6d7 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*) noexcept;
const TEXT* DSC_dtype_tostring(UCHAR) noexcept;
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 48c0c1d6113..46eeedb908f 100644
--- a/src/common/sdl.cpp
+++ b/src/common/sdl.cpp
@@ -791,7 +791,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:
@@ -802,7 +802,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:
@@ -813,7 +813,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/common/tests/CvtTestUtils.h b/src/common/tests/CvtTestUtils.h
index ab1585878b8..b7917242419 100644
--- a/src/common/tests/CvtTestUtils.h
+++ b/src/common/tests/CvtTestUtils.h
@@ -110,11 +110,11 @@ class MockCallback : public Firebird::Callbacks
{}
public:
- bool transliterate(const dsc* from, dsc* to, CHARSET_ID&) override { return true; }
- CHARSET_ID getChid(const dsc* d) override { return 0; }
- Firebird::CharSet* getToCharset(CHARSET_ID charset2) override { return nullptr; }
+ bool transliterate(const dsc* from, dsc* to, CSetId&) override { return true; }
+ CSetId getChid(const dsc* d) override { return CSetId(0); }
+ Firebird::CharSet* getToCharset(CSetId charset2) override { return nullptr; }
void validateData(Firebird::CharSet* toCharset, SLONG length, const UCHAR* q) override { }
- ULONG validateLength(Firebird::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
+ ULONG validateLength(Firebird::CharSet* charSet, CSetId charSetId, ULONG length, const UCHAR* start,
const USHORT size) override { return 0; }
SLONG getLocalDate() override
diff --git a/src/common/tests/StringTest.cpp b/src/common/tests/StringTest.cpp
index aa168b1d5f9..8928d0ad1c9 100644
--- a/src/common/tests/StringTest.cpp
+++ b/src/common/tests/StringTest.cpp
@@ -638,7 +638,7 @@ BOOST_AUTO_TEST_CASE(DefaultPoolMoveTest)
BOOST_AUTO_TEST_CASE(NewPoolMoveTest)
{
- AutoMemoryPool pool(MemoryPool::createPool());
+ AutoMemoryPool pool(MemoryPool::createPool(ALLOC_ARGS0));
string sourceString(*pool, BigStringValue.data(), length(BigStringValue));
// Move c'tor
@@ -664,7 +664,7 @@ BOOST_AUTO_TEST_SUITE(CannotMoveTests)
// Do not move
BOOST_AUTO_TEST_CASE(DifferentVsDefaultPoolMove)
{
- AutoMemoryPool pool(MemoryPool::createPool());
+ AutoMemoryPool pool(MemoryPool::createPool(ALLOC_ARGS0));
string sourceString(*pool, BigStringValue.data(), length(BigStringValue));
// Move c'tor
@@ -685,8 +685,8 @@ BOOST_AUTO_TEST_CASE(DifferentVsDefaultPoolMove)
// Do not move
BOOST_AUTO_TEST_CASE(DifferentPoolsMove)
{
- AutoMemoryPool pool1(MemoryPool::createPool());
- AutoMemoryPool pool2(MemoryPool::createPool());
+ AutoMemoryPool pool1(MemoryPool::createPool(ALLOC_ARGS0));
+ AutoMemoryPool pool2(MemoryPool::createPool(ALLOC_ARGS0));
string sourceString(*pool1, BigStringValue.data(), length(BigStringValue));
// Move c'tor
diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp
index 87fd272e1e1..eb1f03a4d70 100644
--- a/src/dsql/AggNodes.cpp
+++ b/src/dsql/AggNodes.cpp
@@ -410,7 +410,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 8e83a0cc249..073f69eb1c0 100644
--- a/src/dsql/BoolNodes.cpp
+++ b/src/dsql/BoolNodes.cpp
@@ -891,16 +891,17 @@ TriState ComparativeBoolNode::stringBoolean(thread_db* tdbb, Request* request,
{
SET_TDBB(tdbb);
- USHORT type1;
+ TTypeId type1;
if (!desc1->isBlob())
- type1 = INTL_TEXT_TYPE(*desc1);
+ type1 = desc1->getTextType();
else
{
// No MATCHES support for blob
if (blrOp == blr_matching)
return TriState(false);
+ // Non-text blob is treated here as NONE, not OCTETS
type1 = desc1->dsc_sub_type == isc_blob_text ? desc1->dsc_blob_ttype() : ttype_none;
}
@@ -954,7 +955,7 @@ TriState ComparativeBoolNode::stringBoolean(thread_db* tdbb, Request* request,
}
UCHAR* patternStr = nullptr;
- SLONG patternLen = 0;
+ ULONG patternLen = 0;
MoveBuffer patternBuffer;
auto createMatcher = [&]()
@@ -1103,17 +1104,7 @@ bool ComparativeBoolNode::sleuth(thread_db* tdbb, Request* request,
// 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)
@@ -1242,7 +1233,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 9097c30658e..d2d79600c77 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
+
+inline constexpr MetaId MIN_RELATION_ID = USER_DEF_REL_INIT_ID;
+inline constexpr MetaId 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 QualifiedName& childRelName, const QualifiedName& 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 constexpr const char* CHECK_CONSTRAINT_EXCEPTION = "check_constraint";
@@ -390,8 +415,8 @@ void defineComputed(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relati
if (field->dtype <= dtype_any_text)
{
- field->charSetId = saveCharSetIdSpecified ? std::optional{DSC_GET_CHARSET(&saveDesc)} : std::nullopt;
- field->collationId = DSC_GET_COLLATE(&saveDesc);
+ field->charSetId = saveCharSetIdSpecified ? std::optional{saveDesc.getCharSet()} : std::nullopt;
+ field->collationId = saveDesc.getCollation();
}
else
field->subType = saveDesc.dsc_sub_type;
@@ -405,11 +430,12 @@ void defineComputed(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relati
if (field->dtype <= dtype_any_text)
{
- field->charSetId = DSC_GET_CHARSET(&desc);
- field->collationId = DSC_GET_COLLATE(&desc);
+ field->charSetId = desc.getCharSet();
+ field->collationId = desc.getCollation();
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)
@@ -461,13 +487,13 @@ void definePartial(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relatio
//
// On deleting from RDB$RELATION_CONSTRAINTS, 2 system triggers fire:
//
-// (A) pre delete trigger: pre_delete_constraint, will:
+// (A) pre delete trigger beforeDeleteRelationConstraint will:
//
// 1. delete a record first from RDB$REF_CONSTRAINTS where
// RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME =
// RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME
//
-// (B) post delete trigger: post_delete_constraint will:
+// (B) post delete trigger afterDeleteRelationConstraint will:
//
// 1. also delete a record from RDB$INDICES where
// RDB$INDICES.RDB$INDEX_NAME =
@@ -1168,7 +1194,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;
@@ -1829,11 +1855,19 @@ 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);
+ }
+
+ fb_assert(id);
+ MetadataCache::newVersion(tdbb, id);
if (name.package.isEmpty())
{
@@ -1842,13 +1876,6 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql
}
savePoint.release(); // everything is ok
-
- if (alter)
- {
- // Update DSQL cache
- METD_drop_function(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_udf, name);
- }
}
bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
@@ -1861,8 +1888,7 @@ bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_udf))
return false;
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
- DDL_TRIGGER_CREATE_FUNCTION, name, {});
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_FUNCTION, name, {});
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_udf);
}
@@ -1875,12 +1901,13 @@ bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
{
try
{
- SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_fun_id, "RDB$FUNCTIONS");
- id %= (MAX_SSHORT + 1);
-
- if (!id)
+ SINT64 id64 = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_fun_id, "RDB$FUNCTIONS");
+ id64 %= (MAX_SSHORT + 1);
+ if (!id64)
continue;
+ id = id64;
+
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
FUN IN RDB$FUNCTIONS
{
@@ -1934,6 +1961,9 @@ bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
if (name.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;
@@ -1962,8 +1992,15 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
name.toQuotedString());
}
- if (!secondPass && runTriggers && name.package.isEmpty())
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, name, {});
+ id = FUN.RDB$FUNCTION_ID;
+
+ if (!secondPass && runTriggers)
+ {
+ if (name.package.isEmpty())
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, name, {});
+
+ MetadataCache::oldVersion(tdbb, id, CacheFlag::OLD_ALTER);
+ }
MODIFY FUN
if (secondPass)
@@ -2178,6 +2215,8 @@ bool CreateAlterFunctionNode::executeAlterIndividualParameters(thread_db* tdbb,
if (!secondPass && runTriggers && name.package.isEmpty())
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, name, {});
+ id = FUN.RDB$FUNCTION_ID;
+
MODIFY FUN
if (deterministic.isAssigned())
{
@@ -2552,6 +2591,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
@@ -2561,12 +2601,13 @@ void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds
{
found = true;
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION,
- name, {});
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, name, {});
if (!FUN.RDB$ENGINE_NAME.NULL || !FUN.RDB$FUNCTION_BLR.NULL)
status_exception::raise(Arg::Gds(isc_dyn_newfc_oldsyntax) << name.toQuotedString());
+ MetadataCache::oldVersion(tdbb, (id = FUN.RDB$FUNCTION_ID), CacheFlag::OLD_ALTER);
+
MODIFY FUN
if (clauses.name.hasData())
{
@@ -2590,7 +2631,10 @@ void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds
END_FOR
if (found)
+ {
+ MetadataCache::newVersion(tdbb, id);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_FUNCTION, name, {});
+ }
else
{
// msg 41: "Function %s not found"
@@ -2598,10 +2642,6 @@ void AlterExternalFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds
}
savePoint.release(); // everything is ok
-
- // Update DSQL cache
- METD_drop_function(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_udf, name);
}
@@ -2681,6 +2721,11 @@ void DropFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
bool found = false;
+ MetaId id;
+
+ // missing ID in the node - therefore use lookup_function() with appropriate flags
+ // instead: MetadataCache::oldVersion(tdbb, id);
+ MetadataCache::lookup_function(tdbb, name, CacheFlag::OLD_DROP);
dropArguments(tdbb, transaction, name);
@@ -2702,12 +2747,12 @@ void DropFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
if (name.package.isEmpty())
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_FUNCTION, name, {});
+ 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
@@ -2732,14 +2777,15 @@ void DropFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
END_FOR
}
- if (found && name.package.isEmpty())
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_FUNCTION, name, {});
+ if (found)
+ {
+ MetadataCache::erase(tdbb, id);
- savePoint.release(); // everything is ok
+ if (name.package.isEmpty())
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_FUNCTION, name, {});
+ }
- // Update DSQL cache
- METD_drop_function(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_udf, name);
+ savePoint.release(); // everything is ok
}
@@ -2867,11 +2913,19 @@ 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);
+ }
+
+ fb_assert(id);
+ MetadataCache::newVersion(tdbb, id);
if (name.package.isEmpty())
{
@@ -2880,13 +2934,6 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq
}
savePoint.release(); // everything is ok
-
- if (alter)
- {
- // Update DSQL cache
- METD_drop_procedure(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_procedure, name);
- }
}
bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
@@ -2913,12 +2960,13 @@ bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc
{
try
{
- SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_prc_id, "RDB$PROCEDURES");
- id %= (MAX_SSHORT + 1);
-
- if (!id)
+ SINT64 id64 = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_prc_id, "RDB$PROCEDURES");
+ id64 %= (MAX_SSHORT + 1);
+ if (!id64)
continue;
+ id = id64;
+
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
P IN RDB$PROCEDURES
{
@@ -2964,6 +3012,9 @@ bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc
if (name.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;
@@ -2991,8 +3042,15 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch
name.toQuotedString());
}
- if (!secondPass && runTriggers && name.package.isEmpty())
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PROCEDURE, name, {});
+ id = P.RDB$PROCEDURE_ID;
+
+ if (!secondPass && runTriggers)
+ {
+ if (name.package.isEmpty())
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PROCEDURE, name, {});
+
+ MetadataCache::oldVersion(tdbb, id, CacheFlag::OLD_ALTER);
+ }
MODIFY P
if (secondPass)
@@ -3137,6 +3195,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$SCHEMA_NAME EQ name.schema.c_str() AND
+ P.RDB$PROCEDURE_NAME EQ name.object.c_str() AND
+ P.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '')
+ {
+ MODIFY P
+ {
+ P.RDB$PROCEDURE_INPUTS = (USHORT) parameters.getCount();
+ P.RDB$PROCEDURE_OUTPUTS = (USHORT) returns.getCount();
+
+ P.RDB$PROCEDURE_BLR.NULL = TRUE;
+ P.RDB$DEBUG_INFO.NULL = TRUE;
+ P.RDB$PROCEDURE_TYPE.NULL = TRUE;
+ }
+ END_MODIFY
+ }
+ END_FOR
}
return modified;
@@ -3167,6 +3247,8 @@ bool CreateAlterProcedureNode::executeAlterIndividualParameters(thread_db* tdbb,
if (!secondPass && runTriggers && name.package.isEmpty())
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PROCEDURE, name, {});
+ id = P.RDB$PROCEDURE_ID;
+
MODIFY P
if (ssDefiner.has_value())
{
@@ -3508,9 +3590,14 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
AutoSavePoint savePoint(tdbb, transaction);
bool found = false;
+ // missing ID in the node - therefore use lookup_procedure() with appropriate flags
+ // instead: MetadataCache::oldVersion(tdbb, id);
+ MetadataCache::lookup_procedure(tdbb, name, CacheFlag::OLD_DROP);
+
dropParameters(tdbb, transaction, name);
AutoCacheRequest requestHandle(tdbb, drq_e_prcs2, DYN_REQUESTS);
+ MetaId id;
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PRC IN RDB$PROCEDURES
@@ -3528,12 +3615,12 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
if (name.package.isEmpty())
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PROCEDURE, name, {});
+ 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
@@ -3558,14 +3645,15 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
END_FOR
}
- if (found && name.package.isEmpty())
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE, name, {});
+ if (found)
+ {
+ MetadataCache::erase(tdbb, id);
- savePoint.release(); // everything is ok
+ if (name.package.isEmpty())
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE, name, {});
+ }
- // Update DSQL cache
- METD_drop_procedure(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_procedure, name);
+ savePoint.release(); // everything is ok
}
@@ -3830,8 +3918,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
@@ -3844,6 +3930,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
@@ -4190,7 +4279,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
if (fromName.object.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())
{
@@ -4225,24 +4314,22 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
specificAttributes = temp;
}
- info.charsetName = forCharSet;
+ info.charsetName.push(forCharSet);
info.collationName = name;
if (X.RDB$BASE_COLLATION_NAME.NULL)
info.baseCollationName = info.collationName.object.c_str();
else
- {
info.baseCollationName = X.RDB$BASE_COLLATION_NAME;
- info.baseCollationName.rtrim();
- }
info.ignoreAttributes = false;
- if (!IntlManager::collationInstalled(info.baseCollationName, info.charsetName))
+ if (!IntlManager::collationInstalled(info.baseCollationName,
+ info.charsetName[0]))
{
// msg: 223: "Collation @1 not installed for character set @2"
status_exception::raise(
- Arg::PrivateDyn(223) << info.baseCollationName << info.charsetName.toQuotedString());
+ Arg::PrivateDyn(223) << info.baseCollationName << info.charsetName[0].toQuotedString());
}
IntlUtil::SpecificAttributesMap map;
@@ -4260,7 +4347,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
string newSpecificAttributes;
if (!IntlManager::setupCollationAttributes(
- info.baseCollationName, info.charsetName, s,
+ info.baseCollationName, info.charsetName[0], s,
newSpecificAttributes))
{
// msg: 222: "Invalid collation attributes"
@@ -4326,7 +4413,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)
@@ -4518,7 +4605,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);
}
@@ -5132,8 +5219,8 @@ void AlterDomainNode::getDomainType(thread_db* tdbb, jrd_tra* transaction, dyn_f
FLD.RDB$FIELD_NAME EQ dynFld.dyn_fld_source.object.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;
@@ -5358,8 +5445,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;
@@ -6231,8 +6318,155 @@ RelationNode::RelationNode(MemoryPool& p, RelationSourceNode* aDsqlNode)
: DdlNode(p),
dsqlNode(aDsqlNode),
name(p, dsqlNode->dsqlName),
- clauses(p)
+ clauses(p),
+ indexList(p)
+{
+}
+
+// Check id to belong or not to erased but uncommitted objects.
+// True means it does NOT belong.
+
+bool RelationNode::checkDeletedId(thread_db* tdbb, MetaId& relId)
+{
+ // Check for presence of pages belonging to that relation.
+ static const CachedRequestId requestCacheId;
+ AutoCacheRequest request(tdbb, requestCacheId);
+ Attachment* attachment = tdbb->getAttachment();
+
+ FOR (REQUEST_HANDLE request)
+ P IN RDB$PAGES WITH
+ P.RDB$RELATION_ID EQ relId
+ {
+ return false;
+ }
+ END_FOR
+
+ return true;
+}
+
+// Walk relId up to existingRelationId and check each value - is it free?
+
+bool RelationNode::checkIdRange(thread_db* tdbb, MetaId& relId, const MetaId existingRelationId)
+{
+ for (; relId < existingRelationId; ++relId)
+ {
+ if (checkDeletedId(tdbb, relId))
+ return true;
+ }
+
+ ++relId;
+ return false;
+}
+
+// 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.
+
+// Check correctness when ID turns over MAX_RELATION_ID !!!!!!
+
+MetaId RelationNode::generateRelId(thread_db* tdbb, MetaName name)
+{
+ Attachment* attachment = tdbb->getAttachment();
+ MetaId relId = 0;
+
+ static const CachedRequestId idDb;
+ AutoCacheRequest requestDb(tdbb, idDb);
+
+ FOR(REQUEST_HANDLE requestDb)
+ D IN RDB$DATABASE
+ {
+ MetaId startId = D.RDB$RELATION_ID;
+ MetaId stopId = MAX_RELATION_ID + 1;
+
+// !!!!!! printf("suggest %d %s...", startId, name.c_str()); fflush(stdout);
+
+ for(;;)
+ {
+ if (startId < MIN_RELATION_ID || startId > MAX_RELATION_ID)
+ startId = MIN_RELATION_ID;
+
+ relId = startId;
+ enum {MISS, VALID, FREE} found = FREE;
+
+ static const CachedRequestId idRels;
+ AutoCacheRequest requestRels(tdbb, idRels);
+
+ FOR(REQUEST_HANDLE requestRels)
+ R IN RDB$RELATIONS
+ WITH R.RDB$RELATION_ID GE startId
+ AND R.RDB$RELATION_ID LT stopId
+ SORTED BY R.RDB$RELATION_ID
+ {
+ found = MISS;
+ if (checkIdRange(tdbb, relId, R.RDB$RELATION_ID))
+ {
+ found = VALID;
+ break;
+ }
+ }
+ END_FOR
+
+ if (found == FREE)
+ found = checkIdRange(tdbb, relId, stopId) ? VALID : MISS;
+
+ if (found == VALID)
+ {
+ MODIFY D USING
+ D.RDB$RELATION_ID = relId + 1;
+ END_MODIFY
+
+// !!!!!! printf(" %d\n", relId); fflush(stdout);
+ return relId;
+ }
+ fb_assert(found == MISS);
+
+ if (startId == MIN_RELATION_ID)
+ break;
+
+ stopId = startId;
+ startId = MIN_RELATION_ID;
+ }
+ }
+ END_FOR
+
+// !!!!!! printf("\n"); fflush(stdout);
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_table_name) << Arg::Str(name) <<
+ Arg::Gds(isc_imp_exc));
+}
+
+// Calculate the dbkey length to include each of the base relations.
+
+USHORT RelationNode::calcDbKeyLength(thread_db* tdbb)
{
+ const USHORT MIN_RELATION_ID = USER_DEF_REL_INIT_ID;
+ Attachment* attachment = tdbb->getAttachment();
+
+ USHORT dbKeyLength = 0;
+
+ static const CachedRequestId reqId;
+ AutoCacheRequest handle(tdbb, reqId);
+
+ FOR(REQUEST_HANDLE handle)
+ Z IN RDB$VIEW_RELATIONS CROSS
+ R IN RDB$RELATIONS
+ WITH Z.RDB$RELATION_NAME EQ R.RDB$RELATION_NAME AND
+ Z.RDB$RELATION_SCHEMA_NAME EQ R.RDB$SCHEMA_NAME AND
+ Z.RDB$VIEW_NAME EQ name.object.c_str() AND
+ Z.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ (Z.RDB$CONTEXT_TYPE = VCT_TABLE OR
+ Z.RDB$CONTEXT_TYPE = VCT_VIEW)
+ {
+ dbKeyLength += R.RDB$DBKEY_LENGTH;
+ }
+ END_FOR
+
+ fb_assert(dbKeyLength);
+
+ return dbKeyLength;
}
void RelationNode::FieldDefinition::modify(thread_db* tdbb, jrd_tra* transaction)
@@ -6593,6 +6827,8 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction,
IDX.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND
IDX.RDB$RELATION_NAME EQ relationName.object.c_str() AND
IDX_SEG.RDB$FIELD_NAME EQ fieldName.c_str() AND
+ (IDX.RDB$INDEX_INACTIVE MISSING OR
+ IDX.RDB$INDEX_INACTIVE NE MET_index_deferred_drop) AND
NOT ANY REL_CONST IN RDB$RELATION_CONSTRAINTS
WITH REL_CONST.RDB$SCHEMA_NAME EQ IDX.RDB$SCHEMA_NAME AND
REL_CONST.RDB$RELATION_NAME EQ IDX.RDB$RELATION_NAME AND
@@ -7125,7 +7361,8 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
definition.refColumns = constraint.refColumns;
QualifiedName qualifiedIndexName(constraint.index->name, name.schema);
- CreateIndexNode::store(tdbb, transaction, qualifiedIndexName, definition, &referredIndexName);
+ indexList.push(CreateIndexNode::store(tdbb, indexList.getPool(), transaction,
+ qualifiedIndexName, definition, &referredIndexName));
constraint.index->name = qualifiedIndexName.object;
CRT.RDB$INDEX_NAME.NULL = FALSE;
@@ -7812,2256 +8049,3998 @@ void RelationNode::dropFromPublication(thread_db* tdbb,
}
-//----------------------
-
-
-string CreateRelationNode::internalPrint(NodePrinter& printer) const
+void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& relName)
{
- RelationNode::internalPrint(printer);
+/**************************************
+ *
+ * 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;
+ TrigArray* triggers = nullptr;
- NODE_PRINT(printer, externalFile);
- NODE_PRINT(printer, relationType);
+ SET_TDBB(tdbb);
+ Attachment* attachment = tdbb->getAttachment();
+ Database* dbb = tdbb->getDatabase();
+ bool null_view;
+ USHORT n;
- return "CreateRelationNode";
-}
+ AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, TDBB_dont_post_dfw, true);
-void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
-{
- SCL_check_create_access(tdbb, obj_relations, name.schema);
-}
+ {
+ AutoCacheRequest request_fmt1(tdbb, irq_format1, IRQ_REQUESTS);
-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;
+ FOR(REQUEST_HANDLE request_fmt1 TRANSACTION_HANDLE transaction)
+ REL IN RDB$RELATIONS
+ WITH REL.RDB$SCHEMA_NAME EQ relName.schema.c_str() AND
+ REL.RDB$RELATION_NAME EQ relName.object.c_str()
- saveRelation(tdbb, dsqlScratch, name, false, true);
+ {
+ relation = MetadataCache::lookup_relation_id(tdbb, REL.RDB$RELATION_ID, CacheFlag::AUTOCREATE);
+ if (!relation)
+ return;
- if (externalFile)
- {
- fb_assert(dsqlScratch->relation);
- dsqlScratch->relation->rel_flags |= REL_external;
- }
+ const bid blob_id = REL.RDB$VIEW_BLR;
+ null_view = blob_id.isEmpty();
+ external_flag = REL.RDB$EXTERNAL_FILE[0];
+ triggers = &relation->rel_triggers;
- // run all statements under savepoint control
- AutoSavePoint savePoint(tdbb, transaction);
+ if (REL.RDB$VIEW_BLR.NULL)
+ {
+ if (REL.RDB$FORMAT == MAX_TABLE_VERSIONS)
+ raiseTooManyVersionsError(obj_relation, relName);
+ }
+ else
+ {
+ if (REL.RDB$FORMAT == MAX_VIEW_VERSIONS)
+ raiseTooManyVersionsError(obj_view, relName);
+ }
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TABLE, 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$SCHEMA_NAME EQ relName.schema.c_str() AND
+ RFR.RDB$RELATION_NAME EQ relName.object.c_str() AND
+ FLD.RDB$SCHEMA_NAME EQ RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE
+ {
+ // Update RFR to reflect new fields id
- DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation);
+ if (!RFR.RDB$FIELD_ID.NULL && RFR.RDB$FIELD_ID >= REL.RDB$FIELD_ID)
+ REL.RDB$FIELD_ID = RFR.RDB$FIELD_ID + 1;
- fb_assert(relationType.has_value());
+ // force recalculation of RDB$UPDATE_FLAG if field is calculated
+ if (!FLD.RDB$COMPUTED_BLR.isEmpty())
+ RFR.RDB$UPDATE_FLAG.NULL = TRUE;
- checkRelationTempScope(tdbb, transaction, name, relationType.value());
+ 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);
+
+ dsc schemaDesc;
+ schemaDesc.makeText(static_cast(strlen(REL.RDB$SCHEMA_NAME)),
+ CS_METADATA, (UCHAR*) REL.RDB$SCHEMA_NAME);
+
+ DeferredWork* work = DFW_post_work(transaction,
+ dfw_check_not_null, &desc, &schemaDesc, 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);
+ }
+ }
+ }
- AutoCacheRequest request(tdbb, drq_s_rels2, DYN_REQUESTS);
+ 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$SCHEMA_NAME EQ relName.schema.c_str() AND
+ X.RDB$RELATION_NAME EQ relName.object.c_str() AND
+ X.RDB$TRIGGER_TYPE EQ 1
+ {
+ RFR.RDB$UPDATE_FLAG = 1;
+ }
+ END_FOR
+ }
+ }
+ END_MODIFY
+ }
- STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- REL IN RDB$RELATIONS
- {
- strcpy(REL.RDB$SCHEMA_NAME, name.schema.c_str());
- strcpy(REL.RDB$RELATION_NAME, name.object.c_str());
- REL.RDB$SYSTEM_FLAG = 0;
- REL.RDB$FLAGS = REL_sql;
- REL.RDB$RELATION_TYPE = relationType.value();
+ // Store stuff in field summary
- 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;
+ 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));
- REL.RDB$VIEW_BLR.NULL = TRUE;
- REL.RDB$VIEW_SOURCE.NULL = TRUE;
- REL.RDB$EXTERNAL_FILE.NULL = TRUE;
+ QualifiedName fldsrc(RFR.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME);
+ putSummaryRecord(tdbb, blob, RSR_field_source, (UCHAR*)(fldsrc.toQuotedString().c_str()),
+ fldsrc.toQuotedString().length());
- if (externalFile)
- {
- if (externalFile->length() >= sizeof(REL.RDB$EXTERNAL_FILE))
- status_exception::raise(Arg::Gds(isc_dyn_name_longer));
+ 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);
- if (!(tdbb->tdbb_flags & TDBB_replicator) &&
- ISC_check_if_remote(externalFile->c_str(), false))
- {
- status_exception::raise(Arg::PrivateDyn(163));
- }
+ {
+ USHORT len = RFR.RDB$FIELD_POSITION;
+ putSummaryRecord(tdbb, blob, RSR_field_pos, (UCHAR*) &len, sizeof(len));
+ }
- REL.RDB$EXTERNAL_FILE.NULL = FALSE;
- strcpy(REL.RDB$EXTERNAL_FILE, externalFile->c_str());
- REL.RDB$RELATION_TYPE = rel_external;
- }
- }
- END_STORE
+ bid* defaultValue = RFR.RDB$DEFAULT_VALUE.isEmpty() ?
+ &FLD.RDB$DEFAULT_VALUE : &RFR.RDB$DEFAULT_VALUE;
- bool replicationEnabled = false;
+ putSummaryBlob(tdbb, blob, RSR_default_value, defaultValue, transaction);
+ putSummaryBlob(tdbb, blob, RSR_validation_blr, &FLD.RDB$VALIDATION_BLR, transaction);
- if (replicationState.isAssigned())
- replicationEnabled = replicationState.asBool();
- else
- {
- // Apply the default replication state to the table being created
+ bool notNull = (RFR.RDB$NULL_FLAG.NULL ?
+ (FLD.RDB$NULL_FLAG.NULL ? false : (bool) FLD.RDB$NULL_FLAG) :
+ (bool) RFR.RDB$NULL_FLAG);
- AutoCacheRequest request2(tdbb, drq_l_pub_mode, DYN_REQUESTS);
+ if (notNull)
+ {
+ putSummaryRecord(tdbb, blob, RSR_field_not_null,
+ nonnull_validation_blr, sizeof(nonnull_validation_blr));
+ }
- 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
- }
+ 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);
+ }
- if (replicationEnabled)
- {
- // Add table to the default publication
- RelationNode::addToPublication(tdbb, transaction, name, DEFAULT_PUBLICATION);
- }
+ 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);
+ }
- storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES);
+ n = RFR.RDB$IDENTITY_TYPE;
+ if (!RFR.RDB$IDENTITY_TYPE.NULL)
+ putSummaryRecord(tdbb, blob, RSR_field_identity_type, (UCHAR*) &n, sizeof(n));
- ObjectsArray constraints;
- const ObjectsArray* pkCols = findPkColumns();
- SSHORT position = 0;
+ // Make a temporary field block
- 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;
+ TemporaryField* tfb = FB_NEW_POOL(*tdbb->getDefaultPool()) TemporaryField;
+ tfb->tfb_next = stack;
+ stack = tfb;
- case Clause::TYPE_ADD_CONSTRAINT:
- makeConstraint(tdbb, dsqlScratch, transaction,
- static_cast(i->getObject()), constraints);
- break;
+ // for text data types, grab the CHARACTER_SET and
+ // COLLATION to give the type of international text
- default:
- fb_assert(false);
- break;
- }
- }
+ if (FLD.RDB$CHARACTER_SET_ID.NULL)
+ FLD.RDB$CHARACTER_SET_ID = CS_NONE;
- for (ObjectsArray::iterator constraint(constraints.begin());
- constraint != constraints.end();
- ++constraint)
- {
- defineConstraint(tdbb, dsqlScratch, transaction, constraint->name, *constraint->create);
- }
+ CollId collation((!FLD.RDB$COLLATION_ID.NULL) ? FLD.RDB$COLLATION_ID :
+ (!RFR.RDB$COLLATION_ID.NULL) ? RFR.RDB$COLLATION_ID : COLLATE_NONE); // codepoint collation
- dsqlScratch->relation->rel_flags &= ~REL_creating;
+ 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));
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TABLE, name, {});
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_random) << relName.toQuotedString());
+ }
- savePoint.release(); // everything is ok
+ // 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));
- // Update DSQL cache
- METD_drop_relation(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_relation, name);
-}
+ ERR_post_nothrow(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_random) << relName.toQuotedString());
+ INTL_texttype_lookup(tdbb, tfb->tfb_desc.getTextType()); // should punt
+ ERR_punt(); // if INTL_texttype_lookup hasn't punt
+ }
-// 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());
+ // Blob specific
+ if (tfb->tfb_desc.isBlob())
+ {
+ USHORT len = FLD.RDB$SEGMENT_LENGTH;
+ putSummaryRecord(tdbb, blob, RSR_segment_length, (UCHAR*) &len, sizeof(len));
+ }
- if (clause->constraintType == AddConstraintClause::CTYPE_PK)
- return &clause->columns;
- }
- }
+ // Store the default value.
- return NULL;
-}
+ memset(&tfb->tfb_default, 0, sizeof(tfb->tfb_default));
+ if (notNull && !defaultValue->isEmpty())
+ {
+ Jrd::ContextPoolHolder context(tdbb, dbb->createPool(ALLOC_ARGS0));
+ Statement* defaultStatement = NULL;
+ try
+ {
+ ValueExprNode* defaultNode = static_cast(MET_parse_blob(
+ tdbb, &relName.schema, getPermanent(relation), defaultValue, NULL,
+ &defaultStatement, false, false));
-//----------------------
+ AutoPtr defaultRequest(defaultStatement->findRequest(tdbb));
+ // Attention: this is scoped to the end of this "try".
+ AutoSetRestore2 autoRequest(tdbb,
+ &thread_db::getRequest, &thread_db::setRequest, defaultRequest);
-string AlterRelationNode::internalPrint(NodePrinter& printer) const
-{
- RelationNode::internalPrint(printer);
- return "AlterRelationNode";
-}
+ defaultRequest->validateTimeStamp();
-void AlterRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
-{
- SCL_check_relation(tdbb, name, SCL_alter);
-}
+ dsc* result = nullptr;
+ { // scope
+ Firebird::Cleanup detach([&defaultRequest] {TRA_detach_request(defaultRequest);});
+ TRA_attach_request(transaction, defaultRequest);
+ result = EVL_expr(tdbb, defaultRequest, defaultNode);
+ }
-void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
- jrd_tra* transaction)
-{
- saveRelation(tdbb, dsqlScratch, name, false, false);
+ if (result)
+ {
+ dsc desc = *result;
+ MoveBuffer buffer;
- dsql_rel* relation;
- relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name);
+ 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);
+ }
- 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.toQuotedString());
- }
+ impure_value tempValue {};
+ MoveBuffer tempBuffer;
- if (!dsqlScratch->relation)
- {
- //// TODO:
- /***
- char linecol[64];
- snprintf(linecol, sizeof(linecol), "At line %d, column %d.", (int) dsqlNode->line, (int) dsqlNode->column);
- ***/
+ 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);
+ }
+ }
- status_exception::raise(
- Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
- Arg::Gds(isc_dsql_relation_err) <<
- Arg::Gds(isc_random) << name.toQuotedString() /***<<
- Arg::Gds(isc_random) << linecol***/);
- }
+ EVL_make_value(tdbb, &desc, &tfb->tfb_default, relation->rel_pool);
+ }
+ }
+ catch (const Exception&)
+ {
+ if (defaultStatement)
+ defaultStatement->release(tdbb);
+ throw;
+ }
- bool beforeTriggerWasExecuted = false;
+ defaultStatement->release(tdbb);
+ }
- const auto executeBeforeTrigger = [&]()
- {
- if (!beforeTriggerWasExecuted)
- {
- beforeTriggerWasExecuted = true;
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_TABLE, 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 there is an error, get rid of the cached data.
+ tfb->tfb_id = RFR.RDB$FIELD_ID;
- try
- {
- // run all statements under savepoint control
- AutoSavePoint savePoint(tdbb, transaction);
+ if ((n = FLD.RDB$DIMENSIONS))
+ setupArray(tdbb, blob, FLD.RDB$FIELD_NAME, n, tfb);
- ObjectsArray constraints;
+ 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
- for (NestConst* i = clauses.begin(); i != clauses.end(); ++i)
- {
- switch ((*i)->type)
- {
- case Clause::TYPE_ADD_COLUMN:
+ if (null_view && !physical_fields)
{
- const auto addColumnClause = static_cast(i->getObject());
- bool createColumn = true;
+ if (REL.RDB$FORMAT.NULL)
+ DPM_delete_relation(tdbb, getPermanent(relation));
- if (addColumnClause->createIfNotExistsOnly)
- {
- AutoCacheRequest request(tdbb, drq_l_rel_fld_name, DYN_REQUESTS);
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_table_name) << relName.toQuotedString() <<
+ Arg::Gds(isc_must_have_phys_field));
+ }
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- RFL IN RDB$RELATION_FIELDS
- WITH RFL.RDB$SCHEMA_NAME = relation->rel_name.schema.c_str() AND
- RFL.RDB$RELATION_NAME = relation->rel_name.object.c_str() AND
- RFL.RDB$FIELD_NAME = addColumnClause->field->fld_name.c_str()
- {
- createColumn = false;
- break;
- }
- END_FOR
- }
+ blob = setupTriggers(tdbb, relation, null_view, triggers, blob);
- if (createColumn)
+ 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;
+
+ 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
+ WITH Z.RDB$SCHEMA_NAME = relName.schema.c_str() AND
+ Z.RDB$VIEW_NAME = relName.object.c_str() AND
+ R.RDB$SCHEMA_NAME EQ Z.RDB$RELATION_SCHEMA_NAME AND
+ R.RDB$RELATION_NAME EQ Z.RDB$RELATION_NAME
{
- 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;
+ 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
- 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$SCHEMA_NAME EQ name.schema.c_str() AND
- RFL.RDB$RELATION_NAME EQ name.object.c_str()
- {
- found = true;
+ makeFormat(tdbb, transaction, getPermanent(relation), 0, external);
+ }
+ }
+}
- 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.toQuotedString());
- }
- 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;
- if (!found)
- {
- // msg 176: "column %s does not exist in table/view %s"
- status_exception::raise(Arg::PrivateDyn(176) << clause->fromName << name.toQuotedString());
- }
+ Attachment* attachment = tdbb->getAttachment();
- AutoRequest request2;
+ AutoCacheRequest request_fmtx;
- FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- RCL IN RDB$RELATION_CONSTRAINTS CROSS
- CHK IN RDB$CHECK_CONSTRAINTS
- WITH RCL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- RCL.RDB$RELATION_NAME EQ name.object.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
+#ifdef DEV_BUILD
+ // system triggers
- break;
- }
+ request_fmtx.reset(tdbb, irq_format4, IRQ_REQUESTS);
- case Clause::TYPE_ALTER_COL_NULL:
- {
- executeBeforeTrigger();
+ FOR (REQUEST_HANDLE request_fmtx)
+ TRG IN RDB$TRIGGERS
+ WITH TRG.RDB$SCHEMA_NAME EQ relation->getName().schema.c_str() AND
+ TRG.RDB$RELATION_NAME EQ relation->getName().object.c_str() AND
+ TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_system
+ SORTED BY TRG.RDB$TRIGGER_SEQUENCE
+ {
+ fb_assert(false); // should not have any system triggers in the database
+ }
+ END_FOR
+#endif
- const AlterColNullClause* clause =
- static_cast(i->getObject());
+ // user triggers
- AutoRequest request;
- bool found = false;
+ request_fmtx.reset(tdbb, irq_format5, IRQ_REQUESTS);
- 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$SCHEMA_NAME EQ name.schema.c_str() AND
- RFL.RDB$RELATION_NAME EQ name.object.c_str()
- {
- found = true;
+ FOR (REQUEST_HANDLE request_fmtx)
+ TRG IN RDB$TRIGGERS
+ WITH TRG.RDB$SCHEMA_NAME EQ relation->getName().schema.c_str() AND
+ TRG.RDB$RELATION_NAME EQ relation->getName().object.c_str() AND
+ TRG.RDB$SYSTEM_FLAG = fb_sysflag_user AND
+ (NOT ANY
+ CHK IN RDB$CHECK_CONSTRAINTS
+ CROSS RCN IN RDB$RELATION_CONSTRAINTS
+ WITH TRG.RDB$SCHEMA_NAME EQ CHK.RDB$SCHEMA_NAME AND
+ TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME AND
+ CHK.RDB$SCHEMA_NAME EQ RCN.RDB$SCHEMA_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,
+ QualifiedName(TRG.RDB$TRIGGER_NAME, TRG.RDB$SCHEMA_NAME), null_view);
+ }
+ }
+ END_FOR
- 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.toQuotedString());
- }
+ // 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
- 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;
+ request_fmtx.reset(tdbb, irq_format6, IRQ_REQUESTS);
- AutoRequest request2;
+ FOR (REQUEST_HANDLE request_fmtx)
+ TRG IN RDB$TRIGGERS
+ WITH TRG.RDB$SCHEMA_NAME EQ relation->getName().schema.c_str() AND
+ TRG.RDB$RELATION_NAME EQ relation->getName().object.c_str() AND
+ (TRG.RDB$SYSTEM_FLAG BT fb_sysflag_check_constraint AND fb_sysflag_view_check OR
+ (TRG.RDB$SYSTEM_FLAG = fb_sysflag_user AND
+ ANY
+ CHK IN RDB$CHECK_CONSTRAINTS
+ CROSS RCN IN RDB$RELATION_CONSTRAINTS
+ WITH TRG.RDB$SCHEMA_NAME EQ CHK.RDB$SCHEMA_NAME AND
+ TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME AND
+ CHK.RDB$SCHEMA_NAME EQ RCN.RDB$SCHEMA_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,
+ QualifiedName(TRG.RDB$TRIGGER_NAME, TRG.RDB$SCHEMA_NAME), null_view);
+ }
+ }
+ END_FOR
- FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- RCL IN RDB$RELATION_CONSTRAINTS CROSS
- CHK IN RDB$CHECK_CONSTRAINTS
- WITH RCL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- RCL.RDB$RELATION_NAME EQ name.object.c_str() AND
- RCL.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT AND
- CHK.RDB$SCHEMA_NAME EQ RCL.RDB$SCHEMA_NAME 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
+ return blob;
+}
- if (!found)
- {
- // msg 176: "column %s does not exist in table/view %s"
- status_exception::raise(Arg::PrivateDyn(176) << clause->name << name.toQuotedString());
- }
- break;
- }
+void RelationNode::setupTriggerDetails(thread_db* tdbb, jrd_rel* relation, blb* blob,
+ TrigArray* triggers, const QualifiedName& 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
+ *
+ **************************************/
- case Clause::TYPE_ALTER_COL_POS:
- {
- executeBeforeTrigger();
+ putSummaryRecord(tdbb, blob, RSR_trigger_name,
+ (const UCHAR*) trigger_name.object.c_str(), trigger_name.object.length());
- 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;
+ if (!null_view) {
+ MET_load_trigger(tdbb, relation, trigger_name, [&](int t)->Triggers& {return (*triggers)[t];});
+ }
+}
- modifyLocalFieldPosition(tdbb, transaction, name, clause->name, pos);
- break;
- }
+void RelationNode::raiseTooManyVersionsError(const int obj_type, const QualifiedName& obj_name)
+{
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(getErrorCodeByObjectType(obj_type)) << obj_name.toQuotedString() <<
+ Arg::Gds(isc_version_err));
+}
- 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());
+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);
- 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));
- }
+ SET_TDBB(tdbb);
- deleteLocalField(tdbb, transaction, name, clause->name, clause->silent, executeBeforeTrigger);
- break;
- }
+ HalfStaticArray buffer;
+ UCHAR* p = buffer.getBuffer(length + 1);
+ *p++ = (UCHAR) type;
+ memcpy(p, data, length);
- 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;
+ blob->BLB_put_segment(tdbb, buffer.begin(), length + 1);
+}
- 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);
+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.
+ *
+ **************************************/
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- RC IN RDB$RELATION_CONSTRAINTS
- WITH RC.RDB$CONSTRAINT_NAME EQ constraintName.c_str() AND
- RC.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- RC.RDB$RELATION_NAME EQ name.object.c_str()
- {
- found = true;
- break;
- }
- END_FOR
- }
+ SET_TDBB(tdbb);
- 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;
- }
+ if (blob_id->isEmpty()) // If blob is null, don't bother.
+ return;
- break;
- }
+ // Go ahead and open blob
+ blb* blr = blb::open(tdbb, transaction, blob_id);
- case Clause::TYPE_ALTER_SQL_SECURITY:
- {
- executeBeforeTrigger();
+ ULONG length = blr->blb_length;
+ // We cannot deal with chunks longer than a max blob segment (minus one)
+ fb_assert(length < MAX_USHORT);
- AutoRequest request;
+ HalfStaticArray buffer;
+ UCHAR* p = buffer.getBuffer(length + 1);
+ *p++ = (UCHAR) type;
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- REL IN RDB$RELATIONS
- WITH REL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- REL.RDB$RELATION_NAME EQ name.object.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
+ length = blr->BLB_get_data(tdbb, p, length);
- break;
- }
+ blob->BLB_put_segment(tdbb, buffer.begin(), length + 1);
+}
- case Clause::TYPE_ALTER_PUBLICATION:
- {
- fb_assert(replicationState.isAssigned());
- executeBeforeTrigger();
+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
+ *
+ **************************************/
- if (replicationState.asBool())
- {
- // Add table to the publication
+ TTypeId ttId = tfb->tfb_desc.getTextType();
+ switch(ttId)
+ {
+ case TTypeId(CS_NONE):
+ case TTypeId(CS_BINARY):
+ break;
- try
- {
- RelationNode::addToPublication(tdbb, transaction,
- name, DEFAULT_PUBLICATION);
- }
- catch (const status_exception& ex)
- {
- if (ex.value()[1] != isc_unique_key_violation)
- throw;
+ default:
+ return INTL_defined_type(tdbb, ttId);
+ }
- // Ignore duplicated records
- fb_utils::init_status(tdbb->tdbb_status_vector);
- }
- }
- else
- {
- // Drop table from the publication
+ return true;
+}
- RelationNode::dropFromPublication(tdbb, transaction,
- name, DEFAULT_PUBLICATION);
- }
- 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
+ *
+ **************************************/
+
+ SLONG stuff[256];
+
+ 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);
+}
- 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;
+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();
- 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$SCHEMA_NAME EQ name.schema.c_str() AND
- RC.RDB$RELATION_NAME EQ name.object.c_str()
- {
- found = true;
- ERASE RC;
- }
- END_FOR
+ AutoCacheRequest request(tdbb, irq_r_fld_dim, IRQ_REQUESTS);
- if (!found)
- {
- // msg 130: "CONSTRAINT %s does not exist."
- status_exception::raise(Arg::PrivateDyn(130) << constraint->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;
}
+ }
+ END_FOR
- if (beforeTriggerWasExecuted)
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_TABLE, name, {});
+ desc->iad_count = 1;
- savePoint.release(); // everything is ok
-
- // Update DSQL cache
- METD_drop_relation(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_relation, name);
- }
- catch (const Exception&)
+ 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
+
+ auto formats = relation->getFormats();
+ fb_assert(formats->getCount());
+ for (const auto format : *(formats.getPointer()))
{
- dsql_fld* permField = FB_NEW_POOL(dsqlScratch->getAttachment()->dbb_pool)
- dsql_fld(dsqlScratch->getAttachment()->dbb_pool);
- *permField = *field;
+ if (format && (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 RFR.RDB$SCHEMA_NAME = name.schema.c_str() AND
- RFR.RDB$RELATION_NAME = name.object.c_str() AND
- RFR.RDB$FIELD_NAME = field->fld_name.c_str() AND
- REL.RDB$SCHEMA_NAME = RFR.RDB$SCHEMA_NAME AND
- REL.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME AND
- FLD.RDB$SCHEMA_NAME = RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND
- FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE
+ 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;
+ }
- const 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.object = 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 = QualifiedName(RFR.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME);
+ 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) << relation->getName().toQuotedString());
+ // Msg361: new record size of %ld bytes is too big
+ }
- const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.object.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
+ relation->addFormat(format);
- 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.toQuotedString() <<
- name.toQuotedString());
- }
+ // Store format in system relation
- if (hasDimensions)
- {
- // msg 225: "Default value is not allowed for array type in field %s"
- status_exception::raise(Arg::PrivateDyn(225) << field->fld_name);
- }
+ AutoCacheRequest request(tdbb, irq_format3, IRQ_REQUESTS);
- if (clause->computed)
- {
- // msg 233: "Local column %s is computed, cannot set a default value"
- status_exception::raise(Arg::PrivateDyn(233) << field->fld_name);
- }
+ 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);
- string defaultSource;
- BlrDebugWriter::BlrData defaultValue;
+ // Use generic representation of formats with 32-bit offsets
- defineDefault(tdbb, dsqlScratch, field, clause->defaultValue,
- defaultSource, defaultValue);
+ Firebird::Array odsDescs;
+ Ods::Descriptor* odsDesc = odsDescs.getBuffer(format->fmt_count);
- RFR.RDB$DEFAULT_SOURCE.NULL = FALSE;
- attachment->storeMetaDataBlob(tdbb, transaction,
- &RFR.RDB$DEFAULT_SOURCE, defaultSource);
+ for (Format::fmt_desc_const_iterator desc = format->fmt_desc.begin();
+ desc < format->fmt_desc.end(); ++desc, ++odsDesc)
+ {
+ *odsDesc = *desc;
+ }
- 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.toQuotedString() <<
- QualifiedName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME).toQuotedString());
- }
- }
- 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);
- }
+ HalfStaticArray buffer;
- DropSequenceNode::deleteIdentity(tdbb, transaction,
- QualifiedName(RFR.RDB$GENERATOR_NAME, name.schema));
+ buffer.add(UCHAR(format->fmt_count));
+ buffer.add(UCHAR(format->fmt_count >> 8));
- 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;
+ buffer.add((UCHAR*) odsDescs.begin(), odsDescs.getCount() * sizeof(Ods::Descriptor));
- 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 QualifiedName genName(RFR.RDB$GENERATOR_NAME, RFR.RDB$SCHEMA_NAME);
+ const FB_SIZE_T pos = buffer.getCount();
+ buffer.add(0);
+ buffer.add(0);
- 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);
+ 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 = nullptr;
- transaction->getGenIdCache()->put(id, val);
- }
+ Ods::Descriptor odsDflDesc = desc;
- if (clause->identityOptions->type.has_value())
- {
- MODIFY RFR
- RFR.RDB$IDENTITY_TYPE = clause->identityOptions->type.value();
- END_MODIFY
- }
+ 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);
- 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) <<
- name.toQuotedString());
- }
+ ++dflCount;
+ }
+ }
- MET_update_generator_increment(tdbb, id,
- clause->identityOptions->increment.value());
- }
+ buffer[pos] = UCHAR(dflCount);
+ buffer[pos + 1] = UCHAR(dflCount >> 8);
- dsc schemaDesc, nameDesc;
- schemaDesc.makeText((USHORT) genName.schema.length(), ttype_metadata, (UCHAR*) genName.schema.c_str());
- nameDesc.makeText((USHORT) genName.object.length(), ttype_metadata, (UCHAR*) genName.object.c_str());
- DFW_post_work(transaction, dfw_set_generator, &nameDesc, &schemaDesc, id);
+ blob->BLB_put_data(tdbb, buffer.begin(), buffer.getCount());
+ blob->BLB_close(tdbb);
+ }
+ END_STORE
- found = true;
- }
- END_FOR
+ return format;
+}
- 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.
-
- QualifiedName newDomainName;
- dyn_fld newDom;
-
- if (field->typeOfName.object.hasData())
- {
- // Case a1: Internal domain -> domain.
- // Case a2: Domain -> domain.
- newDomainName = field->typeOfName;
+//----------------------
- if (fb_utils::implicit_domain(newDomainName.object.c_str()))
- {
- // msg 224: "Cannot use the internal domain %s as new type for field %s".
- status_exception::raise(
- Arg::PrivateDyn(224) << newDomainName.toQuotedString() << 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.toQuotedString());
- }
+string CreateRelationNode::internalPrint(NodePrinter& printer) const
+{
+ RelationNode::internalPrint(printer);
- QualifiedName dummyCollationName;
- DDL_resolve_intl_type(dsqlScratch, field, dummyCollationName);
+ NODE_PRINT(printer, externalFile);
+ NODE_PRINT(printer, relationType);
- // 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.
+ return "CreateRelationNode";
+}
- newDomainName.schema = name.schema;
+void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
+{
+ SCL_check_create_access(tdbb, obj_relations, name.schema);
+}
- // 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);
+void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
+ jrd_tra* transaction)
+{
+ SET_TDBB(tdbb);
+ Attachment* attachment = tdbb->getAttachment();
+ Database* dbb = tdbb->getDatabase();
- field->dtype = desc.dsc_dtype;
- field->length = desc.dsc_length;
- field->scale = desc.dsc_scale;
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
- 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;
- }
+ if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation))
+ return;
- field->resolve(dsqlScratch, true);
+ saveRelation(tdbb, dsqlScratch, name, false, 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
+ if (externalFile)
+ {
+ fb_assert(dsqlScratch->relation);
+ dsqlScratch->relation->rel_flags |= REL_external;
+ }
- 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
+ // run all statements under savepoint control
+ AutoSavePoint savePoint(tdbb, transaction);
- newDom.dyn_fld_source = origDom.dyn_fld_source;
- }
- else // Case b2: Domain -> internal domain.
- storeGlobalField(tdbb, transaction, newDomainName, field);
- }
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TABLE, name, {});
- if (!clause->computed && !isView)
- {
- if (newDomainName.object.hasData())
- newDom.dyn_fld_source = newDomainName;
+ DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation);
- AlterDomainNode::getDomainType(tdbb, transaction, newDom);
- AlterDomainNode::checkUpdate(origDom, newDom);
+ fb_assert(relationType.has_value());
- 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.toQuotedString() <<
- name.toQuotedString());
- }
- }
- }
+ checkRelationTempScope(tdbb, transaction, name, relationType.value());
- if (newDomainName.object.hasData())
- {
- MODIFY RFR USING
- RFR.RDB$FIELD_SOURCE_SCHEMA_NAME.NULL = FALSE;
- strcpy(RFR.RDB$FIELD_SOURCE_SCHEMA_NAME, newDomainName.schema.c_str());
+ try
+ {
+ // Take a relation lock on 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);
- RFR.RDB$FIELD_SOURCE.NULL = FALSE;
- strcpy(RFR.RDB$FIELD_SOURCE, newDomainName.object.c_str());
+ AutoCacheRequest requestStore(tdbb, drq_s_rels2, DYN_REQUESTS);
- if (clause->computed)
- {
- RFR.RDB$UPDATE_FLAG.NULL = FALSE;
- RFR.RDB$UPDATE_FLAG = 1;
- }
+ STORE(REQUEST_HANDLE requestStore TRANSACTION_HANDLE transaction)
+ REL IN RDB$RELATIONS
+ {
+ strcpy(REL.RDB$SCHEMA_NAME, name.schema.c_str());
+ strcpy(REL.RDB$RELATION_NAME, name.object.c_str());
+ REL.RDB$SYSTEM_FLAG = 0;
+ REL.RDB$FLAGS = REL_sql;
+ REL.RDB$RELATION_TYPE = relationType.value();
- RFR.RDB$COLLATION_ID.NULL = TRUE; // CORE-2426
- END_MODIFY
- }
+ 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;
- if (clause->computed)
+ REL.RDB$VIEW_BLR.NULL = TRUE;
+ REL.RDB$VIEW_SOURCE.NULL = TRUE;
+ REL.RDB$DBKEY_LENGTH = 8;
+ REL.RDB$EXTERNAL_FILE.NULL = TRUE;
+
+ if (externalFile)
{
- // 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 (externalFile->length() >= sizeof(REL.RDB$EXTERNAL_FILE))
+ status_exception::raise(Arg::Gds(isc_dyn_name_longer));
- MODIFY FLD
- FLD.RDB$COMPUTED_SOURCE.NULL = FALSE;
- attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$COMPUTED_SOURCE,
- computedSource);
+ if (ISC_check_if_remote(externalFile->c_str(), false))
+ status_exception::raise(Arg::PrivateDyn(163));
- FLD.RDB$COMPUTED_BLR.NULL = FALSE;
- attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$COMPUTED_BLR,
- computedValue);
- END_MODIFY
+ REL.RDB$EXTERNAL_FILE.NULL = FALSE;
+ strcpy(REL.RDB$EXTERNAL_FILE, externalFile->c_str());
+ REL.RDB$RELATION_TYPE = rel_external;
}
+ }
+ END_STORE
- AutoRequest request2;
+ lock.release(); // ID generated
- FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- PRM IN RDB$PROCEDURE_PARAMETERS
- WITH PRM.RDB$SCHEMA_NAME = name.schema.c_str() AND
- PRM.RDB$RELATION_NAME = name.object.c_str() AND
- PRM.RDB$FIELD_NAME = field->fld_name.c_str()
- {
- MODIFY PRM USING
- strcpy(PRM.RDB$FIELD_SOURCE_SCHEMA_NAME, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME);
- strcpy(PRM.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE);
- END_MODIFY
- }
- END_FOR
+ // replication
- request2.reset();
+ bool replicationEnabled = false;
- FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- ARG IN RDB$FUNCTION_ARGUMENTS
- WITH ARG.RDB$SCHEMA_NAME = name.schema.c_str() AND
- ARG.RDB$RELATION_NAME = name.object.c_str() AND
- ARG.RDB$FIELD_NAME = field->fld_name.c_str()
- {
- MODIFY ARG USING
- strcpy(ARG.RDB$FIELD_SOURCE_SCHEMA_NAME, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME);
- strcpy(ARG.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE);
- END_MODIFY
- }
- END_FOR
+ if (replicationState.isAssigned())
+ replicationEnabled = replicationState.asBool();
+ else
+ {
+ // Apply the default replication state to the table being created
- request2.reset();
+ AutoCacheRequest request2(tdbb, drq_l_pub_mode, DYN_REQUESTS);
- FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- RFR2 IN RDB$RELATION_FIELDS CROSS
- VRL IN RDB$VIEW_RELATIONS
- WITH VRL.RDB$RELATION_SCHEMA_NAME EQ name.schema.c_str() AND
- VRL.RDB$RELATION_NAME EQ name.object.c_str() AND
- VRL.RDB$PACKAGE_NAME MISSING AND
- VRL.RDB$CONTEXT_TYPE EQ VCT_TABLE AND
- RFR2.RDB$SCHEMA_NAME EQ VRL.RDB$SCHEMA_NAME 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()
+ FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
+ PUB IN RDB$PUBLICATIONS
+ WITH PUB.RDB$PUBLICATION_NAME EQ DEFAULT_PUBLICATION
{
- MODIFY RFR2
- {
- strcpy(RFR2.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE);
- }
- END_MODIFY
+ replicationEnabled = (PUB.RDB$AUTO_ENABLE != 0);
}
END_FOR
}
- END_FOR
- if (!found)
+ if (replicationEnabled)
{
- // msg 176: "column %s does not exist in table/view %s"
- status_exception::raise(Arg::PrivateDyn(176) <<
- field->fld_name.toQuotedString() <<
- name.toQuotedString());
+ // Add table to the default publication
+ RelationNode::addToPublication(tdbb, transaction, name, DEFAULT_PUBLICATION);
}
- // 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);
-}
+ // access rights
+ storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES);
-//----------------------
+ // constraints
+ ObjectsArray constraints;
+ const ObjectsArray* pkCols = findPkColumns();
+ SSHORT position = 0;
-// Delete a global field if it's not used in others objects.
-void DropRelationNode::deleteGlobalField(thread_db* tdbb, jrd_tra* transaction,
- const QualifiedName& globalName)
-{
- AutoCacheRequest request(tdbb, drq_e_l_gfld, DYN_REQUESTS);
-
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- FLD IN RDB$FIELDS
- WITH FLD.RDB$SCHEMA_NAME EQ globalName.schema.c_str() AND
- FLD.RDB$FIELD_NAME EQ globalName.object.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_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND
- RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND
- (NOT ANY PRM IN RDB$PROCEDURE_PARAMETERS WITH
- PRM.RDB$FIELD_SOURCE_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND
- PRM.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND
- (NOT ANY ARG IN RDB$FUNCTION_ARGUMENTS WITH
- ARG.RDB$FIELD_SOURCE_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND
- 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)
-{
- if (view)
- SCL_check_view(tdbb, name, SCL_drop);
- else
- SCL_check_relation(tdbb, name, SCL_drop);
-}
-
-void DropRelationNode::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);
+ 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;
- const dsql_rel* relation = METD_get_relation(transaction, dsqlScratch, name);
+ case Clause::TYPE_ADD_CONSTRAINT:
+ makeConstraint(tdbb, dsqlScratch, transaction,
+ static_cast(i->getObject()), constraints);
+ break;
- if (!relation && silent)
- return;
+ default:
+ fb_assert(false);
+ break;
+ }
+ }
- // Check that DROP TABLE is dropping a table and that DROP VIEW is dropping a view.
- if (view)
- {
- if (!relation || (relation && !(relation->rel_flags & REL_view)))
+ for (ObjectsArray::iterator constraint(constraints.begin());
+ constraint != constraints.end();
+ ++constraint)
{
- status_exception::raise(
- Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
- Arg::Gds(isc_dsql_command_err) <<
- Arg::Gds(isc_dsql_view_not_found) << name.toQuotedString());
+ defineConstraint(tdbb, dsqlScratch, transaction, constraint->name, *constraint->create);
}
+
+ auto* relation = RelationPermanent::newVersion(tdbb, name);
+ indexList.exec(tdbb, relation, transaction);
}
- else
+ catch(const Exception&)
{
- if (!relation || (relation && (relation->rel_flags & REL_view)))
+ // 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ X.RDB$RELATION_NAME EQ name.object.c_str() AND
+ X.RDB$RELATION_ID NOT MISSING
{
- status_exception::raise(
- Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
- Arg::Gds(isc_dsql_command_err) <<
- Arg::Gds(isc_dsql_table_not_found) << name.toQuotedString());
+ if (X.RDB$RELATION_ID)
+ {
+ auto* relation = MetadataCache::lookupRelation(tdbb, X.RDB$RELATION_ID, CacheFlag::MINISCAN);
+ if (relation)
+ {
+ RelationPages* const relPages = relation->getBasePages();
+
+ if (relPages->rel_index_root)
+ IDX_delete_indices(tdbb, relation, relPages, true);
+
+ if (relPages->rel_pages)
+ DPM_delete_relation(tdbb, relation);
+ }
+ }
}
+ END_FOR
+
+ throw;
}
- const int ddlTriggerAction = (view ? DDL_TRIGGER_DROP_VIEW : DDL_TRIGGER_DROP_TABLE);
+ dsqlScratch->relation->rel_flags &= ~REL_creating;
- // run all statements under savepoint control
- AutoSavePoint savePoint(tdbb, transaction);
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TABLE, name, {});
- AutoCacheRequest request(tdbb, drq_l_relation, DYN_REQUESTS);
- bool found = false;
+ savePoint.release(); // everything is ok
+}
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- R IN RDB$RELATIONS
- WITH R.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- R.RDB$RELATION_NAME EQ name.object.c_str()
+// 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)
{
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, {});
- found = true;
- }
- END_FOR
-
- request.reset(tdbb, drq_e_rel_con2, DYN_REQUESTS);
+ if ((*i)->type == Clause::TYPE_ADD_CONSTRAINT)
+ {
+ const AddConstraintClause* clause = static_cast(i->getObject());
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- CRT IN RDB$RELATION_CONSTRAINTS
- WITH CRT.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- CRT.RDB$RELATION_NAME EQ name.object.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;
+ if (clause->constraintType == AddConstraintClause::CTYPE_PK)
+ return &clause->columns;
+ }
}
- END_FOR
- request.reset(tdbb, drq_e_rel_idxs, DYN_REQUESTS);
+ return NULL;
+}
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- IDX IN RDB$INDICES
- WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- IDX.RDB$RELATION_NAME EQ name.object.c_str()
- {
- DropIndexNode::deleteSegmentRecords(tdbb, transaction, QualifiedName(IDX.RDB$INDEX_NAME, name.schema));
- ERASE IDX;
- }
- END_FOR
- request.reset(tdbb, drq_e_trg_msgs2, DYN_REQUESTS);
+//----------------------
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- TM IN RDB$TRIGGER_MESSAGES
- CROSS T IN RDB$TRIGGERS
- WITH T.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- T.RDB$RELATION_NAME EQ name.object.c_str() AND
- TM.RDB$SCHEMA_NAME EQ T.RDB$SCHEMA_NAME AND
- TM.RDB$TRIGGER_NAME EQ T.RDB$TRIGGER_NAME
- {
- ERASE TM;
- }
- END_FOR
- // CVC: Moved this block here to avoid SF Bug #1111570.
- request.reset(tdbb, drq_e_rel_con3, DYN_REQUESTS);
+string AlterRelationNode::internalPrint(NodePrinter& printer) const
+{
+ RelationNode::internalPrint(printer);
+ return "AlterRelationNode";
+}
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- CRT IN RDB$RELATION_CONSTRAINTS
- WITH CRT.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- CRT.RDB$RELATION_NAME EQ name.object.c_str() AND
- (CRT.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT OR
- CRT.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT)
- {
- ERASE CRT;
- }
- END_FOR
+void AlterRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
+{
+ SCL_check_relation(tdbb, name, SCL_alter);
+}
- request.reset(tdbb, drq_e_rel_flds, DYN_REQUESTS);
+void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
+ jrd_tra* transaction)
+{
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- RFR IN RDB$RELATION_FIELDS
- WITH RFR.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- RFR.RDB$RELATION_NAME EQ name.object.c_str()
- {
- if (!RFR.RDB$GENERATOR_NAME.NULL)
- {
- DropSequenceNode::deleteIdentity(tdbb, transaction,
- QualifiedName(RFR.RDB$GENERATOR_NAME, name.schema));
- }
+ saveRelation(tdbb, dsqlScratch, name, false, false);
- ERASE RFR;
+ // also ensures oldVersion is present in cache
+ auto* rel = MetadataCache::lookupRelation(tdbb, name, CacheFlag::OLD_ALTER);
- 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);
- }
+ dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name);
- deleteGlobalField(tdbb, transaction, QualifiedName(RFR.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE_SCHEMA_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.toQuotedString());
}
- 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$SCHEMA_NAME EQ name.schema.c_str() AND
- VR.RDB$VIEW_NAME EQ name.object.c_str()
+ if (!dsqlScratch->relation)
{
- ERASE VR;
+ //// TODO:
+ /***
+ char linecol[64];
+ snprintf(linecol, sizeof(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.toQuotedString() /***<<
+ Arg::Gds(isc_random) << linecol***/);
}
- END_FOR
- request.reset(tdbb, drq_e_relation, DYN_REQUESTS);
+ bool beforeTriggerWasExecuted = false;
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- R IN RDB$RELATIONS
- WITH R.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- R.RDB$RELATION_NAME EQ name.object.c_str()
+ const auto executeBeforeTrigger = [&]()
{
- ERASE R;
-
- if (!R.RDB$SECURITY_CLASS.NULL &&
- !strncmp(R.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN))
+ if (!beforeTriggerWasExecuted)
{
- deleteSecurityClass(tdbb, transaction, R.RDB$SECURITY_CLASS);
+ beforeTriggerWasExecuted = true;
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_TABLE, name, {});
}
- }
- END_FOR
+ };
- if (!found)
- {
- // msg 61: "Relation not found"
- status_exception::raise(Arg::PrivateDyn(61));
- }
+ // If there is an error, get rid of the cached data.
- // Triggers must be deleted after check constraints
+ try
+ {
+ // run all statements under savepoint control
+ AutoSavePoint savePoint(tdbb, transaction);
- QualifiedName triggerName;
+ 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$SCHEMA_NAME = relation->rel_name.schema.c_str() AND
+ RFL.RDB$RELATION_NAME = relation->rel_name.object.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$SCHEMA_NAME EQ name.schema.c_str() AND
+ RFL.RDB$RELATION_NAME EQ name.object.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.toQuotedString());
+ }
+ 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.toQuotedString());
+ }
+
+ AutoRequest request2;
+
+ FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
+ RCL IN RDB$RELATION_CONSTRAINTS CROSS
+ CHK IN RDB$CHECK_CONSTRAINTS
+ WITH RCL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ RCL.RDB$RELATION_NAME EQ name.object.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$SCHEMA_NAME EQ name.schema.c_str() AND
+ RFL.RDB$RELATION_NAME EQ name.object.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.toQuotedString());
+ }
+
+ 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ RCL.RDB$RELATION_NAME EQ name.object.c_str() AND
+ RCL.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT AND
+ CHK.RDB$SCHEMA_NAME EQ RCL.RDB$SCHEMA_NAME 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.toQuotedString());
+ }
+
+ 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ RC.RDB$RELATION_NAME EQ name.object.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$SCHEMA_NAME EQ name.schema.c_str() AND
+ REL.RDB$RELATION_NAME EQ name.object.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$SCHEMA_NAME EQ name.schema.c_str() AND
+ RC.RDB$RELATION_NAME EQ name.object.c_str()
+ {
+ found = true;
+
+ if (!RC.RDB$INDEX_NAME.NULL)
+ {
+ // if constraint+index was created in current statement
+ // abort creation exec job
+ indexList.erase(tdbb, RC.RDB$INDEX_NAME);
+ }
+
+ 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, nullptr, rel->getId());
+ indexList.exec(tdbb, rel, transaction);
+ rel->rel_flags |= REL_get_dependencies;
+
+ if (beforeTriggerWasExecuted)
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_TABLE, name, {});
+
+ 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 RFR.RDB$SCHEMA_NAME = name.schema.c_str() AND
+ RFR.RDB$RELATION_NAME = name.object.c_str() AND
+ RFR.RDB$FIELD_NAME = field->fld_name.c_str() AND
+ REL.RDB$SCHEMA_NAME = RFR.RDB$SCHEMA_NAME AND
+ REL.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME AND
+ FLD.RDB$SCHEMA_NAME = RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE
+ {
+ found = true;
+
+ const 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.object = 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 = QualifiedName(RFR.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME);
+
+ // 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.object.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.toQuotedString() <<
+ name.toQuotedString());
+ }
+
+ 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.toQuotedString() <<
+ QualifiedName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME).toQuotedString());
+ }
+ }
+ 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,
+ QualifiedName(RFR.RDB$GENERATOR_NAME, name.schema));
+
+ 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 QualifiedName genName(RFR.RDB$GENERATOR_NAME, RFR.RDB$SCHEMA_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) <<
+ name.toQuotedString());
+ }
+
+ MET_update_generator_increment(tdbb, id,
+ clause->identityOptions->increment.value());
+ }
+
+ dsc schemaDesc, nameDesc;
+ schemaDesc.makeText((USHORT) genName.schema.length(), ttype_metadata, (UCHAR*) genName.schema.c_str());
+ nameDesc.makeText((USHORT) genName.object.length(), ttype_metadata, (UCHAR*) genName.object.c_str());
+ DFW_post_work(transaction, dfw_set_generator, &nameDesc, &schemaDesc, 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.
+
+ QualifiedName newDomainName;
+ dyn_fld newDom;
+
+ if (field->typeOfName.object.hasData())
+ {
+ // Case a1: Internal domain -> domain.
+ // Case a2: Domain -> domain.
+
+ newDomainName = field->typeOfName;
+
+ if (fb_utils::implicit_domain(newDomainName.object.c_str()))
+ {
+ // msg 224: "Cannot use the internal domain %s as new type for field %s".
+ status_exception::raise(
+ Arg::PrivateDyn(224) << newDomainName.toQuotedString() << 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.toQuotedString());
+ }
+
+ QualifiedName dummyCollationName;
+ DDL_resolve_intl_type(dsqlScratch, field, dummyCollationName);
+
+ // 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.
+
+ newDomainName.schema = name.schema;
+
+ // 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 = desc.getCharSet();
+ field->collationId = desc.getCollation();
+ }
+ 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.object.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.toQuotedString() <<
+ name.toQuotedString());
+ }
+ }
+ }
+
+ if (newDomainName.object.hasData())
+ {
+ MODIFY RFR USING
+ RFR.RDB$FIELD_SOURCE_SCHEMA_NAME.NULL = FALSE;
+ strcpy(RFR.RDB$FIELD_SOURCE_SCHEMA_NAME, newDomainName.schema.c_str());
+
+ RFR.RDB$FIELD_SOURCE.NULL = FALSE;
+ strcpy(RFR.RDB$FIELD_SOURCE, newDomainName.object.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$SCHEMA_NAME = name.schema.c_str() AND
+ PRM.RDB$RELATION_NAME = name.object.c_str() AND
+ PRM.RDB$FIELD_NAME = field->fld_name.c_str()
+ {
+ MODIFY PRM USING
+ strcpy(PRM.RDB$FIELD_SOURCE_SCHEMA_NAME, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME);
+ 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$SCHEMA_NAME = name.schema.c_str() AND
+ ARG.RDB$RELATION_NAME = name.object.c_str() AND
+ ARG.RDB$FIELD_NAME = field->fld_name.c_str()
+ {
+ MODIFY ARG USING
+ strcpy(ARG.RDB$FIELD_SOURCE_SCHEMA_NAME, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME);
+ 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_SCHEMA_NAME EQ name.schema.c_str() AND
+ VRL.RDB$RELATION_NAME EQ name.object.c_str() AND
+ VRL.RDB$PACKAGE_NAME MISSING AND
+ VRL.RDB$CONTEXT_TYPE EQ VCT_TABLE AND
+ RFR2.RDB$SCHEMA_NAME EQ VRL.RDB$SCHEMA_NAME 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.toQuotedString() <<
+ name.toQuotedString());
+ }
+
+ // 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 QualifiedName& globalName)
+{
+ AutoCacheRequest request(tdbb, drq_e_l_gfld, DYN_REQUESTS);
+
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ FLD IN RDB$FIELDS
+ WITH FLD.RDB$SCHEMA_NAME EQ globalName.schema.c_str() AND
+ FLD.RDB$FIELD_NAME EQ globalName.object.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_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND
+ RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND
+ (NOT ANY PRM IN RDB$PROCEDURE_PARAMETERS WITH
+ PRM.RDB$FIELD_SOURCE_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND
+ PRM.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) AND
+ (NOT ANY ARG IN RDB$FUNCTION_ARGUMENTS WITH
+ ARG.RDB$FIELD_SOURCE_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND
+ 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)
+{
+ if (view)
+ SCL_check_view(tdbb, name, SCL_drop);
+ else
+ SCL_check_relation(tdbb, name, SCL_drop);
+}
+
+void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
+ jrd_tra* transaction)
+{
+ Attachment* attachment = tdbb->getAttachment();
+ Database* dbb = tdbb->getDatabase();
+
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
+
+ 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.toQuotedString());
+ }
+ }
+ 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.toQuotedString());
+ }
+ }
+
+ const int ddlTriggerAction = (view ? DDL_TRIGGER_DROP_VIEW : DDL_TRIGGER_DROP_TABLE);
+ MetaId relId = rel->getId();
+
+ // run all statements under savepoint control
+ AutoSavePoint savePoint(tdbb, transaction);
+
+ AutoCacheRequest request(tdbb, drq_l_relation, DYN_REQUESTS);
+ bool found = false;
+
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ R IN RDB$RELATIONS
+ WITH R.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ R.RDB$RELATION_NAME EQ name.object.c_str()
+ {
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, {});
+ found = true;
+ }
+ END_FOR
+
+ if (!found)
+ {
+ // msg 61: "Relation not found"
+ status_exception::raise(Arg::PrivateDyn(61));
+ }
+
+ request.reset(tdbb, drq_e_rel_con2, DYN_REQUESTS);
+
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ CRT IN RDB$RELATION_CONSTRAINTS
+ WITH CRT.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CRT.RDB$RELATION_NAME EQ name.object.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
+
+ MemoryPool& p = *MemoryPool::getContextPool();
+ ModifyIndexList indexList(p);
+
+ request.reset(tdbb, drq_e_rel_idxs, DYN_REQUESTS);
+
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ IDX IN RDB$INDICES
+ WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ IDX.RDB$RELATION_NAME EQ name.object.c_str() AND
+ IDX.RDB$INDEX_INACTIVE NE MET_index_deferred_drop
+ {
+ auto* node = FB_NEW_POOL(p) DropIndexNode(p, QualifiedName(IDX.RDB$INDEX_NAME, name.schema));
+ node->drop(tdbb, dsqlScratch, transaction, indexList, false);
+ }
+ END_FOR
+
+ indexList.exec(tdbb, relation, transaction);
+
+ request.reset(tdbb, drq_e_trg_msgs2, DYN_REQUESTS);
+
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ TM IN RDB$TRIGGER_MESSAGES
+ CROSS T IN RDB$TRIGGERS
+ WITH T.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ T.RDB$RELATION_NAME EQ name.object.c_str() AND
+ TM.RDB$SCHEMA_NAME EQ T.RDB$SCHEMA_NAME AND
+ TM.RDB$TRIGGER_NAME EQ T.RDB$TRIGGER_NAME
+ {
+ ERASE TM;
+ }
+ 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ CRT.RDB$RELATION_NAME EQ name.object.c_str() AND
+ (CRT.RDB$CONSTRAINT_TYPE EQ CHECK_CNSTRT OR
+ CRT.RDB$CONSTRAINT_TYPE EQ NOT_NULL_CNSTRT)
+ {
+ ERASE CRT;
+ }
+ END_FOR
+
+ request.reset(tdbb, drq_e_rel_flds, DYN_REQUESTS);
+
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ RFR IN RDB$RELATION_FIELDS
+ WITH RFR.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ RFR.RDB$RELATION_NAME EQ name.object.c_str()
+ {
+ if (!RFR.RDB$GENERATOR_NAME.NULL)
+ {
+ DropSequenceNode::deleteIdentity(tdbb, transaction,
+ QualifiedName(RFR.RDB$GENERATOR_NAME, name.schema));
+ }
+
+ 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, QualifiedName(RFR.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME));
+ }
+ 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ VR.RDB$VIEW_NAME EQ name.object.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$SCHEMA_NAME EQ name.schema.c_str() AND
+ R.RDB$RELATION_NAME EQ name.object.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
+
+ QualifiedName triggerName;
request.reset(tdbb, drq_e_trigger2, DYN_REQUESTS);
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- X IN RDB$TRIGGERS
- WITH X.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- X.RDB$RELATION_NAME EQ name.object.c_str()
- {
- triggerName = QualifiedName(X.RDB$TRIGGER_NAME, X.RDB$SCHEMA_NAME);
- ERASE X;
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ X IN RDB$TRIGGERS
+ WITH X.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ X.RDB$RELATION_NAME EQ name.object.c_str()
+ {
+ triggerName = QualifiedName(X.RDB$TRIGGER_NAME, X.RDB$SCHEMA_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_SCHEMA_NAME EQ triggerName.schema.c_str() AND
+ PRIV.RDB$USER EQ triggerName.object.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_SCHEMA_NAME EQ name.schema.c_str() AND
+ PRIV.RDB$USER EQ name.object.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_SCHEMA_NAME EQ name.schema.c_str() AND
+ PTAB.RDB$TABLE_NAME EQ name.object.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.
+
+ for (int wait = 0; wait < 60; wait++)
+ {
+ fb_assert(relation->isDropped());
+
+ if (!relation->rel_gc_lock.getSweepCount())
+ break;
+
+ EngineCheckout cout(tdbb, FB_FUNCTION);
+ Thread::sleep(1 * 1000);
+ }
+
+ if (relation->rel_gc_lock.getSweepCount())
+ DFW_raiseRelationInUseError(relation);
+
+ // 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_pages)
+ DPM_mark_relation(tdbb, relation);
+
+ AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, TDBB_dont_post_dfw, true);
+
+ // if this is a view (or even if we don't know), delete dependency lists
+
+ if (relation->isView() || !relation->isReady(tdbb))
+ MET_delete_dependencies(tdbb, name, obj_view);
+
+ // Now that the data, pointer, and index pages are gone,
+ // get rid of the relation itself
+
+ AutoRequest request2;
+ request2.reset();
+
+ FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) X IN RDB$FORMATS WITH
+ X.RDB$RELATION_ID EQ rel->getId()
+ {
+ ERASE X;
+ }
+ END_FOR
+
+ // Here we may drop index records - they can't get cleaned out without relation record
+
+ request2.reset();
+
+ FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
+ IDX IN RDB$INDICES
+ WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ IDX.RDB$RELATION_NAME EQ name.object.c_str()
+ {
+ name.object = IDX.RDB$INDEX_NAME;
+ DropIndexNode::deleteSegmentRecords(tdbb, transaction, name);
+ ERASE IDX;
+ }
+ END_FOR
+
+ // Release relation locks
+ if (relation->rel_partners_lock)
+ LCK_release(tdbb, relation->rel_partners_lock);
+ }
+ catch(const Exception&)
+ {
+ if (!relation)
+ return;
+
+ if (relation->isDropped())
+ relation->rollback(tdbb);
+ }
+
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, {});
+
+ savePoint.release(); // everything is ok
+}
+
+
+//----------------------
+
+
+string CreateAlterViewNode::internalPrint(NodePrinter& printer) const
+{
+ RelationNode::internalPrint(printer);
+
+ 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";
+}
+
+DdlNode* CreateAlterViewNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
+{
+ if (create)
+ dsqlScratch->qualifyNewName(name);
+ else
+ dsqlScratch->qualifyExistingName(name, obj_view);
+
+ protectSystemSchema(name.schema, obj_view);
+ dsqlScratch->ddlSchema = name.schema;
+
+ source.ltrim("\n\r\t ");
+
+ return DdlNode::dsqlPass(dsqlScratch);
+}
+
+void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
+{
+ if (alter)
+ {
+ if (SCL_check_view(tdbb, name, SCL_alter) || !create)
+ return;
+ }
+
+ SCL_check_create_access(tdbb, obj_views, name.schema);
+}
+
+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;
+
+ Attachment* const attachment = transaction->tra_attachment;
+ const MetaString& ownerName = attachment->getEffectiveUserName();
+
+ const dsql_rel* modifyingView = NULL;
+
+ if (alter)
+ {
+ modifyingView = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name);
+
+ if (!modifyingView && !create)
+ status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name.toQuotedString());
+ }
+
+ 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, CacheFlag::OLD_ALTER);
+
+ const int ddlTriggerAction = (modifyingView ? DDL_TRIGGER_ALTER_VIEW : DDL_TRIGGER_CREATE_VIEW);
+
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, {});
+
+ 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)
+ {
+ 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ REL.RDB$RELATION_NAME EQ name.object.c_str() AND
+ REL.RDB$VIEW_BLR NOT MISSING
+ {
+ 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.toQuotedString());
+
+ AutoRequest request2;
+
+ FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
+ VR IN RDB$VIEW_RELATIONS
+ WITH VR.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ VR.RDB$VIEW_NAME EQ name.object.c_str()
+ {
+ ERASE VR;
+ }
+ END_FOR
+
+ request2.reset();
+
+ FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
+ TRG IN RDB$TRIGGERS
+ WITH TRG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ TRG.RDB$RELATION_NAME EQ name.object.c_str() AND
+ TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_view_check
+ {
+ ERASE TRG;
+ }
+ END_FOR
+ }
+ else
+ {
+ AutoCacheRequest request(tdbb, drq_s_rels, DYN_REQUESTS);
+
+ STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ REL IN RDB$RELATIONS
+ {
+ strcpy(REL.RDB$SCHEMA_NAME, name.schema.c_str());
+ strcpy(REL.RDB$RELATION_NAME, name.object.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
+
+ storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES);
+ }
+
+ // Define the view source relations from the statement contexts and union contexts.
+
+ while (dsqlScratch->derivedContext.hasData())
+ dsqlScratch->context->push(dsqlScratch->derivedContext.pop());
+
+ while (dsqlScratch->unionContext.hasData())
+ dsqlScratch->context->push(dsqlScratch->unionContext.pop());
+
+ AutoCacheRequest request(tdbb, drq_s_view_rels, DYN_REQUESTS);
+
+ 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;
+
+ if (relation || procedure)
+ {
+ const auto& refName = relation ? relation->rel_name : procedure->prc_name;
+ string contextName;
+
+ if (context->ctx_alias.hasData())
+ contextName = context->getConcatenatedAlias();
+ else
+ contextName = refName.toQuotedString();
+
+ ViewContextType ctxType;
+ if (relation)
+ {
+ if (!(relation->rel_flags & REL_view))
+ ctxType = VCT_TABLE;
+ else
+ ctxType = VCT_VIEW;
+ }
+ else //if (procedure)
+ ctxType = VCT_PROCEDURE;
+
+ STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ VRL IN RDB$VIEW_RELATIONS
+ {
+ strcpy(VRL.RDB$SCHEMA_NAME, name.schema.c_str());
+ strcpy(VRL.RDB$VIEW_NAME, name.object.c_str());
+ strcpy(VRL.RDB$RELATION_SCHEMA_NAME, refName.schema.c_str());
+ strcpy(VRL.RDB$RELATION_NAME, refName.object.c_str());
+ VRL.RDB$CONTEXT_TYPE = SSHORT(ctxType);
+ VRL.RDB$VIEW_CONTEXT = context->ctx_context;
+ strcpy(VRL.RDB$CONTEXT_NAME, contextName.c_str());
+
+ 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
+ }
+ }
+
+ // Check privileges on base tables and views.
+
+ request.reset(tdbb, drq_l_view_rels, DYN_REQUESTS);
+
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ VRL IN RDB$VIEW_RELATIONS
+ CROSS PREL IN RDB$RELATIONS
+ WITH VRL.RDB$PACKAGE_NAME MISSING AND
+ VRL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ VRL.RDB$VIEW_NAME EQ name.object.c_str() AND
+ PREL.RDB$SCHEMA_NAME EQ VRL.RDB$RELATION_SCHEMA_NAME AND
+ PREL.RDB$RELATION_NAME EQ VRL.RDB$RELATION_NAME
+ {
+ // 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 (ownerName != PREL.RDB$OWNER_NAME)
+ {
+ // I think this should be the responsability of DFW or the user will find ways to circumvent DYN.
+
+ SCL_check_schema(tdbb, PREL.RDB$SCHEMA_NAME, SCL_usage);
+
+ if (const auto priv = SCL_get_mask(tdbb, QualifiedName(PREL.RDB$RELATION_NAME, PREL.RDB$SCHEMA_NAME), "");
+ !(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.
+ QualifiedName(PREL.RDB$RELATION_NAME, PREL.RDB$SCHEMA_NAME).toQuotedString());
+ }
+ }
+ }
+ END_FOR
+
+ // 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.
+
+ const NestConst* ptr = NULL;
+ const NestConst* end = NULL;
+
+ if (viewFields)
+ {
+ ptr = viewFields->items.begin();
+ end = viewFields->items.end();
+ }
+
+ // 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.
+
+ 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)
+ {
+ ValueExprNode* fieldNode = *itemsPtr;
+
+ // Determine the proper field name, replacing the default if necessary.
+
+ ValueExprNode* nameNode = fieldNode;
+ const char* aliasName = NULL;
+
+ while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode))
+ {
+ DsqlAliasNode* aliasNode;
+ DsqlMapNode* mapNode;
+ DerivedFieldNode* derivedField;
- AutoCacheRequest request2(tdbb, drq_e_trg_prv, DYN_REQUESTS);
+ 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(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- PRIV IN RDB$USER_PRIVILEGES
- WITH PRIV.RDB$USER_SCHEMA_NAME EQ triggerName.schema.c_str() AND
- PRIV.RDB$USER EQ triggerName.object.c_str() AND
- PRIV.RDB$USER_TYPE = obj_trigger AND
- PRIV.RDB$GRANTOR NOT MISSING
+ const dsql_fld* nameField = NULL;
+ const FieldNode* fieldNameNode = nodeAs(nameNode);
+
+ if (fieldNameNode)
+ nameField = fieldNameNode->dsqlField;
+
+ const TEXT* fieldStr = NULL;
+
+ if (aliasName)
+ fieldStr = aliasName;
+ else if (nameField)
+ fieldStr = nameField->fld_name.c_str();
+
+ // Check if this is a field or an expression.
+
+ DsqlAliasNode* aliasNode = nodeAs(fieldNode);
+
+ 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;
+
+
+ if (field && context && (context->ctx_flags & CTX_blr_fields))
+ {
+ field = nullptr;
+ context = nullptr;
+ updatable = false;
+ }
+
+ // If this is an expression, check to make sure there is a name specified.
+
+ 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));
+ }
+
+ // 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;
+ }
+
+ ++ptr;
+ }
+
+ // If not an expression, point to the proper base relation field,
+ // else make up an SQL field with generated global field for calculations.
+
+ dsql_fld* relField = NULL;
+
+ 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));
+ }
+
+ modifiedFields.add(relField);
+ break;
+ }
+ }
+ }
+
+ FieldDefinition fieldDefinition(*tdbb->getDefaultPool());
+ fieldDefinition.relationName = name;
+ fieldDefinition.name = fieldStr;
+ fieldDefinition.position = position;
+ fieldDefinition.fieldSource.schema = name.schema;
+
+ // 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;
+
+ 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.
+
+ AutoRequest request2;
+
+ 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ RFL.RDB$RELATION_NAME EQ name.object.c_str() AND
+ RFL.RDB$BASE_FIELD MISSING AND
+ FLD.RDB$SCHEMA_NAME EQ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE
+ {
+ const bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME);
+ fb_assert(wasInternalDomain);
+
+ if (wasInternalDomain)
+ ERASE FLD;
+ }
+ END_FOR
+
+ 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);
+
+ // Get the type of the expression.
+ dsc desc;
+ DsqlDescMaker::fromNode(dsqlScratch, &desc, fieldNode);
+
+ dsql_fld newField(*tdbb->getDefaultPool());
+ newField.dtype = desc.dsc_dtype;
+ newField.length = desc.dsc_length;
+ newField.scale = desc.dsc_scale;
+
+ if (desc.isText() || (desc.isBlob() && desc.getBlobSubType() == isc_blob_text))
+ {
+ newField.charSetId = desc.getCharSet();
+ newField.collationId = desc.getCollation();
+ }
+
+ 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));
+
+ newField.charLength = (newField.length - adjust) / bpc;
+ }
+ else
+ newField.subType = desc.dsc_sub_type;
+
+ newField.setExactPrecision();
+
+ if (relField) // modifying a view
+ {
+ AutoRequest request2;
+
+ 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$SCHEMA_NAME EQ name.schema.c_str() AND
+ RFL.RDB$RELATION_NAME EQ name.object.c_str() AND
+ RFL.RDB$BASE_FIELD MISSING AND
+ FLD.RDB$SCHEMA_NAME EQ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE
+ {
+ const bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME);
+ fb_assert(wasInternalDomain);
+
+ if (wasInternalDomain)
+ {
+ fieldDefinition.fieldSource = QualifiedName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME);
+
+ 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.object.isEmpty())
+ {
+ storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField,
+ "", dsqlScratch->getBlrData());
+ }
- deletePrivilegesByRelName(tdbb, transaction, name, obj_relation);
+ fieldDefinition.modify(tdbb, transaction);
+ }
+ else
+ {
+ storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField,
+ "", dsqlScratch->getBlrData());
- request.reset(tdbb, drq_e_view_prv, DYN_REQUESTS);
+ fieldDefinition.store(tdbb, transaction);
+ }
+ }
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- PRIV IN RDB$USER_PRIVILEGES
- WITH PRIV.RDB$USER_SCHEMA_NAME EQ name.schema.c_str() AND
- PRIV.RDB$USER EQ name.object.c_str() AND
- PRIV.RDB$USER_TYPE = obj_view AND
- PRIV.RDB$GRANTOR NOT MISSING
- {
- ERASE PRIV;
+ if (fieldStr)
+ saveField(tdbb, dsqlScratch, fieldStr);
}
- END_FOR
-
- // Drop table from all publications
- request.reset(tdbb, drq_e_pub_tab_all, DYN_REQUESTS);
+ // CVC: This message was not catching the case when
+ // #fields < items in select list, see comment above.
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- PTAB IN RDB$PUBLICATION_TABLES
- WITH PTAB.RDB$TABLE_SCHEMA_NAME EQ name.schema.c_str() AND
- PTAB.RDB$TABLE_NAME EQ name.object.c_str()
+ if (ptr != end)
{
- ERASE PTAB;
+ // 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));
}
- END_FOR
- if (found)
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, {});
- else
+ if (modifyingView) // modifying a view
{
- // msg 61: "Relation not found"
- status_exception::raise(Arg::PrivateDyn(61));
+ // Delete the old fields not present in the new definition.
+ for (dsql_fld* relField = modifyingView->rel_fields; relField; relField = relField->fld_next)
+ {
+ if (!modifiedFields.exist(relField))
+ deleteLocalField(tdbb, transaction, name, relField->fld_name, false);
+ }
}
- savePoint.release(); // everything is ok
-
- METD_drop_relation(transaction, name);
- MET_dsql_cache_release(tdbb, SYM_relation, name);
-}
-
+ // Setup to define triggers for WITH CHECK OPTION.
-//----------------------
+ if (withCheckOption)
+ {
+ 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));
+ }
+ RseNode* querySpec = nodeAs(selectExpr->querySpec);
+ fb_assert(querySpec);
-string CreateAlterViewNode::internalPrint(NodePrinter& printer) const
-{
- RelationNode::internalPrint(printer);
+ 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));
+ }
- NODE_PRINT(printer, create);
- NODE_PRINT(printer, alter);
- NODE_PRINT(printer, viewFields);
- NODE_PRINT(printer, selectExpr);
- NODE_PRINT(printer, source);
- NODE_PRINT(printer, withCheckOption);
+ if (!querySpec->dsqlWhere)
+ {
+ // 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));
+ }
- return "CreateAlterViewNode";
-}
+ 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));
+ }
-DdlNode* CreateAlterViewNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
-{
- if (create)
- dsqlScratch->qualifyNewName(name);
- else
- dsqlScratch->qualifyExistingName(name, obj_view);
+ dsqlScratch->flags |= DsqlCompilerScratch::FLAG_VIEW_WITH_CHECK;
- protectSystemSchema(name.schema, obj_view);
- dsqlScratch->ddlSchema = name.schema;
+ createCheckTrigger(tdbb, dsqlScratch, items, PRE_MODIFY_TRIGGER);
+ createCheckTrigger(tdbb, dsqlScratch, items, PRE_STORE_TRIGGER);
+ }
- source.ltrim("\n\r\t ");
+ dsqlScratch->resetContextStack();
- return DdlNode::dsqlPass(dsqlScratch);
-}
+ if (modifyingView)
+ MetadataCache::newVersion(tdbb, modifyingView->rel_id);
-void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
-{
- if (alter)
- {
- if (SCL_check_view(tdbb, name, SCL_alter) || !create)
- return;
- }
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, {});
- SCL_check_create_access(tdbb, obj_views, name.schema);
+ savePoint.release(); // everything is ok
}
-void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
- jrd_tra* transaction)
+// 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)
{
- if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation))
- return;
-
- Attachment* const attachment = transaction->tra_attachment;
- const MetaString& ownerName = attachment->getEffectiveUserName();
-
- const dsql_rel* modifyingView = NULL;
-
- if (alter)
- {
- modifyingView = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name);
-
- if (!modifyingView && !create)
- status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name.toQuotedString());
- }
-
- saveRelation(tdbb, dsqlScratch, name, true, modifyingView == NULL);
-
- // run all statements under savepoint control
- AutoSavePoint savePoint(tdbb, transaction);
-
- const int ddlTriggerAction = (modifyingView ? DDL_TRIGGER_ALTER_VIEW : DDL_TRIGGER_CREATE_VIEW);
+ MemoryPool& pool = *tdbb->getDefaultPool();
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, {});
+ // Specify that the trigger should abort if the condition is not met.
+ ExceptionNode* exceptionNode = FB_NEW_POOL(pool) ExceptionNode(pool, QualifiedName(CHECK_CONSTRAINT_EXCEPTION));
+ exceptionNode->exception->type = ExceptionItem::GDS_CODE;
- if (!modifyingView)
- DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation);
+ AutoSetRestore autoCheckConstraintTrigger(&dsqlScratch->checkConstraintTrigger, true);
- // 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.
+ RelationSourceNode* relationNode = dsqlNode;
- dsqlScratch->resetContextStack();
- ++dsqlScratch->contextNumber;
- RseNode* rse = PASS1_rse(dsqlScratch, selectExpr);
+ // Generate the trigger blr.
dsqlScratch->getBlrData().clear();
+ dsqlScratch->getDebugData().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)
- {
- 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$SCHEMA_NAME EQ name.schema.c_str() AND
- REL.RDB$RELATION_NAME EQ name.object.c_str() AND
- REL.RDB$VIEW_BLR NOT MISSING
- {
- 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.toQuotedString());
+ dsqlScratch->appendUChar(blr_begin);
- AutoRequest request2;
+ dsqlScratch->resetContextStack();
- FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- VR IN RDB$VIEW_RELATIONS
- WITH VR.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- VR.RDB$VIEW_NAME EQ name.object.c_str()
- {
- ERASE VR;
- }
- END_FOR
+ RseNode* querySpec = nodeAs(selectExpr->querySpec);
+ fb_assert(querySpec);
- request2.reset();
+ ProcedureSourceNode* sourceNode = nodeAs(querySpec->dsqlFrom->items[0]);
- FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
- TRG IN RDB$TRIGGERS
- WITH TRG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- TRG.RDB$RELATION_NAME EQ name.object.c_str() AND
- TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_view_check
- {
- ERASE TRG;
- }
- END_FOR
- }
- else
+ if (triggerType == PRE_MODIFY_TRIGGER)
{
- AutoCacheRequest request(tdbb, drq_s_rels, DYN_REQUESTS);
+ dsqlScratch->contextNumber = 2;
- STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- REL IN RDB$RELATIONS
- {
- strcpy(REL.RDB$SCHEMA_NAME, name.schema.c_str());
- strcpy(REL.RDB$RELATION_NAME, name.object.c_str());
- REL.RDB$SYSTEM_FLAG = 0;
- REL.RDB$FLAGS = REL_sql;
- REL.RDB$RELATION_TYPE = SSHORT(rel_view);
+ RelationSourceNode* baseRelation = FB_NEW_POOL(pool) RelationSourceNode(pool, sourceNode->dsqlName);
+ baseRelation->alias = sourceNode->alias;
- attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source);
- attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, dsqlScratch->getBlrData());
- }
- END_STORE
+ dsqlScratch->appendUChar(blr_for);
- storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES);
- }
+ RseNode* rse = FB_NEW_POOL(pool) RseNode(pool);
+ rse->dsqlStreams = FB_NEW_POOL(pool) RecSourceListNode(pool, 1);
- // Define the view source relations from the statement contexts and union contexts.
+ rse->dsqlStreams->items[0] = baseRelation;
+ rse->dsqlStreams->items[0] = doDsqlPass(dsqlScratch, rse->dsqlStreams->items[0]);
+ rse->dsqlWhere = doDsqlPass(dsqlScratch, querySpec->dsqlWhere);
- while (dsqlScratch->derivedContext.hasData())
- dsqlScratch->context->push(dsqlScratch->derivedContext.pop());
+ dsqlScratch->contextNumber = OLD_CONTEXT_VALUE;
- while (dsqlScratch->unionContext.hasData())
- dsqlScratch->context->push(dsqlScratch->unionContext.pop());
+ dsql_ctx* oldContext;
- AutoCacheRequest request(tdbb, drq_s_view_rels, DYN_REQUESTS);
+ { /// scope
+ AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias);
+ relationNode->alias = OLD_CONTEXT_NAME;
- 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;
+ oldContext = PASS1_make_context(dsqlScratch, relationNode);
+ oldContext->ctx_flags |= CTX_system;
+ }
- if (relation || procedure)
- {
- const auto& refName = relation ? relation->rel_name : procedure->prc_name;
- string contextName;
+ // 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.
- if (context->ctx_alias.hasData())
- contextName = context->getConcatenatedAlias();
- else
- contextName = refName.toQuotedString();
+ ValueListNode* valuesNode = viewFields;
+ ValueListNode* fieldsNode = querySpec->dsqlSelectList;
- ViewContextType ctxType;
- if (relation)
- {
- if (!(relation->rel_flags & REL_view))
- ctxType = VCT_TABLE;
- else
- ctxType = VCT_VIEW;
- }
- else //if (procedure)
- ctxType = VCT_PROCEDURE;
+ if (!fieldsNode)
+ {
+ const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(),
+ dsqlScratch, name);
+ fieldsNode = FB_NEW_POOL(pool) ValueListNode(pool, 0u);
- STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- VRL IN RDB$VIEW_RELATIONS
+ for (const dsql_fld* field = relation->rel_fields; field; field = field->fld_next)
{
- strcpy(VRL.RDB$SCHEMA_NAME, name.schema.c_str());
- strcpy(VRL.RDB$VIEW_NAME, name.object.c_str());
- strcpy(VRL.RDB$RELATION_SCHEMA_NAME, refName.schema.c_str());
- strcpy(VRL.RDB$RELATION_NAME, refName.object.c_str());
- VRL.RDB$CONTEXT_TYPE = SSHORT(ctxType);
- VRL.RDB$VIEW_CONTEXT = context->ctx_context;
- strcpy(VRL.RDB$CONTEXT_NAME, contextName.c_str());
-
- 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;
+ if (!(field->flags & FLD_computed))
+ fieldsNode->add(MAKE_field_name(field->fld_name.c_str()));
}
- END_STORE
}
- }
- // Check privileges on base tables and views.
+ if (!valuesNode)
+ valuesNode = fieldsNode;
- request.reset(tdbb, drq_l_view_rels, DYN_REQUESTS);
+ // Generate the list of assignments to fields in the base relation.
- FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
- VRL IN RDB$VIEW_RELATIONS
- CROSS PREL IN RDB$RELATIONS
- WITH VRL.RDB$PACKAGE_NAME MISSING AND
- VRL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- VRL.RDB$VIEW_NAME EQ name.object.c_str() AND
- PREL.RDB$SCHEMA_NAME EQ VRL.RDB$RELATION_SCHEMA_NAME AND
- PREL.RDB$RELATION_NAME EQ VRL.RDB$RELATION_NAME
- {
- // 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));
+ NestConst* ptr = fieldsNode->items.begin();
+ const NestConst* const end = fieldsNode->items.end();
+ NestConst* ptr2 = valuesNode->items.begin();
+ const NestConst* const end2 = valuesNode->items.end();
- if (ownerName != PREL.RDB$OWNER_NAME)
+ BinaryBoolNode* andNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and);
+
+ for (; ptr != end && ptr2 != end2; ++ptr, ++ptr2)
{
- // I think this should be the responsability of DFW or the user will find ways to circumvent DYN.
+ NestConst fieldNod = *ptr;
+ NestConst valueNod = *ptr2;
+ DsqlAliasNode* aliasNode;
- SCL_check_schema(tdbb, PREL.RDB$SCHEMA_NAME, SCL_usage);
+ if ((aliasNode = nodeAs(fieldNod)))
+ fieldNod = aliasNode->value;
- if (const auto priv = SCL_get_mask(tdbb, QualifiedName(PREL.RDB$RELATION_NAME, PREL.RDB$SCHEMA_NAME), "");
- !(priv & SCL_select)
- )
+ if ((aliasNode = nodeAs(valueNod)))
+ valueNod = aliasNode->value;
+
+ FieldNode* fieldNode = nodeAs(fieldNod);
+ FieldNode* valueNode = nodeAs(valueNod);
+
+ // Generate the actual comparisons.
+
+ if (fieldNode && valueNode)
{
- // 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.
- QualifiedName(PREL.RDB$RELATION_NAME, PREL.RDB$SCHEMA_NAME).toQuotedString());
- }
- }
- }
- END_FOR
+ FieldNode* oldValueNode = FB_NEW_POOL(pool) FieldNode(pool);
+ oldValueNode->dsqlName = (aliasNode ? aliasNode->name : valueNode->dsqlName);
+ oldValueNode->dsqlQualifier.object = OLD_CONTEXT_NAME;
- // 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.
+ valueNod = oldValueNode->dsqlPass(dsqlScratch);
+ fieldNod = fieldNode->dsqlPass(dsqlScratch);
- const NestConst* ptr = NULL;
- const NestConst* end = NULL;
+ BoolExprNode* equivNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_equiv,
+ valueNod, fieldNod);
- if (viewFields)
- {
- ptr = viewFields->items.begin();
- end = viewFields->items.end();
- }
+ rse->dsqlWhere = PASS1_compose(rse->dsqlWhere, equivNode, blr_and);
+ }
+ }
- // 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.
+ GEN_expr(dsqlScratch, rse);
+ }
- ValueListNode* items = rse->dsqlSelectList;
- NestConst* itemsPtr = items->items.begin();
- SortedArray modifiedFields;
- bool updatable = true;
- SSHORT position = 0;
+ // ASF: We'll now map the view's base table into the trigger's NEW context.
- for (NestConst* itemsEnd = items->items.end();
- itemsPtr < itemsEnd; ++itemsPtr, ++position)
- {
- ValueExprNode* fieldNode = *itemsPtr;
+ ++dsqlScratch->scopeLevel;
+ dsqlScratch->contextNumber = NEW_CONTEXT_VALUE;
- // Determine the proper field name, replacing the default if necessary.
+ dsql_ctx* newContext;
- ValueExprNode* nameNode = fieldNode;
- const char* aliasName = NULL;
+ { /// scope
+ AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias);
- while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode))
- {
- DsqlAliasNode* aliasNode;
- DsqlMapNode* mapNode;
- DerivedFieldNode* derivedField;
+ if (relationNode->alias.isEmpty())
+ relationNode->alias = sourceNode->dsqlName.object.c_str();
- 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;
- }
- }
+ newContext = PASS1_make_context(dsqlScratch, relationNode);
+ newContext->ctx_flags |= CTX_system;
- const dsql_fld* nameField = NULL;
- const FieldNode* fieldNameNode = nodeAs(nameNode);
+ if (triggerType == PRE_STORE_TRIGGER)
+ newContext->ctx_flags |= CTX_view_with_check_store;
+ else
+ newContext->ctx_flags |= CTX_view_with_check_modify;
+ }
- if (fieldNameNode)
- nameField = fieldNameNode->dsqlField;
+ // Replace the view's field names by the base table field names. Save the original names
+ // to restore after the condition processing.
- const TEXT* fieldStr = NULL;
+ dsql_fld* field = newContext->ctx_relation->rel_fields;
+ ObjectsArray savedNames;
- if (aliasName)
- fieldStr = aliasName;
- else if (nameField)
- fieldStr = nameField->fld_name.c_str();
+ // 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;
- // Check if this is a field or an expression.
+ if ((aliasNode = nodeAs(valueNode)))
+ valueNode = aliasNode->value;
- DsqlAliasNode* aliasNode = nodeAs(fieldNode);
+ FieldNode* fieldNode = nodeAs(valueNode);
+ fb_assert(fieldNode);
- if (aliasNode)
- fieldNode = aliasNode->value;
+ savedNames.add(field->fld_name);
- dsql_fld* field = NULL;
- const dsql_ctx* context = NULL;
+ dsql_fld* queryField = fieldNode->dsqlField;
- fieldNameNode = nodeAs(fieldNode);
+ 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 (fieldNameNode)
- {
- field = fieldNameNode->dsqlField;
- context = fieldNameNode->dsqlContext;
- }
- else
- updatable = false;
+ dsqlScratch->appendUChar(blr_if);
+ // Process the condition for firing the trigger.
+ NestConst condition = doDsqlPass(dsqlScratch, querySpec->dsqlWhere);
- if (field && context && (context->ctx_flags & CTX_blr_fields))
- {
- field = nullptr;
- context = nullptr;
- updatable = false;
- }
+ // Restore the field names. This must happen before the condition's BLR generation.
- // If this is an expression, check to make sure there is a name specified.
+ field = newContext->ctx_relation->rel_fields;
- 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 (ObjectsArray::iterator i = savedNames.begin(); i != savedNames.end(); ++i)
+ {
+ field->fld_name = *i;
+ field = field->fld_next;
+ }
- // 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;
- }
+ GEN_expr(dsqlScratch, condition);
+ dsqlScratch->appendUChar(blr_begin);
+ dsqlScratch->appendUChar(blr_end);
- ++ptr;
- }
+ // Generate the action statement for the trigger.
+ exceptionNode->dsqlPass(dsqlScratch)->genBlr(dsqlScratch);
- // If not an expression, point to the proper base relation field,
- // else make up an SQL field with generated global field for calculations.
+ dsqlScratch->appendUChar(blr_end); // of begin
+ dsqlScratch->appendUChar(blr_eoc);
- dsql_fld* relField = NULL;
+ dsqlScratch->resetContextStack();
- 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));
- }
+ TriggerDefinition trigger(pool);
+ trigger.systemFlag = fb_sysflag_view_check;
+ trigger.name.schema = name.schema;
+ trigger.relationName = name;
+ trigger.type = triggerType;
+ trigger.blrData = dsqlScratch->getBlrData();
+ trigger.store(tdbb, dsqlScratch, dsqlScratch->getTransaction());
+}
- modifiedFields.add(relField);
- break;
- }
- }
- }
- FieldDefinition fieldDefinition(*tdbb->getDefaultPool());
- fieldDefinition.relationName = name;
- fieldDefinition.name = fieldStr;
- fieldDefinition.position = position;
- fieldDefinition.fieldSource.schema = name.schema;
+//----------------------
- // 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);
+bool ModifyIndexNode::modify(thread_db* tdbb, Cached::Relation* relation, 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.viewContext = context->ctx_context;
- fieldDefinition.baseField = field->fld_name;
+ SET_TDBB(tdbb);
+ Attachment* attachment = transaction->getAttachment();
- if (field->dtype <= dtype_any_text)
- fieldDefinition.collationId = field->collationId;
+ fb_assert(relation);
+ if (!relation)
+ fatal_exception::raiseFmt("Relation for creation of index %s not found", indexName.toQuotedString().c_str());
- 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.
+ bool rc = true;
+ if (create)
+ rc = exec(tdbb, relation, transaction);
- AutoRequest request2;
+ if (relation->rel_flags & REL_temp_conn)
+ {
+ AutoSetRestoreFlag preserveFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, false);
- 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$SCHEMA_NAME EQ name.schema.c_str() AND
- RFL.RDB$RELATION_NAME EQ name.object.c_str() AND
- RFL.RDB$BASE_FIELD MISSING AND
- FLD.RDB$SCHEMA_NAME EQ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND
- FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE
- {
- const bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME);
- fb_assert(wasInternalDomain);
+ if (relation->getPages(tdbb, MAX_TRA_NUMBER, false))
+ exec(tdbb, relation, transaction);
+ }
- if (wasInternalDomain)
- ERASE FLD;
- }
- END_FOR
+ if (!create)
+ exec(tdbb, relation, transaction);
- 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);
+ return rc;
+}
- // Get the type of the expression.
- dsc desc;
- DsqlDescMaker::fromNode(dsqlScratch, &desc, fieldNode);
+string ModifyIndexNode::print(Jrd::NodePrinter& printer) const
+{
+ NODE_PRINT(printer, indexName);
+ NODE_PRINT(printer, create);
- dsql_fld newField(*tdbb->getDefaultPool());
- newField.dtype = desc.dsc_dtype;
- newField.length = desc.dsc_length;
- newField.scale = desc.dsc_scale;
+ return "ModifyIndexNode";
+}
- if (desc.isText() || (desc.isBlob() && desc.getBlobSubType() == isc_blob_text))
- {
- newField.charSetId = desc.getCharSet();
- newField.collationId = desc.getCollation();
- }
+Cached::Relation* ModifyIndexNode::getRelByIndex(thread_db* tdbb, const QualifiedName& index, jrd_tra* transaction)
+{
+ SET_TDBB(tdbb);
- 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));
+ Attachment* attachment = tdbb->getAttachment();
- newField.charLength = (newField.length - adjust) / bpc;
- }
- else
- newField.subType = desc.dsc_sub_type;
+ Cached::Relation* relation = nullptr;
- newField.setExactPrecision();
+ static const CachedRequestId requestCacheId;
+ AutoCacheRequest request(tdbb, requestCacheId);
- if (relField) // modifying a view
- {
- AutoRequest request2;
+ FOR(REQUEST_HANDLE request)
+ IDX IN RDB$INDICES
+ WITH
+ IDX.RDB$SCHEMA_NAME EQ index.schema.c_str() AND
+ IDX.RDB$INDEX_NAME EQ index.object.c_str()
+ {
+ fb_assert(!relation);
+ relation = MetadataCache::lookupRelation(tdbb,
+ QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME), CacheFlag::AUTOCREATE);
+ }
+ END_FOR
- 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$SCHEMA_NAME EQ name.schema.c_str() AND
- RFL.RDB$RELATION_NAME EQ name.object.c_str() AND
- RFL.RDB$BASE_FIELD MISSING AND
- FLD.RDB$SCHEMA_NAME EQ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND
- FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE
- {
- const bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME);
- fb_assert(wasInternalDomain);
+ if (relation && (relation->rel_flags & REL_check_partners))
+ relation->scanPartners(tdbb);
+ return relation;
+}
- if (wasInternalDomain)
- {
- fieldDefinition.fieldSource = QualifiedName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME);
- 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.object.isEmpty())
- {
- storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField,
- "", dsqlScratch->getBlrData());
- }
+bool ModifyIndexList::exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction)
+{
+ fb_assert(rel);
+ if (!rel)
+ return false;
- fieldDefinition.modify(tdbb, transaction);
- }
- else
- {
- storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, &newField,
- "", dsqlScratch->getBlrData());
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
+ bool rc = true;
- fieldDefinition.store(tdbb, transaction);
+ for (auto* node : nodes)
+ {
+ if (node)
+ {
+ if (!node->modify(tdbb, rel, transaction))
+ {
+ fb_assert(nodes.getCount() == 1);
+ rc = false;
}
}
-
- if (fieldStr)
- saveField(tdbb, dsqlScratch, fieldStr);
}
- // CVC: This message was not catching the case when
- // #fields < items in select list, see comment above.
+ nodes.clear();
+ return rc;
+}
- if (ptr != end)
+void ModifyIndexList::erase(thread_db* tdbb, MetaName name)
+{
+ for (FB_SIZE_T n = 0; n < nodes.getCount(); ++n)
{
- // 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));
+ if (nodes[n]->check(tdbb, name))
+ {
+ nodes.remove(n);
+ return;
+ }
}
+}
- if (modifyingView) // modifying a view
+
+//----------------------
+
+
+bool StoreIndexNode::exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction)
+{
+ auto id = expressionIndex ? createExpression(tdbb, rel, transaction) : create(tdbb, rel, transaction);
+ if (id < tdbb->getDatabase()->dbb_max_idx)
+ rel->newIndexVersion(tdbb, id, CacheFlag::MINISCAN);
+
+ return true;
+}
+
+
+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->getVersioned(tdbb, CacheFlag::AUTOCREATE);
+ if (!relation)
{
- // Delete the old fields not present in the new definition.
- for (dsql_fld* relField = modifyingView->rel_fields; relField; relField = relField->fld_next)
- {
- if (!modifiedFields.exist(relField))
- deleteLocalField(tdbb, transaction, name, relField->fld_name, false);
- }
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_idx_create_err) << indexName.toQuotedString());
+ // Msg308: can't create index %s
}
- // Setup to define triggers for WITH CHECK OPTION.
+ index_desc idx;
+ idx.idx_count = 0;
+ int key_count = 0;
+
+ SET_TDBB(tdbb);
+ Attachment* attachment = tdbb->getAttachment();
+ Database* dbb = tdbb->getDatabase();
+ MetaId idxId = dbb->dbb_max_idx;
+ bool active = false;
+ const bool tempConn = !(tdbb->tdbb_flags & TDBB_use_db_page_space);
- if (withCheckOption)
- {
- 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));
- }
+ idx.idx_flags = 0;
- RseNode* querySpec = nodeAs(selectExpr->querySpec);
- fb_assert(querySpec);
+ // Fetch the information necessary to create the index.
+ request.reset(tdbb, irq_c_index, IRQ_REQUESTS);
- if (querySpec->dsqlFrom->items.getCount() != 1 ||
- !nodeIs(querySpec->dsqlFrom->items[0]))
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ IDX IN RDB$INDICES CROSS
+ REL IN RDB$RELATIONS OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME
+ WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ IDX.RDB$INDEX_NAME EQ indexName.object.c_str()
+ {
+ active = IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE;
+
+ // Decide upon ID
+ fb_assert(tempConn != bool(IDX.RDB$INDEX_ID.NULL));
+ if (!IDX.RDB$INDEX_ID.NULL)
{
- // 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));
+ idxId = IDX.RDB$INDEX_ID - 1;
+
+ // Reject here request to 'alter index inactive'
+ fb_assert(IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE);
}
- if (!querySpec->dsqlWhere)
+ 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) << indexName.toQuotedString());
+ // Msg304: segment count of 0 defined for index %s
+ }
+ else
+ {
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_idx_key_err) << indexName.toQuotedString());
+ // 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$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ RC.RDB$INDEX_NAME EQ indexName.object.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, {});
+ MET_get_dependencies(tdbb, relation, 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, QualifiedName(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$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ SEG.RDB$INDEX_NAME EQ indexName.object.c_str() AND
+ RFR.RDB$SCHEMA_NAME EQ SEG.RDB$SCHEMA_NAME AND
+ RFR.RDB$RELATION_NAME EQ rel->getName().object.c_str() AND
+ RFR.RDB$FIELD_NAME EQ SEG.RDB$FIELD_NAME AND
+ FLD.RDB$SCHEMA_NAME EQ RFR.RDB$FIELD_SOURCE_SCHEMA_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) << indexName.toQuotedString());
+ // 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) << indexName.toQuotedString());
+ }
+ else if (FLD.RDB$FIELD_TYPE == blr_blob)
+ {
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_blob_idx_err) << indexName.toQuotedString());
+ // Msg350: attempt to index blob column in index %s
+ }
+ else
+ {
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_array_idx_err) << indexName.toQuotedString());
+ // 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.toQuotedString().c_str());
- if (triggerType == PRE_MODIFY_TRIGGER)
+ if (key_count != idx.idx_count)
{
- dsqlScratch->contextNumber = 2;
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_key_field_err) << indexName.toQuotedString());
+ // Msg352: too few key columns found for index %s (incorrect column name?)
+ }
- RelationSourceNode* baseRelation = FB_NEW_POOL(pool) RelationSourceNode(pool, sourceNode->dsqlName);
- baseRelation->alias = sourceNode->alias;
+ if (rel->isView())
+ {
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_idx_create_err) << indexName.toQuotedString());
+ // Msg308: can't create index %s
+ }
- dsqlScratch->appendUChar(blr_for);
+ // Actually create the index
- RseNode* rse = FB_NEW_POOL(pool) RseNode(pool);
- rse->dsqlStreams = FB_NEW_POOL(pool) RecSourceListNode(pool, 1);
+ // Protect relation from modification to create consistent index
+ ProtectRelations protectRelations(tdbb, transaction);
+ protectRelations.addRelation(rel);
- rse->dsqlStreams->items[0] = baseRelation;
- rse->dsqlStreams->items[0] = doDsqlPass(dsqlScratch, rse->dsqlStreams->items[0]);
- rse->dsqlWhere = doDsqlPass(dsqlScratch, querySpec->dsqlWhere);
+ Cached::Relation* partner_relation = nullptr;
+ if (idx.idx_flags & idx_foreign)
+ {
+ idx.idx_id = idx_invalid;
- dsqlScratch->contextNumber = OLD_CONTEXT_VALUE;
+ if (MET_lookup_partner(tdbb, rel, &idx, indexName))
+ {
+ partner_relation = MetadataCache::lookupRelation(tdbb, idx.idx_primary_relation, CacheFlag::AUTOCREATE);
+ }
- dsql_ctx* oldContext;
+ 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));
+ }
- { /// scope
- AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias);
- relationNode->alias = OLD_CONTEXT_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.
- oldContext = PASS1_make_context(dsqlScratch, relationNode);
- oldContext->ctx_flags |= CTX_system;
+ 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));
}
- // 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.
+ /*** hvlad: this code was never called but i preserve it for Claudio review and decision
- ValueListNode* valuesNode = viewFields;
- ValueListNode* fieldsNode = querySpec->dsqlSelectList;
+ // 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.
- if (!fieldsNode)
- {
- const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(),
- dsqlScratch, name);
- fieldsNode = FB_NEW_POOL(pool) ValueListNode(pool, 0u);
+ // 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 (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()));
- }
+ if (rel->rel_owner_name.length() == 0 ||
+ partner_relation->rel_owner_name.length() == 0 ||
+ rel->rel_owner_name != partner_relation->rel_owner_name)
+ {
+ SCL_check_index(tdbb, partner_relation->getName(),
+ idx.idx_id + 1, SCL_references);
}
+ ***/
+ }
- if (!valuesNode)
- valuesNode = fieldsNode;
+ if (active)
+ {
+ // Actually create the index
+ protectRelations.lock();
- // Generate the list of assignments to fields in the base relation.
+ fb_assert(idxId <= dbb->dbb_max_idx);
+ idx.idx_id = idxId;
+ SelectivityList selectivity(*tdbb->getDefaultPool());
+ IDX_create_index(tdbb, IdxCreate::ForRollback, relation, &idx, indexName,
+ &idxId, transaction, selectivity);
+ fb_assert(idxId == idx.idx_id);
+ if (!tempConn)
+ DFW_update_index(indexName, idxId, selectivity, transaction);
+ }
+ else
+ fb_assert(idxId == dbb->dbb_max_idx);
- NestConst* ptr = fieldsNode->items.begin();
- const NestConst* const end = fieldsNode->items.end();
- NestConst* ptr2 = valuesNode->items.begin();
- const NestConst* const end2 = valuesNode->items.end();
+ // Memleak when IDX_create_index() throws ????????????
+ if (idx.idx_condition_statement)
+ idx.idx_condition_statement->release(tdbb);
- BinaryBoolNode* andNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and);
+ if (partner_relation)
+ {
+ rel->checkPartners(tdbb);
- for (; ptr != end && ptr2 != end2; ++ptr, ++ptr2)
- {
- NestConst fieldNod = *ptr;
- NestConst valueNod = *ptr2;
- DsqlAliasNode* aliasNode;
+ if (rel != partner_relation)
+ partner_relation->checkPartners(tdbb);
+ }
- if ((aliasNode = nodeAs(fieldNod)))
- fieldNod = aliasNode->value;
+ return idxId;
+}
- if ((aliasNode = nodeAs(valueNod)))
- valueNod = aliasNode->value;
- FieldNode* fieldNode = nodeAs(fieldNod);
- FieldNode* valueNode = nodeAs(valueNod);
+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.
+ *
+ **************************************/
- // Generate the actual comparisons.
+ SET_TDBB(tdbb);
- if (fieldNode && valueNode)
- {
- FieldNode* oldValueNode = FB_NEW_POOL(pool) FieldNode(pool);
- oldValueNode->dsqlName = (aliasNode ? aliasNode->name : valueNode->dsqlName);
- oldValueNode->dsqlQualifier.object = OLD_CONTEXT_NAME;
+ jrd_rel* relation = rel->getVersioned(tdbb, CacheFlag::AUTOCREATE);
+ if (!relation)
+ {
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_idx_create_err) << indexName.toQuotedString());
+ // Msg308: can't create index %s
+ }
- valueNod = oldValueNode->dsqlPass(dsqlScratch);
- fieldNod = fieldNode->dsqlPass(dsqlScratch);
+ index_desc idx;
+ MOVE_CLEAR(&idx, sizeof(index_desc));
+ idx.idx_flags = 0;
+ idx.idx_count = 0;
+ CompilerScratch* csb = nullptr;
- BoolExprNode* equivNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_equiv,
- valueNod, fieldNod);
+ const auto dbb = tdbb->getDatabase();
+ const auto attachment = tdbb->getAttachment();
+ MetaId idxId = dbb->dbb_max_idx;
+ bool active = false;
+ const bool tempConn = !(tdbb->tdbb_flags & TDBB_use_db_page_space);
- rse->dsqlWhere = PASS1_compose(rse->dsqlWhere, equivNode, blr_and);
- }
- }
+ AutoCacheRequest request(tdbb, irq_c_exp_index, IRQ_REQUESTS);
- GEN_expr(dsqlScratch, rse);
- }
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ IDX IN RDB$INDICES
+ CROSS REL IN RDB$RELATIONS OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME
+ WITH IDX.RDB$EXPRESSION_BLR NOT MISSING AND
+ IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ IDX.RDB$INDEX_NAME EQ indexName.object.c_str()
+ {
+ active = IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE;
- // ASF: We'll now map the view's base table into the trigger's NEW context.
+ // Decide upon ID
+ fb_assert(tempConn != bool(IDX.RDB$INDEX_ID.NULL));
+ if (!IDX.RDB$INDEX_ID.NULL)
+ {
+ idxId = IDX.RDB$INDEX_ID - 1;
- ++dsqlScratch->scopeLevel;
- dsqlScratch->contextNumber = NEW_CONTEXT_VALUE;
+ // Reject here request to 'alter index inactive'
+ fb_assert(IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE);
+ }
- dsql_ctx* newContext;
+ 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) << indexName.toQuotedString());
+ }
- { /// scope
- AutoSetRestore autoAlias(&relationNode->alias, sourceNode->alias);
+ if (IDX.RDB$UNIQUE_FLAG)
+ idx.idx_flags |= idx_unique;
+ if (IDX.RDB$INDEX_TYPE == 1)
+ idx.idx_flags |= idx_descending;
- if (relationNode->alias.isEmpty())
- relationNode->alias = sourceNode->dsqlName.object.c_str();
+ // Allocate a new pool to contain the expression tree
+ // for index expression
+ const auto new_pool = dbb->createPool(ALLOC_ARGS0);
- newContext = PASS1_make_context(dsqlScratch, relationNode);
- newContext->ctx_flags |= CTX_system;
+ try
+ {
+ Jrd::ContextPoolHolder context(tdbb, new_pool);
- if (triggerType == PRE_STORE_TRIGGER)
- newContext->ctx_flags |= CTX_view_with_check_store;
- else
- newContext->ctx_flags |= CTX_view_with_check_modify;
- }
+ MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$EXPRESSION_BLR,
+ nullptr, &csb, indexName, obj_index_expression, 0,
+ transaction);
- // Replace the view's field names by the base table field names. Save the original names
- // to restore after the condition processing.
+ idx.idx_expression_statement = Statement::makeValueExpression(tdbb,
+ idx.idx_expression_node, idx.idx_expression_desc, csb, false);
- dsql_fld* field = newContext->ctx_relation->rel_fields;
- ObjectsArray savedNames;
+ // fake a description of the index
- // 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_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;
+ }
- if ((aliasNode = nodeAs(valueNode)))
- valueNode = aliasNode->value;
+ 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);
- FieldNode* fieldNode = nodeAs(valueNode);
- fb_assert(fieldNode);
+ try
+ {
+ Jrd::ContextPoolHolder context(tdbb, new_pool);
- savedNames.add(field->fld_name);
+ MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$CONDITION_BLR,
+ nullptr, &csb, indexName, obj_index_condition, 0,
+ transaction);
- dsql_fld* queryField = fieldNode->dsqlField;
+ idx.idx_condition_statement = Statement::makeBoolExpression(tdbb,
+ idx.idx_condition_node, csb, false);
- 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;
+ idx.idx_flags |= idx_condition;
+ }
+ catch (const Exception&)
+ {
+ dbb->deletePool(new_pool);
+ throw;
+ }
+ }
+ }
+ END_FOR
+
+ if (!relation)
+ {
+ // Msg308: can't create index %s
+ ERR_post(Arg::Gds(isc_no_meta_update) <<
+ Arg::Gds(isc_idx_create_err) << indexName.toQuotedString());
}
- dsqlScratch->appendUChar(blr_if);
+ delete csb;
- // Process the condition for firing the trigger.
- NestConst condition = doDsqlPass(dsqlScratch, querySpec->dsqlWhere);
+ if (active)
+ {
+ // Actually create the index
- // Restore the field names. This must happen before the condition's BLR generation.
+ // Protect relation from modification to create consistent index
+ ProtectRelations protectRelation(tdbb, transaction);
+ protectRelation.addRelation(rel);
- field = newContext->ctx_relation->rel_fields;
+ SelectivityList selectivity(*tdbb->getDefaultPool());
- for (ObjectsArray::iterator i = savedNames.begin(); i != savedNames.end(); ++i)
- {
- field->fld_name = *i;
- field = field->fld_next;
- }
+ AutoSave2 currentTransaction(tdbb, &thread_db::getTransaction, &thread_db::setTransaction);
+ AutoSave2 currentRequest(tdbb, &thread_db::getRequest, &thread_db::setRequest);
- GEN_expr(dsqlScratch, condition);
- dsqlScratch->appendUChar(blr_begin);
- dsqlScratch->appendUChar(blr_end);
+ try
+ {
+ fb_assert(idxId <= dbb->dbb_max_idx);
+ idx.idx_id = idxId;
+ IDX_create_index(tdbb, IdxCreate::ForRollback, relation, &idx, indexName, &idxId,
+ transaction, selectivity);
- // Generate the action statement for the trigger.
- exceptionNode->dsqlPass(dsqlScratch)->genBlr(dsqlScratch);
+ fb_assert(idxId == idx.idx_id);
+ }
+ catch (const Exception&)
+ {
+ // Get rid of the expression/condition statements
+ idx.idx_expression_statement->release(tdbb);
+ if (idx.idx_condition_statement)
+ idx.idx_condition_statement->release(tdbb);
- dsqlScratch->appendUChar(blr_end); // of begin
- dsqlScratch->appendUChar(blr_eoc);
+ throw;
+ }
- dsqlScratch->resetContextStack();
+ if (!tempConn)
+ DFW_update_index(indexName, idx.idx_id, selectivity, transaction);
+ }
+ else
+ fb_assert(idxId == dbb->dbb_max_idx);
- TriggerDefinition trigger(pool);
- trigger.systemFlag = fb_sysflag_view_check;
- trigger.name.schema = name.schema;
- 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;
}
@@ -10069,23 +12048,21 @@ void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratc
// Store an index.
-void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName& name,
- const Definition& definition, QualifiedName* referredIndexName)
+ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* transaction,
+ QualifiedName& idxName, Definition& definition, QualifiedName* referredIndexName)
{
- if (name.object.isEmpty())
- DYN_UTIL_generate_index_name(tdbb, transaction, name, definition.type);
-
- fb_assert(name.schema == definition.relation.schema);
-
- 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
{
+ IDX.RDB$INDEX_ID.NULL = TRUE;
IDX.RDB$UNIQUE_FLAG.NULL = TRUE;
IDX.RDB$INDEX_INACTIVE.NULL = TRUE;
IDX.RDB$INDEX_TYPE.NULL = TRUE;
@@ -10093,8 +12070,9 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
IDX.RDB$FOREIGN_KEY.NULL = TRUE;
IDX.RDB$EXPRESSION_SOURCE.NULL = TRUE;
IDX.RDB$EXPRESSION_BLR.NULL = TRUE;
- strcpy(IDX.RDB$SCHEMA_NAME, name.schema.c_str());
- strcpy(IDX.RDB$INDEX_NAME, name.object.c_str());
+ IDX.RDB$RELATION_NAME.NULL = FALSE;
+ strcpy(IDX.RDB$SCHEMA_NAME, idxName.schema.c_str());
+ strcpy(IDX.RDB$INDEX_NAME, idxName.object.c_str());
strcpy(IDX.RDB$RELATION_NAME, definition.relation.object.c_str());
IDX.RDB$SYSTEM_FLAG = 0;
@@ -10163,17 +12141,17 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
if (GF.RDB$FIELD_TYPE == blr_blob)
{
// msg 116 "attempt to index blob field in index %s"
- status_exception::raise(Arg::PrivateDyn(116) << name.toQuotedString());
+ status_exception::raise(Arg::PrivateDyn(116) << idxName.toQuotedString());
}
else if (!GF.RDB$DIMENSIONS.NULL)
{
// msg 117 "attempt to index array field in index %s"
- status_exception::raise(Arg::PrivateDyn(117) << name.toQuotedString());
+ status_exception::raise(Arg::PrivateDyn(117) << idxName.toQuotedString());
}
else if (!GF.RDB$COMPUTED_BLR.NULL)
{
// msg 179 "attempt to index COMPUTED BY field in index %s"
- status_exception::raise(Arg::PrivateDyn(179) << name.toQuotedString());
+ status_exception::raise(Arg::PrivateDyn(179) << idxName.toQuotedString());
}
else if (GF.RDB$FIELD_TYPE == blr_varying || GF.RDB$FIELD_TYPE == blr_text)
{
@@ -10183,15 +12161,15 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
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
@@ -10215,7 +12193,7 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
if (!found)
{
// msg 120 "Unknown columns in index %s"
- status_exception::raise(Arg::PrivateDyn(120) << name.toQuotedString());
+ status_exception::raise(Arg::PrivateDyn(120) << idxName.toQuotedString());
}
}
@@ -10235,7 +12213,7 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
if (keyLength >= MAX_KEY)
{
// msg 118 "key size too big for index %s"
- status_exception::raise(Arg::PrivateDyn(118) << name.toQuotedString());
+ status_exception::raise(Arg::PrivateDyn(118) << idxName.toQuotedString());
}
if (definition.columns.hasData())
@@ -10262,7 +12240,7 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
else if (IDX.RDB$EXPRESSION_BLR.NULL)
{
// msg 119 "no keys for index %s"
- status_exception::raise(Arg::PrivateDyn(119) << name.toQuotedString());
+ status_exception::raise(Arg::PrivateDyn(119) << idxName.toQuotedString());
}
if (definition.refColumns.hasData())
@@ -10423,8 +12401,8 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
IDX IN RDB$INDICES
- WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- IDX.RDB$INDEX_NAME EQ name.object.c_str()
+ WITH IDX.RDB$SCHEMA_NAME EQ idxName.schema.c_str() AND
+ IDX.RDB$INDEX_NAME EQ idxName.object.c_str()
{
MODIFY IDX
if (!definition.conditionBlr.isEmpty())
@@ -10442,6 +12420,8 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, QualifiedName
}
END_FOR
}
+
+ return FB_NEW_POOL(p) StoreIndexNode(definition.index, definition.expressionBlr.hasData());
}
@@ -10467,6 +12447,8 @@ void CreateIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
// Define an index.
void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
{
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
+
Attachment* const attachment = transaction->tra_attachment;
// run all statements under savepoint control
@@ -10477,7 +12459,7 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_INDEX, name, {});
- CreateIndexNode::Definition definition;
+ Definition definition;
definition.type = isc_dyn_def_idx;
definition.relation = relation->dsqlName;
definition.unique = unique;
@@ -10522,11 +12504,14 @@ 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, {});
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);
}
DdlNode* CreateIndexNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
@@ -10556,9 +12541,9 @@ DdlNode* CreateIndexNode::dsqlPass(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";
}
@@ -10566,44 +12551,137 @@ string AlterIndexNode::internalPrint(NodePrinter& printer) const
void AlterIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
{
bool systemIndex;
- const auto relationName = getIndexRelationName(tdbb, transaction, name, systemIndex);
+ const auto relationName = getIndexRelationName(tdbb, transaction, indexName, systemIndex);
SCL_check_relation(tdbb, relationName, SCL_alter, systemIndex);
}
void AlterIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
{
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
+
// 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;
+ bool deferred = false;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
IDX IN RDB$INDICES
- WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- IDX.RDB$INDEX_NAME EQ name.object.c_str()
+ WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ IDX.RDB$INDEX_NAME EQ indexName.object.c_str()
{
found = true;
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, name, {});
+ if (IDX.RDB$INDEX_INACTIVE.NULL)
+ active = true;
+ else
+ {
+ active = IDX.RDB$INDEX_INACTIVE == MET_index_active;
+ deferred = IDX.RDB$INDEX_INACTIVE == MET_index_deferred_active;
+ }
- 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, {});
+
+ auto relName = QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_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())
+ {
+ auto ret = relation->oldIndexVersion(tdbb, idxId.value(), CacheFlag::OLD_ALTER);
+ fb_assert(ret);
+ }
+
+ MODIFY IDX
+ IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
+ IDX.RDB$INDEX_INACTIVE = create ? MET_index_active : MET_index_inactive;
+ if (create)
+ {
+ IDX.RDB$STATISTICS.NULL = FALSE;
+ IDX.RDB$STATISTICS = -1.0;
+ }
+ END_MODIFY
+ }
}
END_FOR
- if (found)
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, name, {});
- else
+ if (!found)
{
// msg 48: "Index not found"
status_exception::raise(Arg::PrivateDyn(48));
}
- savePoint.release(); // everything is ok
+ if (active == create)
+ return;
+
+ if (!deferred)
+ {
+ MemoryPool& pool = *MemoryPool::getContextPool();
+ ModifyIndexList oneItemList(pool);
+ if (idxId.has_value())
+ {
+ oneItemList.push(this);
+ if (!oneItemList.exec(tdbb, relation, transaction))
+ {
+ fb_assert(create);
+
+ idxId.reset();
+
+ AUTO_HANDLE(rq1);
+ FOR(REQUEST_HANDLE rq1 TRANSACTION_HANDLE transaction)
+ IDX IN RDB$INDICES
+ WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ IDX.RDB$INDEX_NAME EQ indexName.object.c_str()
+ {
+ MODIFY IDX
+ IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
+ IDX.RDB$INDEX_INACTIVE = MET_index_active;
+ IDX.RDB$STATISTICS.NULL = FALSE;
+ IDX.RDB$STATISTICS = 0.0;
+ IDX.RDB$INDEX_ID.NULL = TRUE;
+ END_MODIFY
+ }
+ END_FOR
+ }
+ }
+ if (!idxId.has_value())
+ {
+ oneItemList.push(FB_NEW_POOL(pool) StoreIndexNode(indexName, expressionIndex));
+ oneItemList.exec(tdbb, relation, transaction);
+ }
+ }
+
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, indexName, {});
+
+ savePoint.release(); // system table modified OK
+}
+
+bool AlterIndexNode::exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction)
+{
+ if (create)
+ {
+ if (! IDX_activate_index(tdbb, rel, idxId.value()))
+ return false;
+ }
+ else
+ IDX_mark_index(tdbb, rel, idxId.value());
+
+ rel->newIndexVersion(tdbb, idxId.value(), CacheFlag::MINISCAN);
+ return true;
}
@@ -10629,6 +12707,8 @@ void SetStatisticsNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
void SetStatisticsNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
{
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
+
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
@@ -10667,6 +12747,13 @@ void SetStatisticsNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
//----------------------
+DropIndexNode::DropIndexNode(MemoryPool& p, const QualifiedName& 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 QualifiedName& name)
{
@@ -10689,8 +12776,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";
}
@@ -10698,7 +12786,7 @@ string DropIndexNode::internalPrint(NodePrinter& printer) const
void DropIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
{
bool systemIndex;
- const auto relationName = getIndexRelationName(tdbb, transaction, name, systemIndex, silent);
+ const auto relationName = getIndexRelationName(tdbb, transaction, indexName, systemIndex, silent);
if (relationName.object.hasData())
SCL_check_relation(tdbb, relationName, SCL_alter, systemIndex);
@@ -10706,40 +12794,301 @@ void DropIndexNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
void DropIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
{
+ AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true);
+
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
+ ModifyIndexList oneItemList(*MemoryPool::getContextPool());
+ Cached::Relation* rel = drop(tdbb, dsqlScratch, transaction, oneItemList, true);
+ oneItemList.exec(tdbb, rel, transaction);
+
+ savePoint.release(); // everything is ok
+}
+
+
+static MetaName makeTempDependName(MetaId relId, MetaId indexId)
+{
+ MetaName name;
+ name.printf("%s_%u_%u", TEMP_DEPEND, relId, indexId);
+ return name;
+}
+
+
+Cached::Relation* DropIndexNode::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction,
+ ModifyIndexList& list, bool runTriggers)
+{
+ AUTO_HANDLE(req1);
+
+ FOR (REQUEST_HANDLE req1 TRANSACTION_HANDLE transaction)
+ RLC IN RDB$RELATION_CONSTRAINTS
+ WITH RLC.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ RLC.RDB$INDEX_NAME EQ indexName.object.c_str()
+ {
+ ERR_post(Arg::Gds(isc_integ_index_del));
+ }
+ END_FOR
+
AutoCacheRequest request(tdbb, drq_e_indices, DYN_REQUESTS);
- bool found = false;
+ Cached::Relation* rel = nullptr;
+ bool addToTheList = false;
+ MetaName tempName;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
IDX IN RDB$INDICES
- WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
- IDX.RDB$INDEX_NAME EQ name.object.c_str()
+ WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND
+ IDX.RDB$INDEX_NAME EQ indexName.object.c_str() AND
+ IDX.RDB$INDEX_INACTIVE NE MET_index_deferred_drop
{
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_INDEX, name, {});
- ERASE IDX;
+ auto relName = QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME);
+ rel = MetadataCache::lookupRelation(tdbb, relName, CacheFlag::AUTOCREATE);
+ fb_assert(rel);
- if (IDX.RDB$EXPRESSION_BLR.NULL && !deleteSegmentRecords(tdbb, transaction, name))
+ if (runTriggers)
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_INDEX, indexName, {});
+
+ if (rel &&
+ MetadataCache::getIndexStatus(IDX.RDB$INDEX_INACTIVE.NULL, IDX.RDB$INDEX_INACTIVE) == MET_index_active)
+ {
+ fb_assert(IDX.RDB$INDEX_ID);
+ idxId = IDX.RDB$INDEX_ID - 1;
+
+ auto ret = rel->oldIndexVersion(tdbb, idxId, CacheFlag::OLD_DROP);
+ fb_assert(ret);
+
+ addToTheList = true;
+ tempName = makeTempDependName(rel->getId(), idxId);
+
+ MODIFY IDX
+ IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
+ IDX.RDB$INDEX_INACTIVE = MET_index_deferred_drop;
+
+ if (tempName.hasData())
+ strcpy(IDX.RDB$INDEX_NAME, tempName.c_str());
+ END_MODIFY
+ }
+ else
{
- // msg 50: "No segments found for index"
- status_exception::raise(Arg::PrivateDyn(50));
+ ERASE IDX;
}
- found = true;
+ deleteSegmentRecords(tdbb, transaction, indexName);
}
END_FOR
- if (found)
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_INDEX, name, {});
+ if (rel)
+ {
+ if (runTriggers)
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_INDEX, indexName, {});
+ if (addToTheList)
+ list.push(this);
+
+ AUTO_HANDLE(handle2);
+ FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction)
+ DEP IN RDB$DEPENDENCIES
+ WITH DEP.RDB$DEPENDENT_SCHEMA_NAME EQUIV NULLIF(indexName.schema.c_str(), '') AND
+ DEP.RDB$DEPENDENT_NAME EQ indexName.object.c_str() AND
+ (DEP.RDB$DEPENDENT_TYPE EQ obj_index_expression OR
+ DEP.RDB$DEPENDENT_TYPE EQ obj_index_condition)
+ {
+ if (addToTheList)
+ {
+ MODIFY DEP USING
+ {
+ strcpy(DEP.RDB$DEPENDENT_NAME, tempName.c_str());
+ }
+ END_MODIFY
+ }
+ else
+ {
+ ERASE DEP;
+ }
+ }
+ END_FOR
+ }
else if (!silent)
{
// msg 48: "Index not found"
status_exception::raise(Arg::PrivateDyn(48));
}
- savePoint.release(); // everything is ok
+ return rel;
+}
+
+
+bool 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);
+
+ RelationPages* relPages = rel->getPages(tdbb, MAX_TRA_NUMBER, false);
+ if (relPages)
+ {
+ IDX_mark_index(tdbb, rel, idxId);
+ rel->eraseIndex(tdbb, idxId);
+ }
+
+ return true;
+}
+
+
+//
+// clearFrgn()
+// First part of index deletion post-processing.
+//
+// This function is invoked when index enters irt_drop state due to being dropped
+// (not inactivated). That means it should be never seen in database by new requests
+// (but may be used by started earlier requests). It also should not affect FK constraints.
+//
+
+void DropIndexNode::clearFrgn(thread_db* tdbb, MetaId relId, MetaId indexId)
+{
+ SET_TDBB(tdbb);
+ Attachment* attachment = tdbb->getAttachment();
+ Database* dbb = tdbb->getDatabase();
+ QualifiedName partnerIdxName;
+ bool processPartners = false;
+
+ auto rel = MetadataCache::lookupRelation(tdbb, relId, 0);
+
+ // FK cleanup is not related to current transaction
+ auto* transaction = attachment->getSysTransaction();
+ AutoSetRestore2 autoTrans(tdbb, &thread_db::getTransaction, &thread_db::setTransaction,
+ transaction);
+
+ AUTO_HANDLE(handle);
+ FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
+ IND IN RDB$INDICES
+ CROSS REL IN RDB$RELATIONS
+ OVER RDB$RELATION_NAME, RDB$SCHEMA_NAME
+ WITH IND.RDB$INDEX_ID EQ indexId + 1
+ AND REL.RDB$RELATION_ID EQ relId
+ AND IND.RDB$INDEX_INACTIVE NOT MISSING
+ AND IND.RDB$INDEX_INACTIVE EQ MET_index_deferred_drop
+ {
+ if (!IND.RDB$FOREIGN_KEY.NULL)
+ {
+ partnerIdxName = QualifiedName(IND.RDB$FOREIGN_KEY, IND.RDB$FOREIGN_KEY_SCHEMA_NAME);
+
+ MODIFY IND USING
+ {
+ IND.RDB$FOREIGN_KEY.NULL = TRUE;
+ IND.RDB$FOREIGN_KEY_SCHEMA_NAME.NULL = TRUE;
+ }
+ END_MODIFY
+ }
+ }
+ END_FOR
+
+ if (partnerIdxName.hasData())
+ {
+ auto partner = getRelByIndex(tdbb, partnerIdxName, transaction);
+
+ if (rel && partner)
+ {
+ rel->checkPartners(tdbb);
+
+ if (rel != partner)
+ partner->checkPartners(tdbb);
+ }
+ }
+}
+
+
+//
+// clearId()
+// Second part of index deletion post-processing.
+//
+// This function is invoked when index is removed from irt page.
+// In some cases (failed DB creation) cleanup, done by clearName,
+// also should take place.
+//
+
+void DropIndexNode::clearId(thread_db* tdbb, MetaId relId, MetaId indexId)
+{
+ SET_TDBB(tdbb);
+ Attachment* attachment = tdbb->getAttachment();
+ Database* dbb = tdbb->getDatabase();
+ QualifiedName indexName, partnerIdxName;
+ bool processPartners = false;
+
+ auto rel = MetadataCache::lookupRelation(tdbb, relId, 0);
+
+ // cleanup NOT related to current transaction
+ auto* transaction = attachment->getSysTransaction();
+ AutoSetRestore2 autoTrans(tdbb, &thread_db::getTransaction, &thread_db::setTransaction,
+ transaction);
+
+ 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, RDB$SCHEMA_NAME
+ WITH IND.RDB$INDEX_ID EQ indexId + 1
+ AND REL.RDB$RELATION_ID EQ relId
+ {
+ indexName = QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME);
+ fb_assert (!indexName.isEmpty());
+
+ if (!IND.RDB$FOREIGN_KEY.NULL)
+ partnerIdxName = QualifiedName(IND.RDB$FOREIGN_KEY, IND.RDB$FOREIGN_KEY_SCHEMA_NAME);
+
+ switch (MetadataCache::getIndexStatus(IND.RDB$INDEX_INACTIVE.NULL, IND.RDB$INDEX_INACTIVE))
+ {
+ case MET_index_active: // handle table creation error
+ case MET_index_deferred_drop: // drop index
+ ERASE IND;
+ deleteSegmentRecords(tdbb, transaction, indexName);
+ break;
+
+ case MET_index_inactive: // alter index inactive
+ MODIFY IND
+ IND.RDB$INDEX_ID = 0;
+ IND.RDB$INDEX_ID.NULL = TRUE;
+ END_MODIFY
+ break;
+
+ default:
+ fb_assert(false);
+ }
+ }
+ END_FOR
+
+ if (indexName.hasData())
+ {
+ MET_delete_dependencies(tdbb, indexName, obj_index_expression);
+ MET_delete_dependencies(tdbb, indexName, obj_index_condition);
+ }
+
+ if (partnerIdxName.hasData())
+ {
+ auto partner = getRelByIndex(tdbb, partnerIdxName, transaction);
+
+ if (rel && partner)
+ {
+ rel->checkPartners(tdbb);
+
+ if (rel != partner)
+ partner->checkPartners(tdbb);
+ }
+ }
}
@@ -11641,8 +13990,7 @@ void DropRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jr
ROL IN RDB$ROLES
WITH ROL.RDB$ROLE_NAME EQ name.c_str()
{
- executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_ROLE,
- QualifiedName(name), {});
+ executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_ROLE, QualifiedName(name), {});
if (ROL.RDB$SYSTEM_FLAG != 0)
{
@@ -13632,6 +15980,50 @@ 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;
+}
+
//----------------------
@@ -13950,7 +16342,7 @@ void DropSchemaNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
}
bool DropSchemaNode::collectObjects(thread_db* tdbb, jrd_tra* transaction,
- Array>* objects)
+ Array>* objects)
{
if (objects)
objects->clear();
@@ -14129,5 +16521,4 @@ bool DropSchemaNode::collectObjects(thread_db* tdbb, jrd_tra* transaction,
return objects && objects->hasData();
}
-
} // namespace Jrd
diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h
index ab73673a487..5d27c6da74e 100644
--- a/src/dsql/DdlNodes.h
+++ b/src/dsql/DdlNodes.h
@@ -445,6 +445,8 @@ class CreateAlterFunctionNode final : public DdlNode
void compile(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch);
void collectParameters(thread_db* tdbb, jrd_tra* transaction, CollectedParameterMap& items);
+ MetaId id = 0;
+
public:
QualifiedName name;
bool create;
@@ -590,6 +592,8 @@ class CreateAlterProcedureNode final : public DdlNode
void compile(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch);
void collectParameters(thread_db* tdbb, jrd_tra* transaction, CollectedParameterMap& items);
+ MetaId id = 0;
+
public:
QualifiedName name;
bool create;
@@ -888,8 +892,8 @@ class CreateCollationNode final : public DdlNode
private:
USHORT attributesOn;
USHORT attributesOff;
- USHORT forCharSetId;
- USHORT fromCollationId;
+ CSetId forCharSetId;
+ CollId fromCollationId;
};
@@ -1282,6 +1286,41 @@ typedef RecreateNode nodes;
+};
+
+
class RelationNode : public DdlNode
{
public:
@@ -1307,7 +1346,7 @@ class RelationNode : public DdlNode
QualifiedName fieldSource;
QualifiedName identitySequence;
std::optional identityType;
- std::optional collationId;
+ std::optional collationId;
Firebird::TriState notNullFlag; // true = NOT NULL / false = NULL
std::optional position;
Firebird::string defaultSource;
@@ -1601,6 +1640,11 @@ class RelationNode : public DdlNode
RelationNode(MemoryPool& p, RelationSourceNode* aDsqlNode);
+ static MetaId generateRelId(thread_db* tdbb, MetaName name);
+ static bool checkDeletedId(thread_db* tdbb, MetaId& relId);
+ static bool checkIdRange(thread_db* tdbb, MetaId& relId, const MetaId existingRelationId);
+ USHORT calcDbKeyLength(thread_db* tdbb);
+
static bool deleteLocalField(thread_db* tdbb, jrd_tra* transaction,
const QualifiedName& relationName, const MetaName& fieldName, bool silent,
std::function preChangeHandler = {});
@@ -1651,12 +1695,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, const QualifiedName& 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 QualifiedName& 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 QualifiedName& obj_name);
+
public:
NestConst dsqlNode;
QualifiedName name;
Firebird::Array > clauses;
Firebird::TriState ssDefiner;
Firebird::TriState replicationState;
+ ModifyIndexList indexList;
};
@@ -1845,6 +1907,40 @@ class RecreateViewNode final :
};
+// Performs 2-pass index create/drop
+
+class ModifyIndexNode
+{
+public:
+ ModifyIndexNode(const QualifiedName& indexName, bool create)
+ : indexName(indexName),
+ create(create)
+ {
+ }
+
+ virtual ~ModifyIndexNode()
+ {
+ }
+
+ bool modify(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction);
+
+ bool check(thread_db*, MetaName iName)
+ {
+ return create && (indexName.object == iName);
+ }
+
+ virtual bool exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) = 0;
+
+protected:
+ Firebird::string print(NodePrinter& printer) const;
+ static Cached::Relation* getRelByIndex(thread_db* tdbb, const QualifiedName& index, jrd_tra* transaction);
+
+public:
+ QualifiedName indexName;
+ bool create;
+};
+
+
class CreateIndexNode final : public DdlNode
{
public:
@@ -1859,6 +1955,7 @@ class CreateIndexNode final : public DdlNode
conditionSource.clear();
}
+ QualifiedName index;
QualifiedName relation;
Firebird::ObjectsArray columns;
Firebird::TriState unique;
@@ -1881,8 +1978,8 @@ class CreateIndexNode final : public DdlNode
}
public:
- static void store(thread_db* tdbb, jrd_tra* transaction, QualifiedName& name,
- const Definition& definition, QualifiedName* referredIndexName = nullptr);
+ static ModifyIndexNode* store(thread_db* tdbb, MemoryPool& p, jrd_tra* transaction, QualifiedName& name,
+ Definition& definition, QualifiedName* referredIndexName = nullptr);
public:
Firebird::string internalPrint(NodePrinter& printer) const override;
@@ -1909,13 +2006,31 @@ class CreateIndexNode final : public DdlNode
};
-class AlterIndexNode final : public DdlNode
+class StoreIndexNode final : public ModifyIndexNode
{
public:
- AlterIndexNode(MemoryPool& p, const QualifiedName& aName, bool aActive)
- : DdlNode(p),
- name(p, aName),
- active(aActive)
+ StoreIndexNode(const QualifiedName& indexName, bool expressionIndex)
+ : ModifyIndexNode(indexName, true),
+ expressionIndex(expressionIndex)
+ { }
+
+public:
+ bool 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 final : public ModifyIndexNode, public DdlNode
+{
+public:
+ AlterIndexNode(MemoryPool& p, const QualifiedName& name, bool active)
+ : ModifyIndexNode(name, active),
+ DdlNode(p)
{
}
@@ -1926,21 +2041,22 @@ class AlterIndexNode final : public DdlNode
DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override
{
- dsqlScratch->qualifyExistingName(name, obj_index);
- dsqlScratch->ddlSchema = name.schema;
+ dsqlScratch->qualifyExistingName(indexName, obj_index);
+ dsqlScratch->ddlSchema = indexName.schema;
return DdlNode::dsqlPass(dsqlScratch);
}
+ bool exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) override;
+
protected:
void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) override
{
- statusVector << Firebird::Arg::Gds(isc_dsql_alter_index_failed) << name.toQuotedString();
+ statusVector << Firebird::Arg::Gds(isc_dsql_alter_index_failed) << indexName.toQuotedString();
}
public:
- QualifiedName name;
- bool active;
+ std::optional idxId;
};
@@ -1978,38 +2094,42 @@ class SetStatisticsNode final : public DdlNode
};
-class DropIndexNode final : public DdlNode
+class DropIndexNode final : public ModifyIndexNode, public DdlNode
{
public:
- DropIndexNode(MemoryPool& p, const QualifiedName& aName)
- : DdlNode(p),
- name(p, aName)
- {
- }
+ DropIndexNode(MemoryPool& p, const QualifiedName& aName);
static bool deleteSegmentRecords(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name);
+ static void clearFrgn(thread_db* tdbb, MetaId relId, MetaId indexId);
+ static void clearId(thread_db* tdbb, MetaId relId, MetaId indexId);
public:
Firebird::string internalPrint(NodePrinter& printer) const override;
void checkPermission(thread_db* tdbb, jrd_tra* transaction) override;
void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) override;
+ Cached::Relation* drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction,
+ ModifyIndexList& list, bool runTriggers);
DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override
{
- dsqlScratch->qualifyExistingName(name, obj_index);
- dsqlScratch->ddlSchema = name.schema;
+ dsqlScratch->qualifyExistingName(indexName, obj_index);
+ dsqlScratch->ddlSchema = indexName.schema;
return DdlNode::dsqlPass(dsqlScratch);
}
+ bool exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) override;
+
protected:
void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) override
{
- statusVector << Firebird::Arg::Gds(isc_dsql_drop_index_failed) << name.toQuotedString();
+ statusVector << Firebird::Arg::Gds(isc_dsql_drop_index_failed) << indexName.toQuotedString();
}
+private:
+ MetaId idxId;
+
public:
- QualifiedName name;
bool silent = false;
};
@@ -2765,7 +2885,7 @@ class DropSchemaNode final : public DdlNode
private:
bool collectObjects(thread_db* tdbb, jrd_tra* transaction,
- Firebird::Array>* objects = nullptr);
+ Firebird::Array>* objects = nullptr);
public:
MetaName name;
diff --git a/src/dsql/DsqlBatch.cpp b/src/dsql/DsqlBatch.cpp
index 916d0dfe791..27b9b6c36d7 100644
--- a/src/dsql/DsqlBatch.cpp
+++ b/src/dsql/DsqlBatch.cpp
@@ -771,7 +771,7 @@ Firebird::IBatchCompletionState* DsqlBatch::execute(thread_db* tdbb)
if (m_blobMap.count())
{
DEB_BATCH(fprintf(stderr, "BLOBs %d were not used in messages\n", m_blobMap.count()));
- ERR_post_warning(Arg::Warning(isc_random) << "m_blobMap.count() BLOBs were not used in messages"); // !!!!!!! new warning
+ ERR_post_warning(Arg::Warning(isc_random) << "m_blobMap.count() BLOBs were not used in messages"); // !!!!!! new warning
}
// reset to initial state
diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp
index dc778faff07..42bf5db17a7 100644
--- a/src/dsql/DsqlCompilerScratch.cpp
+++ b/src/dsql/DsqlCompilerScratch.cpp
@@ -1170,3 +1170,19 @@ 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 68854524f46..ad569f69d19 100644
--- a/src/dsql/DsqlCompilerScratch.h
+++ b/src/dsql/DsqlCompilerScratch.h
@@ -106,7 +106,10 @@ class DsqlCompilerScratch : public BlrDebugWriter
ctes(p),
cteAliases(p),
subFunctions(p),
- subProcedures(p)
+ subProcedures(p),
+ rels(p),
+ procedures(p),
+ functions(p)
{
}
@@ -218,21 +221,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()
{
@@ -360,6 +349,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 01d9b246c9a..f64d0649d24 100644
--- a/src/dsql/DsqlRequests.cpp
+++ b/src/dsql/DsqlRequests.cpp
@@ -275,13 +275,13 @@ DsqlDmlRequest::DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb,
Request* request = parentRequest->getRequest();
fb_assert(request->req_rpb.getCount() > 0 && request->req_rpb[0].rpb_relation != nullptr);
- const auto& relName = request->req_rpb[0].rpb_relation->rel_name;
+ const auto& relName = request->req_rpb[0].rpb_relation->getName();
bool found = false;
for (FB_SIZE_T i = 0; i < request->req_rpb.getCount(); ++i)
{
jrd_rel* relation = request->req_rpb[i].rpb_relation;
- if (relation && relation->rel_name == relName)
+ if (relation && relation->getName() == relName)
{
if (found)
{
@@ -855,7 +855,7 @@ void DsqlDmlRequest::metadataToFormat(Firebird::IMessageMetadata* meta, const ds
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) meta->getOffset(&st, index);
@@ -940,7 +940,7 @@ void DsqlDmlRequest::gatherRecordKey(RecordKey* buffer) const
if (rpb.rpb_relation && rpb.rpb_number.isValid() && !rpb.rpb_number.isBof())
{
buffer[i].recordNumber.bid_encode(rpb.rpb_number.getValue() + 1);
- buffer[i].recordNumber.bid_relation_id = rpb.rpb_relation->rel_id;
+ buffer[i].recordNumber.bid_relation_id = rpb.rpb_relation->getId();
buffer[i].recordVersion = rpb.rpb_transaction_nr;
}
}
@@ -974,7 +974,7 @@ void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true);
//// Doing it in DFW_perform_work to avoid problems with DDL+DML in the same transaction.
- ///req_dbb->dbb_attachment->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb);
+ /// req_dbb->dbb_attachment->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb);
node->executeDdl(tdbb, internalScratch, req_transaction);
diff --git a/src/dsql/DsqlStatementCache.cpp b/src/dsql/DsqlStatementCache.cpp
index 5d50e5cfab1..8e769132b2d 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 2601077f574..ef53b3b3122 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)
{
@@ -397,14 +398,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;
}
@@ -3517,8 +3518,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);
}
@@ -3679,14 +3680,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;
}
@@ -4027,15 +4025,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)
@@ -4624,14 +4617,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;
@@ -4796,14 +4789,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;
@@ -5128,8 +5121,8 @@ 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 dependency(obj_relation);
+ dependency.relation = MetadataCache::lookupRelation(tdbb, relationName, CacheFlag::AUTOCREATE);
dependency.subName = FB_NEW_POOL(pool) MetaName(fieldName);
csb->addDependency(dependency);
}
@@ -5138,7 +5131,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)
{
@@ -5399,7 +5392,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)
@@ -6046,20 +6039,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)
{
@@ -6078,15 +6058,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)
@@ -6099,20 +6074,20 @@ 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) <<
name.toQuotedString() <<
- relation->rel_name.toQuotedString());
+ relation->getName().toQuotedString());
}
- else if (!(relation->rel_flags & REL_deleted))
+ else if (!relation->getPermanent()->isDropped())
{
PAR_error(csb, Arg::Gds(isc_fldnotdef) <<
name.toQuotedString() <<
- relation->rel_name.toQuotedString());
+ relation->getName().toQuotedString());
}
else
PAR_error(csb, Arg::Gds(isc_ctxnotdef));
@@ -6135,7 +6110,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)
{
@@ -6144,7 +6119,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();
}
}
@@ -6769,9 +6744,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;
@@ -6812,13 +6787,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);
@@ -6828,17 +6803,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&)
{
@@ -6847,9 +6820,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
@@ -6880,21 +6850,21 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb)
privilege = SCL_delete;
}
- const SLONG ssRelationId = tail->csb_view ?
- tail->csb_view->rel_id : (csb->csb_view ? csb->csb_view->rel_id : 0);
+ const SLONG ssRelationId = tail->csb_view ? tail->csb_view()->getId() :
+ csb->csb_view ? csb->csb_view()->getId() : 0;
- CMP_post_access(tdbb, csb, relation->rel_security_name.schema, ssRelationId,
- SCL_usage, obj_schemas, QualifiedName(relation->rel_name.schema));
+ CMP_post_access(tdbb, csb, relation()->rel_security_name.schema, ssRelationId,
+ SCL_usage, obj_schemas, QualifiedName(relation()->getName().schema));
- CMP_post_access(tdbb, csb, relation->rel_security_name.object, ssRelationId,
- privilege, obj_relations, relation->rel_name);
+ CMP_post_access(tdbb, csb, relation()->rel_security_name.object, 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, relation->rel_name, field->fld_name);
+ privilege, obj_column, relation()->getName(), field->fld_name);
}
}
@@ -6902,7 +6872,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);
@@ -6911,7 +6881,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) <<
field->fld_name.toQuotedString() <<
- relation->rel_name.toQuotedString());
+ relation()->getName().toQuotedString());
}
// The previous test below is an apparent temporary fix
@@ -6927,7 +6897,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);
@@ -6955,14 +6925,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) <<
- (relation->rel_name.toQuotedString() + "." + field->fld_name.toQuotedString()));
+ (relation()->getName().toQuotedString() + "." + field->fld_name.toQuotedString()));
}
FB_SIZE_T pos;
@@ -6984,15 +6954,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