2019-09-07 14:22:22 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# encoding: utf-8
|
|
|
|
|
|
|
|
import re
|
|
|
|
import itertools
|
2019-10-04 13:49:34 +00:00
|
|
|
import sys
|
2019-10-24 04:46:52 +00:00
|
|
|
import argparse
|
2019-09-07 14:22:22 +00:00
|
|
|
|
|
|
|
# Create SQL statement to verify dateTime64 is accepted as argument to functions taking DateTime.
|
2019-10-24 04:46:52 +00:00
|
|
|
FUNCTIONS="""
|
|
|
|
toTimeZone(N, 'UTC')
|
|
|
|
toYear(N)
|
|
|
|
toQuarter(N)
|
|
|
|
toMonth(N)
|
|
|
|
toDayOfYear(N)
|
|
|
|
toDayOfMonth(N)
|
|
|
|
toDayOfWeek(N)
|
|
|
|
toHour(N)
|
|
|
|
toMinute(N)
|
|
|
|
toSecond(N)
|
|
|
|
toUnixTimestamp(N)
|
|
|
|
toStartOfYear(N)
|
|
|
|
toStartOfISOYear(N)
|
|
|
|
toStartOfQuarter(N)
|
|
|
|
toStartOfMonth(N)
|
|
|
|
toMonday(N)
|
|
|
|
toStartOfWeek(N)
|
|
|
|
toStartOfDay(N)
|
|
|
|
toStartOfHour(N)
|
|
|
|
toStartOfMinute(N)
|
|
|
|
toStartOfFiveMinute(N)
|
|
|
|
toStartOfTenMinutes(N)
|
|
|
|
toStartOfFifteenMinutes(N)
|
|
|
|
toStartOfInterval(N, INTERVAL 1 year)
|
|
|
|
toStartOfInterval(N, INTERVAL 1 month)
|
|
|
|
toStartOfInterval(N, INTERVAL 1 day)
|
|
|
|
toStartOfInterval(N, INTERVAL 15 minute)
|
|
|
|
toTime(N)
|
|
|
|
toRelativeYearNum(N)
|
|
|
|
toRelativeQuarterNum(N)
|
|
|
|
toRelativeMonthNum(N)
|
|
|
|
toRelativeWeekNum(N)
|
|
|
|
toRelativeDayNum(N)
|
|
|
|
toRelativeHourNum(N)
|
|
|
|
toRelativeMinuteNum(N)
|
|
|
|
toRelativeSecondNum(N)
|
|
|
|
toISOYear(N)
|
|
|
|
toISOWeek(N)
|
|
|
|
toWeek(N)
|
|
|
|
toYearWeek(N)
|
|
|
|
timeSlot(N)
|
|
|
|
toYYYYMM(N)
|
|
|
|
toYYYYMMDD(N)
|
|
|
|
toYYYYMMDDhhmmss(N)
|
|
|
|
addYears(N, 1)
|
|
|
|
addMonths(N, 1)
|
|
|
|
addWeeks(N, 1)
|
|
|
|
addDays(N, 1)
|
|
|
|
addHours(N, 1)
|
|
|
|
addMinutes(N, 1)
|
|
|
|
addSeconds(N, 1)
|
|
|
|
addQuarters(N, 1)
|
|
|
|
subtractYears(N, 1)
|
|
|
|
subtractMonths(N, 1)
|
|
|
|
subtractWeeks(N, 1)
|
|
|
|
subtractDays(N, 1)
|
|
|
|
subtractHours(N, 1)
|
|
|
|
subtractMinutes(N, 1)
|
|
|
|
subtractSeconds(N, 1)
|
|
|
|
subtractQuarters(N, 1)
|
|
|
|
CAST(N as DateTime)
|
|
|
|
CAST(N as Date)
|
|
|
|
CAST(N as UInt64)
|
|
|
|
CAST(N as DateTime64(0))
|
|
|
|
CAST(N as DateTime64(3))
|
|
|
|
CAST(N as DateTime64(6))
|
|
|
|
CAST(N as DateTime64(9))
|
2019-11-10 13:23:02 +00:00
|
|
|
# Casting our test values to DateTime(12) will cause an overflow and hence will fail the test under UB sanitizer.
|
|
|
|
# CAST(N as DateTime64(12))
|
|
|
|
# DateTime64(18) will always fail due to zero precision, but it is Ok to test here:
|
|
|
|
# CAST(N as DateTime64(18))
|
2019-10-31 08:27:48 +00:00
|
|
|
formatDateTime(N, '%C %d %D %e %F %H %I %j %m %M %p %R %S %T %u %V %w %y %Y %%')
|
2019-09-07 14:22:22 +00:00
|
|
|
""".splitlines()
|
|
|
|
|
2019-10-21 11:46:38 +00:00
|
|
|
# Expanded later to cartesian product of all arguments.
|
|
|
|
# NOTE: {N} to be turned into N after str.format() for keys (format string), but not for list of values!
|
2019-09-07 14:22:22 +00:00
|
|
|
extra_ops =\
|
|
|
|
[
|
|
|
|
# With same type:
|
|
|
|
(
|
2019-10-21 11:46:38 +00:00
|
|
|
['N {op} N'],
|
2019-09-07 14:22:22 +00:00
|
|
|
{
|
|
|
|
'op':
|
|
|
|
[
|
|
|
|
'- ', # does not work, but should it?
|
|
|
|
'+ ', # does not work, but should it?
|
2019-10-08 04:57:31 +00:00
|
|
|
'!=', '==', # equality and inequality supposed to take sub-second part in account
|
2019-09-07 14:22:22 +00:00
|
|
|
'< ',
|
|
|
|
'<=',
|
|
|
|
'> ',
|
|
|
|
'>='
|
|
|
|
]
|
|
|
|
}
|
|
|
|
),
|
|
|
|
# With other DateTime types:
|
|
|
|
(
|
2019-10-04 13:49:34 +00:00
|
|
|
[
|
2019-10-21 11:46:38 +00:00
|
|
|
'N {op} {arg}',
|
|
|
|
'{arg} {op} N'
|
2019-10-04 13:49:34 +00:00
|
|
|
],
|
2019-09-07 14:22:22 +00:00
|
|
|
{
|
|
|
|
'op':
|
|
|
|
[
|
|
|
|
'-', # does not work, but should it?
|
2019-10-08 04:57:31 +00:00
|
|
|
'!=', '==',
|
2019-09-07 14:22:22 +00:00
|
|
|
# these are naturally expected to work, but they don't:
|
|
|
|
'< ',
|
|
|
|
'<=',
|
|
|
|
'> ',
|
|
|
|
'>='
|
|
|
|
],
|
2019-10-21 11:46:38 +00:00
|
|
|
'arg': ['DT', 'D', 'DT64'],
|
2019-09-07 14:22:22 +00:00
|
|
|
}
|
|
|
|
),
|
|
|
|
# With arithmetic types
|
|
|
|
(
|
2019-10-04 13:49:34 +00:00
|
|
|
[
|
2019-10-21 11:46:38 +00:00
|
|
|
'N {op} {arg}',
|
|
|
|
'{arg} {op} N'
|
2019-10-04 13:49:34 +00:00
|
|
|
],
|
2019-09-07 14:22:22 +00:00
|
|
|
{
|
|
|
|
'op':
|
|
|
|
[
|
|
|
|
'+ ',
|
|
|
|
'- ',
|
|
|
|
'==',
|
|
|
|
'!=',
|
|
|
|
'< ',
|
|
|
|
'<=',
|
|
|
|
'> ',
|
|
|
|
'>='
|
|
|
|
],
|
|
|
|
'arg':
|
|
|
|
[
|
2019-10-21 14:04:01 +00:00
|
|
|
'toUInt8(1)',
|
|
|
|
'toInt8(-1)',
|
|
|
|
'toUInt16(1)',
|
|
|
|
'toInt16(-1)',
|
|
|
|
'toUInt32(1)',
|
|
|
|
'toInt32(-1)',
|
|
|
|
'toUInt64(1)',
|
2019-09-07 14:22:22 +00:00
|
|
|
'toInt64(-1)'
|
|
|
|
],
|
|
|
|
},
|
|
|
|
),
|
|
|
|
]
|
|
|
|
|
|
|
|
# Expand extra_ops here
|
2019-10-04 13:49:34 +00:00
|
|
|
for funcs, args in extra_ops:
|
2019-09-07 14:22:22 +00:00
|
|
|
args_keys = args.keys()
|
2019-10-08 04:57:31 +00:00
|
|
|
for args_vals in itertools.product(*args.values()):
|
|
|
|
for func in funcs:
|
2019-10-04 13:49:34 +00:00
|
|
|
result_func = func.format(**dict(zip(args_keys, args_vals)))
|
2019-10-24 04:46:52 +00:00
|
|
|
FUNCTIONS.append(result_func)
|
2019-09-07 14:22:22 +00:00
|
|
|
|
2019-10-22 07:45:00 +00:00
|
|
|
# filter out empty lines and commented out lines
|
|
|
|
COMMENTED_OUT_LINE_RE = re.compile(r"^\s*#")
|
2019-10-24 04:46:52 +00:00
|
|
|
FUNCTIONS = list(filter(lambda f: len(f) != 0 and COMMENTED_OUT_LINE_RE.match(f) == None, FUNCTIONS))
|
|
|
|
TYPES = ['D', 'DT', 'DT64']
|
2019-10-22 07:45:00 +00:00
|
|
|
|
2019-10-04 13:49:34 +00:00
|
|
|
if sys.version_info[0] > 2:
|
|
|
|
escape_string_codec = 'unicode_escape'
|
|
|
|
else:
|
|
|
|
escape_string_codec = 'string-escape'
|
|
|
|
|
|
|
|
def escape_string(s):
|
|
|
|
return s.encode(escape_string_codec).decode('utf-8')
|
2019-10-03 02:07:36 +00:00
|
|
|
|
2019-10-24 04:46:52 +00:00
|
|
|
|
|
|
|
def execute_functions_for_types(functions, types):
|
|
|
|
# TODO: use string.Template here to allow lines that do not contain type, like: SELECT CAST(toDateTime64(1234567890), 'DateTime64')
|
|
|
|
for func in functions:
|
2019-11-07 10:58:16 +00:00
|
|
|
print("""SELECT 'SELECT {func}';""".format(func=escape_string(func)))
|
2019-10-24 04:46:52 +00:00
|
|
|
for dt in types:
|
|
|
|
prologue = "\
|
|
|
|
WITH \
|
|
|
|
toDateTime64('2019-09-16 19:20:11.234', 3, 'Europe/Minsk') as DT64, \
|
|
|
|
toDateTime('2019-09-16 19:20:11', 'Europe/Minsk') as DT, \
|
|
|
|
toDate('2019-09-16') as D, {X} as N".format(X=dt)
|
2019-11-07 10:58:16 +00:00
|
|
|
print("""{prologue} SELECT toTypeName(r), {func} as r FORMAT CSV;""".format(prologue=prologue, func=func))
|
|
|
|
print("""SELECT '------------------------------------------';""")
|
2019-10-24 04:46:52 +00:00
|
|
|
|
|
|
|
def main():
|
|
|
|
def parse_args():
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument('--functions_re', type=re.compile, help="RE to enable functions", default=None)
|
|
|
|
parser.add_argument('--types_re',
|
|
|
|
type=lambda s: re.compile('^(' + s + ')$'),
|
|
|
|
help="RE to enable types, supported types: " + ",".join(TYPES), default=None)
|
|
|
|
parser.add_argument('--list_functions', action='store_true', help="List all functions to be tested and exit")
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
|
|
args = parse_args()
|
|
|
|
|
|
|
|
functions = FUNCTIONS
|
|
|
|
types = TYPES
|
|
|
|
|
|
|
|
if args.functions_re:
|
|
|
|
functions = list(filter(lambda f : args.functions_re.search(f), functions))
|
|
|
|
if len(functions) == 0:
|
|
|
|
print("functions list is empty")
|
|
|
|
return -1
|
|
|
|
|
|
|
|
if args.types_re:
|
|
|
|
types = list(filter(lambda t : args.types_re.match(t), types))
|
|
|
|
if len(types) == 0:
|
|
|
|
print("types list is empty")
|
|
|
|
return -1
|
|
|
|
|
|
|
|
if args.list_functions:
|
|
|
|
print("\n".join(functions))
|
|
|
|
return 0
|
|
|
|
|
|
|
|
execute_functions_for_types(functions, types)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
exit(main())
|