activerecord-oracle_enhanced-adapter 7.2.1-java → 8.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1 -7
  3. data/README.md +85 -1
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +2 -2
  6. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +17 -2
  7. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +66 -36
  8. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -1
  9. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +82 -14
  10. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +12 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +8 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +1 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +5 -4
  14. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +28 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +25 -4
  16. data/spec/active_record/connection_adapters/oracle_enhanced/compatibility_spec.rb +6 -2
  17. data/spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb +84 -0
  18. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +19 -4
  19. data/spec/active_record/connection_adapters/oracle_enhanced/dbconsole_spec.rb +63 -0
  20. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +29 -0
  21. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +12 -1
  22. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +11 -9
  23. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +1 -2
  24. data/spec/active_record/oracle_enhanced/type/text_spec.rb +51 -0
  25. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +34 -4
  26. data/spec/spec_helper.rb +13 -2
  27. metadata +10 -6
@@ -266,6 +266,24 @@ module ActiveRecord
266
266
  super
267
267
  end
268
268
 
269
+ # Opens a database console session via sqlplus.
270
+ #
271
+ # Called by Rails' `bin/rails dbconsole` command on the adapter class
272
+ # returned from adapter registration. Builds an Oracle logon string of
273
+ # the form `user[/password]@database` and execs `sqlplus`.
274
+ def self.dbconsole(config, options = {})
275
+ oracle_config = config.configuration_hash
276
+ logon = +""
277
+
278
+ if oracle_config[:username]
279
+ logon << oracle_config[:username]
280
+ logon << "/#{oracle_config[:password]}" if oracle_config[:password] && options[:include_password]
281
+ logon << "@#{config.database}" if config.database
282
+ end
283
+
284
+ find_cmd_and_exec(ActiveRecord.database_cli[:oracle] || "sqlplus", logon)
285
+ end
286
+
269
287
  def build_statement_pool
270
288
  StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
271
289
  end
@@ -410,7 +428,7 @@ module ActiveRecord
410
428
  # :startdoc:
411
429
 
412
430
  def native_database_types # :nodoc:
413
- emulate_booleans_from_strings ? NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS : NATIVE_DATABASE_TYPES
431
+ self.class.native_database_types
414
432
  end
415
433
 
416
434
  # CONNECTION MANAGEMENT ====================================
@@ -670,7 +688,7 @@ module ActiveRecord
670
688
  # remove any ASC/DESC modifiers
671
689
  s.gsub(/\s+(ASC|DESC)\s*?/i, "")
672
690
  }.reject(&:blank?).map.with_index { |column, i|
673
- "FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns} ORDER BY #{column}) AS alias_#{i}__"
691
+ "FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns.join(', ')} ORDER BY #{column}) AS alias_#{i}__"
674
692
  }
675
693
  (order_columns << super).join(", ")
676
694
  end
@@ -711,6 +729,10 @@ module ActiveRecord
711
729
  end
712
730
 
713
731
  class << self
732
+ def native_database_types
733
+ emulate_booleans_from_strings ? NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS : NATIVE_DATABASE_TYPES
734
+ end
735
+
714
736
  def type_map
715
737
  @type_map ||= Type::TypeMap.new.tap { |m| initialize_type_map(m) }
716
738
  @type_map
@@ -848,8 +870,7 @@ module ActiveRecord
848
870
  end
849
871
 
850
872
  # Workaround for https://github.com/jruby/jruby/issues/6267
851
- # JRuby implements ObjectSpace::WeakMap#values natively since 9.4, where the
852
- # internal `map` field this workaround reads does not exist anymore.
873
+ # Fixed in JRuby 9.3.0.0 or higher via https://github.com/jruby/jruby/pull/6683
853
874
  if RUBY_ENGINE == "jruby" && !ObjectSpace::WeakMap.method_defined?(:values)
854
875
  require "jruby"
855
876
 
@@ -25,11 +25,15 @@ describe "compatibility migrations" do
25
25
  end
26
26
  }.new
27
27
 
28
- migration.migrate(:up)
28
+ ActiveRecord::Migration.suppress_messages do
29
+ migration.migrate(:up)
30
+ end
29
31
  expect(@conn.table_exists?(:new_test_employees)).to be_truthy
30
32
  expect(@conn.table_exists?(:test_employees)).not_to be_truthy
31
33
 
32
- migration.migrate(:down)
34
+ ActiveRecord::Migration.suppress_messages do
35
+ migration.migrate(:down)
36
+ end
33
37
  expect(@conn.table_exists?(:new_test_employees)).not_to be_truthy
34
38
  expect(@conn.table_exists?(:test_employees)).to be_truthy
35
39
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe "OracleEnhancedAdapter should support composite primary" do
4
+ include SchemaSpecHelper
5
+
6
+ before(:all) do
7
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
8
+ schema_define do
9
+ create_table :test_authors, force: true do |t|
10
+ t.string :first_name, limit: 20
11
+ t.string :last_name, limit: 25
12
+ end
13
+
14
+ create_table :test_books, force: true do |t|
15
+ t.string :title, limit: 20
16
+ end
17
+
18
+ create_table :test_authors_test_books, primary_key: ["test_author_id", "test_book_id"], force: true do |t|
19
+ t.integer "test_author_id", precision: 38, null: false
20
+ t.integer "test_book_id", precision: 38, null: false
21
+ end
22
+ end
23
+ end
24
+
25
+ after(:all) do
26
+ schema_define do
27
+ drop_table :test_authors
28
+ drop_table :test_books
29
+ drop_table :test_authors_test_books
30
+ end
31
+ end
32
+
33
+ before(:each) do
34
+ class ::TestAuthor < ActiveRecord::Base
35
+ has_many :test_authors_test_books
36
+ has_many :test_books, through: :test_authors_test_books, inverse_of: :test_authors
37
+ end
38
+ class ::TestBook < ActiveRecord::Base
39
+ has_many :test_authors_test_books
40
+ has_many :test_authors, through: :test_authors_test_books, inverse_of: :test_books
41
+ end
42
+ class ::TestAuthorsTestBook < ActiveRecord::Base
43
+ self.primary_key = [:test_author_id, :test_book_id]
44
+ belongs_to :test_author, foreign_key: :test_author_id
45
+ belongs_to :test_book, foreign_key: :test_book_id
46
+ end
47
+
48
+ @author = TestAuthor.create!(
49
+ first_name: "First",
50
+ last_name: "Last",
51
+ )
52
+ @book = TestBook.create!(title: "Nice book")
53
+ @testRel = TestAuthorsTestBook.create!(test_author: @author, test_book: @book)
54
+ expect([@book]).to eq(@author.test_books)
55
+ end
56
+
57
+ after(:each) do
58
+ TestAuthor.delete_all
59
+ TestBook.delete_all
60
+ TestAuthorsTestBook.delete_all
61
+ Object.send(:remove_const, "TestAuthor")
62
+ Object.send(:remove_const, "TestBook")
63
+ Object.send(:remove_const, "TestAuthorsTestBook")
64
+ ActiveRecord::Base.clear_cache!
65
+ end
66
+
67
+ it "should support distinct" do
68
+ expect(TestAuthor.distinct.count).to eq(1)
69
+ skip "this appears to be a rails bug https://github.com/rails/rails/issues/55401"
70
+ expect(TestAuthorsTestBook.distinct.count).to eq(1)
71
+ end
72
+
73
+ it "should support includes when requesting the first record by a referenced composite idx association" do
74
+ expect([@book]).to eq(@author.test_books)
75
+ expect(TestAuthor.includes(:test_authors_test_books).references(:test_authors_test_books).merge(TestAuthorsTestBook.where(test_author: @author)).take).to eq(@author)
76
+ expect(TestAuthor.includes(:test_authors_test_books).references(:test_authors_test_books).merge(TestAuthorsTestBook.where(test_author: @author)).first).to eq(@author)
77
+ end
78
+
79
+ it "should support includes when requesting the first record by a referenced association" do
80
+ expect([@book]).to eq(@author.test_books)
81
+ expect(TestAuthorsTestBook.includes(:test_author).references(:test_author).merge(TestAuthor.where(first_name: "First")).take).to eq(@testRel)
82
+ expect(TestAuthorsTestBook.includes(:test_author).references(:test_author).merge(TestAuthor.where(first_name: "First")).first).to eq(@testRel)
83
+ end
84
+ end
@@ -65,16 +65,20 @@ describe "OracleEnhancedAdapter establish connection" do
65
65
  end
66
66
 
67
67
  it "should not encrypt JDBC network connection" do
68
+ skip "Oracle 11g XE does not support native network encryption" if ENV["DATABASE_VERSION"] == "11.2.0.2"
68
69
  if ORACLE_ENHANCED_CONNECTION == :jdbc
69
70
  ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(jdbc_connect_properties: { "oracle.net.encryption_client" => "REJECTED" }))
70
- expect(ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM v$Session_Connect_Info WHERE SID=SYS_CONTEXT('USERENV', 'SID') AND Network_Service_Banner LIKE '%Encryption service adapter%'")).to eq(0)
71
+ conn = ActiveRecord::Base.connection.send(:_connection)
72
+ expect(conn.select("SELECT COUNT(*) Records FROM v$Session_Connect_Info WHERE SID=SYS_CONTEXT('USERENV', 'SID') AND Network_Service_Banner LIKE '%Encryption service adapter%'")).to eq([{ "records" => 0 }])
71
73
  end
72
74
  end
73
75
 
74
76
  it "should encrypt JDBC network connection" do
77
+ skip "Oracle 11g XE does not support native network encryption" if ENV["DATABASE_VERSION"] == "11.2.0.2"
75
78
  if ORACLE_ENHANCED_CONNECTION == :jdbc
76
79
  ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(jdbc_connect_properties: { "oracle.net.encryption_client" => "REQUESTED" }))
77
- expect(ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM v$Session_Connect_Info WHERE SID=SYS_CONTEXT('USERENV', 'SID') AND Network_Service_Banner LIKE '%Encryption service adapter%'")).to eq(1)
80
+ conn = ActiveRecord::Base.connection.send(:_connection)
81
+ expect(conn.select("SELECT COUNT(*) Records FROM v$Session_Connect_Info WHERE SID=SYS_CONTEXT('USERENV', 'SID') AND Network_Service_Banner LIKE '%Encryption service adapter%'")).to eq([{ "records" => 1 }])
78
82
  end
79
83
  end
80
84
 
@@ -116,6 +120,13 @@ describe "OracleEnhancedConnection" do
116
120
  it "should be in autocommit mode after connection" do
117
121
  expect(@conn).to be_autocommit
118
122
  end
123
+
124
+ it "should raise ArgumentError when JDBC exec is called with bindvars" do
125
+ skip unless ORACLE_ENHANCED_CONNECTION == :jdbc
126
+ expect {
127
+ @conn.exec("SELECT ? FROM dual", 1)
128
+ }.to raise_error(ArgumentError, /JDBC exec does not support bindvars/)
129
+ end
119
130
  end
120
131
 
121
132
  describe "create connection with schema option" do
@@ -403,19 +414,23 @@ describe "OracleEnhancedConnection" do
403
414
  describe "SQL with bind parameters when NLS_NUMERIC_CHARACTERS is set to ', '" do
404
415
  before(:all) do
405
416
  ENV["NLS_NUMERIC_CHARACTERS"] = ", "
406
- @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
417
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
418
+ @conn_base = ActiveRecord::Base.connection
419
+ @conn = @conn_base.send(:_connection)
407
420
  @conn.exec "CREATE TABLE test_employees (age NUMBER(10,2))"
408
421
  end
409
422
 
410
423
  after(:all) do
411
424
  ENV["NLS_NUMERIC_CHARACTERS"] = nil
412
425
  @conn.exec "DROP TABLE test_employees" rescue nil
426
+ ActiveRecord::Base.clear_cache!
413
427
  end
414
428
 
415
429
  it "should execute prepared statement with decimal bind parameter" do
416
430
  cursor = @conn.prepare("INSERT INTO test_employees VALUES(:1)")
417
431
  type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "NUMBER", type: :decimal, limit: 10, precision: nil, scale: 2)
