From 2d92e54640bf958b5761cf45ef116b0f0bdeb61e Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 14 Oct 2025 17:07:29 -0400 Subject: [PATCH] Fix CLOB/BLOB insert failure when prepared_statements disabled Add after_create callback to Lob module to write LOB data during record creation. Previously only after_update was implemented, causing CLOB/BLOB columns to remain empty when prepared_statements: false. The fix introduces record_lobs_for_create to track LOB columns with non-nil values before insert, allowing enhanced_write_lobs to handle both create and update scenarios. Fixes #2477 --- .../oracle_enhanced/lob.rb | 9 ++++ .../oracle_enhanced/type/text_spec.rb | 51 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/lib/active_record/connection_adapters/oracle_enhanced/lob.rb b/lib/active_record/connection_adapters/oracle_enhanced/lob.rb index 5be88a10a..4c4db6658 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced/lob.rb @@ -11,6 +11,8 @@ module Lob # :nodoc: # After setting large objects to empty, select the OCI8::LOB # and write back the data. + before_create :record_lobs_for_create + after_create :enhanced_write_lobs before_update :record_changed_lobs after_update :enhanced_write_lobs end @@ -30,6 +32,13 @@ def enhanced_write_lobs self.class.connection.write_lobs(self.class.table_name, self.class, attributes, @changed_lob_columns) end end + + def record_lobs_for_create + @changed_lob_columns = self.class.lob_columns.select do |col| + !attributes[col.name].nil? && !self.class.readonly_attributes.to_a.include?(col.name) + end + end + def record_changed_lobs @changed_lob_columns = self.class.lob_columns.select do |col| self.will_save_change_to_attribute?(col.name) && !self.class.readonly_attributes.to_a.include?(col.name) diff --git a/spec/active_record/oracle_enhanced/type/text_spec.rb b/spec/active_record/oracle_enhanced/type/text_spec.rb index ccffd5e49..e30d5e80c 100644 --- a/spec/active_record/oracle_enhanced/type/text_spec.rb +++ b/spec/active_record/oracle_enhanced/type/text_spec.rb @@ -241,4 +241,55 @@ class ::TestSerializeEmployee < ActiveRecord::Base ) expect(Test2Employee.where(comments: search_data)).to have_attributes(count: 1) end + + describe "with prepared_statements disabled" do + around(:each) do |example| + old_prepared_statements = @conn.prepared_statements + @conn.instance_variable_set(:@prepared_statements, false) + example.run + @conn.instance_variable_set(:@prepared_statements, old_prepared_statements) + end + + it "should create record with CLOB data when prepared_statements is false" do + @employee = TestEmployee.create!( + first_name: "First", + last_name: "Last", + comments: @char_data + ) + @employee.reload + expect(@employee.comments).to eq(@char_data) + end + + it "should create record with short CLOB data when prepared_statements is false" do + short_data = "Short CLOB content" + @employee = TestEmployee.create!( + first_name: "First", + last_name: "Last", + comments: short_data + ) + @employee.reload + expect(@employee.comments).to eq(short_data) + end + + it "should create record with empty CLOB when prepared_statements is false" do + @employee = TestEmployee.create!( + first_name: "First", + last_name: "Last", + comments: "" + ) + @employee.reload + expect(@employee.comments).to eq("") + end + + it "should create record with serialized CLOB data when prepared_statements is false" do + ruby_data = { "test" => ["ruby", :data, 123] } + @employee = Test2Employee.create!( + first_name: "First", + last_name: "Last", + comments: ruby_data + ) + @employee.reload + expect(@employee.comments).to eq(ruby_data) + end + end end