Skip to content

perf(ast): stop heap-copying nodes in Children() so Walk/Inspect allocate less (#557) - #588

Merged
ajitpratap0 merged 3 commits into
mainfrom
perf/ast-children-no-copy
Oct 13, 2026
Merged

ajitpratap0 merged 3 commits into
mainfrom
perf/ast-children-no-copy

Conversation

@ajitpratap0

Copy link
Copy Markdown
Owner

Summary

#557 perf(ast): stop heap-copying nodes in Children() so Walk/Inspect allocate less

Children() in pkg/sql/ast no longer copies each value-typed child to the heap (x := x; &x). It now returns pointers into the parent's slices (&s.From[i], &s.Joins[i], &f.OrderBy[i], ...) and sizes the result slice up front. Walk and Inspect use an unexported childrenOf, so JoinClause, WindowFrame and MergeStatement are visited in place, and the exported method signatures stay unchanged. Measured with benchstat (-count=6, p=0.002):

  • Inspect/select_join_window: 47 to 26 allocs/op, 3216 to 928 B/op
  • Fingerprint: 182 to 170 allocs/op
  • Lint/Realistic: 728 to 580 allocs/op, -36% B/op
  • Extract*: 20-45% fewer allocs
  • Scan* and Transpile: unchanged

Visit order is unchanged: a golden generated from the parent commit passes on both parent and HEAD, and a Walk-vs-Children equivalence test passes. One intended behaviour change: a visitor that mutates value-typed children (table references, ORDER BY items, WHEN clauses, update assignments, frame bounds, MERGE target/source) now changes the AST, and the change shows up in SQL() and the formatter. The new regression test fails on the parent and passes on HEAD. The full race gate (uncached), go vet and golangci-lint all pass.

Commits (19), one per issue:

146 files changed, 13989 insertions(+), 4969 deletions(-)

Test Plan

  • Independent validator ran the repository race gate (CGO_ENABLED=1 go test -race -timeout 120s $(go list ./... | grep -v /cbinding)), go vet, and golangci-lint on this branch.
  • Each change was reviewed by an independent reviewer with access to the code and benchmarks before validation.
  • Benchmarks compared with benchstat -count=6 before/after on the same machine; allocs/op and B/op are the deterministic signal, ns/op is directional on a loaded machine.

#557 benchstat

I ran benchstat on scratchpad/b2/557/before.txt against after.txt (-count=6, every row p=0.002) and confirmed the HEAD numbers with fresh runs.
- Inspect/select_join_window: 47 to 26 allocs/op, 3216 to 928 B/op. A fresh HEAD run gives 26 allocs and 928 B.
- Inspect/insert_multirow: 16 to 1 allocs/op, 2144 to 576 B/op. A fresh run gives 1 alloc and 576 B.
- Fingerprint: 182 to 170 allocs/op, 20.80 to 19.02 KiB (-8.55%). A fresh run gives 170 allocs and 19481 B.
- Normalize: 178 to 166 allocs/op, 20.42 to 18.65 KiB.
- Lint/Tiny: 135 to 99 allocs/op, -32% B/op.
- Lint/Realistic: 728 to 580 allocs/op, 56.78 to 36.58 KiB.
- ExtractMetadataLarge: 587 to 466 allocs/op.
- ExtractTables 11 to 6, ExtractColumns 15 to 9, ExtractFunctions 12 to 9, ExtractMetadata 24 to 17 allocs/op.
- All 24 Scan*/Scanner_* benchmarks have unchanged allocs/op; B/op is unchanged or down by up to 0.56%.
- Transpile stays at 92 allocs/op (a fresh run agrees), B/op -0.01%.
- ns/op is directional only: the baseline was noisy (±13-191%).

Differential check: I extracted the parent commit 74a9567 with git archive and added the new test files. There, TestWalk_VisitOrderGolden and TestWalk_MatchesChildrenRecursion pass, which shows the golden matches pre-change visit order. TestWalk_VisitorMutationReachesAST fails in all 5 subtests (TableReference/OrderBy, Window/Aggregate/WithinGroup/Frame, WhenClause, UpdateExpression, MergeTargetSource). At HEAD all of them pass.

#557 race gate: PASS. Ran CGO_ENABLED=1 go test -race -timeout 120s $(go list ./... | grep -v /cbinding) twice. The first run was served from cache. The second used -count=1 so every test actually ran, and it exited 0: 44 packages ok, 18 with no test files, no FAIL, no DATA RACE, no panic. Logs are in /private/tmp/claude-501/-Users-ajitpratapsingh-dev-GoSQLX/b99cd3cf-cb92-4294-a0db-d6a7c9a86253/scratchpad/b2/557/val/race_nocache.txt.

Breaking Changes

  • perf(ast): stop heap-copying nodes in Children() so Walk/Inspect allocate less #557: There are no exported signature or method-set changes. All Children()/TokenLiteral receivers stay as they are, so value JoinClause{} / WindowFrame{} / MergeStatement{} still satisfy ast.Node (interface_test.go:127-133 keeps compiling). Additions are unexported only: childrenOf in visitor.go, and childrenInPlace on *JoinClause, *WindowFrame and *MergeStatement.

The issue's step 2 (pointer receivers) is deliberately declined. It would drop Node from the value method sets, a compile-time break for external callers such as ast.Walk(v, sel.Joins[i]), and the ground rules call for non-breaking options; the in-place fast path in Walk gets the same allocation win.

Observable behaviour change, which is intended and fixes the latent bug the issue describes:

  • Children() and Walk/Inspect now yield pointers that alias the AST's stored value nodes (TableReference, JoinClause, OrderByExpression, WindowSpec, WindowFrameBound, WhenClause, UpdateExpression, ColumnDef/ColumnConstraint/TableConstraint, IndexColumn, SetClause, AlterTableAction, PartitionDefinition, Identifier in TriggerEvent).
  • Visitor mutations reach the tree, and pointer identity is stable across calls.
  • Those pointers share the AST's lifetime (invalid after ReleaseAST).

The CHANGELOG entry (Performance, plus a behaviour note) is to be written during the later consolidation step, not on this branch.

Notes

Fixes #557
Part of #566

🤖 Generated with Claude Code

https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve

@vercel

vercel Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
gosqlx-website Ready Ready Preview Oct 13, 2026 6:53am UTC

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 24s —— View job


Review of PR #588 (perf(ast): stop heap-copying nodes in Children())

I read the non-test diff in pkg/sql/ast. I did not run the tests, benchmarks or race gate here, and I did not read the new test files.

Overall: The change looks correct and well scoped. Replacing x := x; &x with &s.From[i] is safe even for value-receiver Children() methods. The receiver copy shares the slice backing array, so the pointers still alias the real AST storage. childrenOf in visitor.go handles the cases where that doesn't hold, because the struct itself is a field of the receiver. Those cases are JoinClause, WindowFrame and MergeStatement.

  • Read the diff for visitor.go, ast.go, ast_clauses.go, ast_expressions.go, dml.go and trigger.go
  • Compare the benchstat claims against the code

Observations

  1. Direct Children() calls still return copies for some nodes.

    • Examples are Update.Children() returning &u.Table, and JoinClause/WindowFrame called on a value.
    • Walk and Inspect now reach the real nodes through childrenOf, but direct calls don't.
    • The doc comments on JoinClause and WindowFrame say this. The Update.Table case is not documented. If Update is a value receiver, &u.Table still points into a copy. Check that TestWalk_VisitorMutationReachesAST covers Update.Table and not only the Updates slice. If it doesn't, add a note or switch to a childrenInPlace pattern for Update.
    • Walk(v, someJoinClauseValue) also still works on a copy. That is expected.
  2. childrenOf is a hand-maintained type switch.

    • A future value-receiver node that takes the address of its own fields would silently regress to the copy behaviour.
    • A small interface such as interface{ childrenInPlace() []Node } would be more extensible. The cost is an interface assertion on every Walk step, which could eat into the gain. Your concrete type switch is the cheaper choice.
    • TestWalk_MatchesChildrenRecursion partly guards this. A test that fails when a value-receiver Children() returns the address of a receiver field not in the switch would close the gap.
  3. Lifetime and aliasing is a behaviour change.

    • Retaining nodes collected during a walk past ReleaseAST is now use-after-release for value-typed children too. Before, those children were detached copies.
    • The doc comments in ast.go and visitor.go cover this. A CHANGELOG "Changed" note is planned for the consolidation PR. Please make sure it isn't dropped, since downstream code that collects *TableReference or *OrderByExpression nodes could break.
  4. Pre-sizing is inconsistent.

    • FunctionCall, CaseExpression and WindowSpec now pre-size their result slice.
    • Select.Children() and Update.Children() still use make([]Node, 0) followed by nodifyExpressions and appends. They could be pre-sized for the same kind of win, but this isn't blocking.
  5. Nit: Update.Children() allocations. &u.Table forces u to escape for a value receiver. This is minor and the same as before.

Tests

The golden-order test, the Walk-versus-Children equivalence test and the mutation regression test are the right safeguards. The golden was generated from the parent commit, which is a good way to prove the visit order is unchanged.

Security: No concerns.

Verdict: Looks good to merge once the Update.Table mutation coverage (point 1) is confirmed.

@ajitpratap0

Copy link
Copy Markdown
Owner Author

Base PR #581 has merged, so this branch was rebased onto main (single commit 87c84da, diff byte-identical to the previous 7b173af) and the PR base switched from perf/ast-pool-release-fixes to main. Race tests for ast/parser/gosqlx re-run green after the rebase.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 17s —— View job


Review of PR #588: stop heap-copying nodes in Children()

I read the non-test diff in pkg/sql/ast and checked UpdateStatement and the mutation test by hand. I did not run the tests, benchmarks or race gate.

Verdict: Looks good to merge.

Correctness

  • &s.From[i] and similar are safe in value-receiver Children() methods. The receiver copy shares the slice backing array, so the pointers still alias the real AST storage.
  • childrenOf in visitor.go handles the cases where aliasing would fail, because the struct is itself a field of the receiver: JoinClause, WindowFrame and MergeStatement.
  • The exported API is unchanged. Declining pointer receivers keeps Node in the value method sets, which avoids a compile-time break for callers.

Correction to the earlier automated review

  • That review asked for Update.Table mutation coverage, but UpdateStatement has no Table field (ast_statements.go:328). It has TableName string, Assignments []UpdateExpression and From []TableReference. Those are all slice-backed, so they alias correctly.
  • TestWalk_VisitorMutationReachesAST/UpdateExpression covers Assignments (visitor_mutation_test.go:150).
  • The From entries are covered indirectly through the shared TableReference path. A one-line UPDATE ... FROM subtest would make that explicit, but it is not blocking.

Non-blocking suggestions

  1. Hand-maintained type switch.
    • A future value-receiver node that takes the address of its own struct field would silently fall back to copy behaviour.
    • TestWalk_MatchesChildrenRecursion only partly guards this, since it compares visit order.
    • A test that checks pointer identity for every node type would close the gap.
  2. Pre-sizing is inconsistent.
    • Select.Children() and Update.Children() still start from make([]Node, 0).
    • Pre-sizing them would give a further small allocation win.
  3. Lifetime change.
    • Nodes collected during a walk are now use-after-release once ReleaseAST runs, including value-typed children. Previously those children were detached copies.
    • The doc comments cover this. Make sure the planned CHANGELOG "Changed" note in the consolidation PR isn't dropped.

Tests

  • The golden visit-order test, the Walk-versus-Children equivalence test and the mutation regression test are the right safeguards.
  • Generating the golden from the parent commit is a good way to prove the visit order is unchanged.

Security: No concerns.

@ajitpratap0

Copy link
Copy Markdown
Owner Author

Closing and reopening to trigger CI on the rebased branch against main (the base switch happened after the push, so the main CI workflow never ran for this PR).

@ajitpratap0 ajitpratap0 reopened this Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 18s —— View job


Review of PR #588: stop heap-copying nodes in Children()

I read the non-test diff (visitor.go, ast.go, dml.go, trigger.go). I did not run the tests, benchmarks or race gate, and I did not read the new test files.

Verdict: Looks good to merge. One doc/coverage gap is worth closing.

Correctness

  • &s.From[i] and similar are safe in value-receiver Children() methods. The receiver copy shares the slice backing array, so the pointers alias the real AST storage.
  • childrenOf handles JoinClause, WindowFrame and MergeStatement. These are structs stored as fields, so aliasing needs a pointer receiver. The exported method sets are unchanged.
  • The childrenOf type switch is a cheap type-word compare. It adds no allocations.

Correction to the two earlier automated reviews

  • The second review said UpdateStatement has no Table field, so the first review's Update.Table concern doesn't apply. That is true for UpdateStatement.
  • dml.go has a separate Update type, which does have a Table field. Update.Children() at dml.go:119 still does &u.Table on a value receiver.
    • That pointer targets the receiver copy, not the AST. It is the same as before this PR.
    • Visitor mutations to that node therefore don't reach the tree. This is inconsistent with the new doc comments in ast.go and visitor.go.
    • Updates[i] is fine.
  • If Update is used in real trees, add it to childrenOf with a childrenInPlace, or note the exception in the docs. If it is legacy and unused by the parser, a one-line comment on Update.Children() is enough.

Non-blocking suggestions

  1. Hand-maintained type switch. A new value-receiver node that takes the address of its own field would silently fall back to copy semantics. A test could use reflection over all Node types to check pointer identity of the children they return. This would catch new cases.
  2. Pre-sizing. Select.Children() and Update.Children() still start from make([]Node, 0) and append. Pre-sizing would remove a few more allocations. The other nodes in this PR already do it.
  3. Behaviour change. Value-typed children were detached copies before. Now they are use-after-release once ReleaseAST runs. The docs cover this. Keep the planned CHANGELOG "Changed" note in the consolidation PR.

Tests and security

  • The golden visit order, the Walk-versus-Children equivalence and the mutation regression are the right safeguards. Generating the golden from the parent commit is a good approach.
  • No security concerns.

@ajitpratap0

Copy link
Copy Markdown
Owner Author

Windows CI failure was a line-ending issue: the runner checks testdata/visit_order.golden out with CRLF and the golden comparison was byte-exact. Pushed a test-only commit that normalizes CRLF→LF on read (verified locally against a CRLF-converted copy). The implementation commit is unchanged.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 15s —— View job


Review of PR #588: stop heap-copying nodes in Children()

I read the non-test diff and checked dml.go by hand. I did not run the tests, benchmarks or race gate, and I did not read the new test files.

Verdict: Looks good to merge. One small doc gap is worth closing.

Correctness

  • &s.From[i] and similar are safe in value-receiver Children() methods. The receiver copy shares the slice backing array, so the pointers alias the real AST storage.
  • childrenOf covers JoinClause, WindowFrame and MergeStatement. Each is a struct stored as a field, so aliasing needs a pointer receiver. The exported method sets are unchanged.

The Update.Table question from the earlier reviews

  • dml.go:104 defines Update with a Table TableReference field. Update.Children() has a value receiver (dml.go:117) and returns &u.Table.
  • That pointer targets the receiver copy, not the AST. The behaviour is the same as before the PR. A visitor that mutates this node will not change the tree, which contradicts the new aliasing docs.
  • Updates[i] is slice-backed and aliases correctly.
  • I found no non-test constructor of Update in pkg/. Only (*Update).SQL() and statementNode() reference it. It looks legacy and unused by the parser, so the impact is low.
  • Suggested fix: add a one-line comment on Update.Children() noting that Table is returned by copy. Or add Update to the childrenOf switch with a childrenInPlace method on *Update.

Non-blocking suggestions

  1. Hand-maintained type switch. A new value-receiver node that takes the address of its own struct field would silently fall back to copy semantics. A reflection test over the Node types that checks child pointer identity would catch this.
  2. Pre-sizing. Select.Children() and Update.Children() still start from make([]Node, 0). Pre-sizing them would remove a few more allocations.
  3. Lifetime change. Value-typed children used to be detached copies. They are now use-after-release once ReleaseAST runs. The doc comments cover this. Keep the planned CHANGELOG "Changed" note in the consolidation PR.

Tests and security

  • The golden visit-order test, the Walk-versus-Children equivalence test and the mutation regression test are the right safeguards. Generating the golden from the parent commit is a good approach.
  • No security concerns.

ajitpratap0 added a commit that referenced this pull request Oct 13, 2026
Git on the windows-latest CI runner has core.autocrlf=true, so without a
.gitattributes it checks text files out with CRLF. Byte-exact golden
comparisons and content-hashed fixtures then pass on macOS/Linux and fail
only on Windows: PR #588 hit this with pkg/sql/ast/testdata/visit_order.golden,
and the #548 tokenizer golden corpus hashes 40 corpus .sql files.

Set `* text=auto eol=lf` repo-wide, pin Go sources, module files, fixtures
and shell scripts to LF explicitly, and mark image/font/wasm/archive
assets binary. No tracked file currently has CRLF endings
(`git ls-files --eol` reports none) and the repo has no Windows scripts,
so `git add --renormalize .` changes nothing.


Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve

Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Ajit Pratap Singh and others added 3 commits October 13, 2026 12:10
…cate less (#557)

Children() used to copy every value-typed child (TableReference, JoinClause,
WindowSpec, OrderByExpression, WhenClause, UpdateExpression, ...) to the heap
through the `x := x; append(children, &x)` pattern. It also built unsized
slices through temporary nodifyExpressions slices. Every Walk/Inspect user
paid for this: linter, security scanner, metadata extraction,
fingerprint/normalize and transpiler. Visitors that mutated those nodes
changed copies, not the AST.

- Take addresses by index (&s.From[i], &s.Joins[i], &f.OrderBy[i], ...) in
  every Children(), removing all G601 copy sites outside sql.go rendering.
- Presize the result and append expressions directly in SelectStatement,
  InsertStatement, WindowSpec, FunctionCall, CaseExpression and
  MergeStatement. Each now makes exactly one allocation.
- Add unexported pointer-receiver childrenInPlace on *JoinClause,
  *WindowFrame and *MergeStatement, and an unexported childrenOf that Walk
  uses. Walk now visits Left/Right, Start and TargetTable/SourceTable in
  place without copying the receiver. The exported value-receiver
  Children()/TokenLiteral are unchanged in signature and output, so no
  method set changes.
- Document the aliasing and lifetime contract on Node and Walk.

Behaviour change (intended): visitor mutations of value-typed children now
reach the AST, and those pointers share the AST's lifetime.

Measured (benchstat, -count=6, allocs/op and B/op):
  Inspect/select_join_window  47 -> 26 allocs, 3216 -> 928 B
  Inspect/insert_multirow     16 -> 1 allocs,  2144 -> 576 B
  Fingerprint                182 -> 170 allocs, 20.8 -> 19.0 KiB
  Normalize                  178 -> 166 allocs
  Lint/Realistic             728 -> 580 allocs, 56.8 -> 36.6 KiB
  ExtractMetadataLarge       587 -> 466 allocs, 49.6 -> 31.8 KiB
  Scan*, Transpile           unchanged

Tests: a visit-order golden generated from the pre-change code, a
Walk-vs-Children equivalence check, pointer-identity tests for every
rewritten Children(), childrenOf/Children() agreement, one-alloc budgets,
visitor-mutation regression tests checked through SQL() and the formatter,
and a concurrent pooled-tree Inspect test under -race. New BenchmarkInspect,
BenchmarkFingerprint and BenchmarkNormalize.

Fixes #557

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
On the windows-latest CI runner git checks testdata/visit_order.golden
out with CRLF line endings (core.autocrlf), so TestWalk_VisitOrderGolden
failed with "got" and "want" lines that print identically. Normalize
CRLF to LF when reading the golden; the test is about visit order, not
line endings. Verified locally against a CRLF-converted copy of the file.

Part of #557

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
…ing with a reflection test (#557 review follow-up)

Pre-merge review found that Insert, Delete and Update still had value-receiver
Children() methods returning &x.Table, a pointer into the receiver copy, so
visitor mutations of Table during Walk/Inspect were silently lost.

Give each a pointer-receiver childrenInPlace and route them through
childrenOf, as already done for JoinClause, WindowFrame and MergeStatement.
The exported value-receiver Children() is unchanged (on a value it still
returns copies). TestChildrenOf_InPlaceFastPath covers the three new cases.

Add TestWalk_ChildPointersAliasParentStorage, which reflectively fills every
listed Node type and asserts each child pointer Walk yields lies inside the
parent's storage. It reported exactly Insert/Delete/Update before this change
and nothing after, and fails if a future value-receiver node returns &field
outside childrenOf.

Part of #557

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
@ajitpratap0
ajitpratap0 force-pushed the perf/ast-children-no-copy branch from 6081b9b to aa8cbea Compare October 13, 2026 06:52
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 13s —— View job


Review of PR #588: stop heap-copying nodes in Children()

I read the non-test diff and the latest commit (aa8cbea). I did not run the tests, benchmarks or race gate.

Verdict: Looks good to merge.

Correctness

  • &s.From[i] and similar are safe in value-receiver Children() methods. The receiver copy shares the slice backing array, so the pointers alias the real AST storage.
  • childrenOf (visitor.go:~203-214) now covers JoinClause, WindowFrame, MergeStatement, Insert, Delete and Update. Each of these is a struct stored as a field, or has a value-receiver Children() that takes the address of one of its own fields. Their pointer-receiver childrenInPlace makes Walk and Inspect visit the real nodes.
  • aa8cbea closes the Update.Table gap the earlier automated reviews raised. It also covers Insert.Table and Delete.Table.
  • The exported method sets are unchanged, so there is no compile-time break for callers.

Tests

  • TestWalk_ChildPointersAliasParentStorage fills every listed Node type by reflection and checks that each child pointer lies inside the parent's storage. This also addresses my earlier suggestion for a test that catches future value-receiver nodes returning &field.
  • The golden visit-order test, the Walk-versus-Children equivalence test and the mutation regression test remain the right safeguards. Generating the golden from the parent commit is a good approach.

Non-blocking suggestions

  1. Pre-sizing. Select.Children() and Update.Children() still start from make([]Node, 0) and append. Pre-sizing them would remove a few more allocations. This can be a follow-up.
  2. Lifetime change. Value-typed children used to be detached copies. They are now use-after-release once ReleaseAST runs. The doc comments cover this. Please keep the planned CHANGELOG "Changed" note in the consolidation PR.
  3. Direct Children() calls on a value. For JoinClause, WindowFrame, MergeStatement, Insert, Delete and Update, calling Children() on a value still returns pointers into a copy. This is documented, and only Walk and Inspect get in-place semantics.

Security: No concerns.

@ajitpratap0
ajitpratap0 merged commit 2d3ea2d into main Oct 13, 2026
28 checks passed
ajitpratap0 added a commit that referenced this pull request Oct 14, 2026
…resh performance baselines (#566)

CHANGELOG [Unreleased] now covers every PR merged since v1.14.0, with a
Behavior changes section for the observable changes in #597, #595, #584,
#585, #588, #581, #582/#570, #517/#518 and #511, plus Added, Changed,
Fixed, Performance, Security and Internal entries. The two existing
entries (#526 and the tokenizer-pool dialect fix) are kept verbatim.

performance_baselines.json is re-measured on an Apple M4 with
BenchmarkPerformanceBaseline -count=10 -benchmem (median). Baselines are
set at about 2.5x the M4 median rather than 1.5x: the CI Benchmark job on
ubuntu-latest measures these queries about 1.5-3.2x slower than M4 (last
9 Tests runs on main, 0715bb9..61ca6c8), so 1.5x with the unchanged
tolerances would fail CI's slowest runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
ajitpratap0 added a commit that referenced this pull request Oct 14, 2026
…xes; refresh performance baselines (#566) (#598)

* docs: consolidate CHANGELOG for the perf audit and batch-2 fixes; refresh performance baselines (#566)

CHANGELOG [Unreleased] now covers every PR merged since v1.14.0, with a
Behavior changes section for the observable changes in #597, #595, #584,
#585, #588, #581, #582/#570, #517/#518 and #511, plus Added, Changed,
Fixed, Performance, Security and Internal entries. The two existing
entries (#526 and the tokenizer-pool dialect fix) are kept verbatim.

performance_baselines.json is re-measured on an Apple M4 with
BenchmarkPerformanceBaseline -count=10 -benchmem (median). Baselines are
set at about 2.5x the M4 median rather than 1.5x: the CI Benchmark job on
ubuntu-latest measures these queries about 1.5-3.2x slower than M4 (last
9 Tests runs on main, 0715bb9..61ca6c8), so 1.5x with the unchanged
tolerances would fail CI's slowest runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve

* docs(changelog): add batch-3 follow-up fixes (#599-#620)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve

* docs(changelog): record #632 render-injection hardening and #624 quote guards

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve

* docs(changelog): correct batch-3 entries after review; record measured costs

- #628: SQLite still rejects count(*) [cnt]; only SQL Server keeps it.
- #631: bare FORMAT / FORMAT <name> rules apply after SETTINGS.
- Performance: record the measured costs of the batch-3 correctness fixes
  (Identifier and TableReference growth, quoting in SQL(), lineStarts
  reallocation, sorted Extract results).
- #632: note the lıke, WITH CHECK OPTION and MINUS rendering changes.
- Fix the [Unreleased] compare link (v1.14.0...HEAD) and add [1.14.0].
- Keep each section a single tight list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve

* docs(changelog): describe the release as source-compatible with runtime behaviour changes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — aa8cbead Deployed Oct 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(ast): stop heap-copying nodes in Children() so Walk/Inspect allocate less [BUG] GoSQLX does not support parentheses

1 participant