From 5ed1bf0b90605c1bf7e1c24846ad897835e47b2f Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Mon, 23 Jun 2025 21:43:51 +0900 Subject: [PATCH 01/17] Release 8.0.0.rc1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 66cac3478..46bdaa48e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.0.0.alpha +8.0.0.rc1 From f34d8d6304f4aeb2546e92000044f57d8a843936 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Thu, 26 Jun 2025 23:40:10 +0900 Subject: [PATCH 02/17] Merge pull request #2462 from yahonda/update_readme_for_80 Update README for 8.0 [skip ci] --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d9f8e6df0..0cb37bf5d 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,20 @@ Oracle enhanced adapter for ActiveRecord DESCRIPTION ----------- -Oracle enhanced ActiveRecord adapter provides Oracle database access from Ruby on Rails applications. Oracle enhanced adapter can be used from Ruby on Rails versions between 2.3.x and 7.2 and it is working with Oracle database versions 10g and higher +Oracle enhanced ActiveRecord adapter provides Oracle database access from Ruby on Rails applications. Oracle enhanced adapter can be used from Ruby on Rails versions between 2.3.x and 8.0 and it is working with Oracle database versions 10g and higher INSTALLATION ------------ +### Rails 8.0 + +Oracle enhanced adapter version 8.0 supports Rails 8.0 +When using Ruby on Rails version 8.0 then in Gemfile include + +```ruby +# Use oracle as the database for Active Record +gem 'activerecord-oracle_enhanced-adapter', '~> 8.0.0' +``` + ### Rails 7.2 Oracle enhanced adapter version 7.2 supports Rails 7.2 From 1153e51eba7ea52ca656555e514c1fb96e6e43bb Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Thu, 26 Jun 2025 23:43:20 +0900 Subject: [PATCH 03/17] Release 8.0.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 46bdaa48e..ae9a76b92 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.0.0.rc1 +8.0.0 From b898c80c1f65bfb02349646e05b273e040843599 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Sun, 29 Jun 2025 09:11:02 +0900 Subject: [PATCH 04/17] Fix bug report templates for 8.0 stable branch Update bug report template configuration to be compatible with release80 branch environment and CIs run these files. - With this commit ```ruby $ ruby -v ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-linux] $ ruby active_record_gem.rb Fetching https://github.com/kubo/ruby-oci8.git Fetching https://github.com/rsim/oracle-enhanced.git Fetching https://github.com/rails/rails.git Fetching gem metadata from https://rubygems.org/.......... Resolving dependencies... -- create_table(:posts, {force: true}) D, [2025-06-29T09:20:53.289823 #1660953] DEBUG -- : (13.4ms) DROP TABLE "POSTS" D, [2025-06-29T09:20:53.294321 #1660953] DEBUG -- : (4.2ms) DROP SEQUENCE "POSTS_SEQ" D, [2025-06-29T09:20:53.300123 #1660953] DEBUG -- : (5.6ms) CREATE TABLE "POSTS" ("ID" NUMBER(38) NOT NULL PRIMARY KEY) D, [2025-06-29T09:20:53.302264 #1660953] DEBUG -- : (2.0ms) CREATE SEQUENCE "POSTS_SEQ" START WITH 1 -> 0.1497s -- create_table(:comments, {force: true}) D, [2025-06-29T09:20:53.415485 #1660953] DEBUG -- : (12.6ms) DROP TABLE "COMMENTS" D, [2025-06-29T09:20:53.418772 #1660953] DEBUG -- : (3.0ms) DROP SEQUENCE "COMMENTS_SEQ" D, [2025-06-29T09:20:53.423895 #1660953] DEBUG -- : (4.9ms) CREATE TABLE "COMMENTS" ("ID" NUMBER(38) NOT NULL PRIMARY KEY, "POST_ID" NUMBER(38)) D, [2025-06-29T09:20:53.425852 #1660953] DEBUG -- : (1.8ms) CREATE SEQUENCE "COMMENTS_SEQ" START WITH 1 -> 0.1235s D, [2025-06-29T09:20:53.437179 #1660953] DEBUG -- : ActiveRecord::InternalMetadata Load (0.5ms) SELECT * FROM (SELECT * FROM "AR_INTERNAL_METADATA" WHERE "AR_INTERNAL_METADATA"."KEY" = :a1 ORDER BY "AR_INTERNAL_METADATA"."KEY" ASC ) WHERE ROWNUM <= 1 [[nil, "environment"]] Run options: --seed 2150 D, [2025-06-29T09:20:53.973878 #1660953] DEBUG -- : Post Create (22.2ms) INSERT INTO "POSTS" ("ID") VALUES (:a1) [["id", 1]] D, [2025-06-29T09:20:54.403818 #1660953] DEBUG -- : Comment Create (17.3ms) INSERT INTO "COMMENTS" ("ID") VALUES (:a1) [["id", 1]] D, [2025-06-29T09:20:54.410472 #1660953] DEBUG -- : Comment Update (3.0ms) UPDATE "COMMENTS" SET "POST_ID" = :a1 WHERE "COMMENTS"."ID" = :a2 [["post_id", 1], ["id", 1]] D, [2025-06-29T09:20:54.414855 #1660953] DEBUG -- : Comment Count (2.6ms) SELECT COUNT(*) FROM "COMMENTS" WHERE "COMMENTS"."POST_ID" = :a1 [["post_id", 1]] D, [2025-06-29T09:20:54.416336 #1660953] DEBUG -- : Comment Count (1.3ms) SELECT COUNT(*) FROM "COMMENTS" D, [2025-06-29T09:20:54.418215 #1660953] DEBUG -- : Comment Load (1.1ms) SELECT * FROM (SELECT "COMMENTS".* FROM "COMMENTS" ORDER BY "COMMENTS"."ID" ASC ) WHERE ROWNUM <= :a1 [["LIMIT", 1]] D, [2025-06-29T09:20:54.420601 #1660953] DEBUG -- : Post Load (1.7ms) SELECT "POSTS".* FROM "POSTS" WHERE "POSTS"."ID" = :a1 AND ROWNUM <= :a2 [["id", 1], ["LIMIT", 1]] . Finished in 0.955570s, 1.0465 runs/s, 3.1395 assertions/s. 1 runs, 3 assertions, 0 failures, 0 errors, 0 skips yahonda@myubuntu:~/src/github.com/rsim/oracle-enhanced/guides/bug_report_templates$ ruby active_record_gem_spec.rb Fetching https://github.com/kubo/ruby-oci8.git Fetching https://github.com/rsim/oracle-enhanced.git Fetching https://github.com/rails/rails.git Fetching gem metadata from https://rubygems.org/.......... Resolving dependencies... -- create_table(:posts, {force: true}) D, [2025-06-29T09:21:03.308979 #1661232] DEBUG -- : (15.6ms) DROP TABLE "POSTS" D, [2025-06-29T09:21:03.313359 #1661232] DEBUG -- : (4.1ms) DROP SEQUENCE "POSTS_SEQ" D, [2025-06-29T09:21:03.320263 #1661232] DEBUG -- : (6.4ms) CREATE TABLE "POSTS" ("ID" NUMBER(38) NOT NULL PRIMARY KEY) D, [2025-06-29T09:21:03.322988 #1661232] DEBUG -- : (2.5ms) CREATE SEQUENCE "POSTS_SEQ" START WITH 1 -> 0.1535s -- create_table(:comments, {force: true}) D, [2025-06-29T09:21:03.434660 #1661232] DEBUG -- : (10.4ms) DROP TABLE "COMMENTS" D, [2025-06-29T09:21:03.437557 #1661232] DEBUG -- : (2.7ms) DROP SEQUENCE "COMMENTS_SEQ" D, [2025-06-29T09:21:03.443303 #1661232] DEBUG -- : (5.5ms) CREATE TABLE "COMMENTS" ("ID" NUMBER(38) NOT NULL PRIMARY KEY, "POST_ID" NUMBER(38)) D, [2025-06-29T09:21:03.446023 #1661232] DEBUG -- : (2.5ms) CREATE SEQUENCE "COMMENTS_SEQ" START WITH 1 -> 0.1230s D, [2025-06-29T09:21:03.458293 #1661232] DEBUG -- : ActiveRecord::InternalMetadata Load (0.5ms) SELECT * FROM (SELECT * FROM "AR_INTERNAL_METADATA" WHERE "AR_INTERNAL_METADATA"."KEY" = :a1 ORDER BY "AR_INTERNAL_METADATA"."KEY" ASC ) WHERE ROWNUM <= 1 [[nil, "environment"]] D, [2025-06-29T09:21:03.978427 #1661232] DEBUG -- : Post Create (27.0ms) INSERT INTO "POSTS" ("ID") VALUES (:a1) [["id", 1]] D, [2025-06-29T09:21:04.399606 #1661232] DEBUG -- : Comment Create (4.3ms) INSERT INTO "COMMENTS" ("ID") VALUES (:a1) [["id", 1]] D, [2025-06-29T09:21:04.408651 #1661232] DEBUG -- : Comment Update (3.5ms) UPDATE "COMMENTS" SET "POST_ID" = :a1 WHERE "COMMENTS"."ID" = :a2 [["post_id", 1], ["id", 1]] D, [2025-06-29T09:21:04.413675 #1661232] DEBUG -- : Comment Count (3.3ms) SELECT COUNT(*) FROM "COMMENTS" WHERE "COMMENTS"."POST_ID" = :a1 [["post_id", 1]] D, [2025-06-29T09:21:04.415549 #1661232] DEBUG -- : Comment Count (1.2ms) SELECT COUNT(*) FROM "COMMENTS" D, [2025-06-29T09:21:04.416853 #1661232] DEBUG -- : Comment Load (1.0ms) SELECT * FROM (SELECT "COMMENTS".* FROM "COMMENTS" ORDER BY "COMMENTS"."ID" ASC ) WHERE ROWNUM <= :a1 [["LIMIT", 1]] D, [2025-06-29T09:21:04.419228 #1661232] DEBUG -- : Post Load (1.7ms) SELECT "POSTS".* FROM "POSTS" WHERE "POSTS"."ID" = :a1 AND ROWNUM <= :a2 [["id", 1], ["LIMIT", 1]] . Finished in 1.29 seconds (files took 0.29285 seconds to load) 1 example, 0 failures yahonda@myubuntu:~/src/github.com/rsim/oracle-enhanced/guides/bug_report_templates$ --- .github/workflows/ruby_head.yml | 1 - .github/workflows/test.yml | 1 - .github/workflows/test_11g.yml | 1 - guides/bug_report_templates/active_record_gem.rb | 4 ++-- guides/bug_report_templates/active_record_gem_spec.rb | 4 ++-- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ruby_head.yml b/.github/workflows/ruby_head.yml index 7c260da61..6028760ac 100644 --- a/.github/workflows/ruby_head.yml +++ b/.github/workflows/ruby_head.yml @@ -77,7 +77,6 @@ jobs: run: | bundle exec rspec - name: Run bug report templates - if: false run: | cd guides/bug_report_templates ruby active_record_gem.rb diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 294257a25..9087c4ece 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -83,7 +83,6 @@ jobs: run: | bundle exec rspec - name: Run bug report templates - if: "false" run: | cd guides/bug_report_templates ruby active_record_gem.rb diff --git a/.github/workflows/test_11g.yml b/.github/workflows/test_11g.yml index eb2d5f5e7..945886602 100644 --- a/.github/workflows/test_11g.yml +++ b/.github/workflows/test_11g.yml @@ -87,7 +87,6 @@ jobs: run: | bundle exec rspec - name: Run bug report templates - if: "false" run: | cd guides/bug_report_templates ruby active_record_gem.rb diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index 0c0b4decd..7c66f9353 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -7,8 +7,8 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } - gem "activerecord", github: "rails/rails", branch: "main" - gem "activerecord-oracle_enhanced-adapter", github: "rsim/oracle-enhanced", branch: "master" + gem "activerecord", github: "rails/rails", branch: "8-0-stable" + gem "activerecord-oracle_enhanced-adapter", github: "rsim/oracle-enhanced", branch: "release80" gem "minitest" platforms :ruby do diff --git a/guides/bug_report_templates/active_record_gem_spec.rb b/guides/bug_report_templates/active_record_gem_spec.rb index e804a1954..68f079d3f 100644 --- a/guides/bug_report_templates/active_record_gem_spec.rb +++ b/guides/bug_report_templates/active_record_gem_spec.rb @@ -7,8 +7,8 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } - gem "activerecord", github: "rails/rails", branch: "main" - gem "activerecord-oracle_enhanced-adapter", github: "rsim/oracle-enhanced", branch: "master" + gem "activerecord", github: "rails/rails", branch: "8-0-stable" + gem "activerecord-oracle_enhanced-adapter", github: "rsim/oracle-enhanced", branch: "release80" gem "rspec", require: "rspec/autorun" platforms :ruby do From 460718e09d83c5c6d62f7ed85615016eeafd8875 Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Fri, 11 Jul 2025 21:20:21 +0300 Subject: [PATCH 05/17] just a bit of troubleshooting info --- RUNNING_TESTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RUNNING_TESTS.md b/RUNNING_TESTS.md index 743bf6b65..a4410ad27 100644 --- a/RUNNING_TESTS.md +++ b/RUNNING_TESTS.md @@ -110,3 +110,7 @@ If no Oracle database with SYS and SYSTEM user access is available, try the dock ```sh bundle exec rake spec ``` + +# Troubleshooting + +If you observe strange errors when running tests, make sure the activerecord version loaded by the tests is the expected one for the oracle_enhanced version. From 14055e5f80a959829c3be0956806b573269f513c Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Tue, 15 Jul 2025 19:04:13 +0300 Subject: [PATCH 06/17] Rails 8.1 API compatibility with rails/rails#54333 --- .../connection_adapters/oracle_enhanced/column.rb | 4 ++-- .../oracle_enhanced/schema_creation.rb | 1 + .../oracle_enhanced/schema_statements.rb | 1 + .../oracle_enhanced/connection_spec.rb | 8 ++++++-- .../type/national_character_string_spec.rb | 3 +-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/oracle_enhanced/column.rb b/lib/active_record/connection_adapters/oracle_enhanced/column.rb index c7c951806..08ff3837f 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced/column.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced/column.rb @@ -6,8 +6,8 @@ module OracleEnhanced class Column < ActiveRecord::ConnectionAdapters::Column delegate :virtual, to: :sql_type_metadata, allow_nil: true - def initialize(name, default, sql_type_metadata = nil, null = true, comment: nil) # :nodoc: - super(name, default, sql_type_metadata, null, comment: comment) + def initialize(name, cast_type, default, sql_type_metadata = nil, null = true, comment: nil) # :nodoc: + super(name, cast_type, default, sql_type_metadata, null, comment: comment) end def virtual? diff --git a/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb b/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb index fd7a697af..d1fbabdaf 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb @@ -12,6 +12,7 @@ def visit_ColumnDefinition(o) @lob_tablespaces[o.name] = tablespace end end + o.cast_type = lookup_cast_type(sql_type) super end diff --git a/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb b/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb index 2faea07a2..4e07a8d93 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb @@ -674,6 +674,7 @@ def new_column_from_field(table_name, field, definitions) default_value = extract_value_from_default(field["data_default"]) default_value = nil if is_virtual OracleEnhanced::Column.new(oracle_downcase(field["name"]), + lookup_cast_type(field["sql_type"]), default_value, type_metadata, field["nullable"] == "Y", diff --git a/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb b/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb index 6f071701d..2b00ce92c 100644 --- a/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +++ b/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb @@ -403,19 +403,23 @@ def lookup(path) describe "SQL with bind parameters when NLS_NUMERIC_CHARACTERS is set to ', '" do before(:all) do ENV["NLS_NUMERIC_CHARACTERS"] = ", " - @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS) + ActiveRecord::Base.establish_connection(CONNECTION_PARAMS) + @conn_base = ActiveRecord::Base.connection + @conn = @conn_base.send(:_connection) @conn.exec "CREATE TABLE test_employees (age NUMBER(10,2))" end after(:all) do ENV["NLS_NUMERIC_CHARACTERS"] = nil @conn.exec "DROP TABLE test_employees" rescue nil + ActiveRecord::Base.clear_cache! end it "should execute prepared statement with decimal bind parameter" do cursor = @conn.prepare("INSERT INTO test_employees VALUES(:1)") type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "NUMBER", type: :decimal, limit: 10, precision: nil, scale: 2) - column = ActiveRecord::ConnectionAdapters::OracleEnhanced::Column.new("age", nil, type_metadata, false, comment: nil) + cast_type = @conn_base.lookup_cast_type("NUMBER(10)") + column = ActiveRecord::ConnectionAdapters::OracleEnhanced::Column.new("age", cast_type, nil, type_metadata, false, comment: nil) expect(column.type).to eq(:decimal) # Here 1.5 expects that this value has been type casted already # it should use bind_params in the long term. diff --git a/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb b/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb index 7ec3d8dce..8807d8e97 100644 --- a/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +++ b/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb @@ -35,10 +35,9 @@ class ::TestItem < ActiveRecord::Base columns = @conn.columns("test_items") %w(nchar_column nvarchar2_column char_column varchar2_column).each do |col| column = columns.detect { |c| c.name == col } - type = @conn.lookup_cast_type_from_column(column) + type = @conn.lookup_cast_type(column.sql_type) value = type.serialize("abc") expect(@conn.quote(value)).to eq(column.sql_type[0, 1] == "N" ? "N'abc'" : "'abc'") - type = @conn.lookup_cast_type_from_column(column) nilvalue = type.serialize(nil) expect(@conn.quote(nilvalue)).to eq("NULL") end From be442161fb34da3f90d3a53988ed9b8b320c0cb7 Mon Sep 17 00:00:00 2001 From: Daria Mayorova Date: Thu, 17 Jul 2025 20:14:05 +0300 Subject: [PATCH 07/17] activerecord 7.1 distinct column api change --- .../connection_adapters/oracle_enhanced_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb b/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb index 67098a5f3..c1756c619 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb @@ -670,7 +670,7 @@ def columns_for_distinct(columns, orders) # :nodoc: # remove any ASC/DESC modifiers s.gsub(/\s+(ASC|DESC)\s*?/i, "") }.reject(&:blank?).map.with_index { |column, i| - "FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns} ORDER BY #{column}) AS alias_#{i}__" + "FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns.join(', ')} ORDER BY #{column}) AS alias_#{i}__" } (order_columns << super).join(", ") end From d4093a76cba4233774a4b918a62bcb8bb82788ab Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Thu, 17 Jul 2025 20:00:51 +0300 Subject: [PATCH 08/17] test rails 7.1 composite indices --- .../oracle_enhanced/composite_spec.rb | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb diff --git a/spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb b/spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb new file mode 100644 index 000000000..1139e494e --- /dev/null +++ b/spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +describe "OracleEnhancedAdapter should support composite primary" do + include SchemaSpecHelper + + before(:all) do + ActiveRecord::Base.establish_connection(CONNECTION_PARAMS) + schema_define do + create_table :test_authors, force: true do |t| + t.string :first_name, limit: 20 + t.string :last_name, limit: 25 + end + + create_table :test_books, force: true do |t| + t.string :title, limit: 20 + end + + create_table :test_authors_test_books, primary_key: ["test_author_id", "test_book_id"], force: true do |t| + t.integer "test_author_id", precision: 38, null: false + t.integer "test_book_id", precision: 38, null: false + end + end + end + + after(:all) do + schema_define do + drop_table :test_authors + drop_table :test_books + drop_table :test_authors_test_books + end + end + + before(:each) do + class ::TestAuthor < ActiveRecord::Base + has_many :test_authors_test_books + has_many :test_books, through: :test_authors_test_books, inverse_of: :test_authors + end + class ::TestBook < ActiveRecord::Base + has_many :test_authors_test_books + has_many :test_authors, through: :test_authors_test_books, inverse_of: :test_books + end + class ::TestAuthorsTestBook < ActiveRecord::Base + self.primary_key = [:test_author_id, :test_book_id] + belongs_to :test_author, foreign_key: :test_author_id + belongs_to :test_book, foreign_key: :test_book_id + end + + @author = TestAuthor.create!( + first_name: "First", + last_name: "Last", + ) + @book = TestBook.create!(title: "Nice book") + @testRel = TestAuthorsTestBook.create!(test_author: @author, test_book: @book) + expect([@book]).to eq(@author.test_books) + end + + after(:each) do + TestAuthor.delete_all + TestBook.delete_all + TestAuthorsTestBook.delete_all + Object.send(:remove_const, "TestAuthor") + Object.send(:remove_const, "TestBook") + Object.send(:remove_const, "TestAuthorsTestBook") + ActiveRecord::Base.clear_cache! + end + + it "should support distinct" do + TestAuthor.distinct.count.should == 1 + skip "this appears to be a rails bug https://github.com/rails/rails/issues/55401" + TestAuthorsTestBook.distinct.count.should == 1 + end + + it "should support includes when requesting the first record by a referenced composite idx association" do + expect([@book]).to eq(@author.test_books) + expect(TestAuthor.includes(:test_authors_test_books).references(:test_authors_test_books).merge(TestAuthorsTestBook.where(test_author: @author)).take).to eq(@author) + expect(TestAuthor.includes(:test_authors_test_books).references(:test_authors_test_books).merge(TestAuthorsTestBook.where(test_author: @author)).first).to eq(@author) + end + + it "should support includes when requesting the first record by a referenced association" do + expect([@book]).to eq(@author.test_books) + expect(TestAuthorsTestBook.includes(:test_author).references(:test_author).merge(TestAuthor.where(first_name: "First")).take).to eq(@testRel) + expect(TestAuthorsTestBook.includes(:test_author).references(:test_author).merge(TestAuthor.where(first_name: "First")).first).to eq(@testRel) + end +end From b003908a3c8a8cbdb552ea66ac7fc6090c5e861b Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Wed, 23 Jul 2025 21:12:12 +0300 Subject: [PATCH 09/17] follow through on #1544 --- .../connection_adapters/oracle_enhanced/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb b/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb index 4e07a8d93..13d656ec0 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb @@ -699,7 +699,7 @@ def tablespace_for(obj_type, tablespace_option, table_name = nil, column_name = end def default_tablespace_for(type) - (default_tablespaces[type] || default_tablespaces[native_database_types[type][:name]]) rescue nil + default_tablespaces[type] end def column_for(table_name, column_name) From 3f219681cacc84e274717854d6f25ad823aef583 Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Wed, 23 Jul 2025 21:32:03 +0300 Subject: [PATCH 10/17] Rails compat - #native_database_types --- .../connection_adapters/oracle_enhanced_adapter.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb b/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb index c1756c619..9b95e2436 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb @@ -410,7 +410,7 @@ def supports_longer_identifier? # :startdoc: def native_database_types # :nodoc: - emulate_booleans_from_strings ? NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS : NATIVE_DATABASE_TYPES + self.class.native_database_types end # CONNECTION MANAGEMENT ==================================== @@ -711,6 +711,10 @@ def check_version end class << self + def native_database_types + emulate_booleans_from_strings ? NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS : NATIVE_DATABASE_TYPES + end + def type_map @type_map ||= Type::TypeMap.new.tap { |m| initialize_type_map(m) } @type_map From 98e1b3a262660a442066e27cf2bcbdaf5f22d68a Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Thu, 24 Jul 2025 23:37:40 +0300 Subject: [PATCH 11/17] Rails #dump_schema_information -> #dump_schema_versions --- .../connection_adapters/oracle_enhanced/schema_statements.rb | 2 +- .../connection_adapters/oracle_enhanced/structure_dump_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb b/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb index 13d656ec0..3a134ba4b 100644 --- a/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +++ b/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb @@ -284,7 +284,7 @@ def insert_versions_sql(versions) # :nodoc: } << "SELECT * FROM DUAL\n" else if versions.is_a?(Array) - # called from ActiveRecord::Base.connection#dump_schema_information + # called from ActiveRecord::Base.connection#dump_schema_versions versions.map { |version| "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})" }.join("\n\n/\n\n") diff --git a/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb b/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb index 7924eb87e..527ebed2c 100644 --- a/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +++ b/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb @@ -331,7 +331,7 @@ class ::TestPost < ActiveRecord::Base end end - let(:dump) { ActiveRecord::Base.connection.dump_schema_information } + let(:dump) { ActiveRecord::Base.connection.dump_schema_versions } before do ActiveRecord::Base.connection_pool.schema_migration.create_table From 98d401ce3f170d8c7d97c17127beb4569dcc26ca Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Fri, 25 Jul 2025 17:28:53 +0300 Subject: [PATCH 12/17] emulate_booleans test reliable on newer Rails --- .../oracle_enhanced/type/integer_spec.rb | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/spec/active_record/oracle_enhanced/type/integer_spec.rb b/spec/active_record/oracle_enhanced/type/integer_spec.rb index fe49ad368..bd341f541 100644 --- a/spec/active_record/oracle_enhanced/type/integer_spec.rb +++ b/spec/active_record/oracle_enhanced/type/integer_spec.rb @@ -3,9 +3,9 @@ describe "OracleEnhancedAdapter integer type detection based on attribute settings" do before(:all) do ActiveRecord::Base.establish_connection(CONNECTION_PARAMS) - @conn = ActiveRecord::Base.connection - @conn.execute "DROP TABLE test2_employees" rescue nil - @conn.execute <<~SQL + conn = ActiveRecord::Base.lease_connection + conn.execute "DROP TABLE test2_employees" rescue nil + conn.execute <<~SQL CREATE TABLE test2_employees ( id NUMBER PRIMARY KEY, first_name VARCHAR2(20), @@ -22,16 +22,18 @@ created_at DATE ) SQL - @conn.execute "DROP SEQUENCE test2_employees_seq" rescue nil - @conn.execute <<~SQL + conn.execute "DROP SEQUENCE test2_employees_seq" rescue nil + conn.execute <<~SQL CREATE SEQUENCE test2_employees_seq MINVALUE 1 INCREMENT BY 1 START WITH 10040 CACHE 20 NOORDER NOCYCLE SQL end after(:all) do - @conn.execute "DROP TABLE test2_employees" - @conn.execute "DROP SEQUENCE test2_employees_seq" + conn = ActiveRecord::Base.lease_connection + conn.execute "DROP TABLE test2_employees" + conn.execute "DROP SEQUENCE test2_employees_seq" + ActiveRecord::Base.release_connection end describe "/ NUMBER values from ActiveRecord model" do @@ -43,6 +45,7 @@ class ::Test2Employee < ActiveRecord::Base after(:each) do Object.send(:remove_const, "Test2Employee") ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans = true + ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.clear_type_map! ActiveRecord::Base.clear_cache! end @@ -90,8 +93,7 @@ class ::Test2Employee < ActiveRecord::Base it "should return Integer value from NUMBER(1) column if emulate_booleans is set to false" do ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans = false - ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.clear_type_map! - ActiveRecord::Base.clear_cache! + ActiveRecord::Base.establish_connection(CONNECTION_PARAMS) create_employee2 expect(@employee2.is_manager).to be_a(Integer) end From 0a4ad58cdf6f0fe2b721974b98879ad5735a014b Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 14 Oct 2025 14:33:47 -0400 Subject: [PATCH 13/17] Fix CLOB insertion when prepared_statements is false Add after_create callbacks to LOB module to handle CLOB/BLOB writing when using empty_clob()/empty_blob() placeholders in INSERT statements. Previously, the LOB module only had after_update callbacks, causing CLOB data to be lost when records were created with prepared_statements disabled (which generates SQL with empty_clob() literals instead of bind parameters). 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 - Add tests for CLOB creation with prepared_statements disabled This fixes the issue where CLOBs would be empty after creation in environments where prepared_statements defaults to false. Issue #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 From 21a4fdca065f7638e94c0d4097642d1104132053 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 23 Oct 2025 15:17:47 -0400 Subject: [PATCH 14/17] Switch to Rails 8.1 (8-1-stable branch) for compatibility testing --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ed0a359f8..42a610476 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ group :development do gem "rspec" gem "rdoc" gem "rake" - gem "activerecord", github: "rails/rails", branch: "8-0-stable" + gem "activerecord", github: "rails/rails", branch: "8-1-stable" gem "ruby-plsql", github: "rsim/ruby-plsql", branch: "master" platforms :ruby do From 85618afa465c0e020ad23f7081d06d15c14e11b2 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 23 Oct 2025 15:43:14 -0400 Subject: [PATCH 15/17] Remove obsolete ActiveRecord::ExplainSubscriber reference for Rails 8.1 Rails 8.1 removed the ExplainSubscriber class (commit f488878f1bc), causing test failures with: NameError: uninitialized constant ActiveRecord::ExplainSubscriber The ExplainSubscriber functionality was refactored into ExplainRegistry with lazy subscription - instead of subscribing at initialization, Rails 8.1 now subscribes only when .explain is first called via ExplainRegistry.start. The spec_helper.rb line that manually subscribed to ExplainSubscriber is no longer needed or valid. Rails 8.1 handles the subscription automatically and lazily. Related Rails commit: - f488878f1bc "Refactor ExplainRegistry to only be subscribed once used" - Author: Jean Boussier - Date: Thu Sep 25 10:37:09 2025 +0200 - Link: https://github.com/rails/rails/commit/f488878f1bc0aecc33485135efc0800f1388cfdb --- spec/spec_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 29ef18ec3..d8eef5c00 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -53,7 +53,6 @@ def set_logger ActiveSupport::Notifications.notifier = @notifier ActiveRecord::LogSubscriber.attach_to(:active_record) - ActiveSupport::Notifications.subscribe("sql.active_record", ActiveRecord::ExplainSubscriber.new) end class MockLogger From 909db2486766bbd0c0262625ad12ce1d7429161b Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 23 Oct 2025 16:50:55 -0400 Subject: [PATCH 16/17] Update ActiveRecord dependency to stable 8.1.0 release Switch from pre-release alpha constraint to stable release constraint for ActiveRecord 8.1.0 dependency, following Rails 8.1 stable release. --- activerecord-oracle_enhanced-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-oracle_enhanced-adapter.gemspec b/activerecord-oracle_enhanced-adapter.gemspec index e0a59c73d..d0144bde6 100644 --- a/activerecord-oracle_enhanced-adapter.gemspec +++ b/activerecord-oracle_enhanced-adapter.gemspec @@ -26,7 +26,7 @@ This adapter is superset of original ActiveRecord Oracle adapter. "rubygems_mfa_required" => "true" } - s.add_runtime_dependency("activerecord", ["~> 8.1.0.alpha"]) + s.add_runtime_dependency("activerecord", ["~> 8.1.0"]) s.add_runtime_dependency("ruby-plsql", [">= 0.6.0"]) if /java/.match?(RUBY_PLATFORM) s.platform = Gem::Platform.new("java") From e834b46f48e1512c91b04c102acace0a96f0e914 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 23 Oct 2025 16:52:36 -0400 Subject: [PATCH 17/17] Add Rails 8.1 installation section to README Document Rails 8.1 support with installation instructions, including the gem version constraint for activerecord-oracle_enhanced-adapter 8.1. --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 649108350..3efac8e14 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,20 @@ Oracle enhanced adapter for ActiveRecord DESCRIPTION ----------- -Oracle enhanced ActiveRecord adapter provides Oracle database access from Ruby on Rails applications. Oracle enhanced adapter can be used from Ruby on Rails versions between 2.3.x and 8.0 and it is working with Oracle database versions 10g and higher +Oracle enhanced ActiveRecord adapter provides Oracle database access from Ruby on Rails applications. Oracle enhanced adapter can be used from Ruby on Rails versions between 2.3.x and 8.1 and it is working with Oracle database versions 10g and higher INSTALLATION ------------ +### Rails 8.1 + +Oracle enhanced adapter version 8.1 supports Rails 8.1 +When using Ruby on Rails version 8.1 then in Gemfile include + +```ruby +# Use oracle as the database for Active Record +gem 'activerecord-oracle_enhanced-adapter', '~> 8.1.0' +``` + ### Rails 8.0 Oracle enhanced adapter version 8.0 supports Rails 8.0