// Expression and literal rendering for the plan tree (split from // render.go for the file-size norm).
package explain import ( ) func fmtAgg( *plan.AggCol) string { := [...]string{"count", "sum", "avg", "min", "max", "collect", "stddev_samp", "stddev_pop", "percentile_cont", "percentile_disc"}[.Kind] := "*" if .Arg != nil { = fmtExpr(.Arg) } := "" if .Distinct { = "DISTINCT " } return + "(" + + + ")" } func fmtSort( ast.SortItem) string { if .Desc { return fmtExpr(.Expr) + " DESC" } return fmtExpr(.Expr) } func fmtLit( ast.Literal) string { switch .Kind { case ast.LitInt: return fmt.Sprintf("%d", .I) case ast.LitFloat: return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", .F), "0"), ".") case ast.LitStr: return "'" + .S + "'" case ast.LitBool: return fmt.Sprintf("%t", .B) case ast.LitNull: return "null" case ast.LitParam: return fmt.Sprintf("$auto%d", .P) default: return "$" + .S } } func fmtExpr( ast.Expr) string { switch n := .(type) { case *ast.Lit: return fmtLit(.Value) case *ast.Var: return .Name case *ast.Prop: return .Var + "." + .Key case *ast.Unary: if .Op == ast.Not { return "NOT " + (.Expr) } return "-" + (.Expr) case *ast.Binary: return (.LHS) + " " + binopStr(.Op) + " " + (.RHS) case *ast.Func: := "" if .Star { = "*" } else { := make([]string, len(.Args)) for , := range .Args { [] = () } = strings.Join(, ", ") } := "" if .Distinct { = "DISTINCT " } return .Name + "(" + + + ")" case *ast.ListExpr: := make([]string, len(.Elems)) for , := range .Elems { [] = () } return "[" + strings.Join(, ", ") + "]" case *ast.In: return (.Expr) + " IN " + (.List) case *ast.IsNull: if .Negated { return (.Expr) + " IS NOT NULL" } return (.Expr) + " IS NULL" case *ast.IsTruth: := " IS TRUE" if !.Want { = " IS FALSE" } if .Negated { = " IS NOT" + [3:] } return (.Expr) + case *ast.IsTyped: := " IS TYPED " if .Negated { = " IS NOT TYPED " } return (.Expr) + + .Kind case *ast.Case: return "CASE…END" case *ast.Cost: return fmt.Sprintf("cost(shortestPath((%s)..(%s)), …)", .From, .To) case *ast.PatternComp: return "[(…) | " + (.Proj) + "]" case *ast.Exists: return "EXISTS {…}" case *ast.CountSub: return "COUNT {…}" case *ast.ListPred: := [...]string{"all", "any", "none", "single"}[.Quant] return fmt.Sprintf("%s(%s IN %s WHERE …)", , .Var, (.List)) case *ast.Reduce: return fmt.Sprintf("reduce(%s = %s, %s IN %s | …)", .Acc, (.Init), .Var, (.List)) case *ast.ListComp: return fmt.Sprintf("[%s IN %s …]", .Var, (.List)) case *ast.Index: return (.Base) + "[" + (.Idx) + "]" case *ast.Slice: , := "", "" if .From != nil { = (.From) } if .To != nil { = (.To) } return (.Base) + "[" + + ".." + + "]" case *ast.PropOf: return (.Base) + "." + .Key case *ast.MapProj: := make([]string, len(.Entries)) for , := range .Entries { switch .Kind { case ast.MapProjProp: [] = "." + .Key case ast.MapProjAll: [] = ".*" default: [] = .Key + ": " + (.Expr) } } return .Var + "{" + strings.Join(, ", ") + "}" case *ast.MapLit: := make([]string, len(.Fields)) for , := range .Fields { [] = .Key + ": " + (.Val) } return "{" + strings.Join(, ", ") + "}" case *ast.HasLabelExpr: return .Var + ":" + fmtLabelExpr(.Expr) } return "?" } // fmtLabelExpr renders a label expression, parenthesizing &/| so // precedence is unambiguous. func fmtLabelExpr( *ast.LabelExpr) string { switch .Kind { case ast.LabelName: return .Name case ast.LabelWild: return "%" case ast.LabelAnd: return "(" + (.L) + "&" + (.R) + ")" case ast.LabelOr: return "(" + (.L) + "|" + (.R) + ")" default: return "!" + (.L) } } func binopStr( ast.BinOp) string { switch { case ast.OpOr: return "OR" case ast.OpAnd: return "AND" case ast.OpEq: return "=" case ast.OpNeq: return "<>" case ast.OpLt: return "<" case ast.OpLte: return "<=" case ast.OpGt: return ">" case ast.OpGte: return ">=" case ast.OpAdd: return "+" case ast.OpSub: return "-" case ast.OpMul: return "*" case ast.OpDiv: return "/" case ast.OpStartsWith: return "STARTS WITH" case ast.OpEndsWith: return "ENDS WITH" default: return "CONTAINS" } }