Added support for named tuples in function tupleElement [#CLICKHOUSE-2].

This commit is contained in:
Alexey Milovidov 2017-12-24 12:34:40 +03:00
parent 5c2c4b34f3
commit 0f51be4191
5 changed files with 47 additions and 35 deletions

View File

@ -24,6 +24,7 @@ namespace ErrorCodes
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int DUPLICATE_COLUMN; extern const int DUPLICATE_COLUMN;
extern const int BAD_ARGUMENTS; extern const int BAD_ARGUMENTS;
extern const int NOT_FOUND_COLUMN_IN_BLOCK;
} }
@ -366,6 +367,16 @@ bool DataTypeTuple::equals(const IDataType & rhs) const
} }
size_t DataTypeTuple::getPositionByName(const String & name) const
{
size_t size = elems.size();
for (size_t i = 0; i < size; ++i)
if (names[i] == name)
return i;
throw Exception("Tuple doesn't have element with name '" + name + "'", ErrorCodes::NOT_FOUND_COLUMN_IN_BLOCK);
}
bool DataTypeTuple::textCanContainOnlyValidUTF8() const bool DataTypeTuple::textCanContainOnlyValidUTF8() const
{ {
return std::all_of(elems.begin(), elems.end(), [](auto && elem) { return elem->textCanContainOnlyValidUTF8(); }); return std::all_of(elems.begin(), elems.end(), [](auto && elem) { return elem->textCanContainOnlyValidUTF8(); });

View File

@ -88,6 +88,8 @@ public:
const DataTypes & getElements() const { return elems; } const DataTypes & getElements() const { return elems; }
const Strings & getElementNames() const { return names; } const Strings & getElementNames() const { return names; }
size_t getPositionByName(const String & name) const;
}; };
} }

View File

@ -813,7 +813,7 @@ public:
}; };
/** Extract element of tuple by constant index. The operation is essentially free. /** Extract element of tuple by constant index or name. The operation is essentially free.
*/ */
class FunctionTupleElement : public IFunction class FunctionTupleElement : public IFunction
{ {
@ -834,61 +834,58 @@ public:
return 2; return 2;
} }
bool useDefaultImplementationForConstants() const override
{
return true;
}
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override
{
return {1};
}
void getReturnTypeAndPrerequisitesImpl( void getReturnTypeAndPrerequisitesImpl(
const ColumnsWithTypeAndName & arguments, DataTypePtr & out_return_type, ExpressionActions::Actions & /*out_prerequisites*/) override const ColumnsWithTypeAndName & arguments, DataTypePtr & out_return_type, ExpressionActions::Actions & /*out_prerequisites*/) override
{ {
auto index_col = checkAndGetColumnConst<ColumnUInt8>(arguments[1].column.get());
if (!index_col)
throw Exception("Second argument to " + getName() + " must be a constant UInt8", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
size_t index = index_col->getValue<UInt8>();
const DataTypeTuple * tuple = checkAndGetDataType<DataTypeTuple>(arguments[0].type.get()); const DataTypeTuple * tuple = checkAndGetDataType<DataTypeTuple>(arguments[0].type.get());
if (!tuple) if (!tuple)
throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
if (index == 0) size_t index = getElementNum(arguments[1].column, *tuple);
throw Exception("Indices in tuples are 1-based.", ErrorCodes::ILLEGAL_INDEX); out_return_type = tuple->getElements()[index];
const DataTypes & elems = tuple->getElements();
if (index > elems.size())
throw Exception("Index for tuple element is out of range.", ErrorCodes::ILLEGAL_INDEX);
out_return_type = elems[index - 1];
} }
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) override void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) override
{ {
const ColumnTuple * tuple_col = typeid_cast<const ColumnTuple *>(block.getByPosition(arguments[0]).column.get()); const ColumnTuple * tuple_col = typeid_cast<const ColumnTuple *>(block.getByPosition(arguments[0]).column.get());
auto const_tuple_col = checkAndGetColumnConst<ColumnTuple>(block.getByPosition(arguments[0]).column.get()); if (!tuple_col)
auto index_col = checkAndGetColumnConst<ColumnUInt8>(block.getByPosition(arguments[1]).column.get());
if (!tuple_col && !const_tuple_col)
throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_COLUMN); throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_COLUMN);
if (!index_col) size_t index = getElementNum(block.getByPosition(arguments[1]).column, typeid_cast<const DataTypeTuple &>(*block.getByPosition(arguments[0]).type));
throw Exception("Second argument for function " + getName() + " must be UInt8 constant literal.", ErrorCodes::ILLEGAL_COLUMN); block.getByPosition(result).column = tuple_col->getColumns()[index];
}
size_t index = index_col->getValue<UInt8>(); private:
if (index == 0) size_t getElementNum(const ColumnPtr & index_column, const DataTypeTuple & tuple)
throw Exception("Indices in tuples is 1-based.", ErrorCodes::ILLEGAL_INDEX); {
if (auto index_col = checkAndGetColumnConst<ColumnUInt8>(index_column.get()))
if (tuple_col)
{ {
const Columns & tuple_columns = tuple_col->getColumns(); size_t index = index_col->getValue<UInt8>();
if (index > tuple_columns.size()) if (index == 0)
throw Exception("Indices in tuples are 1-based.", ErrorCodes::ILLEGAL_INDEX);
if (index > tuple.getElements().size())
throw Exception("Index for tuple element is out of range.", ErrorCodes::ILLEGAL_INDEX); throw Exception("Index for tuple element is out of range.", ErrorCodes::ILLEGAL_INDEX);
block.getByPosition(result).column = tuple_columns[index - 1]; return index - 1;
}
else if (auto name_col = checkAndGetColumnConst<ColumnString>(index_column.get()))
{
return tuple.getPositionByName(name_col->getValue<String>());
} }
else else
{ throw Exception("Second argument to " + getName() + " must be a constant UInt8 or String", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
TupleBackend data = const_tuple_col->getValue<Tuple>();
block.getByPosition(result).column = static_cast<const DataTypeTuple &>(*block.getByPosition(arguments[0]).type)
.getElements()[index - 1]->createColumnConst(block.rows(), data[index - 1]);
}
} }
}; };

View File

@ -0,0 +1 @@
(1,'Hello') Tuple(x UInt64, s String) 1 Hello 1 Hello

View File

@ -0,0 +1 @@
SELECT CAST((1, 'Hello') AS Tuple(x UInt64, s String)) AS t, toTypeName(t), t.1, t.2, tupleElement(t, 'x'), tupleElement(t, 's');