⚡ Bolt: reduce heap allocations in query builders#126
Conversation
Optimized `SelectQuery`, `UpdateQuery`, and `DeleteQuery` to reduce heap allocations and interface overhead during query construction. 1. Replaced `*int` with `int` and boolean flags for `limit` and `offset` to eliminate heap allocations when these options are set via the builder. 2. De-virtualized the primary table source in `SelectQuery` by replacing the `selectTableSource` interface with direct `*schema.TableDef` and `*SelectQuery` (subquery) fields. This avoids interface boxing allocations for the most common table sources. These changes reduce the allocation count for a complex SELECT query from 15 to 14, and potentially more when multiple modifiers are used. Benchmarks (Postgres): BenchmarkSelectToSQL/Simple: 6 allocs/op (no change) BenchmarkSelectToSQL/Complex: 15 -> 14 allocs/op BenchmarkSelectToSQL/BulkColumns: 8 allocs/op (no change) BenchmarkInsertToSQL/BulkInsert1000Rows: 5 allocs/op (no change) Co-authored-by: cungminh2710 <8063319+cungminh2710@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
Greptile SummaryThis PR eliminates heap allocations in the Rain ORM query builders by replacing pointer-based
Confidence Score: 5/5Safe to merge — all changed paths produce identical SQL output and the refactoring is well-guarded throughout. The de-virtualization of the table source is correctly propagated across every branch (isBareCompound, compile, compileAggregate, writeSQL, clone, relation loading). The limit/offset pointer-to-value conversion faithfully preserves semantics. No functional regressions were identified. No files require special attention.
|
| Filename | Overview |
|---|---|
| pkg/rain/query_select.go | Major refactor: table source split into table *schema.TableDef + tableSubquery/tableAlias; limit/offset pointers replaced with int+bool sentinels. All guard checks, clone, isBareCompound, compile, writeSQL, and compileAggregate correctly updated. writeTableSourceSQL introduced to deduplicate subquery rendering. |
| pkg/rain/query_common.go | Removed tableDefFromSelectSource (no longer needed) and updated writeOrderLimit signature from *int to int+bool. Logic preserved faithfully. |
| pkg/rain/query_delete.go | limit/offset changed from pointer to value+bool sentinel; offset/hasOffset fields are dead (no setter), but behavior is unchanged since they're always zero/false. |
| pkg/rain/query_update.go | Same limit/offset pointer-to-value change as DeleteQuery; SQLite FROM+ORDER/LIMIT guard correctly updated to use q.hasLimit; dead offset/hasOffset fields present. |
| pkg/rain/relation_loading.go | Updated to use concrete q.table field directly instead of type-asserting the old selectTableSource interface; internal query construction simplified. |
| pkg/rain/query_common_internal_test.go | Test updated to use new SelectQuery.table field directly (dropping tableDefSource{} wrapper); tableDefFromSelectSource test cases removed. |
| pkg/rain/query_compile_internal_test.go | All test fixtures updated from tableDefSource{table: ...} to users.TableDef() to match the new concrete field type. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
SQ[SelectQuery] --> |table != nil| TBL["*schema.TableDef\n(direct, no boxing)"]
SQ --> |tableSubquery != nil| TSUB["*SelectQuery\n(subquery source)"]
TSUB --> ALIAS["tableAlias string"]
SQ --> WTSS["writeTableSourceSQL()"]
WTSS --> |table != nil| CTX_TABLE["ctx.writeTable(table)"]
WTSS --> |tableSubquery != nil| CTX_SUB["'(' + subquery.writeSQL + ') AS alias'"]
SQ --> LIM["limit int\nhasLimit bool"]
SQ --> OFF["offset int\nhasOffset bool"]
LIM --> WOL["writeOrderLimit()"]
OFF --> WOL
WOL --> |"hasLimit || hasOffset"| FEAT["feature check\nthen LimitOffset(l, o)"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
SQ[SelectQuery] --> |table != nil| TBL["*schema.TableDef\n(direct, no boxing)"]
SQ --> |tableSubquery != nil| TSUB["*SelectQuery\n(subquery source)"]
TSUB --> ALIAS["tableAlias string"]
SQ --> WTSS["writeTableSourceSQL()"]
WTSS --> |table != nil| CTX_TABLE["ctx.writeTable(table)"]
WTSS --> |tableSubquery != nil| CTX_SUB["'(' + subquery.writeSQL + ') AS alias'"]
SQ --> LIM["limit int\nhasLimit bool"]
SQ --> OFF["offset int\nhasOffset bool"]
LIM --> WOL["writeOrderLimit()"]
OFF --> WOL
WOL --> |"hasLimit || hasOffset"| FEAT["feature check\nthen LimitOffset(l, o)"]
Reviews (2): Last reviewed commit: "perf(rain): reduce heap allocations and ..." | Re-trigger Greptile
|
@jules Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes. Issue 1 of 2pkg/rain/query_select.go:519-538 The block that writes a subquery table source (alias validation → Issue 2 of 2pkg/rain/query_select.go:184-188
|
Thank you for the detailed feedback. I've addressed both issues:
Verified the fixes with the full test suite and confirmed that performance improvements are preserved. |
Addressed PR feedback and finalized performance optimizations in query builders.
1. **Allocation Reduction**: Replaced `*int` pointers for LIMIT and OFFSET
with `int` and `bool` pairs (`hasLimit`, `hasOffset`) across `SelectQuery`,
`UpdateQuery`, and `DeleteQuery`. This eliminates heap allocations when
these options are set.
2. **Interface Devirtualization**: Replaced the boxed `selectTableSource`
interface in `SelectQuery` with direct `*schema.TableDef` and
`*SelectQuery` (subquery) fields. This avoids interface overhead for
the primary table source.
3. **DRY Rendering**: Factored out subquery table rendering into a private
`writeTableSourceSQL` helper in `SelectQuery`, eliminating duplicated
logic in `writeSQL` and `compileAggregate`.
4. **Consistency**: Ensured `hasOffset` is tracked and handled identically
to `hasLimit`, resolving ambiguity for `Offset(0)`.
Benchmarks confirmed that complex SELECT queries now perform 14 allocations
(down from 15), and the code remains clean and maintainable. All tests pass.
Co-authored-by: cungminh2710 <8063319+cungminh2710@users.noreply.github.com>
Reduced heap allocations in Rain ORM query builders. replaced pointer-based LIMIT/OFFSET with primitive values and de-virtualized the primary table source in SelectQuery to avoid interface boxing. confirmed performance improvement via benchmarks and verified correctness with the full test suite.
PR created automatically by Jules for task 16792089427694480247 started by @cungminh2710