Skip to content

Commit

Permalink
fix: BI-5410 Quarter support for PostgreSQL and Oracle (#645)
Browse files Browse the repository at this point in the history
* Testcase for quarter

* tests for "quarter" interval

* Added support for "quarter" interval

* Cleaned up updated defenition

* Added support for quarter interval
  • Loading branch information
khamitovdr authored Oct 14, 2024
1 parent 7dbb52b commit 5bb413d
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,18 @@ class FuncDatetrunc2Oracle(base.FuncDatetrunc2):
DEFINITIONS_DATETIME = [
# dateadd
base.FuncDateadd1.for_dialect(D.ORACLE),
base.FuncDateadd2Unit.for_dialect(D.ORACLE),
base.FuncDateadd2Unit(
variants=[
V(
D.ORACLE,
lambda date, what: (
sa.cast(date + datetime_interval(un_literal(what), 1, literal_mult=True), sa.Date)
if un_literal(what).lower() != "quarter"
else sa.cast(date + datetime_interval("month", 3, literal_mult=True), sa.Date)
),
),
]
),
base.FuncDateadd2Number.for_dialect(D.ORACLE),
base.FuncDateadd3Legacy.for_dialect(D.ORACLE),
base.FuncDateadd3DateConstNum(
Expand All @@ -68,6 +79,8 @@ class FuncDatetrunc2Oracle(base.FuncDatetrunc2):
D.ORACLE,
lambda date, what, num: (
sa.cast(date + datetime_interval(un_literal(what), un_literal(num), literal_mult=True), sa.Date)
if un_literal(what).lower() != "quarter"
else sa.cast(date + datetime_interval("month", 3 * un_literal(num), literal_mult=True), sa.Date)
),
),
]
Expand All @@ -78,6 +91,8 @@ class FuncDatetrunc2Oracle(base.FuncDatetrunc2):
D.ORACLE,
lambda dt, what, num: (
sa.cast(dt + datetime_interval(what.value, num.value, literal_mult=True), sa.Date)
if what.value.lower() != "quarter"
else sa.cast(dt + datetime_interval("month", 3 * num.value, literal_mult=True), sa.Date)
),
),
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,20 @@ def _datetrunc2_pg_tz_impl(date_ctx: TranslationCtx, unit_ctx: TranslationCtx) -
DEFINITIONS_DATETIME = [
# dateadd
base.FuncDateadd1.for_dialect(D.POSTGRESQL),
base.FuncDateadd2Unit.for_dialect(D.POSTGRESQL),
base.FuncDateadd2Unit(
variants=[
V(
D.POSTGRESQL,
lambda date, what: (
sa.cast(
date + datetime_interval(un_literal(what), 1, literal_type=True, literal_mult=True), sa.Date
)
if un_literal(what).lower() != "quarter"
else sa.cast(date + datetime_interval("month", 3, literal_type=True, literal_mult=True), sa.Date)
),
),
]
),
base.FuncDateadd2Number.for_dialect(D.POSTGRESQL),
base.FuncDateadd3Legacy.for_dialect(D.POSTGRESQL),
base.FuncDateadd3DateConstNum(
Expand All @@ -61,8 +74,18 @@ def _datetrunc2_pg_tz_impl(date_ctx: TranslationCtx, unit_ctx: TranslationCtx) -
D.POSTGRESQL,
lambda date, what, num: (
sa.cast(
date
+ datetime_interval(un_literal(what), un_literal(num), literal_type=True, literal_mult=True),
(
date
+ (
datetime_interval(
un_literal(what), un_literal(num), literal_type=True, literal_mult=True
)
if un_literal(what).lower() != "quarter"
else datetime_interval(
"month", 3 * un_literal(num), literal_type=True, literal_mult=True
)
)
),
sa.Date,
)
),
Expand All @@ -74,7 +97,12 @@ def _datetrunc2_pg_tz_impl(date_ctx: TranslationCtx, unit_ctx: TranslationCtx) -
V(
D.POSTGRESQL,
lambda dt, what, num: (
dt + datetime_interval(what.value, num.value, literal_type=True, literal_mult=True)
dt
+ (
datetime_interval(what.value, num.value, literal_type=True, literal_mult=True)
if what.value.lower() != "quarter"
else datetime_interval("month", 3 * num.value, literal_type=True, literal_mult=True)
)
),
),
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def _dt_lit(s: str) -> str:
# 2 args (unit)
assert dbe.eval('DATEADD(#2018-01-12#, "day")') == datetime.date(2018, 1, 13)
assert dbe.eval('DATEADD(#2018-01-12#, "month")') == datetime.date(2018, 2, 12)
assert dbe.eval('DATEADD(#2018-01-12#, "quarter")') == datetime.date(2018, 4, 12)
assert dbe.eval('DATEADD(#2018-01-12#, "year")') == datetime.date(2019, 1, 12)

# 2 args (number)
Expand All @@ -78,6 +79,7 @@ def _dt_lit(s: str) -> str:
# 3 args
assert dbe.eval('DATEADD(#2018-01-12#, "day", 6)') == datetime.date(2018, 1, 18)
assert dbe.eval('DATEADD(#2018-01-12#, "month", 6)') == datetime.date(2018, 7, 12)
assert dbe.eval('DATEADD(#2018-01-12#, "quarter", 6)') == datetime.date(2019, 7, 12)
assert dbe.eval('DATEADD(#2018-01-12#, "year", 6)') == datetime.date(2024, 1, 12)
assert dt_strip(dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "second", 6)')) == datetime.datetime(
2018, 1, 12, 1, 2, 9
Expand All @@ -94,13 +96,17 @@ def _dt_lit(s: str) -> str:
assert dt_strip(dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "month", 6)')) == datetime.datetime(
2018, 7, 12, 1, 2, 3
)
assert dt_strip(dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "quarter", 6)')) == datetime.datetime(
2019, 7, 12, 1, 2, 3
)
assert dt_strip(dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "year", 6)')) == datetime.datetime(
2024, 1, 12, 1, 2, 3
)
if self.supports_dateadd_non_const_unit_num:
# non-const number
assert dbe.eval('DATEADD(#2018-01-12#, "day", INT("6"))') == datetime.date(2018, 1, 18)
assert dbe.eval('DATEADD(#2018-01-12#, "month", INT("6"))') == datetime.date(2018, 7, 12)
assert dbe.eval('DATEADD(#2018-01-12#, "quarter", INT("6"))') == datetime.date(2019, 7, 12)
assert dbe.eval('DATEADD(#2018-01-12#, "year", INT("6"))') == datetime.date(2024, 1, 12)
assert dt_strip(
dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "second", INT("6"))')
Expand All @@ -117,6 +123,9 @@ def _dt_lit(s: str) -> str:
assert dt_strip(
dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "month", INT("6"))')
) == datetime.datetime(2018, 7, 12, 1, 2, 3)
assert dt_strip(
dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "quarter", INT("6"))')
) == datetime.datetime(2019, 7, 12, 1, 2, 3)
assert dt_strip(
dbe.eval(f'DATEADD({_dt_lit("2018-01-12 01:02:03")}, "year", INT("6"))')
) == datetime.datetime(2024, 1, 12, 1, 2, 3)
Expand All @@ -131,6 +140,7 @@ def _dt_lit(s: str) -> str:

