The activerecord-oracle_enhanced-adapter gem's write_lobs method assumes every table has a single-column primary key. When prepared_statements is disabled (which happens automatically in Rails 8 development with query logging), Oracle requires LOB data larger than ~4KB to be written in a two-phase process:
- INSERT with
empty_clob()/empty_blob()placeholders - SELECT the row back FOR UPDATE, then write the LOB data via the locator
Step 2 uses the primary key to find the row. On a table with a composite primary key, klass.primary_key returns an Array like ["author_id", "book_id"], and the stock code does this:
id = quote(attributes[klass.primary_key])
# ...
WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATEThis produces:
SELECT "DESCRIPTION" FROM "TEST_TABLE"
WHERE "[author_id, book_id]" = NULL FOR UPDATEattributes[["author_id", "book_id"]]— Hash lookup with an Array key returnsnilquote_column_name(["author_id", "book_id"])— stringifies to a garbage column name- Result:
StatementInvalidorRecordNotFound— LOB data is silently lost
The monkey patch checks whether primary_key is an Array and builds a proper multi-column WHERE clause:
SELECT "DESCRIPTION" FROM "TEST_TABLE"
WHERE "AUTHOR_ID" = 1 AND "BOOK_ID" = 100 FOR UPDATEIt also adds a graceful warning for tables with no primary key at all (a pre-existing limitation).
activerecord-oracle_enhanced-adapter7.x and 8.x- Only manifests when
prepared_statements: false(Rails 8 dev default with query logging, or explicit configuration) - Works with both ruby-oci8 (CRuby) and JDBC (JRuby) — no platform-specific code
Drop the patch file into your app and require it from an initializer:
# config/initializers/oracle_enhanced_lob_fix.rb
require_relative "../../lib/patches/oracle_enhanced_composite_lob_fix"Or inline in the initializer — it's ~40 lines of actual code.