Skip to content

Commit 061957f

Browse files
Enhance SELECT formatting with expression complexity detection
- Implement isComplexSelectTarget method for nuanced complexity detection - Single simple targets: render inline (e.g. SELECT 1;, SELECT name;) - Single complex targets: render multiline (e.g. CASE, COALESCE with multiple args) - Multiple targets: always render multiline regardless of complexity - Complex expressions include: CASE, subqueries, functions with multiple args/clauses - Simple expressions include: literals, column refs, simple arithmetic, basic function calls - All formatting rules only apply when isPretty() is true - Maintains backward compatibility and passes all existing tests Co-Authored-By: Dan Lynch <[email protected]>
1 parent 876b642 commit 061957f

File tree

1 file changed

+75
-2
lines changed

1 file changed

+75
-2
lines changed

packages/deparser/src/deparser.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,20 @@ export class Deparser implements DeparserVisitor {
371371
const targetList = ListUtils.unwrapList(node.targetList);
372372
if (this.formatter.isPretty()) {
373373
if (targetList.length === 1) {
374-
const target = this.visit(targetList[0] as Node, { ...context, select: true });
375-
output.push('SELECT' + distinctPart + ' ' + target);
374+
const targetNode = targetList[0] as Node;
375+
const target = this.visit(targetNode, { ...context, select: true });
376+
377+
// Check if single target is complex - if so, use multiline format
378+
if (this.isComplexSelectTarget(targetNode)) {
379+
output.push('SELECT' + distinctPart);
380+
if (this.containsMultilineStringLiteral(target)) {
381+
output.push(target);
382+
} else {
383+
output.push(this.formatter.indent(target));
384+
}
385+
} else {
386+
output.push('SELECT' + distinctPart + ' ' + target);
387+
}
376388
} else {
377389
const targetStrings = targetList
378390
.map(e => {
@@ -824,6 +836,67 @@ export class Deparser implements DeparserVisitor {
824836
);
825837
}
826838

839+
private isComplexSelectTarget(node: any): boolean {
840+
if (!node) return false;
841+
842+
// Always complex: CASE expressions
843+
if (node.CaseExpr) return true;
844+
845+
// Always complex: Subqueries and subselects
846+
if (node.SubLink) return true;
847+
848+
// Always complex: Boolean tests and expressions
849+
if (node.NullTest || node.BooleanTest || node.BoolExpr) return true;
850+
851+
// COALESCE and similar functions - complex if multiple arguments
852+
if (node.CoalesceExpr) {
853+
const args = node.CoalesceExpr.args;
854+
if (args && Array.isArray(args) && args.length > 1) return true;
855+
}
856+
857+
// Function calls - complex if multiple args or has clauses
858+
if (node.FuncCall) {
859+
const funcCall = node.FuncCall;
860+
const args = funcCall.args ? (Array.isArray(funcCall.args) ? funcCall.args : [funcCall.args]) : [];
861+
862+
// Complex if has window clause, filter, order by, etc.
863+
if (funcCall.over || funcCall.agg_filter || funcCall.agg_order || funcCall.agg_distinct) {
864+
return true;
865+
}
866+
867+
// Complex if multiple arguments
868+
if (args.length > 1) return true;
869+
870+
if (args.length === 1) {
871+
return this.isComplexSelectTarget(args[0]);
872+
}
873+
}
874+
875+
if (node.A_Expr) {
876+
const expr = node.A_Expr;
877+
// Check if operands are complex
878+
if (expr.lexpr && this.isComplexSelectTarget(expr.lexpr)) return true;
879+
if (expr.rexpr && this.isComplexSelectTarget(expr.rexpr)) return true;
880+
return false;
881+
}
882+
883+
if (node.TypeCast) {
884+
return this.isComplexSelectTarget(node.TypeCast.arg);
885+
}
886+
887+
if (node.A_ArrayExpr) return true;
888+
889+
if (node.A_Indirection) {
890+
return this.isComplexSelectTarget(node.A_Indirection.arg);
891+
}
892+
893+
if (node.A_Const || node.ColumnRef || node.ParamRef || node.A_Star) {
894+
return false;
895+
}
896+
897+
return false;
898+
}
899+
827900
visitBetweenRange(rexpr: any, context: DeparserContext): string {
828901
if (rexpr && 'List' in rexpr && rexpr.List?.items) {
829902
const items = rexpr.List.items.map((item: any) => this.visit(item, context));

0 commit comments

Comments
 (0)