Skip to content

Commit 7230717

Browse files
committed
fix: resolve WITH clause nested parentheses and TypeCast CAST syntax preservation
- Add parentheses around WITH clauses in set operations to fix invalid SQL generation - Preserve CAST syntax for complex expressions and numeric types to maintain AST fidelity - Resolves original-upstream-with.test.ts AST mismatch and invalid SQL issues Co-Authored-By: Dan Lynch <[email protected]>
1 parent a68bae4 commit 7230717

File tree

1 file changed

+13
-8
lines changed

1 file changed

+13
-8
lines changed

packages/deparser/src/deparser.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,18 +118,20 @@ export class Deparser implements DeparserVisitor {
118118
const leftStmt = this.SelectStmt(node.larg as t.SelectStmt, context);
119119
const rightStmt = this.SelectStmt(node.rarg as t.SelectStmt, context);
120120

121-
// Add parentheses if the operand is a set operation OR has ORDER BY/LIMIT clauses
121+
// Add parentheses if the operand is a set operation OR has ORDER BY/LIMIT clauses OR has WITH clause
122122
const leftNeedsParens = node.larg && (
123123
((node.larg as t.SelectStmt).op && (node.larg as t.SelectStmt).op !== 'SETOP_NONE') ||
124124
(node.larg as t.SelectStmt).sortClause ||
125125
(node.larg as t.SelectStmt).limitCount ||
126-
(node.larg as t.SelectStmt).limitOffset
126+
(node.larg as t.SelectStmt).limitOffset ||
127+
(node.larg as t.SelectStmt).withClause
127128
);
128129
const rightNeedsParens = node.rarg && (
129130
((node.rarg as t.SelectStmt).op && (node.rarg as t.SelectStmt).op !== 'SETOP_NONE') ||
130131
(node.rarg as t.SelectStmt).sortClause ||
131132
(node.rarg as t.SelectStmt).limitCount ||
132-
(node.rarg as t.SelectStmt).limitOffset
133+
(node.rarg as t.SelectStmt).limitOffset ||
134+
(node.rarg as t.SelectStmt).withClause
133135
);
134136

135137
if (leftNeedsParens) {
@@ -1764,21 +1766,24 @@ export class Deparser implements DeparserVisitor {
17641766
}
17651767
}
17661768

1767-
if (typeName.startsWith('interval') ||
1769+
// Check if the argument is a complex expression that should preserve CAST syntax
1770+
const argType = this.getNodeType(node.arg);
1771+
const isComplexExpression = argType === 'A_Expr' || argType === 'FuncCall' || argType === 'OpExpr';
1772+
1773+
if (!isComplexExpression && (typeName.startsWith('interval') ||
17681774
typeName.startsWith('char') ||
17691775
typeName === '"char"' ||
17701776
typeName.startsWith('bpchar') ||
17711777
typeName === 'bytea' ||
17721778
typeName === 'orderedarray' ||
1773-
typeName.startsWith('numeric') ||
1774-
typeName.startsWith('pg_catalog.numeric') ||
1775-
typeName === 'date') {
1779+
typeName === 'date')) {
17761780
// Remove pg_catalog prefix for :: syntax
17771781
const cleanTypeName = typeName.replace('pg_catalog.', '');
17781782
return `${arg}::${cleanTypeName}`;
17791783
}
17801784

1781-
return `CAST(${arg} AS ${typeName})`;
1785+
const cleanTypeName = typeName.replace('pg_catalog.', '');
1786+
return `CAST(${arg} AS ${cleanTypeName})`;
17821787
}
17831788

17841789
CollateClause(node: t.CollateClause, context: DeparserContext): string {

0 commit comments

Comments
 (0)