Skip to content

feat(duckdb): Add transpilation support for TRUNC date/time function in snowflake#6935

Closed
fivetran-amrutabhimsenayachit wants to merge 2 commits intomainfrom
RD-1069379-truncate-trunc
Closed

feat(duckdb): Add transpilation support for TRUNC date/time function in snowflake#6935
fivetran-amrutabhimsenayachit wants to merge 2 commits intomainfrom
RD-1069379-truncate-trunc

Conversation

@fivetran-amrutabhimsenayachit
Copy link
Collaborator

Snowflake's TRUNC function is overloaded to handle both numeric truncation and date/time truncation. When transpiling from Snowflake to DuckDB, SQLGlot does not recognize TRUNC as a date/time operation and leaves it as-is. DuckDB's TRUNC function only supports numeric types, causing date/time queries to fail. The correct DuckDB function for date/time truncation is DATE_TRUNC, which also uses reversed parameter order.

To fix this, created a custom parser function that inspects the second argument as a string literal to determine if TRUNC is numeric or date/time.

python3 -c "import sqlglot; print(sqlglot.transpile(\"SELECT TRUNCATE(TIMESTAMP '2024-05-09 08:50:57.891', 'HOUR') AS truncated_to_hour\", read='snowflake', write='duckdb')[0])"
SELECT DATE_TRUNC('HOUR', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS truncated_to_hour

duckdb -c "SELECT DATE_TRUNC('HOUR', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS truncated_to_hour"
┌─────────────────────┐
│  truncated_to_hour  │
│      timestamp      │
├─────────────────────┤
│ 2024-05-09 08:00:00 │

Additional tests:

Transpilation command:

python3 -c "import sqlglot; print(sqlglot.transpile(\"SELECT TRUNC(DATE '2024-05-09', 'YEAR') AS trunc_date_year, TRUNC(DATE '2024-05-09', 'QUARTER') AS trunc_date_quarter, TRUNC(DATE '2024-05-09', 'MONTH') AS trunc_date_month, TRUNC(DATE '2024-05-09', 'WEEK') AS trunc_date_week, TRUNC(DATE '2024-05-09', 'DAY') AS trunc_date_day, TRUNC(TIME '08:50:48', 'HOUR') AS trunc_time_hour, TRUNC(TIME '08:50:48', 'MINUTE') AS trunc_time_minute, TRUNC(TIME '08:50:48', 'SECOND') AS trunc_time_second, TRUNC(TIMESTAMP '2024-05-09 08:50:57.891', 'YEAR') AS trunc_ts_year, TRUNC(TIMESTAMP '2024-05-09 08:50:57.891', 'QUARTER') AS trunc_ts_quarter, TRUNC(TIMESTAMP '2024-05-09 08:50:57.891', 'MONTH') AS trunc_ts_month, TRUNC(TIMESTAMP '2024-05-09 08:50:57.891', 'DAY') AS trunc_ts_day, TRUNC(TIMESTAMP '2024-05-09 08:50:57.891', 'HOUR') AS trunc_ts_hour, TRUNC(TIMESTAMP '2024-05-09 08:50:57.891', 'MINUTE') AS trunc_ts_minute, TRUNC(TIMESTAMP '2024-05-09 08:50:57.891', 'SECOND') AS trunc_ts_second, TRUNC(NULL, 'YEAR') AS trunc_null_yr\", read='snowflake', write='duckdb')[0])"

DuckDB:

 SELECT DATE_TRUNC('YEAR', CAST('2024-05-09' AS DATE)) AS trunc_date_year, DATE_TRUNC('QUARTER', CAST('2024-05-09' AS DATE)) AS trunc_date_quarter, DATE_TRUNC('MONTH', CAST('2024-05-09' AS DATE)) AS trunc_date_month, DATE_TRUNC('WEEK', CAST('2024-05-09' AS DATE)) AS trunc_date_week, DATE_TRUNC('DAY', CAST('2024-05-09' AS DATE)) AS trunc_date_day, CAST(DATE_TRUNC('HOUR', CAST('1970-01-01' AS DATE) + CAST('08:50:48' AS TIME)) AS TIME) AS trunc_time_hour, CAST(DATE_TRUNC('MINUTE', CAST('1970-01-01' AS DATE) + CAST('08:50:48' AS TIME)) AS TIME) AS trunc_time_minute, CAST(DATE_TRUNC('SECOND', CAST('1970-01-01' AS DATE) + CAST('08:50:48' AS TIME)) AS TIME) AS trunc_time_second, CAST(DATE_TRUNC('YEAR', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS TIMESTAMP) AS trunc_ts_year, CAST(DATE_TRUNC('QUARTER', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS TIMESTAMP) AS trunc_ts_quarter, CAST(DATE_TRUNC('MONTH', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS TIMESTAMP) AS trunc_ts_month, CAST(DATE_TRUNC('DAY', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS TIMESTAMP) AS trunc_ts_day, DATE_TRUNC('HOUR', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS trunc_ts_hour, DATE_TRUNC('MINUTE', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS trunc_ts_minute, DATE_TRUNC('SECOND', CAST('2024-05-09 08:50:57.891' AS TIMESTAMP)) AS trunc_ts_second, TRUNC(NULL, 'YEAR') AS trunc_null_yr
| trunc_date_year | trunc_date_quarter | trunc_date_month | trunc_date_week | trunc_date_day | trunc_time_hour | trunc_time_minute | trunc_time_second | trunc_ts_year       | trunc_ts_quarter    | trunc_ts_month      | trunc_ts_day        | trunc_ts_hour       | trunc_ts_minute     | trunc_ts_second     | trunc_null_yr |
|-----------------|--------------------|--------------------|-----------------|----------------|-----------------|-------------------|-------------------|---------------------|---------------------|---------------------|---------------------|---------------------|---------------------|---------------------|---------------|
| 2024-01-01      | 2024-04-01         | 2024-05-01         | 2024-05-06      | 2024-05-09     | 08:00:00        | 08:50:00          | 08:50:48          | 2024-01-01 00:00:00 | 2024-04-01 00:00:00 | 2024-05-01 00:00:00 | 2024-05-09 00:00:00 | 2024-05-09 08:00:00 | 2024-05-09 08:50:00 | 2024-05-09 08:50:57 | NULL          |

- Handle TRUNC(NULL, 'YEAR') edge case by checking for explicit NULL
- Handle TRUNC(timestamp, NULL) edge case for date/time types with NULL unit
- Preserve ambiguous cases like TRUNC(col, 'YEAR') where type is unknown
- Fixes integration test failures in DuckDB execution
@VaggelisD
Copy link
Collaborator

As discussed offline, Amruta was working on the Snowflake aspect before #6923. Closing this in favor of the other PR as it attempts to solve all dialects at once.

We can keep this as reference for now or if need be (1) merge the other PR and (2) reopen this one to address any additional Snowflake -> DuckDB shortcomings.

@VaggelisD VaggelisD closed this Feb 3, 2026
@georgesittas georgesittas deleted the RD-1069379-truncate-trunc branch February 4, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments