Skip to content

Commit 6508985

Browse files
committed
Fix CLOB/BLOB insertion when prepared_statements is false
When prepared_statements is disabled, INSERT statements use empty_clob()/ empty_blob() literals instead of bind parameters. The LOB module previously only had after_update callbacks, causing LOB data to be lost on INSERT. This commonly occurs in Rails 8 development environments where config.active_record.query_log_tags_enabled = true (the default) causes Rails to automatically disable prepared statements. Changes: - Add before_create :record_lobs_for_create callback - Add after_create :enhanced_write_lobs callback - Add record_lobs_for_create method to track non-nil LOB columns on create - Skip callback when prepared_statements is enabled (LOB data already written via temporary LOB binding in type_cast()) Fixes rsim#2477 (CLOBs empty after creation) Fixes rsim#2483 (redundant double-write with prepared statements)
1 parent 04841ad commit 6508985

File tree

2 files changed

+63
-0
lines changed
  • lib/active_record/connection_adapters/oracle_enhanced
  • spec/active_record/oracle_enhanced/type

2 files changed

+63
-0
lines changed

lib/active_record/connection_adapters/oracle_enhanced/lob.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ module Lob # :nodoc:
1111

1212
# After setting large objects to empty, select the OCI8::LOB
1313
# and write back the data.
14+
before_create :record_lobs_for_create
15+
after_create :enhanced_write_lobs
1416
before_update :record_changed_lobs
1517
after_update :enhanced_write_lobs
1618
end
@@ -25,11 +27,21 @@ def lob_columns
2527

2628
private
2729
def enhanced_write_lobs
30+
# Skip if using prepared statements - LOB data is already written via temp LOB binding
31+
return if self.class.connection.prepared_statements
32+
2833
if self.class.connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter) &&
2934
!(self.class.custom_create_method || self.class.custom_update_method)
3035
self.class.connection.write_lobs(self.class.table_name, self.class, attributes, @changed_lob_columns)
3136
end
3237
end
38+
39+
def record_lobs_for_create
40+
@changed_lob_columns = self.class.lob_columns.select do |col|
41+
!attributes[col.name].nil? && !self.class.readonly_attributes.to_a.include?(col.name)
42+
end
43+
end
44+
3345
def record_changed_lobs
3446
@changed_lob_columns = self.class.lob_columns.select do |col|
3547
self.will_save_change_to_attribute?(col.name) && !self.class.readonly_attributes.to_a.include?(col.name)

spec/active_record/oracle_enhanced/type/text_spec.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,55 @@ class ::TestSerializeEmployee < ActiveRecord::Base
241241
)
242242
expect(Test2Employee.where(comments: search_data)).to have_attributes(count: 1)
243243
end
244+
245+
describe "with prepared_statements disabled" do
246+
around(:each) do |example|
247+
old_prepared_statements = @conn.prepared_statements
248+
@conn.instance_variable_set(:@prepared_statements, false)
249+
example.run
250+
@conn.instance_variable_set(:@prepared_statements, old_prepared_statements)
251+
end
252+
253+
it "should create record with CLOB data when prepared_statements is false" do
254+
@employee = TestEmployee.create!(
255+
first_name: "First",
256+
last_name: "Last",
257+
comments: @char_data
258+
)
259+
@employee.reload
260+
expect(@employee.comments).to eq(@char_data)
261+
end
262+
263+
it "should create record with short CLOB data when prepared_statements is false" do
264+
short_data = "Short CLOB content"
265+
@employee = TestEmployee.create!(
266+
first_name: "First",
267+
last_name: "Last",
268+
comments: short_data
269+
)
270+
@employee.reload
271+
expect(@employee.comments).to eq(short_data)
272+
end
273+
274+
it "should create record with empty CLOB when prepared_statements is false" do
275+
@employee = TestEmployee.create!(
276+
first_name: "First",
277+
last_name: "Last",
278+
comments: ""
279+
)
280+
@employee.reload
281+
expect(@employee.comments).to eq("")
282+
end
283+
284+
it "should create record with serialized CLOB data when prepared_statements is false" do
285+
ruby_data = { "test" => ["ruby", :data, 123] }
286+
@employee = Test2Employee.create!(
287+
first_name: "First",
288+
last_name: "Last",
289+
comments: ruby_data
290+
)
291+
@employee.reload
292+
expect(@employee.comments).to eq(ruby_data)
293+
end
294+
end
244295
end

0 commit comments

Comments
 (0)