Mark the order_hacks-injected ORDER BY SqlLiteral as retryable#2770
Merged
Conversation
`Arel::Visitors::OracleCommon#order_hacks` rewrites the outer ORDER
BY of `DISTINCT ... FIRST_VALUE(...)` queries (Oracle's
`columns_for_distinct` rewrite for ordering on columns outside the
SELECT list) by pushing `Nodes::SqlLiteral.new("alias_N__ DESC NULLS
...")` back into `o.orders`. `SqlLiteral` defaults to
`retryable: false`, and `visit_Arel_Nodes_SqlLiteral` runs
`collector.retryable &&= o.retryable`, so this single literal flips
the whole SELECT's `collector.retryable` to false even though the
query is a plain idempotent SELECT.
The bug is the same shape as the ROWNUM `SqlLiteral` issue fixed in
rsim#2766 — a visitor-internal `SqlLiteral` defeating AR's
`with_raw_connection(allow_retry: true)`. Both `Arel::Visitors::Oracle`
(pre-12c) and `Arel::Visitors::Oracle12` route through the same
`order_hacks` helper, so DISTINCT queries that hit this branch on
either visitor are affected.
Pass `retryable: true` so the rewrite is neutral on retryability,
matching the upstream Rails convention for visitor-injected
`SqlLiteral` (see `Arel::Visitors::MySQL` line 30/106:
`o.froms ||= Arel.sql("DUAL", retryable: true)` and the
`Arel.sql(quote_column_name(...), retryable: true)` projection
rewrite).
Add a regression spec in `arel/oracle_spec.rb` that builds the
DISTINCT+FIRST_VALUE shape, runs it through the visitor with a
collector preset to `retryable = true` (matching AR's
`to_sql_and_binds` preamble), and asserts the collector still
reports `retryable` true after compilation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Companion to #2766.
Arel::Visitors::OracleCommon#order_hacksrewrites the outerORDER BYofDISTINCT ... FIRST_VALUE(...)queries by pushingNodes::SqlLiteral.new("alias_N__ DESC NULLS ...")intoo.orders.SqlLiteraldefaults toretryable: false, andvisit_Arel_Nodes_SqlLiteralrunscollector.retryable &&= o.retryable, so this single literal flips the whole SELECT'scollector.retryableto false.Both
Arel::Visitors::Oracle(pre-12c) andArel::Visitors::Oracle12route through the sameorder_hackshelper, so DISTINCT-with-outside-order queries on either visitor are affected. AR core'swith_raw_connection(allow_retry: true)readscollector.retryableback intoallow_retry, so the practical fallout is that those SELECTs miss the AR-level reconnect-and-retry layer even though they are plain idempotent SELECTs.Pass
retryable: trueso the rewrite is neutral on retryability. This matches the upstream Rails convention for visitor-injectedSqlLiteral:Arel::Visitors::MySQLline 30 doeso.froms ||= Arel.sql("DUAL", retryable: true)and line 106 does the same for projection rewrites.Adds a regression spec in
arel/oracle_spec.rbthat builds the DISTINCT+FIRST_VALUE shape, runs it through the visitor with a collector preset toretryable = true(matching AR'sto_sql_and_bindspreamble), and asserts the collector still reportsretryabletrue after compilation.Test plan
BUNDLE_ONLY=rubocop bundle exec rubocop— clean (95 files, 0 offenses)test/test_11g—arel/oracle_spec.rbexercises the visitor in both versions viaOracleCommon