418
- column = ActiveRecord::ConnectionAdapters::OracleEnhanced::Column.new("age", nil, type_metadata, false, comment: nil)
432
+ cast_type = @conn_base.lookup_cast_type("NUMBER(10,2)")
433
+ column = ActiveRecord::ConnectionAdapters::OracleEnhanced::Column.new("age", cast_type, nil, type_metadata, false, comment: nil)
419
434
  expect(column.type).to eq(:decimal)
420
435
  # Here 1.5 expects that this value has been type casted already
421
436
  # it should use bind_params in the long term.
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe "Oracle Enhanced adapter dbconsole" do
4
+ subject { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter }
5
+
6
+ it "uses sqlplus with user@db when password is not requested" do
7
+ expect(subject).to receive(:find_cmd_and_exec).with("sqlplus", "user@db")
8
+
9
+ config = make_db_config(adapter: "oracle_enhanced", database: "db", username: "user", password: "secret")
10
+
11
+ subject.dbconsole(config)
12
+ end
13
+
14
+ it "uses sqlplus with user/password@db when include_password is true" do
15
+ expect(subject).to receive(:find_cmd_and_exec).with("sqlplus", "user/secret@db")
16
+
17
+ config = make_db_config(adapter: "oracle_enhanced", database: "db", username: "user", password: "secret")
18
+
19
+ subject.dbconsole(config, include_password: true)
20
+ end
21
+
22
+ it "omits @database when no database is configured" do
23
+ expect(subject).to receive(:find_cmd_and_exec).with("sqlplus", "user")
24
+
25
+ config = make_db_config(adapter: "oracle_enhanced", username: "user")
26
+
27
+ subject.dbconsole(config)
28
+ end
29
+
30
+ it "passes an empty logon string when no username is configured" do
31
+ expect(subject).to receive(:find_cmd_and_exec).with("sqlplus", "")
32
+
33
+ config = make_db_config(adapter: "oracle_enhanced", database: "db")
34
+
35
+ subject.dbconsole(config)
36
+ end
37
+
38
+ it "is inherited by the emulated OracleAdapter" do
39
+ require "active_record/connection_adapters/emulation/oracle_adapter"
40
+ expect(ActiveRecord::ConnectionAdapters::OracleAdapter).to receive(:find_cmd_and_exec).with("sqlplus", "user@db")
41
+
42
+ config = make_db_config(adapter: "oracle", database: "db", username: "user", password: "secret")
43
+
44
+ ActiveRecord::ConnectionAdapters::OracleAdapter.dbconsole(config)
45
+ end
46
+
47
+ it "respects ActiveRecord.database_cli[:oracle] when set" do
48
+ original = ActiveRecord.database_cli[:oracle]
49
+ ActiveRecord.database_cli[:oracle] = "sqlcl"
50
+ expect(subject).to receive(:find_cmd_and_exec).with("sqlcl", "user@db")
51
+
52
+ config = make_db_config(adapter: "oracle_enhanced", database: "db", username: "user")
53
+
54
+ subject.dbconsole(config)
55
+ ensure
56
+ ActiveRecord.database_cli[:oracle] = original
57
+ end
58
+
59
+ private
60
+ def make_db_config(config)
61
+ ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", config)
62
+ end
63
+ end
@@ -69,6 +69,35 @@ describe "OracleEnhancedAdapter schema definition" do
69
69
  end
70
70
  end
71
71
 
72
+ describe "primary_key inside create_table block with type and keyword options" do
73
+ before(:all) do
74
+ @conn = ActiveRecord::Base.connection
75
+ end
76
+
77
+ after(:each) do
78
+ schema_define do
79
+ drop_table :test_lookups, if_exists: true
80
+ end
81
+ end
82
+
83
+ it "accepts a type argument and keyword options without raising ArgumentError" do
84
+ expect {
85
+ schema_define do
86
+ create_table :test_lookups, force: true, id: false do |t|
87
+ t.primary_key :zlookupid, :string, limit: 1, null: false
88
+ t.string :name
89
+ end
90
+ end
91
+ }.not_to raise_error
92
+
93
+ columns = @conn.columns(:test_lookups)
94
+ pk = columns.find { |c| c.name == "zlookupid" }
95
+ expect(pk).not_to be_nil
96
+ expect(pk.sql_type).to match(/VARCHAR2\(1\)/i)
97
+ expect(pk.null).to be(false)
98
+ end
99
+ end
100
+
72
101
  describe "default sequence name" do
