Added tests

This commit is contained in:
Maksim Kita 2021-06-12 13:53:03 +03:00
parent 0bd199eb50
commit 2a016f52e9
19 changed files with 559 additions and 326 deletions

View File

@ -7,6 +7,8 @@
#include <Columns/ColumnString.h>
#include <Columns/ColumnVector.h>
#include <Columns/ColumnArray.h>
#include <Columns/ColumnsNumber.h>
#include <Columns/ColumnNullable.h>
#include <DataStreams/IBlockInputStream.h>
#include <DataTypes/DataTypesDecimal.h>
#include <DataTypes/DataTypeArray.h>
@ -284,6 +286,8 @@ public:
*
* If default_values_column is null then attribute_default_value will be used.
* If default_values_column is not null in constructor than this column values will be used as default values.
*
* For nullable dictionary attribute isNullAt method is provided.
*/
template <typename DictionaryAttributeType>
class DictionaryDefaultValueExtractor
@ -293,23 +297,41 @@ class DictionaryDefaultValueExtractor
public:
using DefaultValueType = DictionaryValueType<DictionaryAttributeType>;
explicit DictionaryDefaultValueExtractor(DictionaryAttributeType attribute_default_value, ColumnPtr default_values_column_ = nullptr)
: default_value(std::move(attribute_default_value))
explicit DictionaryDefaultValueExtractor(
Field attribute_default_value,
ColumnPtr default_values_column_)
{
if (default_values_column_ != nullptr &&
isColumnConst(*default_values_column_))
{
attribute_default_value = (*default_values_column_)[0];
default_values_column_ = nullptr;
}
if (default_values_column_ == nullptr)
{
use_attribute_default_value = true;
if (attribute_default_value.isNull())
default_value_is_null = true;
else
default_value = attribute_default_value.get<NearestFieldType<DictionaryAttributeType>>();
}
else
{
if (const auto * const default_col = checkAndGetColumn<DefaultColumnType>(*default_values_column_))
const IColumn * default_values_column_ptr = default_values_column_.get();
if (const ColumnNullable * nullable_column = typeid_cast<const ColumnNullable *>(default_values_column_.get()))
{
default_values_column_ptr = nullable_column->getNestedColumnPtr().get();
is_null_map = &nullable_column->getNullMapColumn();
}
if (const auto * const default_col = checkAndGetColumn<DefaultColumnType>(default_values_column_ptr))
{
default_values_column = default_col;
use_attribute_default_value = false;
}
else if (const auto * const default_col_const = checkAndGetColumnConst<DefaultColumnType>(default_values_column_.get()))
{
default_value = default_col_const->template getValue<DictionaryAttributeType>();
use_attribute_default_value = true;
}
else
throw Exception(ErrorCodes::TYPE_MISMATCH, "Type of default column is not the same as dictionary attribute type.");
}
@ -332,10 +354,24 @@ public:
else
return default_values_column->getData()[row];
}
bool isNullAt(size_t row)
{
if (default_value_is_null)
return true;
if (is_null_map)
return is_null_map->getData()[row];
return false;
}
private:
DictionaryAttributeType default_value;
DictionaryAttributeType default_value {};
const DefaultColumnType * default_values_column = nullptr;
const ColumnUInt8 * is_null_map = nullptr;
bool use_attribute_default_value = false;
bool default_value_is_null = false;
};
template <DictionaryKeyType key_type>

View File

