Background
The adapter's schema introspection code (in lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb and friends) issues queries against Oracle's data dictionary (all_tab_columns, all_indexes, all_constraints, all_synonyms, all_objects, ...) with owner / table / column names embedded as literals, not as bind variables. Example shape (paraphrased):
select_value("SELECT column_name FROM all_tab_columns WHERE owner = '#{owner}' AND table_name = '#{table_name}'")
Because each unique (owner, table) combination produces a distinct SQL text, the server creates a separate cursor in the shared pool for each one. For installs with very large schemas this can put measurable pressure on the shared pool.
Why this matters now
Historically the adapter mitigated this with cursor_sharing = force set as a session default — the server then rewrote literals into system-generated bind variables (:"SYS_B_0" ...) and shared the cursors. #2626 removes that default because it was the trigger for the #2619 hang on amd64 servers under prepared statements + RETURNING. Once #2626 lands, dictionary queries are no longer protected by that session-level workaround.
The architecturally correct fix is to bind the literals at the call sites instead of relying on a session-level switch. That works regardless of cursor_sharing setting and does not depend on the user opting into anything.
Scope
- Identify all dictionary queries in
lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb, connection.rb, and any related files that interpolate owner/table/column/sequence/etc. names as literals.
- Convert them to use bind parameters (
:owner, :table_name, ...) where the surrounding select_* API supports binds.
- Verify with
v$sql.sql_text that distinct invocations share a cursor (i.e., the SQL text contains the bind names rather than the literal values).
Out of scope
Acceptance criteria
Related
Background
The adapter's schema introspection code (in
lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rband friends) issues queries against Oracle's data dictionary (all_tab_columns,all_indexes,all_constraints,all_synonyms,all_objects, ...) with owner / table / column names embedded as literals, not as bind variables. Example shape (paraphrased):Because each unique
(owner, table)combination produces a distinct SQL text, the server creates a separate cursor in the shared pool for each one. For installs with very large schemas this can put measurable pressure on the shared pool.Why this matters now
Historically the adapter mitigated this with
cursor_sharing = forceset as a session default — the server then rewrote literals into system-generated bind variables (:"SYS_B_0"...) and shared the cursors. #2626 removes that default because it was the trigger for the #2619 hang on amd64 servers under prepared statements + RETURNING. Once #2626 lands, dictionary queries are no longer protected by that session-level workaround.The architecturally correct fix is to bind the literals at the call sites instead of relying on a session-level switch. That works regardless of
cursor_sharingsetting and does not depend on the user opting into anything.Scope
lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb,connection.rb, and any related files that interpolate owner/table/column/sequence/etc. names as literals.:owner,:table_name, ...) where the surroundingselect_*API supports binds.v$sql.sql_textthat distinct invocations share a cursor (i.e., the SQL text contains the bind names rather than the literal values).Out of scope
:cursor_sharingconnection option behavior — handled in Respect the database's CURSOR_SHARING setting by default #2626.Acceptance criteria
all_*/user_*queries in adapter code use binds for owner/table/column names where applicable.table_names, produces a single shared cursor on the server (queryable viav$sql).cursor_sharing: 'force'users (the binds path also benefits them — they just get a single shared cursor either way).Related
cursor_sharing = forcedefault; missing test coverage ofprepared_statements × cursor_sharingmatrix #2622 (broader cursor_sharing default reconsideration)cursor_sharing = forcedefault; this issue removes the underlying need for it on the dictionary-query path)