73
102
  it "should return sequence name without truncating too much" do
74
103
  seq_name_length = ActiveRecord::Base.connection.sequence_name_length
@@ -216,6 +216,17 @@ describe "OracleEnhancedAdapter structure dump" do
216
216
  expect(dump).to match(/CREATE TABLE "BARS" \(\n "ID" NUMBER\(38,0\) NOT NULL,\n "SUPER" RAW\(255\)/)
217
217
  end
218
218
 
219
+ it "should dump check constraints" do
220
+ @conn.execute <<~SQL
221
+ ALTER TABLE test_posts ADD CONSTRAINT test_posts_title_check CHECK (LENGTH(title) > 0)
222
+ SQL
223
+ dump = ActiveRecord::Base.connection.structure_dump_check_constraints("test_posts")
224
+ expect(dump.first).to match(/ALTER TABLE "TEST_POSTS" ADD CONSTRAINT "TEST_POSTS_TITLE_CHECK" CHECK/)
225
+
226
+ dump = ActiveRecord::Base.connection.structure_dump
227
+ expect(dump).to match(/ALTER TABLE "TEST_POSTS" ADD CONSTRAINT "TEST_POSTS_TITLE_CHECK" CHECK/)
228
+ end
229
+
219
230
  it "should dump table comments" do
220
231
  comment_sql = %Q(COMMENT ON TABLE "TEST_POSTS" IS 'Test posts with ''some'' "quotes"')
221
232
  @conn.execute comment_sql
@@ -331,7 +342,7 @@ describe "OracleEnhancedAdapter structure dump" do
331
342
  end
332
343
  end
333
344
 
334
- let(:dump) { ActiveRecord::Base.connection.dump_schema_information }
345
+ let(:dump) { ActiveRecord::Base.connection.dump_schema_versions }
335
346
 
336
347
  before do
337
348
  ActiveRecord::Base.connection_pool.schema_migration.create_table
@@ -3,9 +3,9 @@
3
3
  describe "OracleEnhancedAdapter integer type detection based on attribute settings" do
4
4
  before(:all) do
5
5
  ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
6
- @conn = ActiveRecord::Base.connection
7
- @conn.execute "DROP TABLE test2_employees" rescue nil
8
- @conn.execute <<~SQL
6
+ conn = ActiveRecord::Base.lease_connection
7
+ conn.execute "DROP TABLE test2_employees" rescue nil
8
+ conn.execute <<~SQL
9
9
  CREATE TABLE test2_employees (
10
10
  id NUMBER PRIMARY KEY,
11
11
  first_name VARCHAR2(20),
@@ -22,16 +22,18 @@ describe "OracleEnhancedAdapter integer type detection based on attribute settin
22
22
  created_at DATE
23
23
  )
24
24
  SQL
25
- @conn.execute "DROP SEQUENCE test2_employees_seq" rescue nil
26
- @conn.execute <<~SQL
25
+ conn.execute "DROP SEQUENCE test2_employees_seq" rescue nil
26
+ conn.execute <<~SQL
27
27
  CREATE SEQUENCE test2_employees_seq MINVALUE 1
28
28
  INCREMENT BY 1 START WITH 10040 CACHE 20 NOORDER NOCYCLE
29
29
  SQL
30
30
  end
31
31
 
32
32
  after(:all) do
33
- @conn.execute "DROP TABLE test2_employees"
34
- @conn.execute "DROP SEQUENCE test2_employees_seq"
33
+ conn = ActiveRecord::Base.lease_connection
34
+ conn.execute "DROP TABLE test2_employees"
35
+ conn.execute "DROP SEQUENCE test2_employees_seq"
36
+ ActiveRecord::Base.release_connection
35
37
  end
36
38
 
37
39
  describe "/ NUMBER values from ActiveRecord model" do
@@ -43,6 +45,7 @@ describe "OracleEnhancedAdapter integer type detection based on attribute settin
43
45
  after(:each) do
44
46
  Object.send(:remove_const, "Test2Employee")
45
47
  ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans = true
48
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.clear_type_map!
46
49
  ActiveRecord::Base.clear_cache!
47
50
  end
48
51
 
@@ -90,8 +93,7 @@ describe "OracleEnhancedAdapter integer type detection based on attribute settin
90
93
 
91
94
  it "should return Integer value from NUMBER(1) column if emulate_booleans is set to false" do
92
95
  ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans = false
93
- ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.clear_type_map!
94
- ActiveRecord::Base.clear_cache!
96
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
95
97
  create_employee2
96
98
  expect(@employee2.is_manager).to be_a(Integer)
97
99
  end
@@ -35,10 +35,9 @@ describe "OracleEnhancedAdapter quoting of NCHAR and NVARCHAR2 columns" do
35
35
  columns = @conn.columns("test_items")
36
36
  %w(nchar_column nvarchar2_column char_column varchar2_column).each do |col|
37
37
  column = columns.detect { |c| c.name == col }
38
- type = @conn.lookup_cast_type_from_column(column)
38
+ type = @conn.lookup_cast_type(column.sql_type)
39
39
  value = type.serialize("abc")
40
40
  expect(@conn.quote(value)).to eq(column.sql_type[0, 1] == "N" ? "N'abc'" : "'abc'")
41
- type = @conn.lookup_cast_type_from_column(column)
42
41
  nilvalue = type.serialize(nil)
43
42
  expect(@conn.quote(nilvalue)).to eq("NULL")
44
43
  end
@@ -241,4 +241,55 @@ describe "OracleEnhancedAdapter handling of CLOB columns" do
241
241
  )
