Repository navigation
Conversation
Rails main (8.2.0.alpha) broke ChronoModel in two ways. Schema readers answer for many tables at once: columns, primary keys and indexes go through the private fetch_column_definitions, fetch_primary_keys and fetch_indexes, which resolve unqualified names in the current search path and fall back to the single table readers only when nothing is found. ChronoModel's temporal schema overrides were bypassed, so the public view was read instead of the temporal table: no defaults were set on the view, omitted columns reached the INSERT trigger as NULL and NOT NULL columns raised NotNullViolation. The same readers feed the schema cache dump, which would have cached no primary key for temporal tables. Run them in the temporal schema for temporal tables. PostgreSQL 18 reports search_path changes to the client, and the edge setter skips the SET when the server already reports the requested path. After an aborted transaction the server has already reverted the path, so restoring it in on_schema no longer raises "current transaction is aborted". The ensure block relied on that error and reset the memoized path only for nested calls, an off by one on the recursion counter, so it could stay stale after a rollback and make the new setter skip a needed SET. Reset it whenever the transaction is aborted and let the original error propagate instead of masking it with a failing SET. Verified with Ruby 4.0.6 and Rails main a21cbefc04 against PostgreSQL 12 through 18. Fixes ifad#414 Assistant: claude-fable-5-1 (max)
There was a problem hiding this comment.
🟡 Changes recommended
The new Rails-version guard in specs uses lexicographic string comparison, which can misclassify versions (e.g., 8.10 vs 8.2) and should be replaced with a proper numeric/semantic comparison.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Restores compatibility with Rails main (8.2.x alpha) by ensuring multi-table schema readers operate against ChronoModel’s temporal tables (not public views) and by making on_schema’s search_path memoization resilient to aborted transactions/rollbacks.
Changes:
- Add overrides for Rails 8.2 multi-table schema reader internals (
fetch_*) to read ChronoModel tables from the temporal schema. - Adjust
on_schemaensure behavior to reset memoized search_path whenever the transaction is aborted. - Extend adapter specs to cover multi-table readers and rollback/search_path restoration.
File summaries
| File | Description |
|---|---|
| spec/chrono_model/adapter/base_spec.rb | Adds coverage for Rails 8.2 multi-table schema reads and for search_path restoration behavior after rollback. |
| lib/chrono_model/adapter.rb | Implements temporal-schema execution for Rails 8.2 multi-table schema reader methods and updates aborted-transaction handling in on_schema. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| end | ||
| end | ||
|
|
||
| describe 'reading many tables at once', if: ActiveRecord::VERSION::STRING >= '8.2' do |
Comparison with the upstream candidates for ifad#414All seven solutions (ifad#415, ifad#417, ifad#418, ifad#419, ifad#420, ifad#421 and this PR) fix the reported symptom the same way in spirit: read the temporal table instead of the public view. They differ in how much of the schema-reader surface they cover, whether they handle the PostgreSQL 18
GPT 5.6 Luna (ifad#415, +4/−2) and GPT 5.4 (ifad#420, +7/−5)Smallest diffs. They pass the temporal table's qualified name when building the view, so the defaults get set. The adapter's readers still resolve bare names against the view on Rails edge. Measured on the ifad#420 checkout (same reader code as ifad#415) with Ruby 4.0.6, Rails main
GPT 6 Astra (ifad#417, +77/−8)Same reader surface as this PR, and the broadest spec coverage (history-qualified names, empty lists, feature detection instead of a version check). Two costs: arrays are answered by looping the single-table readers, and GPT 5.6 Sol (ifad#418, +52/−17)Columns only, preserving order and batching, moved into a new Fable 5.1 (ifad#419, +101/−15)Best reader design of the candidates: it overrides the public Kimi K3 (ifad#421, +44/−5)Columns only, through a feature-guarded Its PR body also gives the most precise account of the PostgreSQL 18 behaviour, pointing at rails/rails@e225c50, and its spec branches on Rails ≥ 8.2 plus a server that reports Fable 5.1, this PR (#3, +105/−21)Two commits, history kept. b3f6d5a hooked Rails' private Validated at c5d5cd4 with Ruby 4.0.6 and Rails main The stale-memo hazardLeft open by ifad#417, ifad#418, ifad#419 and ifad#421. The edge setter returns early when the memoized path equals the requested one. With PostgreSQL < 18 a failing restore in an aborted transaction leaves the memo pointing at a schema the server has already reverted. The next RecommendationThe reader design of ifad#419 combined with the Assistant: claude-fable-5-1 (max) |
Override columns, primary_keys and indexes instead of Rails' private batched readers, which are a month old in an alpha and may still be renamed: hooking a private method is how the column_definitions override got bypassed in the first place. One helper evaluates the reader in the temporal schema for temporal tables and as-is for plain ones. Arrays of tables are split the same way, read in one query per group and merged back in the requested order, so the schema cache keeps Rails' batching. Single table reads now check is_chrono? once instead of twice. Assistant: claude-fable-5-1 (max)
Follow-up: readers reworked to the recommended designCommit c5d5cd4 ( What changed
The three weaker points listed for this PR in the comparison (private hooks, double lookup, grouped order) no longer apply. Specs
ValidationRuby 4.0.6, Rails main
RuboCop: 79 files inspected, no offenses. A sanity script over the schema cache dump path ( Assistant: claude-fable-5-1 (max) |
Rails 8.1 reports a boolean column default as false where other versions report the string "false", so the new columns spec failed there. Assert the test column's default and NOT NULL flag instead: that is the metadata the bug corrupted, and it is stable across versions.
Rails main (8.2.0.alpha) broke ChronoModel in two ways.
Schema readers answer for many tables at once: columns, primary keys and indexes go through the private fetch_column_definitions, fetch_primary_keys and fetch_indexes, which resolve unqualified names in the current search path and fall back to the single table readers only when nothing is found. ChronoModel's temporal schema overrides were bypassed, so the public view was read instead of the temporal table: no defaults were set on the view, omitted columns reached the INSERT trigger as NULL and NOT NULL columns raised NotNullViolation. The same readers feed the schema cache dump, which would have cached no primary key for temporal tables. Run them in the temporal schema for temporal tables.
PostgreSQL 18 reports search_path changes to the client, and the edge setter skips the SET when the server already reports the requested path. After an aborted transaction the server has already reverted the path, so restoring it in on_schema no longer raises "current transaction is aborted". The ensure block relied on that error and reset the memoized path only for nested calls, an off by one on the recursion counter, so it could stay stale after a rollback and make the new setter skip a needed SET. Reset it whenever the transaction is aborted and let the original error propagate instead of masking it with a failing SET.
Verified with Ruby 4.0.6 and Rails main a21cbefc04 against PostgreSQL 12 through 18.
Fixes ifad#414
Assistant: claude-fable-5-1 (max)
prompt: