2022-10-19 09:13:02 +00:00
# include <Columns/ColumnsDateTime.h>
2019-02-11 11:59:17 +00:00
# include <Columns/ColumnsNumber.h>
2023-12-11 15:27:41 +00:00
# include <Common/DateLUTImpl.h>
# include <Common/IntervalKind.h>
2019-02-11 11:59:17 +00:00
# include <DataTypes/DataTypeDate.h>
2021-07-15 11:41:52 +00:00
# include <DataTypes/DataTypeDate32.h>
2019-02-11 11:59:17 +00:00
# include <DataTypes/DataTypeDateTime.h>
2019-11-04 14:06:22 +00:00
# include <DataTypes/DataTypeDateTime64.h>
2019-02-11 11:59:17 +00:00
# include <DataTypes/DataTypeInterval.h>
# include <Functions/DateTimeTransforms.h>
# include <Functions/FunctionFactory.h>
2021-05-17 07:30:42 +00:00
# include <Functions/IFunction.h>
2019-02-11 11:59:17 +00:00
# include <IO/WriteHelpers.h>
2023-12-11 15:27:41 +00:00
# include <base/arithmeticOverflow.h>
2019-02-11 11:59:17 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH ;
extern const int ILLEGAL_COLUMN ;
extern const int ILLEGAL_TYPE_OF_ARGUMENT ;
extern const int ARGUMENT_OUT_OF_BOUND ;
}
class FunctionToStartOfInterval : public IFunction
{
public :
2021-04-10 23:33:54 +00:00
static FunctionPtr create ( ContextPtr ) { return std : : make_shared < FunctionToStartOfInterval > ( ) ; }
2019-02-11 11:59:17 +00:00
2023-09-29 16:52:48 +00:00
static constexpr auto name = " toStartOfInterval " ;
2019-02-11 11:59:17 +00:00
String getName ( ) const override { return name ; }
bool isVariadic ( ) const override { return true ; }
size_t getNumberOfArguments ( ) const override { return 0 ; }
2021-06-22 16:21:23 +00:00
bool isSuitableForShortCircuitArgumentsExecution ( const DataTypesWithConstInfo & /*arguments*/ ) const override { return false ; }
2023-09-29 16:52:48 +00:00
bool useDefaultImplementationForConstants ( ) const override { return true ; }
ColumnNumbers getArgumentsThatAreAlwaysConstant ( ) const override { return { 1 , 2 } ; }
bool hasInformationAboutMonotonicity ( ) const override { return true ; }
2023-12-11 15:27:41 +00:00
Monotonicity getMonotonicityForRange ( const IDataType & , const Field & , const Field & ) const override { return { . is_monotonic = true , . is_always_monotonic = true } ; }
2019-02-11 11:59:17 +00:00
DataTypePtr getReturnTypeImpl ( const ColumnsWithTypeAndName & arguments ) const override
{
2023-12-11 15:27:41 +00:00
bool value_is_date = false ;
2019-02-12 14:18:00 +00:00
auto check_first_argument = [ & ]
{
2023-12-11 15:27:41 +00:00
const DataTypePtr & type_arg1 = arguments [ 0 ] . type ;
if ( ! isDate ( type_arg1 ) & & ! isDateTime ( type_arg1 ) & & ! isDateTime64 ( type_arg1 ) )
throw Exception ( ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT ,
" Illegal type {} of 1st argument of function {}, expected a Date, DateTime or DateTime64 " ,
type_arg1 - > getName ( ) , getName ( ) ) ;
value_is_date = isDate ( type_arg1 ) ;
2019-02-11 11:59:17 +00:00
} ;
const DataTypeInterval * interval_type = nullptr ;
2023-12-11 15:27:41 +00:00
enum class ResultType
{
Date ,
DateTime ,
DateTime64
} ;
ResultType result_type ;
auto check_second_argument = [ & ]
2019-02-12 14:18:00 +00:00
{
2023-12-11 15:27:41 +00:00
const DataTypePtr & type_arg2 = arguments [ 1 ] . type ;
interval_type = checkAndGetDataType < DataTypeInterval > ( type_arg2 . get ( ) ) ;
2019-02-11 11:59:17 +00:00
if ( ! interval_type )
2023-12-11 15:27:41 +00:00
throw Exception ( ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT ,
" Illegal type {} of 2nd argument of function {}, expected a time interval " ,
type_arg2 - > getName ( ) , getName ( ) ) ;
2023-10-08 16:30:02 +00:00
switch ( interval_type - > getKind ( ) ) // NOLINT(bugprone-switch-missing-default-case)
2023-09-29 16:52:48 +00:00
{
case IntervalKind : : Nanosecond :
case IntervalKind : : Microsecond :
case IntervalKind : : Millisecond :
2023-12-11 15:27:41 +00:00
result_type = ResultType : : DateTime64 ;
2023-09-29 16:52:48 +00:00
break ;
case IntervalKind : : Second :
case IntervalKind : : Minute :
case IntervalKind : : Hour :
2023-12-11 15:27:41 +00:00
case IntervalKind : : Day : /// weird why Day leads to DateTime but too afraid to change it
result_type = ResultType : : DateTime ;
2023-09-29 16:52:48 +00:00
break ;
case IntervalKind : : Week :
case IntervalKind : : Month :
case IntervalKind : : Quarter :
case IntervalKind : : Year :
2023-12-11 15:27:41 +00:00
result_type = ResultType : : Date ;
2023-09-29 16:52:48 +00:00
break ;
}
2019-02-11 11:59:17 +00:00
} ;
2023-12-11 15:27:41 +00:00
auto check_third_argument = [ & ]
2019-02-12 14:18:00 +00:00
{
2023-12-11 15:27:41 +00:00
const DataTypePtr & type_arg3 = arguments [ 2 ] . type ;
if ( ! isString ( type_arg3 ) )
throw Exception ( ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT ,
" Illegal type {} of 3rd argument of function {}, expected a constant timezone string " ,
type_arg3 - > getName ( ) , getName ( ) ) ;
if ( value_is_date & & result_type = = ResultType : : Date ) /// weird why this is && instead of || but too afraid to change it
2021-09-06 14:24:03 +00:00
throw Exception ( ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT ,
2023-12-11 15:27:41 +00:00
" The timezone argument of function {} with interval type {} is allowed only when the 1st argument has type DateTime or DateTimt64 " ,
getName ( ) , interval_type - > getKind ( ) . toString ( ) ) ;
2019-02-11 11:59:17 +00:00
} ;
if ( arguments . size ( ) = = 2 )
{
2019-02-12 14:18:00 +00:00
check_first_argument ( ) ;
2023-12-11 15:27:41 +00:00
check_second_argument ( ) ;
2019-02-11 11:59:17 +00:00
}
else if ( arguments . size ( ) = = 3 )
{
2019-02-12 14:18:00 +00:00
check_first_argument ( ) ;
2023-12-11 15:27:41 +00:00
check_second_argument ( ) ;
check_third_argument ( ) ;
2019-02-11 11:59:17 +00:00
}
else
{
2023-01-23 21:13:58 +00:00
throw Exception ( ErrorCodes : : NUMBER_OF_ARGUMENTS_DOESNT_MATCH ,
" Number of arguments for function {} doesn't match: passed {}, should be 2 or 3 " ,
getName ( ) , arguments . size ( ) ) ;
2019-02-11 11:59:17 +00:00
}
2023-12-11 15:27:41 +00:00
switch ( result_type )
2022-02-15 23:43:08 +00:00
{
2023-12-11 15:27:41 +00:00
case ResultType : : Date :
return std : : make_shared < DataTypeDate > ( ) ;
case ResultType : : DateTime :
return std : : make_shared < DataTypeDateTime > ( extractTimeZoneNameFromFunctionArguments ( arguments , 2 , 0 , false ) ) ;
case ResultType : : DateTime64 :
{
UInt32 scale = 0 ;
if ( interval_type - > getKind ( ) = = IntervalKind : : Nanosecond )
scale = 9 ;
else if ( interval_type - > getKind ( ) = = IntervalKind : : Microsecond )
scale = 6 ;
else if ( interval_type - > getKind ( ) = = IntervalKind : : Millisecond )
scale = 3 ;
return std : : make_shared < DataTypeDateTime64 > ( scale , extractTimeZoneNameFromFunctionArguments ( arguments , 2 , 0 , false ) ) ;
}
2022-02-15 23:43:08 +00:00
}
2023-12-11 15:27:41 +00:00
std : : unreachable ( ) ;
2019-02-11 11:59:17 +00:00
}
2022-02-15 23:43:08 +00:00
ColumnPtr executeImpl ( const ColumnsWithTypeAndName & arguments , const DataTypePtr & result_type , size_t /* input_rows_count */ ) const override
2019-02-11 11:59:17 +00:00
{
2020-10-19 15:27:41 +00:00
const auto & time_column = arguments [ 0 ] ;
const auto & interval_column = arguments [ 1 ] ;
2020-04-17 13:26:44 +00:00
const auto & time_zone = extractTimeZoneFromFunctionArguments ( arguments , 2 , 0 ) ;
2023-09-29 16:52:48 +00:00
auto result_column = dispatchForTimeColumn ( time_column , interval_column , result_type , time_zone ) ;
2020-10-19 15:27:41 +00:00
return result_column ;
2019-02-11 11:59:17 +00:00
}
private :
2023-09-29 16:52:48 +00:00
ColumnPtr dispatchForTimeColumn (
2023-12-11 15:27:41 +00:00
const ColumnWithTypeAndName & time_column , const ColumnWithTypeAndName & interval_column ,
const DataTypePtr & result_type , const DateLUTImpl & time_zone ) const
2019-02-11 11:59:17 +00:00
{
2023-12-11 15:27:41 +00:00
const auto & time_column_type = * time_column . type . get ( ) ;
const auto & time_column_col = * time_column . column . get ( ) ;
2022-02-15 23:43:08 +00:00
2023-12-11 15:27:41 +00:00
if ( isDateTime64 ( time_column_type ) )
2022-02-15 23:43:08 +00:00
{
2023-12-11 15:27:41 +00:00
const auto * time_column_vec = checkAndGetColumn < ColumnDateTime64 > ( time_column_col ) ;
auto scale = assert_cast < const DataTypeDateTime64 & > ( time_column_type ) . getScale ( ) ;
2022-02-15 23:43:08 +00:00
if ( time_column_vec )
2023-12-11 15:27:41 +00:00
return dispatchForIntervalColumn ( assert_cast < const DataTypeDateTime64 & > ( time_column_type ) , * time_column_vec , interval_column , result_type , time_zone , scale ) ;
2019-02-11 11:59:17 +00:00
}
2023-12-11 15:27:41 +00:00
else if ( isDateTime ( time_column_type ) )
2019-02-11 11:59:17 +00:00
{
2023-12-11 15:27:41 +00:00
const auto * time_column_vec = checkAndGetColumn < ColumnDateTime > ( time_column_col ) ;
2019-02-11 11:59:17 +00:00
if ( time_column_vec )
2023-12-11 15:27:41 +00:00
return dispatchForIntervalColumn ( assert_cast < const DataTypeDateTime & > ( time_column_type ) , * time_column_vec , interval_column , result_type , time_zone ) ;
2019-10-22 07:43:14 +00:00
}
2023-12-11 15:27:41 +00:00
else if ( isDate ( time_column_type ) )
2021-07-15 11:41:52 +00:00
{
2023-12-11 15:27:41 +00:00
const auto * time_column_vec = checkAndGetColumn < ColumnDate > ( time_column_col ) ;
2021-07-15 11:41:52 +00:00
if ( time_column_vec )
2023-12-11 15:27:41 +00:00
return dispatchForIntervalColumn ( assert_cast < const DataTypeDate & > ( time_column_type ) , * time_column_vec , interval_column , result_type , time_zone ) ;
2019-02-11 11:59:17 +00:00
}
2023-12-11 15:27:41 +00:00
throw Exception ( ErrorCodes : : ILLEGAL_TYPE_OF_ARGUMENT , " Illegal column for 1st argument of function {}, expected a Date, DateTime or DateTime64 " , getName ( ) ) ;
2019-02-11 11:59:17 +00:00
}
2023-12-11 15:27:41 +00:00
template < typename TimeDataType , typename TimeColumnType >
2019-02-11 11:59:17 +00:00
ColumnPtr dispatchForIntervalColumn (
2023-09-29 16:52:48 +00:00
const TimeDataType & time_data_type , const TimeColumnType & time_column , const ColumnWithTypeAndName & interval_column ,
2023-12-11 15:27:41 +00:00
const DataTypePtr & result_type , const DateLUTImpl & time_zone , UInt16 scale = 1 ) const
2019-02-11 11:59:17 +00:00
{
const auto * interval_type = checkAndGetDataType < DataTypeInterval > ( interval_column . type . get ( ) ) ;
if ( ! interval_type )
2023-12-11 15:27:41 +00:00
throw Exception ( ErrorCodes : : ILLEGAL_COLUMN , " Illegal column for 2nd argument of function {}, must be a time interval " , getName ( ) ) ;
2023-09-29 16:52:48 +00:00
2019-02-11 11:59:17 +00:00
const auto * interval_column_const_int64 = checkAndGetColumnConst < ColumnInt64 > ( interval_column . column . get ( ) ) ;
if ( ! interval_column_const_int64 )
2023-12-11 15:27:41 +00:00
throw Exception ( ErrorCodes : : ILLEGAL_COLUMN , " Illegal column for 2nd argument of function {}, must be a const time interval " , getName ( ) ) ;
2023-09-29 16:52:48 +00:00
2023-12-11 15:27:41 +00:00
const Int64 num_units = interval_column_const_int64 - > getValue < Int64 > ( ) ;
2019-02-11 11:59:17 +00:00
if ( num_units < = 0 )
2023-12-11 15:27:41 +00:00
throw Exception ( ErrorCodes : : ARGUMENT_OUT_OF_BOUND , " Value for 2nd argument of function {} must be positive " , getName ( ) ) ;
2019-02-11 11:59:17 +00:00
2023-10-08 16:30:02 +00:00
switch ( interval_type - > getKind ( ) ) // NOLINT(bugprone-switch-missing-default-case)
2019-02-11 11:59:17 +00:00
{
2022-02-09 07:28:15 +00:00
case IntervalKind : : Nanosecond :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDateTime64 , IntervalKind : : Nanosecond > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2022-02-09 07:28:15 +00:00
case IntervalKind : : Microsecond :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDateTime64 , IntervalKind : : Microsecond > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2022-02-09 07:28:15 +00:00
case IntervalKind : : Millisecond :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDateTime64 , IntervalKind : : Millisecond > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Second :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDateTime , IntervalKind : : Second > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Minute :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDateTime , IntervalKind : : Minute > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Hour :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDateTime , IntervalKind : : Hour > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Day :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDateTime , IntervalKind : : Day > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Week :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDate , IntervalKind : : Week > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Month :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDate , IntervalKind : : Month > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Quarter :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDate , IntervalKind : : Quarter > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-11-16 13:54:52 +00:00
case IntervalKind : : Year :
2023-12-11 15:27:41 +00:00
return execute < TimeDataType , TimeColumnType , DataTypeDate , IntervalKind : : Year > ( time_data_type , time_column , num_units , result_type , time_zone , scale ) ;
2019-02-11 11:59:17 +00:00
}
2023-12-11 15:27:41 +00:00
std : : unreachable ( ) ;
2019-02-11 11:59:17 +00:00
}
2023-12-11 15:27:41 +00:00
template < typename TimeDataType , typename TimeColumnType , typename ResultDataType , IntervalKind : : Kind unit >
ColumnPtr execute (
const TimeDataType & , const TimeColumnType & time_column_type , Int64 num_units ,
const DataTypePtr & result_type , const DateLUTImpl & time_zone , UInt16 scale ) const
2019-02-11 11:59:17 +00:00
{
2023-12-11 15:27:41 +00:00
using ResultColumnType = typename ResultDataType : : ColumnType ;
using ResultFieldType = typename ResultDataType : : FieldType ;
2022-02-15 23:43:08 +00:00
const auto & time_data = time_column_type . getData ( ) ;
size_t size = time_data . size ( ) ;
auto result_col = result_type - > createColumn ( ) ;
2023-12-11 15:27:41 +00:00
auto * col_to = assert_cast < ResultColumnType * > ( result_col . get ( ) ) ;
2022-02-15 23:43:08 +00:00
auto & result_data = col_to - > getData ( ) ;
2019-02-11 11:59:17 +00:00
result_data . resize ( size ) ;
2019-10-22 07:43:14 +00:00
2022-02-15 23:43:08 +00:00
Int64 scale_multiplier = DecimalUtils : : scaleMultiplier < DateTime64 > ( scale ) ;
for ( size_t i = 0 ; i ! = size ; + + i )
2023-12-11 15:27:41 +00:00
result_data [ i ] = static_cast < ResultFieldType > ( ToStartOfInterval < unit > : : execute ( time_data [ i ] , num_units , time_zone , scale_multiplier ) ) ;
2022-02-15 23:43:08 +00:00
return result_col ;
2019-02-11 11:59:17 +00:00
}
} ;
2022-07-04 07:01:39 +00:00
REGISTER_FUNCTION ( ToStartOfInterval )
2019-02-11 11:59:17 +00:00
{
factory . registerFunction < FunctionToStartOfInterval > ( ) ;
}
}