// The public entry points: Run/RunWithParams execute a GQL query over an // engine snapshot; Explain renders the plan. Pipeline: parse -> desugar -> // plan (cost-based) -> execute, mapping each stage's typed error onto the // package sentinels.
package gql import ( chickpeas ) // Run executes a GQL query over the snapshot, through the snapshot's // implicit plan cache (see DefaultCacheFor): repeats skip parse + plan, // results are identical to the uncached path by the -cached-parity // contract, and structurally flipped templates route through sighted // planning. Use RunUncached for the always-literal-planned path. func ( *chickpeas.Snapshot, string) (*Rows, error) { return RunWithParams(, , nil) } // RunWithParams is Run with explicit $name parameter values. An // unsupplied parameter reads as null. func ( *chickpeas.Snapshot, string, map[string]value.Value) (*Rows, error) { return DefaultCacheFor().RunWithParams(, , ) } // RunUncached executes a GQL query with literal-sighted planning on // every call, bypassing the implicit plan cache -- the cold path, for // benchmarks separating cold from warm and for callers that mutate // planner behavior between runs. func ( *chickpeas.Snapshot, string) (*Rows, error) { return RunUncachedWithParams(, , nil) } // RunUncachedWithParams is RunUncached with explicit $name parameter // values. func ( *chickpeas.Snapshot, string, map[string]value.Value) (*Rows, error) { , , , , := prepare(, ) if != nil { return nil, } := &eval.Ctx{G: , Named: , ForceInterp: forceInterp} return execPlan(, , .Mode, , ) } // execPlan executes a built plan in its query mode: Run returns result // rows; EXPLAIN returns the rendered plan; PROFILE executes while // recording per-operator produced-row counts and returns the annotated // plan (cardinalities, not the result set). func execPlan( *graph.SnapshotGraph, *plan.Plan, ast.QueryMode, time.Duration, *eval.Ctx) (*Rows, error) { switch { case ast.Explain: return renderPlanRows(, , nil, ), nil case ast.Profile: return renderPlanRows(, , exec.ExecuteProfiled(, ), ), nil } , := exec.Execute(, ) if != nil { return nil, wrapStage() } return newRows(.Columns, ), nil } // Explain renders the query's plan (with cardinality estimates and anchor // notes) without executing it. func ( *chickpeas.Snapshot, string) (string, error) { , , , , := prepare(, ) if != nil { return "", } return strings.Join(explain.Render(, nil, , plan.Estimate(, )), "\n"), nil } // ExplainCanonical renders the query's plan as a stable, canonical shape string // for golden plan-regression snapshots: operator order, scan sources, hop // patterns, the recognizers that fired, and the chosen anchor -- with the // volatile parts (wall-clock planning time, the numeric estimates, the anchor // note's magnitudes) excluded. Unlike Explain the output is deterministic across // plannings, so a diff between two commits is a review prompt that a planner // change moved the plan, invisible to a row-level differential. func ( *chickpeas.Snapshot, string) (string, error) { , , , , := prepare(, ) if != nil { return "", } return strings.Join(explain.Canonical(, plan.Estimate(, )), "\n"), nil } // prepare runs the shared front half: parse, desugar, plan -- timing the // planning for the EXPLAIN/PROFILE header. func prepare( *chickpeas.Snapshot, string) (*ast.Query, *graph.SnapshotGraph, *plan.Plan, time.Duration, error) { := time.Now() , := parseDesugar() if != nil { return nil, nil, nil, 0, } := graph.New() , := plan.Build(, ) if != nil { return nil, nil, nil, 0, wrapStage() } return , , , time.Since(), nil } // parseDesugar is the language front half: parse, then normalize. func parseDesugar( string) (*ast.Query, error) { , := parser.Parse() if != nil { return nil, fmt.Errorf("%s: %w", .Error(), ErrParse) } if := semantics.Desugar(); != nil { return nil, wrapStage() } return , nil } // renderPlanRows renders an EXPLAIN/PROFILE-mode query as a one-column // result (per-operator counts included when prof is non-nil). func renderPlanRows( graph.Graph, *plan.Plan, *explain.Profile, time.Duration) *Rows { := explain.Render(, , , plan.Estimate(, )) := make([][]value.Value, len()) for , := range { [] = []value.Value{value.Str()} } return newRows([]string{"plan"}, ) } // forceInterp pins execution to the interpreted eval path -- the // dual-path differential test hook (package tests set it; never exported). var forceInterp bool // wrapStage maps a pipeline stage's typed error onto the package // sentinels, keeping the message. func wrapStage( error) error { var *semantics.Error if errors.As(, &) { switch .Kind { case semantics.KindBind: return fmt.Errorf("%s: %w", .Msg, ErrBind) default: return fmt.Errorf("%s: %w", .Msg, ErrPlan) } } return }