@ -373,6 +373,7 @@ std::vector<DictionaryAttribute> DictionaryStructure::getAttributes(
const auto type_string = config.getString(prefix + "type");
const auto initial_type = DataTypeFactory::instance().get(type_string);
const auto initial_type_serialization = initial_type->getDefaultSerialization();
bool is_nullable = initial_type->isNullable();
auto non_nullable_type = removeNullable(initial_type);
@ -385,20 +386,19 @@ std::vector<DictionaryAttribute> DictionaryStructure::getAttributes(
Field null_value;
if (allow_null_values)
{
/// TODO: Fix serialization for nullable type.
const auto null_value_string = config.getString(prefix + "null_value");
try
{
if (null_value_string.empty())
{
null_value = non_nullable_type->getDefault();
null_value = initial_type->getDefault();
}
else
{
ReadBufferFromString null_value_buffer{null_value_string};
auto column_with_null_value = non_nullable_type->createColumn();
non_nullable_type->getDefaultSerialization()->deserializeTextEscaped(*column_with_null_value, null_value_buffer, format_settings);
auto column_with_null_value = initial_type->createColumn();
initial_type_serialization->deserializeWholeText(*column_with_null_value, null_value_buffer, format_settings);
null_value = (*column_with_null_value)[0];
}
}
@ -428,6 +428,7 @@ std::vector<DictionaryAttribute> DictionaryStructure::getAttributes(
name,
underlying_type,
initial_type,
initial_type_serialization,
expression,
null_value,
hierarchical,

View File

@ -75,6 +75,7 @@ struct DictionaryAttribute final
const std::string name;
const AttributeUnderlyingType underlying_type;
const DataTypePtr type;
const SerializationPtr type_serialization;
const std::string expression;
const Field null_value;
const bool hierarchical;

View File

@ -358,8 +358,7 @@ void ExternalQueryBuilder::composeKeyCondition(const Columns & key_columns, cons
/// key_i=value_i
writeQuoted(key_description.name, out);
writeString("=", out);
auto serialization = key_description.type->getDefaultSerialization();
serialization->serializeTextQuoted(*key_columns[i], row, out, format_settings);
key_description.type_serialization->serializeTextQuoted(*key_columns[i], row, out, format_settings);
}
}
@ -416,7 +415,7 @@ void ExternalQueryBuilder::composeKeyTuple(const Columns & key_columns, const si
writeString(", ", out);
first = false;
auto serialization = (*dict_struct.key)[i].type->getDefaultSerialization();
auto serialization = (*dict_struct.key)[i].type_serialization;
serialization->serializeTextQuoted(*key_columns[i], row, out, format_settings);
}

View File

@ -63,6 +63,16 @@ ColumnPtr FlatDictionary::getColumn(
size_t attribute_index = dict_struct.attribute_name_to_index.find(attribute_name)->second;
const auto & attribute = attributes[attribute_index];
bool is_attribute_nullable = attribute.is_nullable_set.has_value();
ColumnUInt8::MutablePtr col_null_map_to;
ColumnUInt8::Container * vec_null_map_to = nullptr;
if (is_attribute_nullable)
{
col_null_map_to = ColumnUInt8::create(size, false);
vec_null_map_to = &col_null_map_to->getData();
}
auto type_call = [&](const auto & dictionary_attribute_type)
{
using Type = std::decay_t<decltype(dictionary_attribute_type)>;
@ -70,9 +80,7 @@ ColumnPtr FlatDictionary::getColumn(
using ValueType = DictionaryValueType<AttributeType>;
using ColumnProvider = DictionaryAttributeColumnProvider<AttributeType>;
const auto & attribute_null_value = std::get<ValueType>(attribute.null_values);
AttributeType null_value = static_cast<AttributeType>(attribute_null_value);
DictionaryDefaultValueExtractor<AttributeType> default_value_extractor(std::move(null_value), default_values_column);
DictionaryDefaultValueExtractor<AttributeType> default_value_extractor(dictionary_attribute.null_value, default_values_column);
auto column = ColumnProvider::getColumn(dictionary_attribute, size);
@ -80,31 +88,53 @@ ColumnPtr FlatDictionary::getColumn(
{
auto * out = column.get();
getItemsImpl<ValueType>(
getItemsImpl<ValueType, false>(
attribute,
ids,
[&](const size_t, const Array & value) { out->insert(value); },
[&](size_t, const Array & value, bool) { out->insert(value); },
default_value_extractor);
}
else if constexpr (std::is_same_v<ValueType, StringRef>)
{
auto * out = column.get();
getItemsImpl<ValueType>(
attribute,
ids,
[&](const size_t, const StringRef value) { out->insertData(value.data, value.size); },
default_value_extractor);
if (is_attribute_nullable)
getItemsImpl<ValueType, true>(
attribute,
ids,
[&](size_t row, const StringRef value, bool is_null)
{
(*vec_null_map_to)[row] = is_null;
out->insertData(value.data, value.size);
},
default_value_extractor);
else
getItemsImpl<ValueType, false>(
attribute,
ids,
[&](size_t, const StringRef value, bool) { out->insertData(value.data, value.size); },
default_value_extractor);
}
else
{
auto & out = column->getData();
getItemsImpl<ValueType>(
attribute,
ids,
[&](const size_t row, const auto value) { out[row] = value; },
default_value_extractor);
if (is_attribute_nullable)
getItemsImpl<ValueType, true>(
attribute,
ids,
[&](size_t row, const auto value, bool is_null)
{
(*vec_null_map_to)[row] = is_null;
out[row] = value;
},
default_value_extractor);
else
getItemsImpl<ValueType, false>(
attribute,
ids,
[&](size_t row, const auto value, bool) { out[row] = value; },
default_value_extractor);
}
result = std::move(column);
@ -112,21 +142,8 @@ ColumnPtr FlatDictionary::getColumn(
callOnDictionaryAttributeType(attribute.type, type_call);
if (attribute.nullable_set)
{
ColumnUInt8::MutablePtr col_null_map_to = ColumnUInt8::create(size, false);
ColumnUInt8::Container & vec_null_map_to = col_null_map_to->getData();
for (size_t row = 0; row < ids.size(); ++row)
{
auto id = ids[row];
if (attribute.nullable_set->find(id) != nullptr)
vec_null_map_to[row] = true;
}
result = ColumnNullable::create(result, std::move(col_null_map_to));
}
if (attribute.is_nullable_set)
result = ColumnNullable::create(std::move(result), std::move(col_null_map_to));
return result;
}
@ -161,9 +178,10 @@ ColumnPtr FlatDictionary::getHierarchy(ColumnPtr key_column, const DataTypePtr &
const auto & keys = getColumnVectorData(this, key_column, keys_backup_storage);
size_t hierarchical_attribute_index = *dict_struct.hierarchical_attribute_index;
const auto & dictionary_attribute = dict_struct.attributes[hierarchical_attribute_index];
const auto & hierarchical_attribute = attributes[hierarchical_attribute_index];
const UInt64 null_value = std::get<UInt64>(hierarchical_attribute.null_values);
const UInt64 null_value = dictionary_attribute.null_value.get<UInt64>();
const ContainerType<UInt64> & parent_keys = std::get<ContainerType<UInt64>>(hierarchical_attribute.container);
auto is_key_valid_func = [&, this](auto & key) { return key < loaded_keys.size() && loaded_keys[key]; };
@ -198,9 +216,10 @@ ColumnUInt8::Ptr FlatDictionary::isInHierarchy(
const auto & keys_in = getColumnVectorData(this, in_key_column, keys_in_backup_storage);
size_t hierarchical_attribute_index = *dict_struct.hierarchical_attribute_index;
const auto & dictionary_attribute = dict_struct.attributes[hierarchical_attribute_index];
const auto & hierarchical_attribute = attributes[hierarchical_attribute_index];
const UInt64 null_value = std::get<UInt64>(hierarchical_attribute.null_values);
const UInt64 null_value = dictionary_attribute.null_value.get<UInt64>();
const ContainerType<UInt64> & parent_keys = std::get<ContainerType<UInt64>>(hierarchical_attribute.container);
auto is_key_valid_func = [&, this](auto & key) { return key < loaded_keys.size() && loaded_keys[key]; };
@ -260,7 +279,7 @@ void FlatDictionary::createAttributes()
attributes.reserve(size);
for (const auto & attribute : dict_struct.attributes)
attributes.push_back(createAttribute(attribute, attribute.null_value));
attributes.push_back(createAttribute(attribute));
}
void FlatDictionary::blockToAttributes(const Block & block)
@ -388,10 +407,10 @@ void FlatDictionary::calculateBytesAllocated()
bytes_allocated += update_field_loaded_block->allocatedBytes();
}
FlatDictionary::Attribute FlatDictionary::createAttribute(const DictionaryAttribute & dictionary_attribute, const Field & null_value)
FlatDictionary::Attribute FlatDictionary::createAttribute(const DictionaryAttribute & dictionary_attribute)
{
auto nullable_set = dictionary_attribute.is_nullable ? std::make_optional<NullableSet>() : std::optional<NullableSet>{};
Attribute attribute{dictionary_attribute.underlying_type, std::move(nullable_set), {}, {}, {}};
auto is_nullable_set = dictionary_attribute.is_nullable ? std::make_optional<NullableSet>() : std::optional<NullableSet>{};
Attribute attribute{dictionary_attribute.underlying_type, std::move(is_nullable_set), {}, {}};
auto type_call = [&](const auto & dictionary_attribute_type)
{
@ -400,17 +419,9 @@ FlatDictionary::Attribute FlatDictionary::createAttribute(const DictionaryAttrib
using ValueType = DictionaryValueType<AttributeType>;
if constexpr (std::is_same_v<ValueType, StringRef>)
{
attribute.string_arena = std::make_unique<Arena>();
const String & string = null_value.get<String>();
const char * string_in_arena = attribute.string_arena->insert(string.data(), string.size());
attribute.null_values.emplace<StringRef>(string_in_arena, string.size());
}
else
attribute.null_values = ValueType(null_value.get<NearestFieldType<ValueType>>());
const auto & null_value_ref = std::get<ValueType>(attribute.null_values);
attribute.container.emplace<ContainerType<ValueType>>(configuration.initial_array_size, null_value_ref);
attribute.container.emplace<ContainerType<ValueType>>(configuration.initial_array_size, ValueType());
};
callOnDictionaryAttributeType(dictionary_attribute.underlying_type, type_call);
@ -418,7 +429,7 @@ FlatDictionary::Attribute FlatDictionary::createAttribute(const DictionaryAttrib
return attribute;
}
template <typename AttributeType, typename ValueSetter, typename DefaultValueExtractor>
template <typename AttributeType, bool is_nullable, typename ValueSetter, typename DefaultValueExtractor>
void FlatDictionary::getItemsImpl(
const Attribute & attribute,
const PaddedPODArray<UInt64> & keys,
@ -436,11 +447,20 @@ void FlatDictionary::getItemsImpl(
if (key < loaded_keys.size() && loaded_keys[key])
{
set_value(row, container[key]);
if constexpr (is_nullable)
set_value(row, container[key], attribute.is_nullable_set->find(key) != nullptr);
else
set_value(row, container[key], false);
++keys_found;
}
else
set_value(row, default_value_extractor[row]);
{
if constexpr (is_nullable)
set_value(row, default_value_extractor[row], default_value_extractor.isNullAt(row));
else
set_value(row, default_value_extractor[row], false);
}
}
query_count.fetch_add(rows, std::memory_order_relaxed);
@ -464,9 +484,9 @@ void FlatDictionary::resize(Attribute & attribute, UInt64 key)
loaded_keys.resize(elements_count, false);
if constexpr (std::is_same_v<T, Array>)
container.resize(elements_count, std::get<T>(attribute.null_values));
container.resize(elements_count, T{});
else
container.resize_fill(elements_count, std::get<T>(attribute.null_values));
container.resize_fill(elements_count, T{});
}
}
@ -495,11 +515,11 @@ void FlatDictionary::setAttributeValue(Attribute & attribute, const UInt64 key,
resize<ValueType>(attribute, key);
if (attribute.nullable_set)
if (attribute.is_nullable_set)
{
if (value.isNull())
{
attribute.nullable_set->insert(key);
attribute.is_nullable_set->insert(key);
loaded_keys[key] = true;
return;
}

View File

@ -5,16 +5,14 @@
#include <vector>
#include <optional>
#include <Common/HashTable/HashSet.h>
#include <Common/Arena.h>
#include <Columns/ColumnDecimal.h>
#include <Columns/ColumnString.h>
#include <Columns/ColumnArray.h>
#include <DataTypes/IDataType.h>
#include <Core/Block.h>
#include <ext/range.h>
#include <ext/size.h>
#include <Common/HashTable/HashSet.h>
#include <Common/Arena.h>
#include <DataTypes/IDataType.h>
#include <Core/Block.h>
#include "DictionaryStructure.h"
#include "IDictionary.h"
#include "IDictionarySource.h"
@ -113,31 +111,7 @@ private:
struct Attribute final
{
AttributeUnderlyingType type;
std::optional<NullableSet> nullable_set;
std::variant<
UInt8,
UInt16,
UInt32,
UInt64,
UInt128,
UInt256,
Int8,
Int16,
Int32,
Int64,
Int128,
Int256,
Decimal32,
Decimal64,
Decimal128,
Decimal256,
Float32,
Float64,
UUID,
StringRef,
Array>
null_values;
std::optional<NullableSet> is_nullable_set;
std::variant<
ContainerType<UInt8>,
@ -173,9 +147,9 @@ private:
void calculateBytesAllocated();
Attribute createAttribute(const DictionaryAttribute& attribute, const Field & null_value);
Attribute createAttribute(const DictionaryAttribute & attribute);
template <typename AttributeType, typename ValueSetter, typename DefaultValueExtractor>
template <typename AttributeType, bool is_nullable, typename ValueSetter, typename DefaultValueExtractor>
void getItemsImpl(
const Attribute & attribute,
const PaddedPODArray<UInt64> & keys,

View File

@ -75,6 +75,8 @@ ColumnPtr HashedDictionary<dictionary_key_type, sparse>::getColumn(
const size_t attribute_index = dict_struct.attribute_name_to_index.find(attribute_name)->second;
auto & attribute = attributes[attribute_index];
bool is_attribute_nullable = attribute.is_nullable_set.has_value();
ColumnUInt8::MutablePtr col_null_map_to;
ColumnUInt8::Container * vec_null_map_to = nullptr;
if (attribute.is_nullable_set)
@ -90,9 +92,7 @@ ColumnPtr HashedDictionary<dictionary_key_type, sparse>::getColumn(
using ValueType = DictionaryValueType<AttributeType>;
using ColumnProvider = DictionaryAttributeColumnProvider<AttributeType>;
const auto & attribute_null_value = std::get<ValueType>(attribute.null_values);
AttributeType null_value = static_cast<AttributeType>(attribute_null_value);
DictionaryDefaultValueExtractor<AttributeType> default_value_extractor(std::move(null_value), default_values_column);
DictionaryDefaultValueExtractor<AttributeType> default_value_extractor(dictionary_attribute.null_value, default_values_column);
auto column = ColumnProvider::getColumn(dictionary_attribute, size);
@ -100,44 +100,53 @@ ColumnPtr HashedDictionary<dictionary_key_type, sparse>::getColumn(
{
auto * out = column.get();
getItemsImpl<ValueType>(
getItemsImpl<ValueType, false>(
attribute,
extractor,
[&](const size_t, const Array & value) { out->insert(value); },
[&](const size_t)
{
},
[&](const size_t, const Array & value, bool) { out->insert(value); },
default_value_extractor);
}
else if constexpr (std::is_same_v<ValueType, StringRef>)
{
auto * out = column.get();
getItemsImpl<ValueType>(
attribute,
extractor,
[&](const size_t, const StringRef value) { out->insertData(value.data, value.size); },
[&](const size_t row)
{
out->insertDefault();
(*vec_null_map_to)[row] = true;
},
default_value_extractor);
if (is_attribute_nullable)
getItemsImpl<ValueType, true>(
attribute,
extractor,
[&](size_t row, const StringRef value, bool is_null)
{
(*vec_null_map_to)[row] = is_null;
out->insertData(value.data, value.size);
},
default_value_extractor);
else
getItemsImpl<ValueType, false>(
attribute,
extractor,
[&](size_t, const StringRef value, bool) { out->insertData(value.data, value.size); },
default_value_extractor);
}
else
{
auto & out = column->getData();
getItemsImpl<ValueType>(
attribute,
extractor,
[&](const size_t row, const auto value) { return out[row] = value; },
[&](const size_t row)
{
out[row] = ValueType();
(*vec_null_map_to)[row] = true;
},
default_value_extractor);
if (is_attribute_nullable)
getItemsImpl<ValueType, true>(
attribute,
extractor,
[&](size_t row, const auto value, bool is_null)
{
(*vec_null_map_to)[row] = is_null;
out[row] = value;
},
default_value_extractor);
else
getItemsImpl<ValueType, false>(
attribute,
extractor,
[&](size_t row, const auto value, bool) { out[row] = value; },
default_value_extractor);
}
result = std::move(column);
@ -145,7 +154,7 @@ ColumnPtr HashedDictionary<dictionary_key_type, sparse>::getColumn(
callOnDictionaryAttributeType(attribute.type, type_call);
if (attribute.is_nullable_set)
if (is_attribute_nullable)
result = ColumnNullable::create(result, std::move(col_null_map_to));
return result;
@ -343,23 +352,7 @@ void HashedDictionary<dictionary_key_type, sparse>::createAttributes()
auto is_nullable_set = dictionary_attribute.is_nullable ? std::make_optional<NullableSet>() : std::optional<NullableSet>{};
std::unique_ptr<Arena> string_arena = std::is_same_v<AttributeType, String> ? std::make_unique<Arena>() : nullptr;
ValueType default_value;
if constexpr (std::is_same_v<ValueType, StringRef>)
{
string_arena = std::make_unique<Arena>();
const auto & string_null_value = dictionary_attribute.null_value.template get<String>();
const size_t string_null_value_size = string_null_value.size();
const char * string_in_arena = string_arena->insert(string_null_value.data(), string_null_value_size);
default_value = {string_in_arena, string_null_value_size};
}
else
default_value = dictionary_attribute.null_value.template get<NearestFieldType<ValueType>>();
Attribute attribute{dictionary_attribute.underlying_type, std::move(is_nullable_set), default_value, CollectionType<ValueType>(), std::move(string_arena)};
Attribute attribute{dictionary_attribute.underlying_type, std::move(is_nullable_set), CollectionType<ValueType>(), std::move(string_arena)};
attributes.emplace_back(std::move(attribute));
};
@ -509,19 +502,16 @@ void HashedDictionary<dictionary_key_type, sparse>::resize(size_t added_rows)
}
template <DictionaryKeyType dictionary_key_type, bool sparse>
template <typename AttributeType, typename ValueSetter, typename NullableValueSetter, typename DefaultValueExtractor>
template <typename AttributeType, bool is_nullable, typename ValueSetter, typename DefaultValueExtractor>
void HashedDictionary<dictionary_key_type, sparse>::getItemsImpl(
const Attribute & attribute,
DictionaryKeysExtractor<dictionary_key_type> & keys_extractor,
ValueSetter && set_value [[maybe_unused]],
NullableValueSetter && set_nullable_value [[maybe_unused]],
DefaultValueExtractor & default_value_extractor) const
{
const auto & attribute_container = std::get<CollectionType<AttributeType>>(attribute.container);
const size_t keys_size = keys_extractor.getKeysSize();
bool is_attribute_nullable = attribute.is_nullable_set.has_value();
size_t keys_found = 0;
for (size_t key_index = 0; key_index < keys_size; ++key_index)
@ -532,15 +522,15 @@ void HashedDictionary<dictionary_key_type, sparse>::getItemsImpl(
if (it != attribute_container.end())
{
set_value(key_index, getValueFromCell(it));
set_value(key_index, getValueFromCell(it), false);
++keys_found;
}
else
{
if (is_attribute_nullable && attribute.is_nullable_set->find(key) != nullptr)
set_nullable_value(key_index);
if constexpr (is_nullable)
set_value(key_index, default_value_extractor[key_index], default_value_extractor.isNullAt(key_index));
else
set_value(key_index, default_value_extractor[key_index]);
set_value(key_index, default_value_extractor[key_index], false);
}
keys_extractor.rollbackCurrentKey();

View File

@ -12,9 +12,6 @@
#include <Common/HashTable/HashSet.h>
#include <Core/Block.h>
#include <Columns/ColumnDecimal.h>
#include <Columns/ColumnString.h>
#include <Dictionaries/DictionaryStructure.h>
#include <Dictionaries/IDictionary.h>
#include <Dictionaries/IDictionarySource.h>
@ -56,7 +53,7 @@ public:
else if constexpr (dictionary_key_type == DictionaryKeyType::simple && !sparse)
return "Hashed";
else if constexpr (dictionary_key_type == DictionaryKeyType::complex && sparse)
return "ComplexKeySpareseHashed";
return "ComplexKeySparseHashed";
else
return "ComplexKeyHashed";
}
@ -153,30 +150,6 @@ private:
AttributeUnderlyingType type;
std::optional<NullableSet> is_nullable_set;
std::variant<
UInt8,
UInt16,
UInt32,
UInt64,
UInt128,
UInt256,
Int8,
Int16,
Int32,
Int64,
Int128,
Int256,
Decimal32,
Decimal64,
Decimal128,
Decimal256,
Float32,
Float64,
UUID,
StringRef,
Array>
null_values;
std::variant<
CollectionType<UInt8>,
CollectionType<UInt16>,
@ -214,12 +187,11 @@ private:
void calculateBytesAllocated();
template <typename AttributeType, typename ValueSetter, typename NullableValueSetter, typename DefaultValueExtractor>
template <typename AttributeType, bool is_nullable, typename ValueSetter, typename DefaultValueExtractor>
void getItemsImpl(
const Attribute & attribute,
DictionaryKeysExtractor<dictionary_key_type> & keys_extractor,
ValueSetter && set_value,
NullableValueSetter && set_nullable_value,
DefaultValueExtractor & default_value_extractor) const;
template <typename GetContainerFunc>

View File

@ -205,7 +205,6 @@ IPAddressDictionary::IPAddressDictionary(
, logger(&Poco::Logger::get("IPAddressDictionary"))
{
createAttributes();
loadData();
calculateBytesAllocated();
}

View File

@ -110,9 +110,11 @@ ColumnPtr RangeHashedDictionary::getColumn(
auto range_column_storage_type = std::make_shared<DataTypeInt64>();
modified_key_columns[1] = castColumnAccurate(column_to_cast, range_column_storage_type);
bool is_attribute_nullable = attribute.is_nullable;
ColumnUInt8::MutablePtr col_null_map_to;
ColumnUInt8::Container * vec_null_map_to = nullptr;
if (attribute.is_nullable)
if (is_attribute_nullable)
{
col_null_map_to = ColumnUInt8::create(keys_size, false);
vec_null_map_to = &col_null_map_to->getData();
@ -125,9 +127,7 @@ ColumnPtr RangeHashedDictionary::getColumn(
using ValueType = DictionaryValueType<AttributeType>;
using ColumnProvider = DictionaryAttributeColumnProvider<AttributeType>;
const auto & attribute_null_value = std::get<ValueType>(attribute.null_values);
AttributeType null_value = static_cast<AttributeType>(attribute_null_value);
DictionaryDefaultValueExtractor<AttributeType> default_value_extractor(std::move(null_value), default_values_column);
DictionaryDefaultValueExtractor<AttributeType> default_value_extractor(dictionary_attribute.null_value, default_values_column);
auto column = ColumnProvider::getColumn(dictionary_attribute, keys_size);
@ -135,10 +135,10 @@ ColumnPtr RangeHashedDictionary::getColumn(
{
auto * out = column.get();
getItemsImpl<ValueType>(
getItemsImpl<ValueType, false>(
attribute,
modified_key_columns,
[&](const size_t, const Array & value, bool)
[&](size_t, const Array & value, bool)
{
out->insert(value);
},
@ -148,33 +148,49 @@ ColumnPtr RangeHashedDictionary::getColumn(
{
auto * out = column.get();
getItemsImpl<ValueType>(
attribute,
modified_key_columns,
[&](const size_t row, const StringRef value, bool is_null)
{
if (attribute.is_nullable)
if (is_attribute_nullable)
getItemsImpl<ValueType, true>(
attribute,
modified_key_columns,
[&](size_t row, const StringRef value, bool is_null)
{
(*vec_null_map_to)[row] = is_null;
out->insertData(value.data, value.size);
},
default_value_extractor);
out->insertData(value.data, value.size);
},
default_value_extractor);
else
getItemsImpl<ValueType, false>(
attribute,
modified_key_columns,
[&](size_t, const StringRef value, bool)
{
out->insertData(value.data, value.size);
},
default_value_extractor);
}
else
{
auto & out = column->getData();
getItemsImpl<ValueType>(
attribute,
modified_key_columns,
[&](const size_t row, const auto value, bool is_null)
{
if (attribute.is_nullable)
if (is_attribute_nullable)
getItemsImpl<ValueType, true>(
attribute,
modified_key_columns,
[&](size_t row, const auto value, bool is_null)
{
(*vec_null_map_to)[row] = is_null;
out[row] = value;
},
default_value_extractor);
out[row] = value;
},
default_value_extractor);
else
getItemsImpl<ValueType, false>(
attribute,
modified_key_columns,
[&](size_t row, const auto value, bool)
{
out[row] = value;
},
default_value_extractor);
}
result = std::move(column);
@ -182,10 +198,8 @@ ColumnPtr RangeHashedDictionary::getColumn(
callOnDictionaryAttributeType(attribute.type, type_call);
if (attribute.is_nullable)
{
if (is_attribute_nullable)
result = ColumnNullable::create(result, std::move(col_null_map_to));
}
return result;
}
@ -274,7 +288,7 @@ void RangeHashedDictionary::createAttributes()
for (const auto & attribute : dict_struct.attributes)
{
attribute_index_by_name.emplace(attribute.name, attributes.size());
attributes.push_back(createAttribute(attribute, attribute.null_value));
attributes.push_back(createAttribute(attribute));
if (attribute.hierarchical)
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Hierarchical attributes not supported by {} dictionary.",
@ -364,41 +378,28 @@ void RangeHashedDictionary::calculateBytesAllocated()
}
}
template <typename T>
void RangeHashedDictionary::createAttributeImpl(Attribute & attribute, const Field & null_value)
RangeHashedDictionary::Attribute RangeHashedDictionary::createAttribute(const DictionaryAttribute & dictionary_attribute)
{
attribute.null_values = T(null_value.get<T>());
attribute.maps = std::make_unique<Collection<T>>();
}
template <>
void RangeHashedDictionary::createAttributeImpl<String>(Attribute & attribute, const Field & null_value)
{
attribute.string_arena = std::make_unique<Arena>();
const String & string = null_value.get<String>();
const char * string_in_arena = attribute.string_arena->insert(string.data(), string.size());
attribute.null_values.emplace<StringRef>(string_in_arena, string.size());
attribute.maps = std::make_unique<Collection<StringRef>>();
}
RangeHashedDictionary::Attribute
RangeHashedDictionary::createAttribute(const DictionaryAttribute & attribute, const Field & null_value)
{
Attribute attr{attribute.underlying_type, attribute.is_nullable, {}, {}, {}};
Attribute attribute{dictionary_attribute.underlying_type, dictionary_attribute.is_nullable, {}, {}};
auto type_call = [&](const auto &dictionary_attribute_type)
{
using Type = std::decay_t<decltype(dictionary_attribute_type)>;
using AttributeType = typename Type::AttributeType;
createAttributeImpl<AttributeType>(attr, null_value);
using ValueType = DictionaryValueType<AttributeType>;
if constexpr (std::is_same_v<AttributeType, StringRef>)
attribute.string_arena = std::make_unique<Arena>();
attribute.maps = std::make_unique<Collection<ValueType>>();
};
callOnDictionaryAttributeType(attribute.underlying_type, type_call);
callOnDictionaryAttributeType(dictionary_attribute.underlying_type, type_call);
return attr;
return attribute;
}
template <typename AttributeType, typename ValueSetter, typename DefaultValueExtractor>
template <typename AttributeType, bool is_nullable, typename ValueSetter, typename DefaultValueExtractor>
void RangeHashedDictionary::getItemsImpl(
const Attribute & attribute,
const Columns & key_columns,
@ -435,20 +436,26 @@ void RangeHashedDictionary::getItemsImpl(
++keys_found;
auto & value = val_it->value;
if (value.has_value())
set_value(row, *value, false);
if constexpr (is_nullable)
{
if (value.has_value())
set_value(row, *value, false);
else
set_value(row, default_value_extractor[row], true);
}
else
set_value(row, default_value_extractor[row], true);
}
else
{
set_value(row, default_value_extractor[row], false);
{
set_value(row, *value, false);
}
continue;
}
}
if constexpr (is_nullable)
set_value(row, default_value_extractor[row], default_value_extractor.isNullAt(row));
else
{
set_value(row, default_value_extractor[row], false);
}
}
query_count.fetch_add(ids.size(), std::memory_order_relaxed);

View File

@ -107,29 +107,6 @@ private:
AttributeUnderlyingType type;
bool is_nullable;
std::variant<
UInt8,
UInt16,
UInt32,
UInt64,
UInt128,
UInt256,
Int8,
Int16,
Int32,
Int64,
Int128,
Int256,
Decimal32,
Decimal64,
Decimal128,
Decimal256,
Float32,
Float64,
UUID,
StringRef,
Array>
null_values;
std::variant<
Ptr<UInt8>,
Ptr<UInt16>,
@ -165,12 +142,9 @@ private:
void calculateBytesAllocated();
template <typename T>
static void createAttributeImpl(Attribute & attribute, const Field & null_value);
static Attribute createAttribute(const DictionaryAttribute & dictionary_attribute);
static Attribute createAttribute(const DictionaryAttribute& attribute, const Field & null_value);
template <typename AttributeType, typename ValueSetter, typename DefaultValueExtractor>
template <typename AttributeType, bool is_nullable, typename ValueSetter, typename DefaultValueExtractor>
void getItemsImpl(
const Attribute & attribute,
const Columns & key_columns,

View File

@ -283,7 +283,7 @@ public:
size_t getNumberOfArguments() const override { return 0; }
bool useDefaultImplementationForConstants() const final { return true; }
// bool useDefaultImplementationForNulls() const final { return false; }
bool useDefaultImplementationForNulls() const final { return false; }
ColumnNumbers getArgumentsThatAreAlwaysConstant() const final { return {0, 1}; }
bool isDeterministic() const override { return false; }
@ -395,27 +395,9 @@ public:
arguments.size() + 1);
const auto & column_before_cast = arguments[current_arguments_index];
if (const DataTypeTuple * type_tuple = typeid_cast<const DataTypeTuple *>(column_before_cast.type.get()))
{
const DataTypes & nested_types = type_tuple->getElements();
for (const auto & nested_type : nested_types)
if (nested_type->isNullable())
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"Wrong argument for function {} default values column nullable is not supported",
getName());
}
else if (column_before_cast.type->isNullable())
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"Wrong argument for function {} default values column nullable is not supported",
getName());
auto result_type_no_nullable = removeNullable(result_type);
ColumnWithTypeAndName column_to_cast = {column_before_cast.column->convertToFullColumnIfConst(), column_before_cast.type, column_before_cast.name};
auto result = castColumnAccurate(column_to_cast, result_type_no_nullable);
auto result = castColumnAccurate(column_to_cast, result_type);
if (attribute_names.size() > 1)
{

View File

@ -132,6 +132,7 @@ SELECT dictGetOrDefault('polygon_dictionary', 'array_value', tuple(1.5, 1.5), [2
DROP DICTIONARY polygon_dictionary;
DROP TABLE polygon_dictionary_array_source_table;
DROP TABLE IF EXISTS range_dictionary_array_source_table;
CREATE TABLE range_dictionary_array_source_table
(
key UInt64,

View File

@ -0,0 +1,49 @@
Flat dictionary
0
\N
\N
2
\N
2
\N
Hashed dictionary
0
\N
\N
2
\N
2
\N
Cache dictionary
0
\N
\N
2
\N
2
\N
Direct dictionary
0
\N
\N
2
\N
2
\N
IPTrie dictionary
Polygon dictionary
0
\N
\N
2
\N
2
\N
Range dictionary
0
\N
\N
2
\N
2
\N

View File

@ -0,0 +1,200 @@
DROP TABLE IF EXISTS dictionary_nullable_source_table;
CREATE TABLE dictionary_nullable_source_table
(
id UInt64,
value Nullable(Int64)
) ENGINE=TinyLog;
DROP TABLE IF EXISTS dictionary_nullable_default_source_table;
CREATE TABLE dictionary_nullable_default_source_table
(
id UInt64,
value Nullable(UInt64)
) ENGINE=TinyLog;
INSERT INTO dictionary_nullable_source_table VALUES (0, 0), (1, NULL);
INSERT INTO dictionary_nullable_default_source_table VALUES (2, 2), (3, NULL);
DROP DICTIONARY IF EXISTS flat_dictionary;
CREATE DICTIONARY flat_dictionary
(
id UInt64,
value Nullable(Int64) DEFAULT NULL
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'dictionary_nullable_source_table'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(FLAT());
SELECT 'Flat dictionary';
SELECT dictGet('flat_dictionary', 'value', toUInt64(0));
SELECT dictGet('flat_dictionary', 'value', toUInt64(1));
SELECT dictGet('flat_dictionary', 'value', toUInt64(2));
SELECT dictGetOrDefault('flat_dictionary', 'value', toUInt64(2), 2);
SELECT dictGetOrDefault('flat_dictionary', 'value', toUInt64(2), NULL);
SELECT dictGetOrDefault('flat_dictionary', 'value', id, value) FROM dictionary_nullable_default_source_table;
DROP DICTIONARY flat_dictionary;
DROP DICTIONARY IF EXISTS hashed_dictionary;
CREATE DICTIONARY hashed_dictionary
(
id UInt64,
value Nullable(Int64) DEFAULT NULL
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'dictionary_nullable_source_table'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(HASHED());
SELECT 'Hashed dictionary';
SELECT dictGet('hashed_dictionary', 'value', toUInt64(0));
SELECT dictGet('hashed_dictionary', 'value', toUInt64(1));
SELECT dictGet('hashed_dictionary', 'value', toUInt64(2));
SELECT dictGetOrDefault('hashed_dictionary', 'value', toUInt64(2), 2);
SELECT dictGetOrDefault('hashed_dictionary', 'value', toUInt64(2), NULL);
SELECT dictGetOrDefault('hashed_dictionary', 'value', id, value) FROM dictionary_nullable_default_source_table;
DROP DICTIONARY hashed_dictionary;
DROP DICTIONARY IF EXISTS cache_dictionary;
CREATE DICTIONARY cache_dictionary
(
id UInt64,
value Nullable(Int64) DEFAULT NULL
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'dictionary_nullable_source_table'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(CACHE(SIZE_IN_CELLS 10));
SELECT 'Cache dictionary';
SELECT dictGet('cache_dictionary', 'value', toUInt64(0));
SELECT dictGet('cache_dictionary', 'value', toUInt64(1));
SELECT dictGet('cache_dictionary', 'value', toUInt64(2));
SELECT dictGetOrDefault('cache_dictionary', 'value', toUInt64(2), 2);
SELECT dictGetOrDefault('cache_dictionary', 'value', toUInt64(2), NULL);
SELECT dictGetOrDefault('cache_dictionary', 'value', id, value) FROM dictionary_nullable_default_source_table;
DROP DICTIONARY cache_dictionary;
DROP DICTIONARY IF EXISTS direct_dictionary;
CREATE DICTIONARY direct_dictionary
(
id UInt64,
value Nullable(Int64) DEFAULT NULL
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'dictionary_nullable_source_table'))
LAYOUT(DIRECT());
SELECT 'Direct dictionary';
SELECT dictGet('direct_dictionary', 'value', toUInt64(0));
SELECT dictGet('direct_dictionary', 'value', toUInt64(1));
SELECT dictGet('direct_dictionary', 'value', toUInt64(2));
SELECT dictGetOrDefault('direct_dictionary', 'value', toUInt64(2), 2);
SELECT dictGetOrDefault('direct_dictionary', 'value', toUInt64(2), NULL);
SELECT dictGetOrDefault('direct_dictionary', 'value', id, value) FROM dictionary_nullable_default_source_table;
DROP DICTIONARY direct_dictionary;
DROP DICTIONARY IF EXISTS ip_trie_dictionary;
CREATE DICTIONARY ip_trie_dictionary
(
prefix String,
value Nullable(Int64) DEFAULT NULL
)
PRIMARY KEY prefix
SOURCE(CLICKHOUSE(HOST 'localhost' port tcpPort() TABLE 'dictionary_nullable_source_table'))
LIFETIME(MIN 10 MAX 1000)
LAYOUT(IP_TRIE());
-- Nullable type is not supported by IPTrie dictionary
SELECT 'IPTrie dictionary';
SELECT dictGet('ip_trie_dictionary', 'value', tuple(IPv4StringToNum('127.0.0.0'))); --{serverError 1}
DROP TABLE dictionary_nullable_source_table;
DROP TABLE dictionary_nullable_default_source_table;
DROP TABLE IF EXISTS polygon_dictionary_nullable_source_table;
CREATE TABLE polygon_dictionary_nullable_source_table
(
key Array(Array(Array(Tuple(Float64, Float64)))),
value Nullable(Int64)
) ENGINE = TinyLog;
DROP TABLE IF EXISTS polygon_dictionary_nullable_default_source_table;
CREATE TABLE polygon_dictionary_nullable_default_source_table
(
key Tuple(Float64, Float64),
value Nullable(UInt64)
) ENGINE=TinyLog;
INSERT INTO polygon_dictionary_nullable_source_table VALUES ([[[(0, 0), (0, 1), (1, 1), (1, 0)]]], 0), ([[[(0, 0), (0, 1.5), (1.5, 1.5), (1.5, 0)]]], NULL);
INSERT INTO polygon_dictionary_nullable_default_source_table VALUES ((2.0, 2.0), 2), ((4, 4), NULL);
DROP DICTIONARY IF EXISTS polygon_dictionary;
CREATE DICTIONARY polygon_dictionary
(
key Array(Array(Array(Tuple(Float64, Float64)))),
value Nullable(UInt64) DEFAULT NULL
)
PRIMARY KEY key
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'polygon_dictionary_nullable_source_table'))
LIFETIME(MIN 0 MAX 1000)
LAYOUT(POLYGON());
SELECT 'Polygon dictionary';
SELECT dictGet('polygon_dictionary', 'value', tuple(0.5, 0.5));
SELECT dictGet('polygon_dictionary', 'value', tuple(1.5, 1.5));
SELECT dictGet('polygon_dictionary', 'value', tuple(2.0, 2.0));
SELECT dictGetOrDefault('polygon_dictionary', 'value', tuple(2.0, 2.0), 2);
SELECT dictGetOrDefault('polygon_dictionary', 'value', tuple(2.0, 2.0), NULL);
SELECT dictGetOrDefault('polygon_dictionary', 'value', key, value) FROM polygon_dictionary_nullable_default_source_table;
DROP DICTIONARY polygon_dictionary;
DROP TABLE polygon_dictionary_nullable_source_table;
DROP TABLE polygon_dictionary_nullable_default_source_table;
DROP TABLE IF EXISTS range_dictionary_nullable_source_table;
CREATE TABLE range_dictionary_nullable_source_table
(
key UInt64,
start_date Date,
end_date Date,
value Nullable(UInt64)
)
ENGINE = TinyLog;
DROP TABLE IF EXISTS range_dictionary_nullable_default_source_table;
CREATE TABLE range_dictionary_nullable_default_source_table
(
key UInt64,
value Nullable(UInt64)
) ENGINE=TinyLog;
INSERT INTO range_dictionary_nullable_source_table VALUES (0, toDate('2019-05-05'), toDate('2019-05-20'), 0), (1, toDate('2019-05-05'), toDate('2019-05-20'), NULL);
INSERT INTO range_dictionary_nullable_default_source_table VALUES (2, 2), (3, NULL);
DROP DICTIONARY IF EXISTS range_dictionary;
CREATE DICTIONARY range_dictionary
(
key UInt64,
start_date Date,
end_date Date,
value Nullable(UInt64) DEFAULT NULL
)
PRIMARY KEY key
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'range_dictionary_nullable_source_table'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(RANGE_HASHED())
RANGE(MIN start_date MAX end_date);
SELECT 'Range dictionary';
SELECT dictGet('range_dictionary', 'value', toUInt64(0), toDate('2019-05-15'));
SELECT dictGet('range_dictionary', 'value', toUInt64(1), toDate('2019-05-15'));
SELECT dictGet('range_dictionary', 'value', toUInt64(2), toDate('2019-05-15'));
SELECT dictGetOrDefault('range_dictionary', 'value', toUInt64(2), toDate('2019-05-15'), 2);
SELECT dictGetOrDefault('range_dictionary', 'value', toUInt64(2), toDate('2019-05-15'), NULL);
SELECT dictGetOrDefault('range_dictionary', 'value', key, toDate('2019-05-15'), value) FROM range_dictionary_nullable_default_source_table;
DROP DICTIONARY range_dictionary;
DROP TABLE range_dictionary_nullable_source_table;
DROP TABLE range_dictionary_nullable_default_source_table;

View File

@ -1,2 +0,0 @@
Flat dictionary
\N

View File

@ -1,26 +0,0 @@
DROP TABLE IF EXISTS dictionary_nullable_source_table;
CREATE TABLE dictionary_nullable_source_table
(
id UInt64,
value Nullable(Int64)
) ENGINE=TinyLog;
INSERT INTO dictionary_nullable_source_table VALUES (0, 0), (1, NULL);
DROP DICTIONARY IF EXISTS flat_dictionary;
CREATE DICTIONARY flat_dictionary
(
id UInt64,
value Nullable(Int64) DEFAULT NULL
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'dictionary_nullable_source_table'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(FLAT());
SELECT 'Flat dictionary';
-- SELECT dictGet('flat_dictionary', 'value', toUInt64(0));
-- SELECT dictGet('flat_dictionary', 'value', toUInt64(1));
-- SELECT dictGet('flat_dictionary', 'value', toUInt64(2));
SELECT dictGetOrDefault('flat_dictionary', 'value', toUInt64(2), NULL);
DROP DICTIONARY flat_dictionary;

View File

@ -0,0 +1,8 @@
SSDCache dictionary
0
\N
\N
2
\N
2
\N

View File

@ -0,0 +1,48 @@
#!/usr/bin/env bash
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CURDIR"/../shell_config.sh
USER_FILES_PATH=$(clickhouse-client --query "select _path,_file from file('nonexist.txt', 'CSV', 'val1 char')" 2>&1 | grep Exception | awk '{gsub("/nonexist.txt","",$9); print $9}')
$CLICKHOUSE_CLIENT -n --query="
DROP TABLE IF EXISTS dictionary_nullable_source_table;
CREATE TABLE dictionary_nullable_source_table
(
id UInt64,
value Nullable(Int64)
) ENGINE=TinyLog;
DROP TABLE IF EXISTS dictionary_nullable_default_source_table;
CREATE TABLE dictionary_nullable_default_source_table
(
id UInt64,
value Nullable(UInt64)
) ENGINE=TinyLog;
INSERT INTO dictionary_nullable_source_table VALUES (0, 0), (1, NULL);
INSERT INTO dictionary_nullable_default_source_table VALUES (2, 2), (3, NULL);
DROP DICTIONARY IF EXISTS ssd_cache_dictionary;
CREATE DICTIONARY ssd_cache_dictionary
(
id UInt64,
value Nullable(UInt64) DEFAULT NULL
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() TABLE 'dictionary_nullable_source_table'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(SSD_CACHE(BLOCK_SIZE 4096 FILE_SIZE 8192 PATH '$USER_FILES_PATH/0d'));
SELECT 'SSDCache dictionary';
SELECT dictGet('ssd_cache_dictionary', 'value', toUInt64(0));
SELECT dictGet('ssd_cache_dictionary', 'value', toUInt64(1));
SELECT dictGet('ssd_cache_dictionary', 'value', toUInt64(2));
SELECT dictGetOrDefault('ssd_cache_dictionary', 'value', toUInt64(2), 2);
SELECT dictGetOrDefault('ssd_cache_dictionary', 'value', toUInt64(2), NULL);
SELECT dictGetOrDefault('ssd_cache_dictionary', 'value', id, value) FROM dictionary_nullable_default_source_table;
DROP DICTIONARY ssd_cache_dictionary;
DROP TABLE dictionary_nullable_source_table;
DROP TABLE dictionary_nullable_default_source_table;"