mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 08:32:02 +00:00
Added support for named tuples in function tupleElement [#CLICKHOUSE-2].
This commit is contained in:
parent
5c2c4b34f3
commit
0f51be4191
@ -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(); });
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
(1,'Hello') Tuple(x UInt64, s String) 1 Hello 1 Hello
|
1
dbms/tests/queries/0_stateless/00547_named_tuples.sql
Normal file
1
dbms/tests/queries/0_stateless/00547_named_tuples.sql
Normal 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');
|
Loading…
Reference in New Issue
Block a user