Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 0 additions & 276 deletions PLAN.md

This file was deleted.

9 changes: 7 additions & 2 deletions internal/explain/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
case *ast.CaseExpr:
explainCaseExpr(sb, n, indent, depth)
case *ast.IntervalExpr:
explainIntervalExpr(sb, n, indent, depth)
explainIntervalExpr(sb, n, "", indent, depth)
case *ast.ExistsExpr:
explainExistsExpr(sb, n, indent, depth)
case *ast.ExtractExpr:
Expand Down Expand Up @@ -166,7 +166,9 @@ func Column(sb *strings.Builder, col *ast.ColumnDeclaration, depth int) {
if col.Type != nil {
children++
}
if col.Default != nil {
// EPHEMERAL columns without explicit default get defaultValueOfTypeName
hasEphemeralDefault := col.DefaultKind == "EPHEMERAL" && col.Default == nil
if col.Default != nil || hasEphemeralDefault {
children++
}
fmt.Fprintf(sb, "%sColumnDeclaration %s (children %d)\n", indent, col.Name, children)
Expand All @@ -175,6 +177,9 @@ func Column(sb *strings.Builder, col *ast.ColumnDeclaration, depth int) {
}
if col.Default != nil {
Node(sb, col.Default, depth+1)
} else if hasEphemeralDefault {
// EPHEMERAL columns without explicit default value show defaultValueOfTypeName function
fmt.Fprintf(sb, "%s Function defaultValueOfTypeName\n", indent)
}
}

Expand Down
53 changes: 49 additions & 4 deletions internal/explain/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,34 @@ import (
)

func explainIdentifier(sb *strings.Builder, n *ast.Identifier, indent string) {
name := n.Name()
name := formatIdentifierName(n)
if n.Alias != "" {
fmt.Fprintf(sb, "%sIdentifier %s (alias %s)\n", indent, name, n.Alias)
} else {
fmt.Fprintf(sb, "%sIdentifier %s\n", indent, name)
}
}

// formatIdentifierName formats an identifier name, handling JSON path notation
func formatIdentifierName(n *ast.Identifier) string {
if len(n.Parts) == 0 {
return ""
}
if len(n.Parts) == 1 {
return n.Parts[0]
}
result := n.Parts[0]
for _, p := range n.Parts[1:] {
// JSON path notation: ^fieldname should be formatted as ^`fieldname`
if strings.HasPrefix(p, "^") {
result += ".^`" + p[1:] + "`"
} else {
result += "." + p
}
}
return result
}

func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth int) {
// Check if this is a tuple - either with expressions or empty
if n.Type == ast.LiteralTuple {
Expand Down Expand Up @@ -63,9 +83,7 @@ func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth in
}
hasComplexExpr := false
for _, e := range exprs {
lit, isLit := e.(*ast.Literal)
// Non-literals or tuple/array literals count as complex
if !isLit || (isLit && (lit.Type == ast.LiteralTuple || lit.Type == ast.LiteralArray)) {
if !isSimpleLiteralOrNegation(e) {
hasComplexExpr = true
break
}
Expand All @@ -89,6 +107,23 @@ func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth in
fmt.Fprintf(sb, "%sLiteral %s\n", indent, FormatLiteral(n))
}

// isSimpleLiteralOrNegation checks if an expression is a simple literal
// or a unary negation of a numeric literal (for array elements)
func isSimpleLiteralOrNegation(e ast.Expression) bool {
// Direct literal check
if lit, ok := e.(*ast.Literal); ok {
// Nested arrays/tuples are complex
return lit.Type != ast.LiteralTuple && lit.Type != ast.LiteralArray
}
// Unary minus of a literal integer/float is also simple (negative number)
if unary, ok := e.(*ast.UnaryExpr); ok && unary.Op == "-" {
if lit, ok := unary.Operand.(*ast.Literal); ok {
return lit.Type == ast.LiteralInteger || lit.Type == ast.LiteralFloat
}
}
return false
}

func explainBinaryExpr(sb *strings.Builder, n *ast.BinaryExpr, indent string, depth int) {
// Convert operator to function name
fnName := OperatorToFunction(n.Op)
Expand Down Expand Up @@ -224,6 +259,16 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
case *ast.Identifier:
// Identifiers with alias
fmt.Fprintf(sb, "%sIdentifier %s (alias %s)\n", indent, e.Name(), n.Alias)
case *ast.IntervalExpr:
// Interval expressions with alias
explainIntervalExpr(sb, e, n.Alias, indent, depth)
case *ast.TernaryExpr:
// Ternary expressions become if functions with alias
fmt.Fprintf(sb, "%sFunction if (alias %s) (children %d)\n", indent, n.Alias, 1)
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 3)
Node(sb, e.Condition, depth+2)
Node(sb, e.Then, depth+2)
Node(sb, e.Else, depth+2)
default:
// For other types, recursively explain and add alias info
Node(sb, n.Expr, depth)
Expand Down
Loading