Skip to content

fix(redshift): roundtrip for HH12 time mapping#7626

Merged
georgesittas merged 4 commits into
mainfrom
fix/redshift-to_char_cast
May 12, 2026
Merged

fix(redshift): roundtrip for HH12 time mapping#7626
georgesittas merged 4 commits into
mainfrom
fix/redshift-to_char_cast

Conversation

@fivetran-kwoodbeck

Copy link
Copy Markdown
Collaborator

TO_CHAR format strings with HH12 round-tripped back as HH. Root cause: TIME_MAPPING had both "HH12": "%I" (from Postgres) and "HH": "%I" (Redshift override), and since HH appeared later in the dict, the auto-generated INVERSE_TIME_MAPPING mapped %I → HH instead of %I → HH12.

Fix: Added INVERSE_TIME_MAPPING override in dialects/redshift.py

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

SQLGlot Integration Test Results

Comparing:

  • this branch (sqlglot:fix/redshift-to_char_cast, sqlglot version: fix/redshift-to_char_cast)
  • baseline (main, sqlglot version: 0.0.1.dev1)

By Dialect

dialect main sqlglot:fix/redshift-to_char_cast transitions links
bigquery -> bigquery 24647/24652 passed (100.0%) 23497/23497 passed (100.0%) No change full result / delta
bigquery -> duckdb 867/1154 passed (75.1%) 0/0 passed (0.0%) Results not found full result / delta
duckdb -> duckdb 5823/5823 passed (100.0%) 0/0 passed (0.0%) Results not found full result / delta
snowflake -> duckdb 1129/1935 passed (58.3%) 0/0 passed (0.0%) Results not found full result / delta
snowflake -> snowflake 65133/65133 passed (100.0%) 63027/63027 passed (100.0%) No change full result / delta
databricks -> databricks 1370/1370 passed (100.0%) 1370/1370 passed (100.0%) No change full result / delta
postgres -> postgres 6042/6042 passed (100.0%) 6042/6042 passed (100.0%) No change full result / delta
redshift -> redshift 7101/7101 passed (100.0%) 7101/7101 passed (100.0%) No change full result / delta

Overall

main: 113210 total, 112112 passed (pass rate: 99.0%), sqlglot version: 0.0.1.dev1

sqlglot:fix/redshift-to_char_cast: 101037 total, 101037 passed (pass rate: 100.0%), sqlglot version: fix/redshift-to_char_cast

Transitions:
No change

Dialect pair changes: 0 previous results not found, 3 current results not found

✅ 60 test(s) passed

Comment thread sqlglot/dialects/redshift.py Outdated
# ref: https://docs.aws.amazon.com/redshift/latest/dg/r_FORMAT_strings.html
TIME_FORMAT = "'YYYY-MM-DD HH24:MI:SS'"
TIME_MAPPING = {**Postgres.TIME_MAPPING, "MON": "%b", "HH24": "%H", "HH": "%I"}
INVERSE_TIME_MAPPING = {**Postgres.INVERSE_TIME_MAPPING, "%I": "HH12", "%H": "HH24"}

@georgesittas georgesittas May 11, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this live in Postgres' TIME_MAPPING? Why is it Redshift-specific?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll test it all in Postgres and move it over if needs be.

@fivetran-kwoodbeck fivetran-kwoodbeck May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@georgesittas Postgres didn't have the problem, it looks like the "HH": "%I" mapping for Redshift was added accidentally about a year ago. The "HH24": "%H" mapping isn't needed either, it's inherited from Postgres. INVERSE_TIME_MAPPING isn't needed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough about HH24, but don't we still need to map HH to %I for transpilation purposes, though? From their docs, it appears to be equivalent to HH12:

HH or HH12 | Hour (12-hour clock, 01–12)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That one is the same in Postgres, so it should probably be handled there. Right now, everything roundtrips OK in Redshift (and Postgres). Do you want me to update that part in Postgres for this PR?

@fivetran-kwoodbeck
fivetran-kwoodbeck force-pushed the fix/redshift-to_char_cast branch 3 times, most recently from 94f98f4 to 67d6899 Compare May 11, 2026 14:57
@georgesittas

Copy link
Copy Markdown
Collaborator

@fivetran-kwoodbeck I think there may be another issue with time formatting in Redshift and other derivatives of Postgres: certain datetime functions call build_formatted_time on their format argument, which should generally take the dialect's TIME_FORMAT into account. For instance, this is inherited by Redshift:

"TO_CHAR": build_formatted_time(exp.TimeToStr, "postgres"),

This means that overrides in Redshift such as the ones relevant to this PR won't be accounted for. This issue may exist in other dialect hierarchies, too, so it's worth investigating further.

@fivetran-kwoodbeck
fivetran-kwoodbeck force-pushed the fix/redshift-to_char_cast branch from 67d6899 to eca672f Compare May 11, 2026 16:55
@fivetran-kwoodbeck

Copy link
Copy Markdown
Collaborator Author

@fivetran-kwoodbeck I think there may be another issue with time formatting in Redshift and other derivatives of Postgres: certain datetime functions call build_formatted_time on their format argument, which should generally take the dialect's TIME_FORMAT into account. For instance, this is inherited by Redshift:

"TO_CHAR": build_formatted_time(exp.TimeToStr, "postgres"),

This means that overrides in Redshift such as the ones relevant to this PR won't be accounted for. This issue may exist in other dialect hierarchies, too, so it's worth investigating further.

Yes @georgesittas it was an issue, I added overrides for "TO_CHAR" and a few others which fixes it. Was that what you were thinking?

@georgesittas
georgesittas force-pushed the fix/redshift-to_char_cast branch from eca672f to dbe1c83 Compare May 12, 2026 11:24
TIME_FORMAT = "'YYYY-MM-DD HH24:MI:SS'"
TIME_MAPPING = {**Postgres.TIME_MAPPING, "MON": "%b", "HH24": "%H", "HH": "%I"}
TIME_MAPPING = {**Postgres.TIME_MAPPING, "MON": "%b", "MONTH": "%B"}
INVERSE_TIME_MAPPING = {**Postgres.INVERSE_TIME_MAPPING, "%b": "MON", "%B": "MONTH"}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary. If it is, something is wrong with the population of INVERSE_TIME_MAPPING.

Comment on lines +66 to +72
"TO_CHAR": build_formatted_time(exp.TimeToStr, "redshift"),
"TO_DATE": build_formatted_time(exp.StrToDate, "redshift"),
"TO_TIMESTAMP": lambda args: (
exp.UnixToTime.from_arg_list(args)
if len(args) == 1
else build_formatted_time(exp.StrToTime, "redshift")(args)
),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a better refactor we can do, I'll get this in and do it as a follow-up.

@georgesittas
georgesittas merged commit c876570 into main May 12, 2026
8 checks passed
@georgesittas
georgesittas deleted the fix/redshift-to_char_cast branch May 12, 2026 11:31
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