242
242
  expect(Test2Employee.where(comments: search_data)).to have_attributes(count: 1)
243
243
  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
244
295
  end
@@ -4,10 +4,6 @@ describe "OracleEnhancedAdapter timestamp with timezone support" do
4
4
  include SchemaSpecHelper
5
5
 
6
6
  before(:all) do
7
- skip if ENV["DATABASE_SERVER_AND_CLIENT_VERSION_DO_NOT_MATCH"] == "true"
8
- if ENV["DATABASE_VERSION"] == "11.2.0.2" && ENV["ORACLE_HOME"] == "/usr/lib/oracle/21/client64"
9
- skip
10
- end
11
7
  ActiveRecord.default_timezone = :local
12
8
  ActiveRecord::Base.establish_connection(CONNECTION_WITH_TIMEZONE_PARAMS)
13
9
  @conn = ActiveRecord::Base.connection
@@ -73,5 +69,39 @@ describe "OracleEnhancedAdapter timestamp with timezone support" do
73
69
  expect(@employee.send(c).to_f).to eq(@now.to_f)
74
70
  end
75
71
  end
72
+
73
+ it "should return Time value with millisecond fractional seconds from TIMESTAMP columns" do
74
+ @now = Time.local(2008, 5, 26, 23, 11, 11, 123_000) # 123 ms = 123_000 μs
75
+ @employee = TestEmployee.create(
76
+ created_at: @now,
77
+ created_at_tz: @now,
78
+ created_at_ltz: @now
79
+ )
80
+ @employee.reload
81
+ [:created_at, :created_at_tz, :created_at_ltz].each do |c|
82
+ expect(@employee.send(c).class).to eq(Time)
83
+ expect(@employee.send(c).to_f).to eq(@now.to_f)
84
+ end
85
+ end
86
+
87
+ it "should preserve microsecond precision when binding Time values" do
88
+ # Guard against the JDBC bind path going through Float (which can drift
89
+ # by 1 ms for current epoch values). Assert on Time#to_i and Time#usec
90
+ # directly instead of Time#to_f, which is too coarse to catch drift.
91
+ # Note: the TIMESTAMP columns above use the default fractional precision
92
+ # of 6, so the round-trip is truncated to microseconds.
93
+ @now = Time.local(2008, 5, 26, 23, 11, 11, 123_456)
94
+ @employee = TestEmployee.create(
95
+ created_at: @now,
96
+ created_at_tz: @now,
97
+ created_at_ltz: @now
98
+ )
99
+ @employee.reload
100
+ [:created_at, :created_at_tz, :created_at_ltz].each do |c|
101
+ expect(@employee.send(c).to_i).to eq(@now.to_i)
102
+ expect(@employee.send(c).usec).to eq(@now.usec)
103
+ expect(@employee.send(c).nsec).to eq(@now.nsec)
104
+ end
105
+ end
76
106
  end
