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 DUPLICATE_COLUMN;
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
{
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 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
{
@ -834,61 +834,58 @@ public:
return 2;
}
bool useDefaultImplementationForConstants() const override
{
return true;
}
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override
{
return {1};
}
void getReturnTypeAndPrerequisitesImpl(
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());
if (!tuple)
throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
if (index == 0)
throw Exception("Indices in tuples are 1-based.", ErrorCodes::ILLEGAL_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];
size_t index = getElementNum(arguments[1].column, *tuple);
out_return_type = tuple->getElements()[index];
}
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());
auto const_tuple_col = checkAndGetColumnConst<ColumnTuple>(block.getByPosition(arguments[0]).column.get());
auto index_col = checkAndGetColumnConst<ColumnUInt8>(block.getByPosition(arguments[1]).column.get());
if (!tuple_col && !const_tuple_col)
if (!tuple_col)
throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_COLUMN);
if (!index_col)
throw Exception("Second argument for function " + getName() + " must be UInt8 constant literal.", ErrorCodes::ILLEGAL_COLUMN);
size_t index = getElementNum(block.getByPosition(arguments[1]).column, typeid_cast<const DataTypeTuple &>(*block.getByPosition(arguments[0]).type));
block.getByPosition(result).column = tuple_col->getColumns()[index];
}
size_t index = index_col->getValue<UInt8>();
if (index == 0)
throw Exception("Indices in tuples is 1-based.", ErrorCodes::ILLEGAL_INDEX);
if (tuple_col)
private:
size_t getElementNum(const ColumnPtr & index_column, const DataTypeTuple & tuple)
{
if (auto index_col = checkAndGetColumnConst<ColumnUInt8>(index_column.get()))
{
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);
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
{
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]);
}
throw Exception("Second argument to " + getName() + " must be a constant UInt8 or String", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
};

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');