diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 984a6268d..6ff557351 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ jobs: test: name: Run test suite runs-on: ubuntu-latest + timeout-minutes: 10 env: COMPOSE_FILE: docker-compose.ci.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e244a01..299be0ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [#1341](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1341) Support more Azure services by changing language source. +#### Fixed + +- [#1357(https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts. + ## v7.2.6 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 828dcbff2..6ccf31755 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -571,6 +571,7 @@ def column_definitions(table_name) end def column_definitions_sql(database, identifier) + database = "TEMPDB" if identifier.temporary_table? schema_name = "schema_name()" if prepared_statements @@ -581,12 +582,8 @@ def column_definitions_sql(database, identifier) schema_name = quote(identifier.schema) if identifier.schema.present? end - object_id_arg = identifier.schema.present? ? "CONCAT(#{schema_name},'.',#{object_name})" : object_name - - if identifier.temporary_table? - database = "TEMPDB" - object_id_arg = "CONCAT('#{database}','..',#{object_name})" - end + object_id_arg = identifier.schema.present? ? "CONCAT('.',#{schema_name},'.',#{object_name})" : "CONCAT('..',#{object_name})" + object_id_arg = "CONCAT('#{database}',#{object_id_arg})" %{ SELECT diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 7b9f0eca5..21b5fa340 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -7,10 +7,17 @@ require "models/subscriber" require "models/minimalistic" require "models/college" +require "models/dog" +require "models/other_dog" class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks + let(:arunit_connection) { Topic.lease_connection } + let(:arunit2_connection) { College.lease_connection } + let(:arunit_database) { arunit_connection.pool.db_config.database } + let(:arunit2_database) { arunit2_connection.pool.db_config.database } + let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" } @@ -50,8 +57,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists." # Test when database and owner included in table name. - db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") - Topic.table_name = "#{db_config.database}.dbo.topics" + Topic.table_name = "#{arunit_database}.dbo.topics" assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists." ensure Topic.table_name = "topics" @@ -59,12 +65,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "test table existence across database schemas" do - arunit_connection = Topic.lease_connection - arunit2_connection = College.lease_connection - - arunit_database = arunit_connection.pool.db_config.database - arunit2_database = arunit2_connection.pool.db_config.database - # Assert that connections use different default databases schemas. assert_not_equal arunit_database, arunit2_database @@ -200,6 +200,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_non_dbo_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([id],[name]) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" @identity_insert_sql_non_dbo_unquoted_sp = "EXEC sp_executesql N'INSERT INTO test.aliens (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" @identity_insert_sql_non_dbo_unordered_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Mork', @1 = 420" + + @non_identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs SELECT * FROM #{arunit_database}.dbo.dogs" + @identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs(id) SELECT id FROM #{arunit_database}.dbo.dogs" end it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do @@ -216,20 +219,32 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_sp) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted_sp) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered_sp) + + assert_equal "[#{arunit2_database}].[dbo].[dogs]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_cross_database) end it "return false to #query_requires_identity_insert? for normal SQL" do - [basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql| + [basic_insert_sql, basic_update_sql, basic_select_sql, @non_identity_insert_sql_cross_database].each do |sql| assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}" end end - it "find identity column using #identity_columns" do + it "find identity column" do task_id_column = Task.columns_hash["id"] assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type end + it "find identity column cross database" do + id_column = Dog.columns_hash["id"] + assert_equal id_column.name, arunit2_connection.send(:identity_columns, Dog.table_name).first.name + assert_equal id_column.sql_type, arunit2_connection.send(:identity_columns, Dog.table_name).first.sql_type + + id_column = OtherDog.columns_hash["id"] + assert_equal id_column.name, arunit_connection.send(:identity_columns, OtherDog.table_name).first.name + assert_equal id_column.sql_type, arunit_connection.send(:identity_columns, OtherDog.table_name).first.sql_type + end + it "return an empty array when calling #identity_columns for a table_name with no identity" do _(connection.send(:identity_columns, Subscriber.table_name)).must_equal [] end diff --git a/test/cases/temp_test_sqlserver.rb b/test/cases/temp_test_sqlserver.rb new file mode 100644 index 000000000..c9fae9490 --- /dev/null +++ b/test/cases/temp_test_sqlserver.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class TempTestSQLServer < ActiveRecord::TestCase + # it "assert true" do + # assert true + # end +end