assert dbe.eval('DATEADD("day", 6, #2018-01-12#)') == datetime.date(2018, 1, 18)
assert dbe.eval('DATEADD("month", 6, #2018-01-12#)') == datetime.date(2018, 7, 12)
assert dbe.eval('DATEADD("quarter", 6, #2018-01-12#)') == datetime.date(2019, 7, 12)
assert dbe.eval('DATEADD("year", 6, #2018-01-12#)') == datetime.date(2024, 1, 12)
assert dt_strip(dbe.eval(f'DATEADD("second", 6, {_dt_lit("2018-01-12 01:02:03")})')) == datetime.datetime(
2018, 1, 12, 1, 2, 9
Expand All @@ -147,6 +157,9 @@ def _dt_lit(s: str) -> str:
assert dt_strip(dbe.eval(f'DATEADD("month", 6, {_dt_lit("2018-01-12 01:02:03")})')) == datetime.datetime(
2018, 7, 12, 1, 2, 3
)
assert dt_strip(dbe.eval(f'DATEADD("quarter", 6, {_dt_lit("2018-01-12 01:02:03")})')) == datetime.datetime(
2019, 7, 12, 1, 2, 3
)
assert dt_strip(dbe.eval(f'DATEADD("year", 6, {_dt_lit("2018-01-12 01:02:03")})')) == datetime.datetime(
2024, 1, 12, 1, 2, 3
)
Expand Down Expand Up @@ -222,6 +235,7 @@ def test_datepart_2_deprecated(self, dbe: DbEvaluator, forced_literal_use: Any)
assert dbe.eval('DATEPART("hour", #2018-01-12#)') == 0
assert dbe.eval('DATEPART("day", #2018-01-12#)') == 12
assert dbe.eval('DATEPART("month", #2018-01-12#)') == 1
assert dbe.eval('DATEPART("quarter", #2018-01-12#)') == 1
assert dbe.eval('DATEPART("year", #2018-01-12#)') == 2018
assert dbe.eval('DATEPART("dayofweek", #1971-01-14#)') == 4
assert dbe.eval('DATEPART("dow", #1971-01-14#)') == 4
Expand All @@ -231,6 +245,7 @@ def test_datepart_2_deprecated(self, dbe: DbEvaluator, forced_literal_use: Any)
assert dbe.eval('DATEPART("hour", #2018-01-12 01:02:03#)') == 1
assert dbe.eval('DATEPART("day", #2018-01-12 01:02:03#)') == 12
assert dbe.eval('DATEPART("month", #2018-01-12 01:02:03#)') == 1
assert dbe.eval('DATEPART("quarter", #2018-01-12 01:02:03#)') == 1
assert dbe.eval('DATEPART("year", #2018-01-12 01:02:03#)') == 2018
assert dbe.eval('DATEPART("dayofweek", #1971-01-14 01:02:03#)') == 4
assert dbe.eval('DATEPART("dow", #1971-01-14 01:02:03#)') == 4
Expand All @@ -242,6 +257,7 @@ def test_datepart_2_deprecated(self, dbe: DbEvaluator, forced_literal_use: Any)
assert dbe.eval('DATEPART(__LIT__("hour"), #2018-01-12#)') == 0
assert dbe.eval('DATEPART(__LIT__("day"), #2018-01-12#)') == 12
assert dbe.eval('DATEPART(__LIT__("month"), #2018-01-12#)') == 1
assert dbe.eval('DATEPART(__LIT__("quarter"), #2018-01-12#)') == 1
assert dbe.eval('DATEPART(__LIT__("year"), #2018-01-12#)') == 2018
assert dbe.eval('DATEPART(__LIT__("dayofweek"), #1971-01-14#)') == 4
assert dbe.eval('DATEPART(__LIT__("dow"), #1971-01-14#)') == 4
Expand All @@ -251,6 +267,7 @@ def test_datepart_2_deprecated(self, dbe: DbEvaluator, forced_literal_use: Any)
assert dbe.eval('DATEPART(__LIT__("hour"), #2018-01-12 01:02:03#)') == 1
assert dbe.eval('DATEPART(__LIT__("day"), #2018-01-12 01:02:03#)') == 12
assert dbe.eval('DATEPART(__LIT__("month"), #2018-01-12 01:02:03#)') == 1
assert dbe.eval('DATEPART(__LIT__("quarter"), #2018-01-12 01:02:03#)') == 1
assert dbe.eval('DATEPART(__LIT__("year"), #2018-01-12 01:02:03#)') == 2018
assert dbe.eval('DATEPART(__LIT__("dayofweek"), #1971-01-14 01:02:03#)') == 4
assert dbe.eval('DATEPART(__LIT__("dow"), #1971-01-14 01:02:03#)') == 4
Expand Down

0 comments on commit 5bb413d

Please sign in to comment.