2021-05-17 07:30:42 +00:00
# include <Functions/IFunction.h>
2017-12-25 19:10:25 +00:00
# include <Functions/FunctionFactory.h>
# include <Functions/FunctionHelpers.h>
# include <DataTypes/IDataType.h>
# include <DataTypes/DataTypeTuple.h>
# include <DataTypes/DataTypeArray.h>
# include <Columns/ColumnTuple.h>
# include <Columns/ColumnArray.h>
# include <Columns/ColumnString.h>
# include <Columns/ColumnsNumber.h>
2019-08-21 02:28:04 +00:00
# include <Common/assert_cast.h>
2018-04-24 07:16:39 +00:00
# include <memory>
2017-12-25 19:10:25 +00:00
namespace DB
{
namespace ErrorCodes
{
2018-09-10 00:58:04 +00:00
extern const int ILLEGAL_TYPE_OF_ARGUMENT ;
2017-12-25 19:10:25 +00:00
extern const int ILLEGAL_INDEX ;
2022-07-08 06:21:59 +00:00
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH ;
extern const int NOT_FOUND_COLUMN_IN_BLOCK ;
2022-07-13 07:52:12 +00:00
extern const int NUMBER_OF_DIMENSIONS_MISMATCHED ;
2022-07-13 07:01:32 +00:00
extern const int SIZES_OF_ARRAYS_DOESNT_MATCH ;
2017-12-25 19:10:25 +00:00
}
2020-09-07 18:00:37 +00:00
namespace
{
2017-12-25 19:10:25 +00:00
/** Extract element of tuple by constant index or name. The operation is essentially free.
* Also the function looks through Arrays : you can get Array of tuple elements from Array of Tuples .
*/
class FunctionTupleElement : public IFunction
{
public :
static constexpr auto name = " tupleElement " ;
2021-06-01 12:20:52 +00:00
static FunctionPtr create ( ContextPtr )
2017-12-25 19:10:25 +00:00
{
return std : : make_shared < FunctionTupleElement > ( ) ;
}
String getName ( ) const override
{
return name ;
}
2022-07-08 06:21:59 +00:00
bool isVariadic ( ) const override { return true ; }
2017-12-25 19:10:25 +00:00
size_t getNumberOfArguments ( ) const override
{
2022-07-08 06:21:59 +00:00
return 0 ;
2017-12-25 19:10:25 +00:00
}
bool useDefaultImplementationForConstants ( ) const override
{
return true ;
}
ColumnNumbers getArgumentsThatAreAlwaysConstant ( ) const override
{
2022-07-13 07:01:32 +00:00
return { 1 } ;
2017-12-25 19:10:25 +00:00
}
2021-06-22 16:21:23 +00:00
bool isSuitableForShortCircuitArgumentsExecution ( const DataTypesWithConstInfo & /*arguments*/ ) const override { return false ; }
2021-04-29 14:48:26 +00:00
2018-02-02 08:33:36 +00:00
DataTypePtr getReturnTypeImpl ( const ColumnsWithTypeAndName & arguments ) const override
2017-12-25 19:10:25 +00:00
{
2022-07-08 06:21:59 +00:00
const size_t number_of_arguments = arguments . size ( ) ;
if ( number_of_arguments < 2 | | number_of_arguments > 3 )
throw Exception ( " Number of arguments for function " + getName ( ) + " doesn't match: passed "
+ toString ( number_of_arguments ) + " , should be 2 or 3 " ,
ErrorCodes : : NUMBER_OF_ARGUMENTS_DOESNT_MATCH ) ;
2017-12-25 19:10:25 +00:00
size_t count_arrays = 0 ;
const IDataType * tuple_col = arguments [ 0 ] . type . get ( ) ;
while ( const DataTypeArray * array = checkAndGetDataType < DataTypeArray > ( tuple_col ) )
{
tuple_col = array - > getNestedType ( ) . get ( ) ;
+ + count_arrays ;
}
const DataTypeTuple * tuple = checkAndGetDataType < DataTypeTuple > ( tuple_col ) ;
if ( ! tuple )
throw Exception ( " First argument for function " + getName ( ) + " must be tuple or array of tuple. " , ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT ) ;
2022-07-13 07:01:32 +00:00
auto index = getElementNum ( arguments [ 1 ] . column , * tuple , number_of_arguments ) ;
if ( index . has_value ( ) )
2022-07-08 06:21:59 +00:00
{
2022-07-13 07:01:32 +00:00
DataTypePtr out_return_type = tuple - > getElements ( ) [ index . value ( ) ] ;
2022-07-08 06:21:59 +00:00
2022-07-13 07:01:32 +00:00
for ( ; count_arrays ; - - count_arrays )
out_return_type = std : : make_shared < DataTypeArray > ( out_return_type ) ;
2017-12-25 19:10:25 +00:00
2022-07-13 07:01:32 +00:00
return out_return_type ;
2022-07-16 11:50:08 +00:00
}
else
2022-07-13 07:01:32 +00:00
{
const IDataType * default_col = arguments [ 2 ] . type . get ( ) ;
size_t default_argument_count_arrays = 0 ;
if ( const DataTypeArray * array = checkAndGetDataType < DataTypeArray > ( default_col ) )
{
default_argument_count_arrays = array - > getNumberOfDimensions ( ) ;
}
2018-02-02 08:33:36 +00:00
2022-07-13 07:01:32 +00:00
if ( count_arrays ! = default_argument_count_arrays )
{
2022-07-13 07:52:12 +00:00
throw Exception ( ErrorCodes : : NUMBER_OF_DIMENSIONS_MISMATCHED , " Dimension of types mismatched between first argument and third argument. Dimension of 1st argument: {}. Dimension of 3rd argument: {}. " , count_arrays , default_argument_count_arrays ) ;
2022-07-13 07:01:32 +00:00
}
return arguments [ 2 ] . type ;
}
2017-12-25 19:10:25 +00:00
}
2022-07-13 07:01:32 +00:00
ColumnPtr executeImpl ( const ColumnsWithTypeAndName & arguments , const DataTypePtr & , size_t input_rows_count ) const override
2017-12-25 19:10:25 +00:00
{
Columns array_offsets ;
2020-10-19 15:27:41 +00:00
const auto & first_arg = arguments [ 0 ] ;
2017-12-25 19:10:25 +00:00
const IDataType * tuple_type = first_arg . type . get ( ) ;
const IColumn * tuple_col = first_arg . column . get ( ) ;
2022-07-13 07:01:32 +00:00
bool first_arg_is_const = false ;
if ( typeid_cast < const ColumnConst * > ( tuple_col ) )
{
tuple_col = assert_cast < const ColumnConst * > ( tuple_col ) - > getDataColumnPtr ( ) . get ( ) ;
first_arg_is_const = true ;
}
2017-12-25 19:10:25 +00:00
while ( const DataTypeArray * array_type = checkAndGetDataType < DataTypeArray > ( tuple_type ) )
{
2019-08-21 02:28:04 +00:00
const ColumnArray * array_col = assert_cast < const ColumnArray * > ( tuple_col ) ;
2017-12-25 19:10:25 +00:00
tuple_type = array_type - > getNestedType ( ) . get ( ) ;
tuple_col = & array_col - > getData ( ) ;
array_offsets . push_back ( array_col - > getOffsetsPtr ( ) ) ;
}
const DataTypeTuple * tuple_type_concrete = checkAndGetDataType < DataTypeTuple > ( tuple_type ) ;
const ColumnTuple * tuple_col_concrete = checkAndGetColumn < ColumnTuple > ( tuple_col ) ;
if ( ! tuple_type_concrete | | ! tuple_col_concrete )
throw Exception ( " First argument for function " + getName ( ) + " must be tuple or array of tuple. " , ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT ) ;
2022-07-13 07:01:32 +00:00
auto index = getElementNum ( arguments [ 1 ] . column , * tuple_type_concrete , arguments . size ( ) ) ;
if ( ! index . has_value ( ) )
2022-07-08 06:21:59 +00:00
{
2022-07-13 07:01:32 +00:00
if ( ! array_offsets . empty ( ) )
{
2022-07-15 05:39:04 +00:00
recursiveCheckArrayOffsets ( arguments [ 0 ] . column , arguments [ 2 ] . column , array_offsets . size ( ) ) ;
2022-07-13 07:01:32 +00:00
}
2022-07-08 10:17:31 +00:00
return arguments [ 2 ] . column ;
2022-07-08 06:21:59 +00:00
}
2022-07-13 07:01:32 +00:00
ColumnPtr res = tuple_col_concrete - > getColumns ( ) [ index . value ( ) ] ;
2017-12-25 19:10:25 +00:00
/// Wrap into Arrays
for ( auto it = array_offsets . rbegin ( ) ; it ! = array_offsets . rend ( ) ; + + it )
res = ColumnArray : : create ( res , * it ) ;
2022-07-13 07:01:32 +00:00
if ( first_arg_is_const )
{
res = ColumnConst : : create ( res , input_rows_count ) ;
}
2020-10-19 15:27:41 +00:00
return res ;
2017-12-25 19:10:25 +00:00
}
private :
2022-07-13 07:01:32 +00:00
2022-07-15 05:39:04 +00:00
void recursiveCheckArrayOffsets ( ColumnPtr col_x , ColumnPtr col_y , size_t depth ) const
{
for ( size_t i = 1 ; i < depth ; + + i )
{
checkArrayOffsets ( col_x , col_y ) ;
col_x = assert_cast < const ColumnArray * > ( col_x . get ( ) ) - > getDataPtr ( ) ;
col_y = assert_cast < const ColumnArray * > ( col_y . get ( ) ) - > getDataPtr ( ) ;
}
checkArrayOffsets ( col_x , col_y ) ;
}
2022-07-13 14:22:54 +00:00
void checkArrayOffsets ( ColumnPtr col_x , ColumnPtr col_y ) const
2022-07-13 07:01:32 +00:00
{
if ( isColumnConst ( * col_x ) )
{
2022-07-13 14:22:54 +00:00
checkArrayOffsetsWithFirstArgConst ( col_x , col_y ) ;
2022-07-16 11:50:08 +00:00
}
else if ( isColumnConst ( * col_y ) )
2022-07-13 07:01:32 +00:00
{
2022-07-13 14:22:54 +00:00
checkArrayOffsetsWithFirstArgConst ( col_y , col_x ) ;
2022-07-16 11:50:08 +00:00
}
else
2022-07-13 07:01:32 +00:00
{
const auto & array_x = * assert_cast < const ColumnArray * > ( col_x . get ( ) ) ;
const auto & array_y = * assert_cast < const ColumnArray * > ( col_y . get ( ) ) ;
if ( ! array_x . hasEqualOffsets ( array_y ) )
{
throw Exception ( " The argument 1 and argument 3 of function " + getName ( ) + " have different array sizes " , ErrorCodes : : SIZES_OF_ARRAYS_DOESNT_MATCH ) ;
}
}
}
2022-07-13 14:22:54 +00:00
void checkArrayOffsetsWithFirstArgConst ( ColumnPtr col_x , ColumnPtr col_y ) const
2022-07-13 07:01:32 +00:00
{
col_x = assert_cast < const ColumnConst * > ( col_x . get ( ) ) - > getDataColumnPtr ( ) ;
col_y = col_y - > convertToFullColumnIfConst ( ) ;
const auto & array_x = * assert_cast < const ColumnArray * > ( col_x . get ( ) ) ;
const auto & array_y = * assert_cast < const ColumnArray * > ( col_y . get ( ) ) ;
const auto & offsets_x = array_x . getOffsets ( ) ;
const auto & offsets_y = array_y . getOffsets ( ) ;
ColumnArray : : Offset prev_offset = 0 ;
size_t row_size = offsets_y . size ( ) ;
for ( size_t row = 0 ; row < row_size ; + + row )
{
if ( unlikely ( offsets_x [ 0 ] ! = offsets_y [ row ] - prev_offset ) )
{
throw Exception ( " The argument 1 and argument 3 of function " + getName ( ) + " have different array sizes " , ErrorCodes : : SIZES_OF_ARRAYS_DOESNT_MATCH ) ;
}
prev_offset = offsets_y [ row ] ;
}
}
std : : optional < size_t > getElementNum ( const ColumnPtr & index_column , const DataTypeTuple & tuple , const size_t argument_size ) const
2017-12-25 19:10:25 +00:00
{
2020-05-13 03:53:40 +00:00
if (
checkAndGetColumnConst < ColumnUInt8 > ( index_column . get ( ) )
| | checkAndGetColumnConst < ColumnUInt16 > ( index_column . get ( ) )
| | checkAndGetColumnConst < ColumnUInt32 > ( index_column . get ( ) )
| | checkAndGetColumnConst < ColumnUInt64 > ( index_column . get ( ) )
)
2017-12-25 19:10:25 +00:00
{
2022-07-13 07:01:32 +00:00
size_t index = index_column - > getUInt ( 0 ) ;
2017-12-25 19:10:25 +00:00
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 ) ;
2022-07-13 07:01:32 +00:00
return std : : optional < size_t > ( index - 1 ) ;
2017-12-25 19:10:25 +00:00
}
2020-04-22 08:45:14 +00:00
else if ( const auto * name_col = checkAndGetColumnConst < ColumnString > ( index_column . get ( ) ) )
2017-12-25 19:10:25 +00:00
{
2022-07-13 07:01:32 +00:00
auto index = tuple . tryGetPositionByName ( name_col - > getValue < String > ( ) ) ;
if ( index . has_value ( ) )
2022-07-08 06:21:59 +00:00
{
2022-07-13 07:01:32 +00:00
return index ;
2022-07-08 06:21:59 +00:00
}
if ( argument_size = = 2 )
{
throw Exception ( " Tuple doesn't have element with name ' " + name_col - > getValue < String > ( ) + " ' " , ErrorCodes : : NOT_FOUND_COLUMN_IN_BLOCK ) ;
}
2022-07-13 07:01:32 +00:00
return std : : nullopt ;
2017-12-25 19:10:25 +00:00
}
else
2020-05-13 03:53:40 +00:00
throw Exception ( " Second argument to " + getName ( ) + " must be a constant UInt or String " , ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT ) ;
2017-12-25 19:10:25 +00:00
}
} ;
2020-09-07 18:00:37 +00:00
}
2017-12-25 19:10:25 +00:00
2018-09-10 00:58:04 +00:00
void registerFunctionTupleElement ( FunctionFactory & factory )
2017-12-25 19:10:25 +00:00
{
factory . registerFunction < FunctionTupleElement > ( ) ;
}
}