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. 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..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") @@ -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", @@ -698,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) diff --git a/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb b/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb index 67098a5f3..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 ==================================== @@ -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 @@ -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 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 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/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 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 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