Skip to content

Commit bbb4a86

Browse files
committed
Support cross database inserts
1 parent 44d7ae1 commit bbb4a86

File tree

5 files changed

+40
-11
lines changed

5 files changed

+40
-11
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ jobs:
66
test:
77
name: Run test suite
88
runs-on: ubuntu-latest
9+
timeout-minutes: 10
910

1011
env:
1112
COMPOSE_FILE: compose.ci.yaml

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#### Fixed
88
- [#1345](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1345) Maintain index options during `change_column` operations.
9+
- [#1357](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts.
910

1011
## v8.0.7
1112

lib/active_record/connection_adapters/sqlserver/schema_statements.rb

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ def column_type(ci:)
594594
end
595595

596596
def column_definitions_sql(database, identifier)
597+
database = "TEMPDB" if identifier.temporary_table?
597598
schema_name = "schema_name()"
598599

599600
if prepared_statements
@@ -604,12 +605,8 @@ def column_definitions_sql(database, identifier)
604605
schema_name = quote(identifier.schema) if identifier.schema.present?
605606
end
606607

607-
object_id_arg = identifier.schema.present? ? "CONCAT(#{schema_name},'.',#{object_name})" : object_name
608-
609-
if identifier.temporary_table?
610-
database = "TEMPDB"
611-
object_id_arg = "CONCAT('#{database}','..',#{object_name})"
612-
end
608+
object_id_arg = identifier.schema.present? ? "CONCAT('.',#{schema_name},'.',#{object_name})" : "CONCAT('..',#{object_name})"
609+
object_id_arg = "CONCAT('#{database}',#{object_id_arg})"
613610

614611
%{
615612
SELECT

test/cases/adapter_test_sqlserver.rb

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,18 @@
77
require "models/subscriber"
88
require "models/minimalistic"
99
require "models/college"
10+
require "models/dog"
11+
require "models/other_dog"
1012
require "models/discount"
1113

1214
class AdapterTestSQLServer < ActiveRecord::TestCase
1315
fixtures :tasks
1416

17+
let(:arunit_connection) { Topic.lease_connection }
18+
let(:arunit2_connection) { College.lease_connection }
19+
let(:arunit_database) { arunit_connection.pool.db_config.database }
20+
let(:arunit2_database) { arunit2_connection.pool.db_config.database }
21+
1522
let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" }
1623
let(:basic_merge_sql) { "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [id], [name], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([id], [name]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name]" }
1724
let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" }
@@ -52,8 +59,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
5259
assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists."
5360

5461
# Test when database and owner included in table name.
55-
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
56-
Topic.table_name = "#{db_config.database}.dbo.topics"
62+
Topic.table_name = "#{arunit_database}.dbo.topics"
5763
assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists."
5864
ensure
5965
Topic.table_name = "topics"
@@ -225,6 +231,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
225231
@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'"
226232
@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'"
227233
@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"
234+
235+
@non_identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs SELECT * FROM #{arunit_database}.dbo.dogs"
236+
@identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs(id) SELECT id FROM #{arunit_database}.dbo.dogs"
228237
end
229238

230239
it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do
@@ -245,20 +254,32 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
245254
assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_sp)
246255
assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted_sp)
247256
assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered_sp)
257+
258+
assert_equal "[#{arunit2_database}].[dbo].[dogs]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_cross_database)
248259
end
249260

250261
it "return false to #query_requires_identity_insert? for normal SQL" do
251-
[basic_insert_sql, basic_merge_sql, basic_update_sql, basic_select_sql].each do |sql|
262+
[basic_insert_sql, basic_merge_sql, basic_update_sql, basic_select_sql, @non_identity_insert_sql_cross_database].each do |sql|
252263
assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}"
253264
end
254265
end
255266

256-
it "find identity column using #identity_columns" do
267+
it "find identity column" do
257268
task_id_column = Task.columns_hash["id"]
258269
assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name
259270
assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type
260271
end
261272

273+
it "find identity column cross database" do
274+
id_column = Dog.columns_hash["id"]
275+
assert_equal id_column.name, arunit2_connection.send(:identity_columns, Dog.table_name).first.name
276+
assert_equal id_column.sql_type, arunit2_connection.send(:identity_columns, Dog.table_name).first.sql_type
277+
278+
id_column = OtherDog.columns_hash["id"]
279+
assert_equal id_column.name, arunit_connection.send(:identity_columns, OtherDog.table_name).first.name
280+
assert_equal id_column.sql_type, arunit_connection.send(:identity_columns, OtherDog.table_name).first.sql_type
281+
end
282+
262283
it "return an empty array when calling #identity_columns for a table_name with no identity" do
263284
_(connection.send(:identity_columns, Subscriber.table_name)).must_equal []
264285
end
@@ -616,7 +637,7 @@ def setup
616637
end
617638

618639
it 'raises an error when the foreign key is mismatched' do
619-
error = assert_raises(ActiveRecord::MismatchedForeignKey) do
640+
error = assert_raises(ActiveRecord::MismatchedForeignKey) do
620641
@conn.add_reference :engines, :old_car
621642
@conn.add_foreign_key :engines, :old_cars
622643
end

test/cases/temp_test_sqlserver.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
require "cases/helper_sqlserver"
4+
5+
class TempTestSQLServer < ActiveRecord::TestCase
6+
# it "assert true" do
7+
# assert true
8+
# end
9+
end

0 commit comments

Comments
 (0)