Skip to content

Conversation

@pyramation
Copy link
Collaborator

@pyramation pyramation commented Nov 23, 2025

Fix #217: Wrap FuncCall arguments in parentheses when using :: cast syntax

Summary

Fixes a bug where double type casts with the :: operator were being deparsed incorrectly due to operator precedence issues. When a FuncCall (like AT TIME ZONE) was cast using the :: shorthand syntax, the cast would bind to the wrong operand.

Example of the bug:

  • Input: SELECT CAST(t.date AT TIME ZONE 'America/New_York' AS text)::date FROM tbl t
  • Incorrect output: SELECT CAST(t.date AT TIME ZONE 'America/New_York'::text AS date) FROM tbl AS t
    • The ::text was binding to 'America/New_York' instead of the entire CAST(...) expression

The fix:
When using the :: cast shorthand syntax with a FuncCall argument, wrap the FuncCall in parentheses: (t.date AT TIME ZONE 'America/New_York')::text

Changes:

  • Modified TypeCast function in packages/deparser/src/deparser.ts (13 lines changed)
  • Added test fixture for issue Double CAST Error #217
  • Updated pg-catalog snapshot (function calls now wrapped in parens when cast)

Review & Testing Checklist for Human

  • Test the original issue: Verify that SELECT CAST(t.date AT TIME ZONE 'America/New_York' AS text)::date FROM tbl t now deparses correctly and can be re-parsed without AST changes (parse → deparse → reparse should produce identical AST)
  • Verify simple casts still work: Test that simple casts like '123'::int and '1 day'::interval still work correctly without unnecessary parentheses
  • Review the snapshot change: The pg-catalog snapshot shows public.gen_random_uuid()::text now becomes (public.gen_random_uuid())::text. Verify this is correct and doesn't break any downstream consumers

Test Plan

  1. Run the deparser test suite: cd packages/deparser && yarn test (all 654 tests should pass ✓)
  2. Test the specific issue manually:
    const sql = `SELECT CAST(t.date AT TIME ZONE 'America/New_York' AS text)::date FROM tbl t`;
    const ast = await parse(sql);
    const deparsed = await deparse(ast);
    const reparsed = await parse(deparsed);
    // Verify ast and reparsed are identical
  3. Spot check other type casts to ensure no regressions

Notes


Devin Session: https://app.devin.ai/sessions/111a29019c274f83a61a873f35081622
Requested by: Dan Lynch (@pyramation)

pyramation and others added 3 commits November 22, 2025 16:08
…cast syntax

- Fixes operator precedence issues with double casts like CAST(...AS text)::date
- Wraps FuncCall arguments (e.g., AT TIME ZONE expressions) in parentheses
- Prevents :: operator from binding to wrong operand
- Refactored empty if block to pass lint checks
- Updated pg-catalog snapshot to reflect correct parenthesization

This is a minimal fix with only 13 lines changed in the TypeCast function.

Co-Authored-By: Dan Lynch <[email protected]>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

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.

Double CAST Error

2 participants