77
107
  end
data/spec/spec_helper.rb CHANGED
@@ -33,9 +33,21 @@ require "active_support/log_subscriber"
33
33
  require "active_record/log_subscriber"
34
34
 
35
35
  require "logger"
36
- require "ruby-plsql"
37
36
 
37
+ # On JRuby, load the oracle_enhanced adapter first so that the JDBC driver
38
+ # (ojdbc17.jar) is registered with DriverManager before ruby-plsql tries to
39
+ # load it. ruby-plsql only looks for ojdbc6/7.jar and would fail otherwise.
40
+ #
41
+ # File.exists? was removed in Ruby 3.2. Restore it temporarily so that
42
+ # ruby-plsql's JDBC connection code can load under JRuby 10.x (Ruby 3.4).
38
43
  require "active_record/connection_adapters/oracle_enhanced_adapter"
44
+ File.singleton_class.alias_method(:exists?, :exist?) unless File.respond_to?(:exists?)
45
+ # ruby-plsql calls ActiveRecord::Base.default_timezone (moved to ActiveRecord
46
+ # module in Rails 7.0). Restore the class-level accessor as a shim.
47
+ unless ActiveRecord::Base.respond_to?(:default_timezone)
48
+ ActiveRecord::Base.define_singleton_method(:default_timezone) { ActiveRecord.default_timezone }
49
+ end
50
+ require "ruby-plsql"
39
51
 
40
52
  puts "==> Effective ActiveRecord version #{ActiveRecord::VERSION::STRING}"
41
53
 
@@ -53,7 +65,6 @@ module LoggerSpecHelper
53
65
  ActiveSupport::Notifications.notifier = @notifier
54
66
 
55
67
  ActiveRecord::LogSubscriber.attach_to(:active_record)
56
- ActiveSupport::Notifications.subscribe("sql.active_record", ActiveRecord::ExplainSubscriber.new)
57
68
  end
58
69
 
59
70
  class MockLogger
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-oracle_enhanced-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.1
4
+ version: 8.1.0
5
5
  platform: java
6
6
  authors:
7
7
  - Raimonds Simanovskis
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-07-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activerecord
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 7.2.0
18
+ version: 8.1.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 7.2.0
25
+ version: 8.1.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: ruby-plsql
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -90,9 +90,11 @@ files:
90
90
  - lib/arel/visitors/oracle_common.rb
91
91
  - spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb
92
92
  - spec/active_record/connection_adapters/oracle_enhanced/compatibility_spec.rb
93
+ - spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb
93
94
  - spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb
94
95
  - spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb
95
96
  - spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb
97
+ - spec/active_record/connection_adapters/oracle_enhanced/dbconsole_spec.rb
96
98
  - spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb
97
99
  - spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb
98
100
  - spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb
@@ -132,22 +134,24 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
134
  requirements:
133
135
  - - ">="
134
136
  - !ruby/object:Gem::Version
135
- version: 3.1.0
137
+ version: 3.2.0
136
138
  required_rubygems_version: !ruby/object:Gem::Requirement
137
139
  requirements:
138
140
  - - ">="
139
141
  - !ruby/object:Gem::Version
140
142
  version: 1.8.11
141
143
  requirements: []
142
- rubygems_version: 3.6.3
144
+ rubygems_version: 4.0.10
143
145
  specification_version: 4
144
146
  summary: Oracle enhanced adapter for ActiveRecord
145
147
  test_files:
146
148
  - spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb
147
149
  - spec/active_record/connection_adapters/oracle_enhanced/compatibility_spec.rb
150
+ - spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb
148
151
  - spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb
149
152
  - spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb
150
153
  - spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb
154
+ - spec/active_record/connection_adapters/oracle_enhanced/dbconsole_spec.rb
151
155
  - spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb
152
156
  - spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb
153
157
  - spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb