From 64a4594cc66dff9de3308b39745eabdb28d5b90a Mon Sep 17 00:00:00 2001 From: tkaunlaky-e6 Date: Wed, 12 Aug 2026 15:33:39 +0530 Subject: [PATCH] fix(e6): subtract 1 from EXTRACT(DOW) on the multidialect path e6's EXTRACT(DOW) is 1-7 (Sun=1) while Postgres' is 0-6 (Sun=0). BI-tool (Tableau) queries emit `1 + EXTRACT(DOW)` expecting Postgres' 0-6, so on e6 the value ends up 2-8. Rewrite `EXTRACT(DOW ..)` -> `EXTRACT(DOW ..) - 1` so e6 reproduces the Postgres value: `1 + (EXTRACT(DOW) - 1)` = 1-7, and a bare `EXTRACT(DOW)` = 0-6. The transform (subtract_one_from_dow in the e6 dialect) is applied in the multidialect-only _region_to_e6 helper, so it is gated on the MULTIDIALECT flag and covers both the primary (pg->databricks->e6) and fallback (pg->e6) paths; plain databricks->e6 is untouched. Verified on the engine: Sun=1, Mon=2, Sat=7 on both paths. --- converter_api.py | 4 ++++ sqlglot/dialects/e6.py | 17 +++++++++++++++++ tests/dialects/test_e6.py | 21 +++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/converter_api.py b/converter_api.py index 3b543eaff7..8a8fd9fb62 100644 --- a/converter_api.py +++ b/converter_api.py @@ -14,6 +14,7 @@ from sqlglot.optimizer.qualify_columns import quote_identifiers from sqlglot import parse_one from sqlglot.dialects.snowflake_backticks import SnowflakeBackticks +from sqlglot.dialects.e6 import subtract_one_from_dow from apis.utils.multidialect import pg_outer_to_inner, split_pg_outer, _splice from guardrail.main import StorageServiceClient from guardrail.main import extract_sql_components_per_table_with_alias, get_table_infos @@ -115,6 +116,9 @@ def _region_to_e6(region_sql: str, from_sql: str, pretty: bool) -> str: region_sql, in_replacements = extract_large_in_clauses(region_sql) # Parse with the region's own source dialect, then run the standard e6 steps. tree = sqlglot.parse_one(region_sql, read=from_sql, error_level=None) + # MULTIDIALECT-only (this helper runs only under the flag): e6 EXTRACT(DOW) is + # 1-7 vs Postgres' 0-6, so subtract 1 so the value matches the Postgres source. + tree = tree.transform(subtract_one_from_dow) tree = sanitize_comments(tree) tree = quote_identifiers(tree, dialect="e6") tree = ensure_select_from_values(tree) diff --git a/sqlglot/dialects/e6.py b/sqlglot/dialects/e6.py index 7b53d212d7..b49a071d1f 100644 --- a/sqlglot/dialects/e6.py +++ b/sqlglot/dialects/e6.py @@ -38,6 +38,23 @@ def epoch_cast_to_ts_e6(expression: exp.Expression) -> exp.Expression: return expression +def subtract_one_from_dow(node: exp.Expression) -> exp.Expression: + """Subtract 1 from every ``EXTRACT(DOW ..)`` so e6 matches Postgres numbering. + + Postgres ``EXTRACT(DOW)`` is 0-6 (Sun=0); e6's is 1-7 (Sun=1). Rewriting + ``EXTRACT(DOW ..)`` -> ``EXTRACT(DOW ..) - 1`` makes e6 reproduce the Postgres + value in every context: the BI-tool idiom ``1 + EXTRACT(DOW)`` becomes + ``1 + (EXTRACT(DOW) - 1)`` = 1-7, and a bare ``EXTRACT(DOW)`` becomes 0-6. + """ + if ( + isinstance(node, exp.Extract) + and isinstance(node.this, exp.Var) + and node.this.name.upper() == "DOW" + ): + return exp.Sub(this=node, expression=exp.Literal.number(1)) + return node + + def _to_int(expression: exp.Expression) -> exp.Expression: if not expression.type: from sqlglot.optimizer.annotate_types import annotate_types diff --git a/tests/dialects/test_e6.py b/tests/dialects/test_e6.py index 0f1646568d..ca0611929c 100644 --- a/tests/dialects/test_e6.py +++ b/tests/dialects/test_e6.py @@ -3400,6 +3400,27 @@ def pg_to_e6(sql): 'JOIN (SELECT "y" FROM "t2") AS "q" ON "p"."x" = "q"."y"', ) + def test_subtract_one_from_dow(self): + """e6 EXTRACT(DOW) is 1-7 vs Postgres' 0-6, so subtract 1. The BI-tool + idiom `1 + EXTRACT(DOW)` becomes `1 + (EXTRACT(DOW) - 1)` = 1-7; a bare + EXTRACT(DOW) becomes 0-6. Non-DOW extracts are untouched.""" + import sqlglot + from sqlglot.dialects.e6 import subtract_one_from_dow + + def apply(sql): + tree = sqlglot.parse_one(sql, read="postgres").transform(subtract_one_from_dow) + return tree.sql(dialect="e6", from_dialect="postgres") + + self.assertEqual( + apply("SELECT 1 + CAST(EXTRACT(DOW FROM d) AS INTEGER)"), + "SELECT 1 + CAST(EXTRACT(DOW FROM d) - 1 AS INT)", + ) + self.assertEqual(apply("SELECT EXTRACT(DOW FROM d)"), "SELECT EXTRACT(DOW FROM d) - 1") + self.assertEqual( + apply("SELECT 1 + EXTRACT(MONTH FROM d)"), + "SELECT 1 + EXTRACT(MONTH FROM d)", + ) + def test_make_interval(self): """Test make_interval transpilation from Databricks to E6."""