mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Merge pull request #8436 from achulkov2/polygon-dict-basic-interface-improvements
[WIP] Basic interface for polygon dictionaries
This commit is contained in:
commit
7aeb900922
@ -74,6 +74,11 @@ AttributeUnderlyingType getAttributeUnderlyingType(const std::string & type)
|
||||
return AttributeUnderlyingType::utDecimal128;
|
||||
}
|
||||
|
||||
// Temporary hack to allow arrays in keys, since they are never retrieved for polygon dictionaries.
|
||||
// TODO: This should be fixed by fully supporting arrays in dictionaries.
|
||||
if (type.find("Array") == 0)
|
||||
return AttributeUnderlyingType::utString;
|
||||
|
||||
throw Exception{"Unknown type " + type, ErrorCodes::UNKNOWN_TYPE};
|
||||
}
|
||||
|
||||
|
748
dbms/src/Dictionaries/PolygonDictionary.cpp
Normal file
748
dbms/src/Dictionaries/PolygonDictionary.cpp
Normal file
@ -0,0 +1,748 @@
|
||||
#include <ext/map.h>
|
||||
#include <Columns/ColumnArray.h>
|
||||
#include <Columns/ColumnTuple.h>
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include "PolygonDictionary.h"
|
||||
#include "DictionaryBlockInputStream.h"
|
||||
#include "DictionaryFactory.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int TYPE_MISMATCH;
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int UNSUPPORTED_METHOD;
|
||||
}
|
||||
|
||||
|
||||
IPolygonDictionary::IPolygonDictionary(
|
||||
const std::string & database_,
|
||||
const std::string & name_,
|
||||
const DictionaryStructure & dict_struct_,
|
||||
DictionarySourcePtr source_ptr_,
|
||||
const DictionaryLifetime dict_lifetime_,
|
||||
InputType input_type_,
|
||||
PointType point_type_)
|
||||
: database(database_)
|
||||
, name(name_)
|
||||
, full_name{database_.empty() ? name_ : (database_ + "." + name_)}
|
||||
, dict_struct(dict_struct_)
|
||||
, source_ptr(std::move(source_ptr_))
|
||||
, dict_lifetime(dict_lifetime_)
|
||||
, input_type(input_type_)
|
||||
, point_type(point_type_)
|
||||
{
|
||||
createAttributes();
|
||||
loadData();
|
||||
}
|
||||
|
||||
const std::string & IPolygonDictionary::getDatabase() const
|
||||
{
|
||||
return database;
|
||||
}
|
||||
|
||||
const std::string & IPolygonDictionary::getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
const std::string & IPolygonDictionary::getFullName() const
|
||||
{
|
||||
return full_name;
|
||||
}
|
||||
|
||||
std::string IPolygonDictionary::getTypeName() const
|
||||
{
|
||||
return "Polygon";
|
||||
}
|
||||
|
||||
std::string IPolygonDictionary::getKeyDescription() const
|
||||
{
|
||||
return dict_struct.getKeyDescription();
|
||||
}
|
||||
|
||||
size_t IPolygonDictionary::getBytesAllocated() const
|
||||
{
|
||||
return bytes_allocated;
|
||||
}
|
||||
|
||||
size_t IPolygonDictionary::getQueryCount() const
|
||||
{
|
||||
return query_count.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
double IPolygonDictionary::getHitRate() const
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
size_t IPolygonDictionary::getElementCount() const
|
||||
{
|
||||
return element_count;
|
||||
}
|
||||
|
||||
double IPolygonDictionary::getLoadFactor() const
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
const IDictionarySource * IPolygonDictionary::getSource() const
|
||||
{
|
||||
return source_ptr.get();
|
||||
}
|
||||
|
||||
const DictionaryLifetime & IPolygonDictionary::getLifetime() const
|
||||
{
|
||||
return dict_lifetime;
|
||||
}
|
||||
|
||||
const DictionaryStructure & IPolygonDictionary::getStructure() const
|
||||
{
|
||||
return dict_struct;
|
||||
}
|
||||
|
||||
bool IPolygonDictionary::isInjective(const std::string &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockInputStreamPtr IPolygonDictionary::getBlockInputStream(const Names &, size_t) const
|
||||
{
|
||||
// TODO: In order for this to work one would first have to support retrieving arrays from dictionaries.
|
||||
// I believe this is a separate task done by some other people.
|
||||
throw Exception{"Reading the dictionary is not allowed", ErrorCodes::UNSUPPORTED_METHOD};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void IPolygonDictionary::appendNullValueImpl(const Field & null_value)
|
||||
{
|
||||
null_values.emplace_back(T(null_value.get<NearestFieldType<T>>()));
|
||||
}
|
||||
|
||||
void IPolygonDictionary::appendNullValue(AttributeUnderlyingType type, const Field & null_value)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AttributeUnderlyingType::utUInt8:
|
||||
appendNullValueImpl<UInt8>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utUInt16:
|
||||
appendNullValueImpl<UInt16>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utUInt32:
|
||||
appendNullValueImpl<UInt32>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utUInt64:
|
||||
appendNullValueImpl<UInt64>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utUInt128:
|
||||
appendNullValueImpl<UInt128>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utInt8:
|
||||
appendNullValueImpl<Int8>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utInt16:
|
||||
appendNullValueImpl<Int16>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utInt32:
|
||||
appendNullValueImpl<Int32>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utInt64:
|
||||
appendNullValueImpl<Int64>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utFloat32:
|
||||
appendNullValueImpl<Float32>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utFloat64:
|
||||
appendNullValueImpl<Float64>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utDecimal32:
|
||||
appendNullValueImpl<Decimal32>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utDecimal64:
|
||||
appendNullValueImpl<Decimal64>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utDecimal128:
|
||||
appendNullValueImpl<Decimal128>(null_value);
|
||||
break;
|
||||
case AttributeUnderlyingType::utString:
|
||||
appendNullValueImpl<String>(null_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IPolygonDictionary::createAttributes()
|
||||
{
|
||||
attributes.resize(dict_struct.attributes.size());
|
||||
for (size_t i = 0; i < dict_struct.attributes.size(); ++i)
|
||||
{
|
||||
const auto & attr = dict_struct.attributes[i];
|
||||
attribute_index_by_name.emplace(attr.name, i);
|
||||
|
||||
appendNullValue(attr.underlying_type, attr.null_value);
|
||||
|
||||
if (attr.hierarchical)
|
||||
throw Exception{name + ": hierarchical attributes not supported for dictionary of type " + getTypeName(),
|
||||
ErrorCodes::TYPE_MISMATCH};
|
||||
}
|
||||
}
|
||||
|
||||
void IPolygonDictionary::blockToAttributes(const DB::Block &block)
|
||||
{
|
||||
const auto rows = block.rows();
|
||||
element_count += rows;
|
||||
for (size_t i = 0; i < attributes.size(); ++i)
|
||||
{
|
||||
const auto & column = block.safeGetByPosition(i + 1);
|
||||
if (attributes[i])
|
||||
{
|
||||
MutableColumnPtr mutated = std::move(*attributes[i]).mutate();
|
||||
mutated->insertRangeFrom(*column.column, 0, column.column->size());
|
||||
attributes[i] = std::move(mutated);
|
||||
}
|
||||
else
|
||||
attributes[i] = column.column;
|
||||
}
|
||||
/** Multi-polygons could cause bigger sizes, but this is better than nothing. */
|
||||
polygons.reserve(polygons.size() + rows);
|
||||
ids.reserve(ids.size() + rows);
|
||||
const auto & key = block.safeGetByPosition(0).column;
|
||||
extractPolygons(key);
|
||||
}
|
||||
|
||||
void IPolygonDictionary::loadData()
|
||||
{
|
||||
auto stream = source_ptr->loadAll();
|
||||
stream->readPrefix();
|
||||
while (const auto block = stream->read())
|
||||
blockToAttributes(block);
|
||||
stream->readSuffix();
|
||||
|
||||
for (auto & polygon : polygons)
|
||||
bg::correct(polygon);
|
||||
}
|
||||
|
||||
void IPolygonDictionary::calculateBytesAllocated()
|
||||
{
|
||||
// TODO:: Account for key.
|
||||
for (const auto & column : attributes)
|
||||
bytes_allocated += column->allocatedBytes();
|
||||
}
|
||||
|
||||
std::vector<IPolygonDictionary::Point> IPolygonDictionary::extractPoints(const Columns &key_columns)
|
||||
{
|
||||
if (key_columns.size() != 2)
|
||||
throw Exception{"Expected two columns of coordinates", ErrorCodes::BAD_ARGUMENTS};
|
||||
const auto column_x = typeid_cast<const ColumnVector<Float64>*>(key_columns[0].get());
|
||||
const auto column_y = typeid_cast<const ColumnVector<Float64>*>(key_columns[1].get());
|
||||
if (!column_x || !column_y)
|
||||
throw Exception{"Expected columns of Float64", ErrorCodes::TYPE_MISMATCH};
|
||||
const auto rows = key_columns.front()->size();
|
||||
std::vector<Point> result;
|
||||
result.reserve(rows);
|
||||
for (const auto row : ext::range(0, rows))
|
||||
result.emplace_back(column_x->getElement(row), column_y->getElement(row));
|
||||
return result;
|
||||
}
|
||||
|
||||
void IPolygonDictionary::has(const Columns &key_columns, const DataTypes &, PaddedPODArray<UInt8> &out) const
|
||||
{
|
||||
size_t row = 0;
|
||||
for (const auto & pt : extractPoints(key_columns))
|
||||
{
|
||||
size_t trash = 0;
|
||||
out[row] = find(pt, trash);
|
||||
++row;
|
||||
}
|
||||
|
||||
query_count.fetch_add(row, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
size_t IPolygonDictionary::getAttributeIndex(const std::string & attribute_name) const
|
||||
{
|
||||
const auto it = attribute_index_by_name.find(attribute_name);
|
||||
if (it == attribute_index_by_name.end())
|
||||
throw Exception{"No such attribute: " + attribute_name, ErrorCodes::BAD_ARGUMENTS};
|
||||
return it->second;
|
||||
}
|
||||
|
||||
#define DECLARE(TYPE) \
|
||||
void IPolygonDictionary::get##TYPE( \
|
||||
const std::string & attribute_name, const Columns & key_columns, const DataTypes &, ResultArrayType<TYPE> & out) const \
|
||||
{ \
|
||||
const auto ind = getAttributeIndex(attribute_name); \
|
||||
checkAttributeType(name, attribute_name, dict_struct.attributes[ind].underlying_type, AttributeUnderlyingType::ut##TYPE); \
|
||||
\
|
||||
const auto null_value = std::get<TYPE>(null_values[ind]); \
|
||||
\
|
||||
getItemsImpl<TYPE, TYPE>( \
|
||||
ind, \
|
||||
key_columns, \
|
||||
[&](const size_t row, const auto value) { out[row] = value; }, \
|
||||
[&](const size_t) { return null_value; }); \
|
||||
}
|
||||
DECLARE(UInt8)
|
||||
DECLARE(UInt16)
|
||||
DECLARE(UInt32)
|
||||
DECLARE(UInt64)
|
||||
DECLARE(UInt128)
|
||||
DECLARE(Int8)
|
||||
DECLARE(Int16)
|
||||
DECLARE(Int32)
|
||||
DECLARE(Int64)
|
||||
DECLARE(Float32)
|
||||
DECLARE(Float64)
|
||||
DECLARE(Decimal32)
|
||||
DECLARE(Decimal64)
|
||||
DECLARE(Decimal128)
|
||||
#undef DECLARE
|
||||
|
||||
void IPolygonDictionary::getString(
|
||||
const std::string & attribute_name, const Columns & key_columns, const DataTypes &, ColumnString * out) const
|
||||
{
|
||||
const auto ind = getAttributeIndex(attribute_name);
|
||||
checkAttributeType(name, attribute_name, dict_struct.attributes[ind].underlying_type, AttributeUnderlyingType::utString);
|
||||
|
||||
const auto & null_value = StringRef{std::get<String>(null_values[ind])};
|
||||
|
||||
getItemsImpl<String, StringRef>(
|
||||
ind,
|
||||
key_columns,
|
||||
[&](const size_t, const StringRef & value) { out->insertData(value.data, value.size); },
|
||||
[&](const size_t) { return null_value; });
|
||||
}
|
||||
|
||||
#define DECLARE(TYPE) \
|
||||
void IPolygonDictionary::get##TYPE( \
|
||||
const std::string & attribute_name, \
|
||||
const Columns & key_columns, \
|
||||
const DataTypes &, \
|
||||
const PaddedPODArray<TYPE> & def, \
|
||||
ResultArrayType<TYPE> & out) const \
|
||||
{ \
|
||||
const auto ind = getAttributeIndex(attribute_name); \
|
||||
checkAttributeType(name, attribute_name, dict_struct.attributes[ind].underlying_type, AttributeUnderlyingType::ut##TYPE); \
|
||||
\
|
||||
getItemsImpl<TYPE, TYPE>( \
|
||||
ind, \
|
||||
key_columns, \
|
||||
[&](const size_t row, const auto value) { out[row] = value; }, \
|
||||
[&](const size_t row) { return def[row]; }); \
|
||||
}
|
||||
DECLARE(UInt8)
|
||||
DECLARE(UInt16)
|
||||
DECLARE(UInt32)
|
||||
DECLARE(UInt64)
|
||||
DECLARE(UInt128)
|
||||
DECLARE(Int8)
|
||||
DECLARE(Int16)
|
||||
DECLARE(Int32)
|
||||
DECLARE(Int64)
|
||||
DECLARE(Float32)
|
||||
DECLARE(Float64)
|
||||
DECLARE(Decimal32)
|
||||
DECLARE(Decimal64)
|
||||
DECLARE(Decimal128)
|
||||
#undef DECLARE
|
||||
|
||||
void IPolygonDictionary::getString(
|
||||
const std::string & attribute_name,
|
||||
const Columns & key_columns,
|
||||
const DataTypes &,
|
||||
const ColumnString * const def,
|
||||
ColumnString * const out) const
|
||||
{
|
||||
const auto ind = getAttributeIndex(attribute_name);
|
||||
checkAttributeType(name, attribute_name, dict_struct.attributes[ind].underlying_type, AttributeUnderlyingType::utString);
|
||||
|
||||
getItemsImpl<String, StringRef>(
|
||||
ind,
|
||||
key_columns,
|
||||
[&](const size_t, const StringRef value) { out->insertData(value.data, value.size); },
|
||||
[&](const size_t row) { return def->getDataAt(row); });
|
||||
}
|
||||
|
||||
#define DECLARE(TYPE) \
|
||||
void IPolygonDictionary::get##TYPE( \
|
||||
const std::string & attribute_name, \
|
||||
const Columns & key_columns, \
|
||||
const DataTypes &, \
|
||||
const TYPE def, \
|
||||
ResultArrayType<TYPE> & out) const \
|
||||
{ \
|
||||
const auto ind = getAttributeIndex(attribute_name); \
|
||||
checkAttributeType(name, attribute_name, dict_struct.attributes[ind].underlying_type, AttributeUnderlyingType::ut##TYPE); \
|
||||
\
|
||||
getItemsImpl<TYPE, TYPE>( \
|
||||
ind, key_columns, [&](const size_t row, const auto value) { out[row] = value; }, [&](const size_t) { return def; }); \
|
||||
}
|
||||
DECLARE(UInt8)
|
||||
DECLARE(UInt16)
|
||||
DECLARE(UInt32)
|
||||
DECLARE(UInt64)
|
||||
DECLARE(UInt128)
|
||||
DECLARE(Int8)
|
||||
DECLARE(Int16)
|
||||
DECLARE(Int32)
|
||||
DECLARE(Int64)
|
||||
DECLARE(Float32)
|
||||
DECLARE(Float64)
|
||||
DECLARE(Decimal32)
|
||||
DECLARE(Decimal64)
|
||||
DECLARE(Decimal128)
|
||||
#undef DECLARE
|
||||
|
||||
void IPolygonDictionary::getString(
|
||||
const std::string & attribute_name,
|
||||
const Columns & key_columns,
|
||||
const DataTypes &,
|
||||
const String & def,
|
||||
ColumnString * const out) const
|
||||
{
|
||||
const auto ind = getAttributeIndex(attribute_name);
|
||||
checkAttributeType(name, attribute_name, dict_struct.attributes[ind].underlying_type, AttributeUnderlyingType::utString);
|
||||
|
||||
getItemsImpl<String, StringRef>(
|
||||
ind,
|
||||
key_columns,
|
||||
[&](const size_t, const StringRef value) { out->insertData(value.data, value.size); },
|
||||
[&](const size_t) { return StringRef{def}; });
|
||||
}
|
||||
|
||||
template <typename AttributeType, typename OutputType, typename ValueSetter, typename DefaultGetter>
|
||||
void IPolygonDictionary::getItemsImpl(
|
||||
size_t attribute_ind, const Columns & key_columns, ValueSetter && set_value, DefaultGetter && get_default) const
|
||||
{
|
||||
const auto points = extractPoints(key_columns);
|
||||
|
||||
using ColVecType = std::conditional_t<IsDecimalNumber<AttributeType>, ColumnDecimal<AttributeType>, ColumnVector<AttributeType>>;
|
||||
using ColType = std::conditional_t<std::is_same<AttributeType, String>::value, ColumnString, ColVecType>;
|
||||
const auto column = typeid_cast<const ColType *>(attributes[attribute_ind].get());
|
||||
if (!column)
|
||||
throw Exception{"An attribute should be a column of its type", ErrorCodes::BAD_ARGUMENTS};
|
||||
for (const auto i : ext::range(0, points.size()))
|
||||
{
|
||||
size_t id = 0;
|
||||
const auto found = find(points[i], id);
|
||||
id = ids[id];
|
||||
if (!found)
|
||||
{
|
||||
set_value(i, static_cast<OutputType>(get_default(i)));
|
||||
continue;
|
||||
}
|
||||
if constexpr (std::is_same<AttributeType, String>::value)
|
||||
set_value(i, static_cast<OutputType>(column->getDataAt(id)));
|
||||
else
|
||||
set_value(i, static_cast<OutputType>(column->getElement(id)));
|
||||
}
|
||||
|
||||
query_count.fetch_add(points.size(), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct Offset
|
||||
{
|
||||
Offset() = default;
|
||||
|
||||
IColumn::Offsets ring_offsets;
|
||||
IColumn::Offsets polygon_offsets;
|
||||
IColumn::Offsets multi_polygon_offsets;
|
||||
|
||||
IColumn::Offset points_added = 0;
|
||||
IColumn::Offset current_ring = 0;
|
||||
IColumn::Offset current_polygon = 0;
|
||||
IColumn::Offset current_multi_polygon = 0;
|
||||
|
||||
Offset& operator++()
|
||||
{
|
||||
++points_added;
|
||||
if (points_added <= ring_offsets[current_ring])
|
||||
return *this;
|
||||
|
||||
++current_ring;
|
||||
if (current_ring < polygon_offsets[current_polygon])
|
||||
return *this;
|
||||
|
||||
++current_polygon;
|
||||
if (current_polygon < multi_polygon_offsets[current_multi_polygon])
|
||||
return *this;
|
||||
|
||||
++current_multi_polygon;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool atLastPolygonOfMultiPolygon() { return current_polygon + 1 == multi_polygon_offsets[current_multi_polygon]; }
|
||||
bool atLastRingOfPolygon() { return current_ring + 1 == polygon_offsets[current_polygon]; }
|
||||
bool atLastPointOfRing() { return points_added == ring_offsets[current_ring]; }
|
||||
|
||||
bool allRingsHaveAPositiveArea()
|
||||
{
|
||||
IColumn::Offset prev_offset = 0;
|
||||
for (const auto offset : ring_offsets)
|
||||
{
|
||||
if (offset - prev_offset < 3)
|
||||
return false;
|
||||
prev_offset = offset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct Data
|
||||
{
|
||||
std::vector<IPolygonDictionary::Polygon> & dest;
|
||||
std::vector<size_t> & ids;
|
||||
|
||||
void addPolygon(bool new_multi_polygon = false)
|
||||
{
|
||||
dest.emplace_back();
|
||||
ids.push_back((ids.empty() ? 0 : ids.back() + new_multi_polygon));
|
||||
}
|
||||
|
||||
void addPoint(Float64 x, Float64 y)
|
||||
{
|
||||
auto & last_polygon = dest.back();
|
||||
auto & last_ring = (last_polygon.inners().empty() ? last_polygon.outer() : last_polygon.inners().back());
|
||||
last_ring.emplace_back(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
void addNewPoint(Float64 x, Float64 y, Data & data, Offset & offset)
|
||||
{
|
||||
if (offset.atLastPointOfRing())
|
||||
{
|
||||
if (offset.atLastRingOfPolygon())
|
||||
data.addPolygon(offset.atLastPolygonOfMultiPolygon());
|
||||
else
|
||||
{
|
||||
/** An outer ring is added automatically with a new polygon, thus we need the else statement here.
|
||||
* This also implies that if we are at this point we have to add an inner ring.
|
||||
*/
|
||||
auto & last_polygon = data.dest.back();
|
||||
last_polygon.inners().emplace_back();
|
||||
}
|
||||
}
|
||||
data.addPoint(x, y);
|
||||
++offset;
|
||||
}
|
||||
|
||||
const IColumn * unrollMultiPolygons(const ColumnPtr & column, Offset & offset)
|
||||
{
|
||||
const auto ptr_multi_polygons = typeid_cast<const ColumnArray*>(column.get());
|
||||
if (!ptr_multi_polygons)
|
||||
throw Exception{"Expected a column containing arrays of polygons", ErrorCodes::TYPE_MISMATCH};
|
||||
offset.multi_polygon_offsets.assign(ptr_multi_polygons->getOffsets());
|
||||
|
||||
const auto ptr_polygons = typeid_cast<const ColumnArray*>(&ptr_multi_polygons->getData());
|
||||
if (!ptr_polygons)
|
||||
throw Exception{"Expected a column containing arrays of rings when reading polygons", ErrorCodes::TYPE_MISMATCH};
|
||||
offset.polygon_offsets.assign(ptr_polygons->getOffsets());
|
||||
|
||||
const auto ptr_rings = typeid_cast<const ColumnArray*>(&ptr_polygons->getData());
|
||||
if (!ptr_rings)
|
||||
throw Exception{"Expected a column containing arrays of points when reading rings", ErrorCodes::TYPE_MISMATCH};
|
||||
offset.ring_offsets.assign(ptr_rings->getOffsets());
|
||||
|
||||
return ptr_rings->getDataPtr().get();
|
||||
}
|
||||
|
||||
const IColumn * unrollSimplePolygons(const ColumnPtr & column, Offset & offset)
|
||||
{
|
||||
const auto ptr_polygons = typeid_cast<const ColumnArray*>(column.get());
|
||||
if (!ptr_polygons)
|
||||
throw Exception{"Expected a column containing arrays of points", ErrorCodes::TYPE_MISMATCH};
|
||||
offset.ring_offsets.assign(ptr_polygons->getOffsets());
|
||||
std::iota(offset.polygon_offsets.begin(), offset.polygon_offsets.end(), 1);
|
||||
offset.multi_polygon_offsets.assign(offset.polygon_offsets);
|
||||
|
||||
return ptr_polygons->getDataPtr().get();
|
||||
}
|
||||
|
||||
void handlePointsReprByArrays(const IColumn * column, Data & data, Offset & offset)
|
||||
{
|
||||
const auto ptr_points = typeid_cast<const ColumnArray*>(column);
|
||||
const auto ptr_coord = typeid_cast<const ColumnVector<Float64>*>(&ptr_points->getData());
|
||||
if (!ptr_coord)
|
||||
throw Exception{"Expected coordinates to be of type Float64", ErrorCodes::TYPE_MISMATCH};
|
||||
const auto & offsets = ptr_points->getOffsets();
|
||||
IColumn::Offset prev_offset = 0;
|
||||
for (size_t i = 0; i < offsets.size(); ++i)
|
||||
{
|
||||
if (offsets[i] - prev_offset != 2)
|
||||
throw Exception{"All points should be two-dimensional", ErrorCodes::BAD_ARGUMENTS};
|
||||
prev_offset = offsets[i];
|
||||
addNewPoint(ptr_coord->getElement(2 * i), ptr_coord->getElement(2 * i + 1), data, offset);
|
||||
}
|
||||
}
|
||||
|
||||
void handlePointsReprByTuples(const IColumn * column, Data & data, Offset & offset)
|
||||
{
|
||||
const auto ptr_points = typeid_cast<const ColumnTuple*>(column);
|
||||
if (!ptr_points)
|
||||
throw Exception{"Expected a column of tuples representing points", ErrorCodes::TYPE_MISMATCH};
|
||||
if (ptr_points->tupleSize() != 2)
|
||||
throw Exception{"Points should be two-dimensional", ErrorCodes::BAD_ARGUMENTS};
|
||||
const auto column_x = typeid_cast<const ColumnVector<Float64>*>(&ptr_points->getColumn(0));
|
||||
const auto column_y = typeid_cast<const ColumnVector<Float64>*>(&ptr_points->getColumn(1));
|
||||
if (!column_x || !column_y)
|
||||
throw Exception{"Expected coordinates to be of type Float64", ErrorCodes::TYPE_MISMATCH};
|
||||
for (size_t i = 0; i < column_x->size(); ++i)
|
||||
{
|
||||
addNewPoint(column_x->getElement(i), column_y->getElement(i), data, offset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void IPolygonDictionary::extractPolygons(const ColumnPtr &column)
|
||||
{
|
||||
Data data = {polygons, ids};
|
||||
Offset offset;
|
||||
|
||||
const IColumn * points_collection = nullptr;
|
||||
switch (input_type)
|
||||
{
|
||||
case InputType::MultiPolygon:
|
||||
points_collection = unrollMultiPolygons(column, offset);
|
||||
break;
|
||||
case InputType::SimplePolygon:
|
||||
points_collection = unrollSimplePolygons(column, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!offset.allRingsHaveAPositiveArea())
|
||||
throw Exception{"Every ring included in a polygon or excluded from it should contain at least 3 points",
|
||||
ErrorCodes::BAD_ARGUMENTS};
|
||||
|
||||
/** Adding the first empty polygon */
|
||||
data.addPolygon(true);
|
||||
|
||||
switch (point_type)
|
||||
{
|
||||
case PointType::Array:
|
||||
handlePointsReprByArrays(points_collection, data, offset);
|
||||
break;
|
||||
case PointType::Tuple:
|
||||
handlePointsReprByTuples(points_collection, data, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SimplePolygonDictionary::SimplePolygonDictionary(
|
||||
const std::string & database_,
|
||||
const std::string & name_,
|
||||
const DictionaryStructure & dict_struct_,
|
||||
DictionarySourcePtr source_ptr_,
|
||||
const DictionaryLifetime dict_lifetime_,
|
||||
InputType input_type_,
|
||||
PointType point_type_)
|
||||
: IPolygonDictionary(database_, name_, dict_struct_, std::move(source_ptr_), dict_lifetime_, input_type_, point_type_)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const IExternalLoadable> SimplePolygonDictionary::clone() const
|
||||
{
|
||||
return std::make_shared<SimplePolygonDictionary>(
|
||||
this->database,
|
||||
this->name,
|
||||
this->dict_struct,
|
||||
this->source_ptr->clone(),
|
||||
this->dict_lifetime,
|
||||
this->input_type,
|
||||
this->point_type);
|
||||
}
|
||||
|
||||
bool SimplePolygonDictionary::find(const Point &point, size_t & id) const
|
||||
{
|
||||
bool found = false;
|
||||
double area = 0;
|
||||
for (size_t i = 0; i < (this->polygons).size(); ++i)
|
||||
{
|
||||
if (bg::covered_by(point, (this->polygons)[i]))
|
||||
{
|
||||
double new_area = bg::area((this->polygons)[i]);
|
||||
if (!found || new_area < area)
|
||||
{
|
||||
found = true;
|
||||
id = i;
|
||||
area = new_area;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void registerDictionaryPolygon(DictionaryFactory & factory)
|
||||
{
|
||||
auto create_layout = [=](const std::string &,
|
||||
const DictionaryStructure & dict_struct,
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & config_prefix,
|
||||
DictionarySourcePtr source_ptr) -> DictionaryPtr
|
||||
{
|
||||
const String database = config.getString(config_prefix + ".database", "");
|
||||
const String name = config.getString(config_prefix + ".name");
|
||||
|
||||
if (!dict_struct.key)
|
||||
throw Exception{"'key' is required for a dictionary of layout 'polygon'", ErrorCodes::BAD_ARGUMENTS};
|
||||
if (dict_struct.key->size() != 1)
|
||||
throw Exception{"The 'key' should consist of a single attribute for a dictionary of layout 'polygon'",
|
||||
ErrorCodes::BAD_ARGUMENTS};
|
||||
IPolygonDictionary::InputType input_type;
|
||||
IPolygonDictionary::PointType point_type;
|
||||
const auto key_type = (*dict_struct.key)[0].type;
|
||||
const auto f64 = std::make_shared<DataTypeFloat64>();
|
||||
const auto multi_polygon_array = DataTypeArray(std::make_shared<DataTypeArray>(std::make_shared<DataTypeArray>(std::make_shared<DataTypeArray>(f64))));
|
||||
const auto multi_polygon_tuple = DataTypeArray(std::make_shared<DataTypeArray>(std::make_shared<DataTypeArray>(std::make_shared<DataTypeTuple>(std::vector<DataTypePtr>{f64, f64}))));
|
||||
const auto simple_polygon_array = DataTypeArray(std::make_shared<DataTypeArray>(f64));
|
||||
const auto simple_polygon_tuple = DataTypeArray(std::make_shared<DataTypeTuple>(std::vector<DataTypePtr>{f64, f64}));
|
||||
if (key_type->equals(multi_polygon_array))
|
||||
{
|
||||
input_type = IPolygonDictionary::InputType::MultiPolygon;
|
||||
point_type = IPolygonDictionary::PointType::Array;
|
||||
}
|
||||
else if (key_type->equals(multi_polygon_tuple))
|
||||
{
|
||||
input_type = IPolygonDictionary::InputType::MultiPolygon;
|
||||
point_type = IPolygonDictionary::PointType::Tuple;
|
||||
}
|
||||
else if (key_type->equals(simple_polygon_array))
|
||||
{
|
||||
input_type = IPolygonDictionary::InputType::SimplePolygon;
|
||||
point_type = IPolygonDictionary::PointType::Array;
|
||||
}
|
||||
else if (key_type->equals(simple_polygon_tuple))
|
||||
{
|
||||
input_type = IPolygonDictionary::InputType::SimplePolygon;
|
||||
point_type = IPolygonDictionary::PointType::Tuple;
|
||||
}
|
||||
else
|
||||
throw Exception{"The key type " + key_type->getName() +
|
||||
" is not one of the following allowed types for a dictionary of layout 'polygon': " +
|
||||
multi_polygon_array.getName() + " " +
|
||||
multi_polygon_tuple.getName() + " " +
|
||||
simple_polygon_array.getName() + " " +
|
||||
simple_polygon_tuple.getName() + " ",
|
||||
ErrorCodes::BAD_ARGUMENTS};
|
||||
|
||||
if (dict_struct.range_min || dict_struct.range_max)
|
||||
throw Exception{name
|
||||
+ ": elements range_min and range_max should be defined only "
|
||||
"for a dictionary of layout 'range_hashed'",
|
||||
ErrorCodes::BAD_ARGUMENTS};
|
||||
|
||||
const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"};
|
||||
return std::make_unique<SimplePolygonDictionary>(database, name, dict_struct, std::move(source_ptr), dict_lifetime, input_type, point_type);
|
||||
};
|
||||
factory.registerLayout("polygon", create_layout, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
293
dbms/src/Dictionaries/PolygonDictionary.h
Normal file
293
dbms/src/Dictionaries/PolygonDictionary.h
Normal file
@ -0,0 +1,293 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <variant>
|
||||
#include <Core/Block.h>
|
||||
#include <Columns/ColumnDecimal.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Common/Arena.h>
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/multi_polygon.hpp>
|
||||
|
||||
#include "DictionaryStructure.h"
|
||||
#include "IDictionary.h"
|
||||
#include "IDictionarySource.h"
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace bg = boost::geometry;
|
||||
|
||||
/** An interface for polygon dictionaries.
|
||||
* Polygons are read and stored as multi_polygons from boost::geometry in Euclidean coordinates.
|
||||
* An implementation should inherit from this base class and preprocess the data upon construction if needed.
|
||||
* It must override the find method of this class which retrieves the polygon containing a single point.
|
||||
*/
|
||||
class IPolygonDictionary : public IDictionaryBase
|
||||
{
|
||||
public:
|
||||
/** Controls the different types of polygons allowed as input.
|
||||
* The structure of a multi-polygon is as follows:
|
||||
* - A multi-polygon is represented by a nonempty array of polygons.
|
||||
* - A polygon is represented by a nonempty array of rings. The first element represents the outer ring. Zero
|
||||
* or more following rings are cut out from the polygon.
|
||||
* - A ring is represented by a nonempty array of points.
|
||||
* - A point is represented by its coordinates stored in an according structure (see below).
|
||||
* A simple polygon is represented by an one-dimensional array of points, stored in the according structure.
|
||||
*/
|
||||
enum class InputType
|
||||
{
|
||||
MultiPolygon,
|
||||
SimplePolygon
|
||||
};
|
||||
/** Controls the different types allowed for providing the coordinates of points.
|
||||
* Right now a point can be represented by either an array or a tuple of two Float64 values.
|
||||
*/
|
||||
enum class PointType
|
||||
{
|
||||
Array,
|
||||
Tuple,
|
||||
};
|
||||
IPolygonDictionary(
|
||||
const std::string & database_,
|
||||
const std::string & name_,
|
||||
const DictionaryStructure & dict_struct_,
|
||||
DictionarySourcePtr source_ptr_,
|
||||
DictionaryLifetime dict_lifetime_,
|
||||
InputType input_type_,
|
||||
PointType point_type_);
|
||||
|
||||
const std::string & getDatabase() const override;
|
||||
const std::string & getName() const override;
|
||||
const std::string & getFullName() const override;
|
||||
|
||||
std::string getTypeName() const override;
|
||||
|
||||
std::string getKeyDescription() const;
|
||||
|
||||
size_t getBytesAllocated() const override;
|
||||
|
||||
size_t getQueryCount() const override;
|
||||
|
||||
double getHitRate() const override;
|
||||
|
||||
size_t getElementCount() const override;
|
||||
|
||||
double getLoadFactor() const override;
|
||||
|
||||
const IDictionarySource * getSource() const override;
|
||||
|
||||
const DictionaryStructure & getStructure() const override;
|
||||
|
||||
const DictionaryLifetime & getLifetime() const override;
|
||||
|
||||
bool isInjective(const std::string & attribute_name) const override;
|
||||
|
||||
BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override;
|
||||
|
||||
template <typename T>
|
||||
using ResultArrayType = std::conditional_t<IsDecimalNumber<T>, DecimalPaddedPODArray<T>, PaddedPODArray<T>>;
|
||||
|
||||
/** Functions used to retrieve attributes of specific type by key. */
|
||||
|
||||
#define DECLARE(TYPE) \
|
||||
void get##TYPE( \
|
||||
const std::string & attribute_name, const Columns & key_columns, const DataTypes &, ResultArrayType<TYPE> & out) const;
|
||||
DECLARE(UInt8)
|
||||
DECLARE(UInt16)
|
||||
DECLARE(UInt32)
|
||||
DECLARE(UInt64)
|
||||
DECLARE(UInt128)
|
||||
DECLARE(Int8)
|
||||
DECLARE(Int16)
|
||||
DECLARE(Int32)
|
||||
DECLARE(Int64)
|
||||
DECLARE(Float32)
|
||||
DECLARE(Float64)
|
||||
DECLARE(Decimal32)
|
||||
DECLARE(Decimal64)
|
||||
DECLARE(Decimal128)
|
||||
#undef DECLARE
|
||||
|
||||
void getString(const std::string & attribute_name, const Columns & key_columns, const DataTypes &, ColumnString * out) const;
|
||||
|
||||
#define DECLARE(TYPE) \
|
||||
void get##TYPE( \
|
||||
const std::string & attribute_name, \
|
||||
const Columns & key_columns, \
|
||||
const DataTypes &, \
|
||||
const PaddedPODArray<TYPE> & def, \
|
||||
ResultArrayType<TYPE> & out) const;
|
||||
DECLARE(UInt8)
|
||||
DECLARE(UInt16)
|
||||
DECLARE(UInt32)
|
||||
DECLARE(UInt64)
|
||||
DECLARE(UInt128)
|
||||
DECLARE(Int8)
|
||||
DECLARE(Int16)
|
||||
DECLARE(Int32)
|
||||
DECLARE(Int64)
|
||||
DECLARE(Float32)
|
||||
DECLARE(Float64)
|
||||
DECLARE(Decimal32)
|
||||
DECLARE(Decimal64)
|
||||
DECLARE(Decimal128)
|
||||
#undef DECLARE
|
||||
|
||||
void getString(
|
||||
const std::string & attribute_name,
|
||||
const Columns & key_columns,
|
||||
const DataTypes &,
|
||||
const ColumnString * const def,
|
||||
ColumnString * const out) const;
|
||||
|
||||
#define DECLARE(TYPE) \
|
||||
void get##TYPE( \
|
||||
const std::string & attribute_name, \
|
||||
const Columns & key_columns, \
|
||||
const DataTypes &, \
|
||||
const TYPE def, \
|
||||
ResultArrayType<TYPE> & out) const;
|
||||
DECLARE(UInt8)
|
||||
DECLARE(UInt16)
|
||||
DECLARE(UInt32)
|
||||
DECLARE(UInt64)
|
||||
DECLARE(UInt128)
|
||||
DECLARE(Int8)
|
||||
DECLARE(Int16)
|
||||
DECLARE(Int32)
|
||||
DECLARE(Int64)
|
||||
DECLARE(Float32)
|
||||
DECLARE(Float64)
|
||||
DECLARE(Decimal32)
|
||||
DECLARE(Decimal64)
|
||||
DECLARE(Decimal128)
|
||||
#undef DECLARE
|
||||
|
||||
void getString(
|
||||
const std::string & attribute_name,
|
||||
const Columns & key_columns,
|
||||
const DataTypes & key_types,
|
||||
const String & def,
|
||||
ColumnString * const out) const;
|
||||
|
||||
/** Checks whether or not a point can be found in one of the polygons in the dictionary.
|
||||
* The check is performed for multiple points represented by columns of their x and y coordinates.
|
||||
* The boolean result is written to out.
|
||||
*/
|
||||
// TODO: Refactor the whole dictionary design to perform stronger checks, i.e. make this an override.
|
||||
void has(const Columns & key_columns, const DataTypes & key_types, PaddedPODArray<UInt8> & out) const;
|
||||
|
||||
/** A two-dimensional point in Euclidean coordinates. */
|
||||
using Point = bg::model::point<Float64, 2, bg::cs::cartesian>;
|
||||
/** A polygon in boost is a an outer ring of points with zero or more cut out inner rings. */
|
||||
using Polygon = bg::model::polygon<Point>;
|
||||
|
||||
protected:
|
||||
/** Returns true if the given point can be found in the polygon dictionary.
|
||||
* If true id is set to the index of a polygon containing the given point.
|
||||
* Overridden in different implementations of this interface.
|
||||
*/
|
||||
virtual bool find(const Point & point, size_t & id) const = 0;
|
||||
|
||||
std::vector<Polygon> polygons;
|
||||
/** Since the original data may have been in the form of multi-polygons, an id is stored for each single polygon
|
||||
* corresponding to the row in which any other attributes for this entry are located.
|
||||
*/
|
||||
std::vector<size_t> ids;
|
||||
|
||||
const std::string database;
|
||||
const std::string name;
|
||||
const std::string full_name;
|
||||
const DictionaryStructure dict_struct;
|
||||
const DictionarySourcePtr source_ptr;
|
||||
const DictionaryLifetime dict_lifetime;
|
||||
|
||||
const InputType input_type;
|
||||
const PointType point_type;
|
||||
|
||||
private:
|
||||
/** Helper functions for loading the data from the configuration.
|
||||
* The polygons serving as keys are extracted into boost types.
|
||||
* All other values are stored in one column per attribute.
|
||||
*/
|
||||
void createAttributes();
|
||||
void blockToAttributes(const Block & block);
|
||||
void loadData();
|
||||
|
||||
void calculateBytesAllocated();
|
||||
|
||||
/** Checks whether a given attribute exists and returns its index */
|
||||
size_t getAttributeIndex(const std::string & attribute_name) const;
|
||||
|
||||
/** Helper functions to retrieve and instantiate the provided null value of an attribute.
|
||||
* Since a null value is obligatory for every attribute they are simply appended to null_values defined below.
|
||||
*/
|
||||
template <typename T>
|
||||
void appendNullValueImpl(const Field & null_value);
|
||||
void appendNullValue(AttributeUnderlyingType type, const Field & value);
|
||||
|
||||
/** Helper function for retrieving the value of an attribute by key. */
|
||||
template <typename AttributeType, typename OutputType, typename ValueSetter, typename DefaultGetter>
|
||||
void getItemsImpl(size_t attribute_ind, const Columns & key_columns, ValueSetter && set_value, DefaultGetter && get_default) const;
|
||||
|
||||
/** A mapping from the names of the attributes to their index in the two vectors defined below. */
|
||||
std::map<std::string, size_t> attribute_index_by_name;
|
||||
/** A vector of columns storing the values of each attribute. */
|
||||
Columns attributes;
|
||||
/** A vector of null values corresponding to each attribute. */
|
||||
std::vector<std::variant<
|
||||
UInt8,
|
||||
UInt16,
|
||||
UInt32,
|
||||
UInt64,
|
||||
UInt128,
|
||||
Int8,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
Decimal32,
|
||||
Decimal64,
|
||||
Decimal128,
|
||||
Float32,
|
||||
Float64,
|
||||
String>> null_values;
|
||||
|
||||
size_t bytes_allocated = 0;
|
||||
size_t element_count = 0;
|
||||
mutable std::atomic<size_t> query_count{0};
|
||||
|
||||
/** Extracts a list of polygons from a column according to input_type and point_type.
|
||||
* The polygons are appended to the dictionary with the corresponding ids.
|
||||
*/
|
||||
void extractPolygons(const ColumnPtr & column);
|
||||
|
||||
/** Extracts a list of points from two columns representing their x and y coordinates. */
|
||||
static std::vector<Point> extractPoints(const Columns &key_columns);
|
||||
};
|
||||
|
||||
/** Simple implementation of the polygon dictionary. Doesn't generate anything during its construction.
|
||||
* Iterates over all stored polygons for each query, checking each of them in linear time.
|
||||
* Retrieves the polygon with the smallest area containing the given point. If there is more than one any such polygon
|
||||
* may be returned.
|
||||
*/
|
||||
class SimplePolygonDictionary : public IPolygonDictionary
|
||||
{
|
||||
public:
|
||||
SimplePolygonDictionary(
|
||||
const std::string & database_,
|
||||
const std::string & name_,
|
||||
const DictionaryStructure & dict_struct_,
|
||||
DictionarySourcePtr source_ptr_,
|
||||
DictionaryLifetime dict_lifetime_,
|
||||
InputType input_type_,
|
||||
PointType point_type_);
|
||||
|
||||
std::shared_ptr<const IExternalLoadable> clone() const override;
|
||||
|
||||
private:
|
||||
bool find(const Point & point, size_t & id) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ void registerDictionaries()
|
||||
registerDictionaryFlat(factory);
|
||||
registerDictionaryHashed(factory);
|
||||
registerDictionaryCache(factory);
|
||||
registerDictionaryPolygon(factory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ void registerDictionaryTrie(DictionaryFactory & factory);
|
||||
void registerDictionaryFlat(DictionaryFactory & factory);
|
||||
void registerDictionaryHashed(DictionaryFactory & factory);
|
||||
void registerDictionaryCache(DictionaryFactory & factory);
|
||||
void registerDictionaryPolygon(DictionaryFactory & factory);
|
||||
|
||||
void registerDictionaries();
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <Dictionaries/ComplexKeyCacheDictionary.h>
|
||||
#include <Dictionaries/RangeHashedDictionary.h>
|
||||
#include <Dictionaries/TrieDictionary.h>
|
||||
#include <Dictionaries/PolygonDictionary.h>
|
||||
|
||||
#include <ext/range.h>
|
||||
|
||||
@ -134,7 +135,8 @@ private:
|
||||
!executeDispatchSimple<CacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyHashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyCacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<TrieDictionary>(block, arguments, result, dict_ptr))
|
||||
!executeDispatchComplex<TrieDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<SimplePolygonDictionary>(block, arguments, result, dict_ptr))
|
||||
throw Exception{"Unsupported dictionary type " + dict_ptr->getTypeName(), ErrorCodes::UNKNOWN_TYPE};
|
||||
}
|
||||
|
||||
@ -305,6 +307,7 @@ private:
|
||||
!executeDispatchComplex<ComplexKeyHashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyCacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<TrieDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<SimplePolygonDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchRange<RangeHashedDictionary>(block, arguments, result, dict_ptr))
|
||||
throw Exception{"Unsupported dictionary type " + dict_ptr->getTypeName(), ErrorCodes::UNKNOWN_TYPE};
|
||||
}
|
||||
@ -485,6 +488,7 @@ private:
|
||||
!executeDispatch<CacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyHashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyCacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<SimplePolygonDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<TrieDictionary>(block, arguments, result, dict_ptr))
|
||||
throw Exception{"Unsupported dictionary type " + dict_ptr->getTypeName(), ErrorCodes::UNKNOWN_TYPE};
|
||||
}
|
||||
@ -823,6 +827,7 @@ private:
|
||||
!executeDispatchComplex<ComplexKeyHashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyCacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<TrieDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<SimplePolygonDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchRange<RangeHashedDictionary>(block, arguments, result, dict_ptr))
|
||||
throw Exception{"Unsupported dictionary type " + dict_ptr->getTypeName(), ErrorCodes::UNKNOWN_TYPE};
|
||||
}
|
||||
@ -1081,6 +1086,7 @@ private:
|
||||
!executeDispatch<CacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyHashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<ComplexKeyCacheDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<SimplePolygonDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchComplex<TrieDictionary>(block, arguments, result, dict_ptr))
|
||||
throw Exception{"Unsupported dictionary type " + dict_ptr->getTypeName(), ErrorCodes::UNKNOWN_TYPE};
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
dictGet test_01037.dict_array (-100,-42) qqq 101
|
||||
dictGet test_01037.dict_array (-1,0) Click South 423
|
||||
dictGet test_01037.dict_array (-0.1,0) Click South 423
|
||||
dictGet test_01037.dict_array (0,-2) Click West 424
|
||||
dictGet test_01037.dict_array (0,-1.1) Click West 424
|
||||
dictGet test_01037.dict_array (0,1.1) Click North 422
|
||||
dictGet test_01037.dict_array (0,2) Click North 422
|
||||
dictGet test_01037.dict_array (0.1,0) Click East 421
|
||||
dictGet test_01037.dict_array (0.99,2.99) Click North 422
|
||||
dictGet test_01037.dict_array (1,0) Click East 421
|
||||
dictGet test_01037.dict_array (3,3) House 314159
|
||||
dictGet test_01037.dict_array (5,6) Click 42
|
||||
dictGet test_01037.dict_array (7.01,7.01) qqq 101
|
||||
dictGetOrDefault test_01037.dict_array (-100,-42) www 1234
|
||||
dictGetOrDefault test_01037.dict_array (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (3,3) House 314159
|
||||
dictGetOrDefault test_01037.dict_array (5,6) Click 42
|
||||
dictGetOrDefault test_01037.dict_array (7.01,7.01) www 1234
|
||||
dictGetOrDefault test_01037.dict_array (-100,-42) dd 44
|
||||
dictGetOrDefault test_01037.dict_array (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (3,3) House 314159
|
||||
dictGetOrDefault test_01037.dict_array (5,6) Click 42
|
||||
dictGetOrDefault test_01037.dict_array (7.01,7.01) ee 55
|
||||
dictGet test_01037.dict_tuple (-100,-42) qqq 101
|
||||
dictGet test_01037.dict_tuple (-1,0) Click South 423
|
||||
dictGet test_01037.dict_tuple (-0.1,0) Click South 423
|
||||
dictGet test_01037.dict_tuple (0,-2) Click West 424
|
||||
dictGet test_01037.dict_tuple (0,-1.1) Click West 424
|
||||
dictGet test_01037.dict_tuple (0,1.1) Click North 422
|
||||
dictGet test_01037.dict_tuple (0,2) Click North 422
|
||||
dictGet test_01037.dict_tuple (0.1,0) Click East 421
|
||||
dictGet test_01037.dict_tuple (0.99,2.99) Click North 422
|
||||
dictGet test_01037.dict_tuple (1,0) Click East 421
|
||||
dictGet test_01037.dict_tuple (3,3) House 314159
|
||||
dictGet test_01037.dict_tuple (5,6) Click 42
|
||||
dictGet test_01037.dict_tuple (7.01,7.01) qqq 101
|
||||
dictGetOrDefault test_01037.dict_tuple (-100,-42) www 1234
|
||||
dictGetOrDefault test_01037.dict_tuple (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (3,3) House 314159
|
||||
dictGetOrDefault test_01037.dict_tuple (5,6) Click 42
|
||||
dictGetOrDefault test_01037.dict_tuple (7.01,7.01) www 1234
|
||||
dictGetOrDefault test_01037.dict_tuple (-100,-42) dd 44
|
||||
dictGetOrDefault test_01037.dict_tuple (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (3,3) House 314159
|
||||
dictGetOrDefault test_01037.dict_tuple (5,6) Click 42
|
||||
dictGetOrDefault test_01037.dict_tuple (7.01,7.01) ee 55
|
||||
dictHas test_01037.dict_array (-100,-42) 0
|
||||
dictHas test_01037.dict_array (-1,0) 1
|
||||
dictHas test_01037.dict_array (-0.1,0) 1
|
||||
dictHas test_01037.dict_array (0,-2) 1
|
||||
dictHas test_01037.dict_array (0,-1.1) 1
|
||||
dictHas test_01037.dict_array (0,-1) 1
|
||||
dictHas test_01037.dict_array (0,0) 1
|
||||
dictHas test_01037.dict_array (0,1) 1
|
||||
dictHas test_01037.dict_array (0,1.1) 1
|
||||
dictHas test_01037.dict_array (0,2) 1
|
||||
dictHas test_01037.dict_array (0.1,0) 1
|
||||
dictHas test_01037.dict_array (0.99,2.99) 1
|
||||
dictHas test_01037.dict_array (1,0) 1
|
||||
dictHas test_01037.dict_array (1,1) 1
|
||||
dictHas test_01037.dict_array (1,3) 1
|
||||
dictHas test_01037.dict_array (3,3) 1
|
||||
dictHas test_01037.dict_array (5,1) 1
|
||||
dictHas test_01037.dict_array (5,5) 1
|
||||
dictHas test_01037.dict_array (5,6) 1
|
||||
dictHas test_01037.dict_array (7.01,7.01) 0
|
||||
dictHas test_01037.dict_tuple (-100,-42) 0
|
||||
dictHas test_01037.dict_tuple (-1,0) 1
|
||||
dictHas test_01037.dict_tuple (-0.1,0) 1
|
||||
dictHas test_01037.dict_tuple (0,-2) 1
|
||||
dictHas test_01037.dict_tuple (0,-1.1) 1
|
||||
dictHas test_01037.dict_tuple (0,-1) 1
|
||||
dictHas test_01037.dict_tuple (0,0) 1
|
||||
dictHas test_01037.dict_tuple (0,1) 1
|
||||
dictHas test_01037.dict_tuple (0,1.1) 1
|
||||
dictHas test_01037.dict_tuple (0,2) 1
|
||||
dictHas test_01037.dict_tuple (0.1,0) 1
|
||||
dictHas test_01037.dict_tuple (0.99,2.99) 1
|
||||
dictHas test_01037.dict_tuple (1,0) 1
|
||||
dictHas test_01037.dict_tuple (1,1) 1
|
||||
dictHas test_01037.dict_tuple (1,3) 1
|
||||
dictHas test_01037.dict_tuple (3,3) 1
|
||||
dictHas test_01037.dict_tuple (5,1) 1
|
||||
dictHas test_01037.dict_tuple (5,5) 1
|
||||
dictHas test_01037.dict_tuple (5,6) 1
|
||||
dictHas test_01037.dict_tuple (7.01,7.01) 0
|
@ -0,0 +1,107 @@
|
||||
SET send_logs_level = 'none';
|
||||
|
||||
DROP DATABASE IF EXISTS test_01037;
|
||||
|
||||
CREATE DATABASE test_01037 Engine = Ordinary;
|
||||
|
||||
DROP DICTIONARY IF EXISTS test_01037.dict_array;
|
||||
DROP TABLE IF EXISTS test_01037.polygons_array;
|
||||
|
||||
CREATE TABLE test_01037.polygons_array (key Array(Array(Array(Array(Float64)))), name String, value UInt64) ENGINE = Memory;
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[[[1, 3], [1, 1], [3, 1], [3, -1], [1, -1], [1, -3], [-1, -3], [-1, -1], [-3, -1], [-3, 1], [-1, 1], [-1, 3]]], [[[5, 5], [5, 1], [7, 1], [7, 7], [1, 7], [1, 5]]]], 'Click', 42);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[[[5, 5], [5, -5], [-5, -5], [-5, 5]], [[1, 3], [1, 1], [3, 1], [3, -1], [1, -1], [1, -3], [-1, -3], [-1, -1], [-3, -1], [-3, 1], [-1, 1], [-1, 3]]]], 'House', 314159);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[[[3, 1], [0, 1], [0, -1], [3, -1]]]], 'Click East', 421);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[[[-1, 1], [1, 1], [1, 3], [-1, 3]]]], 'Click North', 422);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[[[-3, 1], [-3, -1], [0, -1], [0, 1]]]], 'Click South', 423);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[[[-1, -1], [1, -1], [1, -3], [-1, -3]]]], 'Click West', 424);
|
||||
|
||||
CREATE DICTIONARY test_01037.dict_array
|
||||
(
|
||||
key Array(Array(Array(Array(Float64)))),
|
||||
name String DEFAULT 'qqq',
|
||||
value UInt64 DEFAULT 101
|
||||
)
|
||||
PRIMARY KEY key
|
||||
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'polygons_array' PASSWORD '' DB 'test_01037'))
|
||||
LIFETIME(MIN 1 MAX 10)
|
||||
LAYOUT(POLYGON());
|
||||
|
||||
DROP DICTIONARY IF EXISTS test_01037.dict_tuple;
|
||||
DROP TABLE IF EXISTS test_01037.polygons_tuple;
|
||||
|
||||
CREATE TABLE test_01037.polygons_tuple (key Array(Array(Array(Tuple(Float64, Float64)))), name String, value UInt64) ENGINE = Memory;
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([[[(1, 3), (1, 1), (3, 1), (3, -1), (1, -1), (1, -3), (-1, -3), (-1, -1), (-3, -1), (-3, 1), (-1, 1), (-1, 3)]], [[(5, 5), (5, 1), (7, 1), (7, 7), (1, 7), (1, 5)]]], 'Click', 42);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([[[(5, 5), (5, -5), (-5, -5), (-5, 5)], [(1, 3), (1, 1), (3, 1), (3, -1), (1, -1), (1, -3), (-1, -3), (-1, -1), (-3, -1), (-3, 1), (-1, 1), (-1, 3)]]], 'House', 314159);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([[[(3, 1), (0, 1), (0, -1), (3, -1)]]], 'Click East', 421);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([[[(-1, 1), (1, 1), (1, 3), (-1, 3)]]], 'Click North', 422);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([[[(-3, 1), (-3, -1), (0, -1), (0, 1)]]], 'Click South', 423);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([[[(-1, -1), (1, -1), (1, -3), (-1, -3)]]], 'Click West', 424);
|
||||
|
||||
CREATE DICTIONARY test_01037.dict_tuple
|
||||
(
|
||||
key Array(Array(Array(Tuple(Float64, Float64)))),
|
||||
name String DEFAULT 'qqq',
|
||||
value UInt64 DEFAULT 101
|
||||
)
|
||||
PRIMARY KEY key
|
||||
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'polygons_tuple' PASSWORD '' DB 'test_01037'))
|
||||
LIFETIME(MIN 1 MAX 10)
|
||||
LAYOUT(POLYGON());
|
||||
|
||||
DROP TABLE IF EXISTS test_01037.points;
|
||||
|
||||
CREATE TABLE test_01037.points (x Float64, y Float64, def_i UInt64, def_s String) ENGINE = Memory;
|
||||
INSERT INTO test_01037.points VALUES (0.1, 0.0, 112, 'aax');
|
||||
INSERT INTO test_01037.points VALUES (-0.1, 0.0, 113, 'aay');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 1.1, 114, 'aaz');
|
||||
INSERT INTO test_01037.points VALUES (0.0, -1.1, 115, 'aat');
|
||||
INSERT INTO test_01037.points VALUES (3.0, 3.0, 22, 'bb');
|
||||
INSERT INTO test_01037.points VALUES (5.0, 6.0, 33, 'cc');
|
||||
INSERT INTO test_01037.points VALUES (-100.0, -42.0, 44, 'dd');
|
||||
INSERT INTO test_01037.points VALUES (7.01, 7.01, 55, 'ee')
|
||||
INSERT INTO test_01037.points VALUES (0.99, 2.99, 66, 'ee');
|
||||
INSERT INTO test_01037.points VALUES (1.0, 0.0, 771, 'ffa');
|
||||
INSERT INTO test_01037.points VALUES (-1.0, 0.0, 772, 'ffb');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 2.0, 773, 'ffc');
|
||||
INSERT INTO test_01037.points VALUES (0.0, -2.0, 774, 'ffd');
|
||||
|
||||
select 'dictGet', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictGet(dict_name, 'name', key),
|
||||
dictGet(dict_name, 'value', key) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, 'www'),
|
||||
dictGetOrDefault(dict_name, 'value', key, toUInt64(1234)) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, def_s),
|
||||
dictGetOrDefault(dict_name, 'value', key, def_i) from test_01037.points order by x, y;
|
||||
|
||||
select 'dictGet', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictGet(dict_name, 'name', key),
|
||||
dictGet(dict_name, 'value', key) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, 'www'),
|
||||
dictGetOrDefault(dict_name, 'value', key, toUInt64(1234)) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, def_s),
|
||||
dictGetOrDefault(dict_name, 'value', key, def_i) from test_01037.points order by x, y;
|
||||
|
||||
INSERT INTO test_01037.points VALUES (5.0, 5.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (5.0, 1.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (1.0, 3.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 0.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 1.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (0.0, -1.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (1.0, 1.0, 0, '');
|
||||
|
||||
select 'dictHas', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictHas(dict_name, key) from test_01037.points order by x, y;
|
||||
|
||||
select 'dictHas', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictHas(dict_name, key) from test_01037.points order by x, y;
|
||||
|
||||
DROP DICTIONARY test_01037.dict_array;
|
||||
DROP DICTIONARY test_01037.dict_tuple;
|
||||
DROP TABLE test_01037.polygons_array;
|
||||
DROP TABLE test_01037.polygons_tuple;
|
||||
DROP TABLE test_01037.points;
|
||||
DROP DATABASE test_01037;
|
@ -0,0 +1,142 @@
|
||||
dictGet test_01037.dict_array (-100,-42) qqq 101
|
||||
dictGet test_01037.dict_array (-1,0) Click South 423
|
||||
dictGet test_01037.dict_array (-0.1,0) Click South 423
|
||||
dictGet test_01037.dict_array (0,-2) Click West 424
|
||||
dictGet test_01037.dict_array (0,-1.1) Click West 424
|
||||
dictGet test_01037.dict_array (0,1.1) Click North 422
|
||||
dictGet test_01037.dict_array (0,2) Click North 422
|
||||
dictGet test_01037.dict_array (0.1,0) Click East 421
|
||||
dictGet test_01037.dict_array (0.99,2.99) Click North 422
|
||||
dictGet test_01037.dict_array (1,0) Click East 421
|
||||
dictGet test_01037.dict_array (2,4) House 523
|
||||
dictGet test_01037.dict_array (2,4.1) qqq 101
|
||||
dictGet test_01037.dict_array (3,3) House 523
|
||||
dictGet test_01037.dict_array (4,4) House 523
|
||||
dictGet test_01037.dict_array (5,6) qqq 101
|
||||
dictGet test_01037.dict_array (7.01,7.01) qqq 101
|
||||
dictGetOrDefault test_01037.dict_array (-100,-42) www 1234
|
||||
dictGetOrDefault test_01037.dict_array (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (2,4) House 523
|
||||
dictGetOrDefault test_01037.dict_array (2,4.1) www 1234
|
||||
dictGetOrDefault test_01037.dict_array (3,3) House 523
|
||||
dictGetOrDefault test_01037.dict_array (4,4) House 523
|
||||
dictGetOrDefault test_01037.dict_array (5,6) www 1234
|
||||
dictGetOrDefault test_01037.dict_array (7.01,7.01) www 1234
|
||||
dictGetOrDefault test_01037.dict_array (-100,-42) dd 44
|
||||
dictGetOrDefault test_01037.dict_array (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_array (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_array (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_array (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_array (2,4) House 523
|
||||
dictGetOrDefault test_01037.dict_array (2,4.1) gac 803
|
||||
dictGetOrDefault test_01037.dict_array (3,3) House 523
|
||||
dictGetOrDefault test_01037.dict_array (4,4) House 523
|
||||
dictGetOrDefault test_01037.dict_array (5,6) cc 33
|
||||
dictGetOrDefault test_01037.dict_array (7.01,7.01) ee 55
|
||||
dictGet test_01037.dict_tuple (-100,-42) qqq 101
|
||||
dictGet test_01037.dict_tuple (-1,0) Click South 423
|
||||
dictGet test_01037.dict_tuple (-0.1,0) Click South 423
|
||||
dictGet test_01037.dict_tuple (0,-2) Click West 424
|
||||
dictGet test_01037.dict_tuple (0,-1.1) Click West 424
|
||||
dictGet test_01037.dict_tuple (0,1.1) Click North 422
|
||||
dictGet test_01037.dict_tuple (0,2) Click North 422
|
||||
dictGet test_01037.dict_tuple (0.1,0) Click East 421
|
||||
dictGet test_01037.dict_tuple (0.99,2.99) Click North 422
|
||||
dictGet test_01037.dict_tuple (1,0) Click East 421
|
||||
dictGet test_01037.dict_tuple (2,4) House 523
|
||||
dictGet test_01037.dict_tuple (2,4.1) qqq 101
|
||||
dictGet test_01037.dict_tuple (3,3) House 523
|
||||
dictGet test_01037.dict_tuple (4,4) House 523
|
||||
dictGet test_01037.dict_tuple (5,6) qqq 101
|
||||
dictGet test_01037.dict_tuple (7.01,7.01) qqq 101
|
||||
dictGetOrDefault test_01037.dict_tuple (-100,-42) www 1234
|
||||
dictGetOrDefault test_01037.dict_tuple (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (2,4) House 523
|
||||
dictGetOrDefault test_01037.dict_tuple (2,4.1) www 1234
|
||||
dictGetOrDefault test_01037.dict_tuple (3,3) House 523
|
||||
dictGetOrDefault test_01037.dict_tuple (4,4) House 523
|
||||
dictGetOrDefault test_01037.dict_tuple (5,6) www 1234
|
||||
dictGetOrDefault test_01037.dict_tuple (7.01,7.01) www 1234
|
||||
dictGetOrDefault test_01037.dict_tuple (-100,-42) dd 44
|
||||
dictGetOrDefault test_01037.dict_tuple (-1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (-0.1,0) Click South 423
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-2) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,-1.1) Click West 424
|
||||
dictGetOrDefault test_01037.dict_tuple (0,1.1) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0,2) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (0.1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (0.99,2.99) Click North 422
|
||||
dictGetOrDefault test_01037.dict_tuple (1,0) Click East 421
|
||||
dictGetOrDefault test_01037.dict_tuple (2,4) House 523
|
||||
dictGetOrDefault test_01037.dict_tuple (2,4.1) gac 803
|
||||
dictGetOrDefault test_01037.dict_tuple (3,3) House 523
|
||||
dictGetOrDefault test_01037.dict_tuple (4,4) House 523
|
||||
dictGetOrDefault test_01037.dict_tuple (5,6) cc 33
|
||||
dictGetOrDefault test_01037.dict_tuple (7.01,7.01) ee 55
|
||||
dictHas test_01037.dict_array (-100,-42) 0
|
||||
dictHas test_01037.dict_array (-1,0) 1
|
||||
dictHas test_01037.dict_array (-0.1,0) 1
|
||||
dictHas test_01037.dict_array (0,-2) 1
|
||||
dictHas test_01037.dict_array (0,-1.1) 1
|
||||
dictHas test_01037.dict_array (0,-1) 1
|
||||
dictHas test_01037.dict_array (0,0) 1
|
||||
dictHas test_01037.dict_array (0,1) 1
|
||||
dictHas test_01037.dict_array (0,1.1) 1
|
||||
dictHas test_01037.dict_array (0,2) 1
|
||||
dictHas test_01037.dict_array (0.1,0) 1
|
||||
dictHas test_01037.dict_array (0.99,2.99) 1
|
||||
dictHas test_01037.dict_array (1,0) 1
|
||||
dictHas test_01037.dict_array (1,1) 1
|
||||
dictHas test_01037.dict_array (1,3) 1
|
||||
dictHas test_01037.dict_array (2,4) 1
|
||||
dictHas test_01037.dict_array (2,4.1) 0
|
||||
dictHas test_01037.dict_array (3,3) 1
|
||||
dictHas test_01037.dict_array (4,4) 1
|
||||
dictHas test_01037.dict_array (5,1) 1
|
||||
dictHas test_01037.dict_array (5,5) 1
|
||||
dictHas test_01037.dict_array (5,6) 0
|
||||
dictHas test_01037.dict_array (7.01,7.01) 0
|
||||
dictHas test_01037.dict_tuple (-100,-42) 0
|
||||
dictHas test_01037.dict_tuple (-1,0) 1
|
||||
dictHas test_01037.dict_tuple (-0.1,0) 1
|
||||
dictHas test_01037.dict_tuple (0,-2) 1
|
||||
dictHas test_01037.dict_tuple (0,-1.1) 1
|
||||
dictHas test_01037.dict_tuple (0,-1) 1
|
||||
dictHas test_01037.dict_tuple (0,0) 1
|
||||
dictHas test_01037.dict_tuple (0,1) 1
|
||||
dictHas test_01037.dict_tuple (0,1.1) 1
|
||||
dictHas test_01037.dict_tuple (0,2) 1
|
||||
dictHas test_01037.dict_tuple (0.1,0) 1
|
||||
dictHas test_01037.dict_tuple (0.99,2.99) 1
|
||||
dictHas test_01037.dict_tuple (1,0) 1
|
||||
dictHas test_01037.dict_tuple (1,1) 1
|
||||
dictHas test_01037.dict_tuple (1,3) 1
|
||||
dictHas test_01037.dict_tuple (2,4) 1
|
||||
dictHas test_01037.dict_tuple (2,4.1) 0
|
||||
dictHas test_01037.dict_tuple (3,3) 1
|
||||
dictHas test_01037.dict_tuple (4,4) 1
|
||||
dictHas test_01037.dict_tuple (5,1) 1
|
||||
dictHas test_01037.dict_tuple (5,5) 1
|
||||
dictHas test_01037.dict_tuple (5,6) 0
|
||||
dictHas test_01037.dict_tuple (7.01,7.01) 0
|
@ -0,0 +1,108 @@
|
||||
SET send_logs_level = 'none';
|
||||
|
||||
DROP DATABASE IF EXISTS test_01037;
|
||||
|
||||
CREATE DATABASE test_01037 Engine = Ordinary;
|
||||
|
||||
DROP DICTIONARY IF EXISTS test_01037.dict_array;
|
||||
DROP TABLE IF EXISTS test_01037.polygons_array;
|
||||
|
||||
CREATE TABLE test_01037.polygons_array (key Array(Array(Float64)), name String, value UInt64) ENGINE = Memory;
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[3, 1], [0, 1], [0, -1], [3, -1]], 'Click East', 421);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[-1, 1], [1, 1], [1, 3], [-1, 3]], 'Click North', 422);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[-3, 1], [-3, -1], [0, -1], [0, 1]], 'Click South', 423);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[-1, -1], [1, -1], [1, -3], [-1, -3]], 'Click West', 424);
|
||||
INSERT INTO test_01037.polygons_array VALUES ([[1, 1], [1, 3], [3, 5], [5, 5], [5, 1]], 'House', 523);
|
||||
|
||||
CREATE DICTIONARY test_01037.dict_array
|
||||
(
|
||||
key Array(Array(Float64)),
|
||||
name String DEFAULT 'qqq',
|
||||
value UInt64 DEFAULT 101
|
||||
)
|
||||
PRIMARY KEY key
|
||||
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'polygons_array' PASSWORD '' DB 'test_01037'))
|
||||
LIFETIME(MIN 1 MAX 10)
|
||||
LAYOUT(POLYGON());
|
||||
|
||||
DROP DICTIONARY IF EXISTS test_01037.dict_tuple;
|
||||
DROP TABLE IF EXISTS test_01037.polygons_tuple;
|
||||
|
||||
CREATE TABLE test_01037.polygons_tuple (key Array(Tuple(Float64, Float64)), name String, value UInt64) ENGINE = Memory;
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([(3.0, 1.0), (0.0, 1.0), (0.0, -1.0), (3.0, -1.0)], 'Click East', 421);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([(-1, 1), (1, 1), (1, 3), (-1, 3)], 'Click North', 422);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([(-3, 1), (-3, -1), (0, -1), (0, 1)], 'Click South', 423);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([(-1, -1), (1, -1), (1, -3), (-1, -3)], 'Click West', 424);
|
||||
INSERT INTO test_01037.polygons_tuple VALUES ([(1, 1), (1, 3), (3, 5), (5, 5), (5, 1)], 'House', 523);
|
||||
|
||||
CREATE DICTIONARY test_01037.dict_tuple
|
||||
(
|
||||
key Array(Tuple(Float64, Float64)),
|
||||
name String DEFAULT 'qqq',
|
||||
value UInt64 DEFAULT 101
|
||||
)
|
||||
PRIMARY KEY key
|
||||
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'polygons_tuple' PASSWORD '' DB 'test_01037'))
|
||||
LIFETIME(MIN 1 MAX 10)
|
||||
LAYOUT(POLYGON());
|
||||
|
||||
DROP TABLE IF EXISTS test_01037.points;
|
||||
|
||||
CREATE TABLE test_01037.points (x Float64, y Float64, def_i UInt64, def_s String) ENGINE = Memory;
|
||||
INSERT INTO test_01037.points VALUES (0.1, 0.0, 112, 'aax');
|
||||
INSERT INTO test_01037.points VALUES (-0.1, 0.0, 113, 'aay');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 1.1, 114, 'aaz');
|
||||
INSERT INTO test_01037.points VALUES (0.0, -1.1, 115, 'aat');
|
||||
INSERT INTO test_01037.points VALUES (3.0, 3.0, 22, 'bb');
|
||||
INSERT INTO test_01037.points VALUES (5.0, 6.0, 33, 'cc');
|
||||
INSERT INTO test_01037.points VALUES (-100.0, -42.0, 44, 'dd');
|
||||
INSERT INTO test_01037.points VALUES (7.01, 7.01, 55, 'ee')
|
||||
INSERT INTO test_01037.points VALUES (0.99, 2.99, 66, 'ee');
|
||||
INSERT INTO test_01037.points VALUES (1.0, 0.0, 771, 'ffa');
|
||||
INSERT INTO test_01037.points VALUES (-1.0, 0.0, 772, 'ffb');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 2.0, 773, 'ffc');
|
||||
INSERT INTO test_01037.points VALUES (0.0, -2.0, 774, 'ffd');
|
||||
INSERT INTO test_01037.points VALUES (2.0, 4.0, 801, 'gaa')
|
||||
INSERT INTO test_01037.points VALUES (4.0, 4.0, 802, 'gab')
|
||||
INSERT INTO test_01037.points VALUES (2.0, 4.1, 803, 'gac')
|
||||
|
||||
select 'dictGet', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictGet(dict_name, 'name', key),
|
||||
dictGet(dict_name, 'value', key) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, 'www'),
|
||||
dictGetOrDefault(dict_name, 'value', key, toUInt64(1234)) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, def_s),
|
||||
dictGetOrDefault(dict_name, 'value', key, def_i) from test_01037.points order by x, y;
|
||||
|
||||
select 'dictGet', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictGet(dict_name, 'name', key),
|
||||
dictGet(dict_name, 'value', key) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, 'www'),
|
||||
dictGetOrDefault(dict_name, 'value', key, toUInt64(1234)) from test_01037.points order by x, y;
|
||||
select 'dictGetOrDefault', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictGetOrDefault(dict_name, 'name', key, def_s),
|
||||
dictGetOrDefault(dict_name, 'value', key, def_i) from test_01037.points order by x, y;
|
||||
|
||||
INSERT INTO test_01037.points VALUES (5.0, 5.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (5.0, 1.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (1.0, 3.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 0.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (0.0, 1.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (0.0, -1.0, 0, '');
|
||||
INSERT INTO test_01037.points VALUES (1.0, 1.0, 0, '');
|
||||
|
||||
select 'dictHas', 'test_01037.dict_array' as dict_name, tuple(x, y) as key,
|
||||
dictHas(dict_name, key) from test_01037.points order by x, y;
|
||||
|
||||
select 'dictHas', 'test_01037.dict_tuple' as dict_name, tuple(x, y) as key,
|
||||
dictHas(dict_name, key) from test_01037.points order by x, y;
|
||||
|
||||
DROP DICTIONARY test_01037.dict_array;
|
||||
DROP DICTIONARY test_01037.dict_tuple;
|
||||
DROP TABLE test_01037.polygons_array;
|
||||
DROP TABLE test_01037.polygons_tuple;
|
||||
DROP TABLE test_01037.points;
|
||||
DROP DATABASE test_01037;
|
Loading…
Reference in New Issue
Block a user