Skip to content

Commit 8e75e39

Browse files
Support cross database inserts (#1357)
Co-authored-by: Chris Ortman <[email protected]>
1 parent bea0578 commit 8e75e39

File tree

5 files changed

+42
-16
lines changed

5 files changed

+42
-16
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: docker-compose.ci.yml

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
- [#1341](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1341) Support more Azure services by changing language source.
66

7+
#### Fixed
8+
9+
- [#1357(https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts.
10+
711
## v7.2.6
812

913
#### Fixed

lib/active_record/connection_adapters/sqlserver/schema_statements.rb

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ def column_definitions(table_name)
571571
end
572572

573573
def column_definitions_sql(database, identifier)
574+
database = "TEMPDB" if identifier.temporary_table?
574575
schema_name = "schema_name()"
575576

576577
if prepared_statements
@@ -581,12 +582,8 @@ def column_definitions_sql(database, identifier)
581582
schema_name = quote(identifier.schema) if identifier.schema.present?
582583
end
583584

584-
object_id_arg = identifier.schema.present? ? "CONCAT(#{schema_name},'.',#{object_name})" : object_name
585-
586-
if identifier.temporary_table?
587-
database = "TEMPDB"
588-
object_id_arg = "CONCAT('#{database}','..',#{object_name})"
589-
end
585+
object_id_arg = identifier.schema.present? ? "CONCAT('.',#{schema_name},'.',#{object_name})" : "CONCAT('..',#{object_name})"
586+
object_id_arg = "CONCAT('#{database}',#{object_id_arg})"
590587

591588
%{
592589
SELECT

test/cases/adapter_test_sqlserver.rb

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

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

16+
let(:arunit_connection) { Topic.lease_connection }
17+
let(:arunit2_connection) { College.lease_connection }
18+
let(:arunit_database) { arunit_connection.pool.db_config.database }
19+
let(:arunit2_database) { arunit2_connection.pool.db_config.database }
20+
1421
let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" }
1522
let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" }
1623
let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" }
@@ -50,21 +57,14 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
5057
assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists."
5158

5259
# Test when database and owner included in table name.
53-
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
54-
Topic.table_name = "#{db_config.database}.dbo.topics"
60+
Topic.table_name = "#{arunit_database}.dbo.topics"
5561
assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists."
5662
ensure
5763
Topic.table_name = "topics"
5864
end
5965
end
6066

6167
it "test table existence across database schemas" do
62-
arunit_connection = Topic.lease_connection
63-
arunit2_connection = College.lease_connection
64-
65-
arunit_database = arunit_connection.pool.db_config.database
66-
arunit2_database = arunit2_connection.pool.db_config.database
67-
6868
# Assert that connections use different default databases schemas.
6969
assert_not_equal arunit_database, arunit2_database
7070

@@ -200,6 +200,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
200200
@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'"
201201
@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'"
202202
@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"
203+
204+
@non_identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs SELECT * FROM #{arunit_database}.dbo.dogs"
205+
@identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs(id) SELECT id FROM #{arunit_database}.dbo.dogs"
203206
end
204207

205208
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
216219
assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_sp)
217220
assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted_sp)
218221
assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered_sp)
222+
223+
assert_equal "[#{arunit2_database}].[dbo].[dogs]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_cross_database)
219224
end
220225

221226
it "return false to #query_requires_identity_insert? for normal SQL" do
222-
[basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql|
227+
[basic_insert_sql, basic_update_sql, basic_select_sql, @non_identity_insert_sql_cross_database].each do |sql|
223228
assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}"
224229
end
225230
end
226231

227-
it "find identity column using #identity_columns" do
232+
it "find identity column" do
228233
task_id_column = Task.columns_hash["id"]
229234
assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name
230235
assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type
231236
end
232237

238+
it "find identity column cross database" do
239+
id_column = Dog.columns_hash["id"]
240+
assert_equal id_column.name, arunit2_connection.send(:identity_columns, Dog.table_name).first.name
241+
assert_equal id_column.sql_type, arunit2_connection.send(:identity_columns, Dog.table_name).first.sql_type
242+
243+
id_column = OtherDog.columns_hash["id"]
244+
assert_equal id_column.name, arunit_connection.send(:identity_columns, OtherDog.table_name).first.name
245+
assert_equal id_column.sql_type, arunit_connection.send(:identity_columns, OtherDog.table_name).first.sql_type
246+
end
247+
233248
it "return an empty array when calling #identity_columns for a table_name with no identity" do
234249
_(connection.send(:identity_columns, Subscriber.table_name)).must_equal []
235250
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)