activerecord-sqlserver-adapter-odbc-extended 8.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.github/issue_template.md +22 -0
  3. data/.github/workflows/ci.yml +32 -0
  4. data/.gitignore +9 -0
  5. data/.rubocop.yml +69 -0
  6. data/CHANGELOG.md +5 -0
  7. data/CODE_OF_CONDUCT.md +132 -0
  8. data/Dockerfile.ci +14 -0
  9. data/Gemfile +26 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +104 -0
  12. data/RUNNING_UNIT_TESTS.md +38 -0
  13. data/Rakefile +45 -0
  14. data/VERSION +1 -0
  15. data/activerecord-sqlserver-adapter-odbc-extended.gemspec +34 -0
  16. data/compose.ci.yaml +15 -0
  17. data/lib/active_record/connection_adapters/extended_sqlserver_adapter.rb +204 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +41 -0
  19. data/lib/active_record/connection_adapters/sqlserver/odbc_database_statements.rb +234 -0
  20. data/lib/active_record/connection_adapters/sqlserver/type/binary_ext.rb +25 -0
  21. data/lib/activerecord-sqlserver-adapter-odbc-extended.rb +12 -0
  22. data/test/appveyor/dbsetup.ps1 +27 -0
  23. data/test/appveyor/dbsetup.sql +11 -0
  24. data/test/cases/active_schema_test_sqlserver.rb +127 -0
  25. data/test/cases/adapter_test_sqlserver.rb +648 -0
  26. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  27. data/test/cases/change_column_null_test_sqlserver.rb +44 -0
  28. data/test/cases/coerced_tests.rb +2796 -0
  29. data/test/cases/column_test_sqlserver.rb +848 -0
  30. data/test/cases/connection_test_sqlserver.rb +138 -0
  31. data/test/cases/dbconsole.rb +19 -0
  32. data/test/cases/disconnected_test_sqlserver.rb +42 -0
  33. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  34. data/test/cases/enum_test_sqlserver.rb +49 -0
  35. data/test/cases/execute_procedure_test_sqlserver.rb +57 -0
  36. data/test/cases/fetch_test_sqlserver.rb +88 -0
  37. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +72 -0
  38. data/test/cases/helper_sqlserver.rb +61 -0
  39. data/test/cases/migration_test_sqlserver.rb +144 -0
  40. data/test/cases/order_test_sqlserver.rb +153 -0
  41. data/test/cases/pessimistic_locking_test_sqlserver.rb +102 -0
  42. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  43. data/test/cases/rake_test_sqlserver.rb +198 -0
  44. data/test/cases/schema_dumper_test_sqlserver.rb +296 -0
  45. data/test/cases/schema_test_sqlserver.rb +111 -0
  46. data/test/cases/trigger_test_sqlserver.rb +51 -0
  47. data/test/cases/utils_test_sqlserver.rb +129 -0
  48. data/test/cases/uuid_test_sqlserver.rb +54 -0
  49. data/test/cases/view_test_sqlserver.rb +58 -0
  50. data/test/config.yml +38 -0
  51. data/test/debug.rb +16 -0
  52. data/test/fixtures/1px.gif +0 -0
  53. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  54. data/test/migrations/create_clients_and_change_column_null.rb +25 -0
  55. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  56. data/test/models/sqlserver/alien.rb +5 -0
  57. data/test/models/sqlserver/booking.rb +5 -0
  58. data/test/models/sqlserver/composite_pk.rb +9 -0
  59. data/test/models/sqlserver/customers_view.rb +5 -0
  60. data/test/models/sqlserver/datatype.rb +5 -0
  61. data/test/models/sqlserver/datatype_migration.rb +10 -0
  62. data/test/models/sqlserver/dollar_table_name.rb +5 -0
  63. data/test/models/sqlserver/edge_schema.rb +13 -0
  64. data/test/models/sqlserver/fk_has_fk.rb +5 -0
  65. data/test/models/sqlserver/fk_has_pk.rb +5 -0
  66. data/test/models/sqlserver/natural_pk_data.rb +6 -0
  67. data/test/models/sqlserver/natural_pk_int_data.rb +5 -0
  68. data/test/models/sqlserver/no_pk_data.rb +5 -0
  69. data/test/models/sqlserver/object_default.rb +5 -0
  70. data/test/models/sqlserver/quoted_table.rb +9 -0
  71. data/test/models/sqlserver/quoted_view_1.rb +5 -0
  72. data/test/models/sqlserver/quoted_view_2.rb +5 -0
  73. data/test/models/sqlserver/sst_memory.rb +5 -0
  74. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  75. data/test/models/sqlserver/string_default.rb +5 -0
  76. data/test/models/sqlserver/string_defaults_big_view.rb +5 -0
  77. data/test/models/sqlserver/string_defaults_view.rb +5 -0
  78. data/test/models/sqlserver/table_with_spaces.rb +5 -0
  79. data/test/models/sqlserver/tinyint_pk.rb +5 -0
  80. data/test/models/sqlserver/trigger.rb +17 -0
  81. data/test/models/sqlserver/trigger_history.rb +5 -0
  82. data/test/models/sqlserver/upper.rb +5 -0
  83. data/test/models/sqlserver/uppered.rb +5 -0
  84. data/test/models/sqlserver/uuid.rb +5 -0
  85. data/test/schema/datatypes/2012.sql +56 -0
  86. data/test/schema/enable-in-memory-oltp.sql +81 -0
  87. data/test/schema/sqlserver_specific_schema.rb +363 -0
  88. data/test/support/coerceable_test_sqlserver.rb +55 -0
  89. data/test/support/connection_reflection.rb +32 -0
  90. data/test/support/core_ext/query_cache.rb +38 -0
  91. data/test/support/load_schema_sqlserver.rb +29 -0
  92. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  93. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  94. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump +0 -0
  95. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump +0 -0
  96. data/test/support/minitest_sqlserver.rb +3 -0
  97. data/test/support/paths_sqlserver.rb +50 -0
  98. data/test/support/query_assertions.rb +49 -0
  99. data/test/support/rake_helpers.rb +46 -0
  100. data/test/support/table_definition_sqlserver.rb +24 -0
  101. data/test/support/test_in_memory_oltp.rb +17 -0
  102. metadata +240 -0
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "models/reply"
5
+ require "models/topic"
6
+
7
+ class ConnectionTestSQLServer < ActiveRecord::TestCase
8
+ self.use_transactional_tests = false
9
+
10
+ fixtures :topics, :accounts
11
+
12
+ before do
13
+ connection.reconnect!
14
+ assert connection.active?
15
+ end
16
+
17
+ it "affect rows" do
18
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
19
+ updated = Topic.update(topic_data.keys, topic_data.values)
20
+ assert_equal 2, updated.size
21
+ assert_equal "1 updated", Topic.find(1).content
22
+ assert_equal "2 updated", Topic.find(2).content
23
+ assert_equal 2, Topic.delete([1, 2])
24
+ end
25
+
26
+ it "allow usage of :database connection option to remove setting from dsn" do
27
+ assert_equal "activerecord_unittest", connection.current_database
28
+ begin
29
+ connection.use_database("activerecord_unittest2")
30
+ assert_equal "activerecord_unittest2", connection.current_database
31
+ ensure
32
+ connection.use_database
33
+ assert_equal "activerecord_unittest", connection.current_database, "Would default back to connection options"
34
+ end
35
+ end unless connection_sqlserver_azure?
36
+
37
+ describe 'ODBC connection management' do
38
+ it "return finished ODBC statement handle from #execute without block" do
39
+ assert_all_odbc_statements_used_are_closed do
40
+ connection.execute('SELECT * FROM [topics]')
41
+ end
42
+ end
43
+
44
+ it "finish ODBC statement handle from #execute with block" do
45
+ assert_all_odbc_statements_used_are_closed do
46
+ connection.execute('SELECT * FROM [topics]') { }
47
+ end
48
+ end
49
+
50
+ it "finish connection from #raw_select" do
51
+ assert_all_odbc_statements_used_are_closed do
52
+ connection.send(:raw_select,'SELECT * FROM [topics]')
53
+ end
54
+ end
55
+
56
+ it "execute without block closes statement" do
57
+ assert_all_odbc_statements_used_are_closed do
58
+ connection.execute("SELECT 1")
59
+ end
60
+ end
61
+
62
+ it "execute with block closes statement" do
63
+ assert_all_odbc_statements_used_are_closed do
64
+ connection.execute("SELECT 1") do |sth|
65
+ assert !sth.finished?, "Statement should still be alive within block"
66
+ end
67
+ end
68
+ end
69
+
70
+ it "insert with identity closes statement" do
71
+ assert_all_odbc_statements_used_are_closed do
72
+ connection.exec_insert "INSERT INTO accounts ([id],[firm_id],[credit_limit]) VALUES (999, 1, 50)", "SQL", []
73
+ end
74
+ end
75
+
76
+ it "insert without identity closes statement" do
77
+ assert_all_odbc_statements_used_are_closed do
78
+ connection.exec_insert "INSERT INTO accounts ([firm_id],[credit_limit]) VALUES (1, 50)", "SQL", []
79
+ end
80
+ end
81
+
82
+ it "active closes statement" do
83
+ assert_all_odbc_statements_used_are_closed do
84
+ connection.active?
85
+ end
86
+ end
87
+ end if connection_odbc?
88
+
89
+ describe "Connection management" do
90
+ it "set spid on connect" do
91
+ _(["Fixnum", "Integer"]).must_include connection.spid.class.name
92
+ end
93
+
94
+ it "reset spid on disconnect!" do
95
+ connection.disconnect!
96
+ assert connection.spid.nil?
97
+ end
98
+
99
+ it "reset raw connection on disconnect!" do
100
+ connection.disconnect!
101
+ _(connection.instance_variable_get(:@raw_connection)).must_be_nil
102
+ end
103
+
104
+ it "be able to disconnect and reconnect at will" do
105
+ disconnect_raw_connection!
106
+ assert !connection.active?
107
+ connection.reconnect!
108
+ assert connection.active?
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def disconnect_raw_connection!
115
+ connection_options[:mode]
116
+ when :dblib
117
+ connection.raw_connection.close rescue nil
118
+ when :odbc
119
+ connection.raw_connection.disconnect rescue nil
120
+ end
121
+ end
122
+
123
+ def assert_all_odbc_statements_used_are_closed(&block)
124
+ odbc = connection.raw_connection.class.parent
125
+ existing_handles = []
126
+ ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h }
127
+ existing_handle_ids = existing_handles.map(&:object_id)
128
+ assert existing_handles.all?(&:finished?), "Somewhere before the block some statements were not closed"
129
+ GC.disable
130
+ yield
131
+ used_handles = []
132
+ ObjectSpace.each_object(odbc::Statement) { |h| used_handles << h unless existing_handle_ids.include?(h.object_id) }
133
+ assert used_handles.size > 0, "No statements were used within given block"
134
+ assert used_handles.all?(&:finished?), "Statement should have been closed within given block"
135
+ ensure
136
+ GC.enable
137
+ end
138
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DbConsole < ActiveRecord::TestCase
4
+ subject { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter }
5
+
6
+ it "uses sqlplus to connect to database" do
7
+ subject.expects(:find_cmd_and_exec).with("sqlcmd", "-d", "db", "-U", "user", "-P", "secret", "-S", "tcp:localhost,1433")
8
+
9
+ config = make_db_config(adapter: "sqlserver", database: "db", username: "user", password: "secret", host: "localhost", port: 1433)
10
+
11
+ subject.dbconsole(config)
12
+ end
13
+
14
+ private
15
+
16
+ def make_db_config(config)
17
+ ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", config)
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+
5
+ class TestDisconnectedAdapter < ActiveRecord::TestCase
6
+ self.use_transactional_tests = false
7
+
8
+ undef_method :setup
9
+ def setup
10
+ @connection = ActiveRecord::Base.lease_connection
11
+ end
12
+
13
+ teardown do
14
+ return if in_memory_db?
15
+ db_config = ActiveRecord::Base.connection_db_config
16
+ ActiveRecord::Base.establish_connection(db_config)
17
+ end
18
+
19
+ test "execute procedure after disconnect reconnects" do
20
+ @connection.execute_procedure :sp_tables, "sst_datatypes"
21
+ @connection.disconnect!
22
+
23
+ assert_nothing_raised do
24
+ @connection.execute_procedure :sp_tables, "sst_datatypes"
25
+ end
26
+ end
27
+
28
+ test "execute query after disconnect reconnects" do
29
+ sql = "SELECT count(*) from products WHERE id IN(@0, @1)"
30
+ binds = [
31
+ ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new),
32
+ ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new)
33
+ ]
34
+
35
+ @connection.exec_query sql, "TEST", binds
36
+ @connection.disconnect!
37
+
38
+ assert_nothing_raised do
39
+ @connection.exec_query sql, "TEST", binds
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ require "cases/helper_sqlserver"
2
+ require "models/citation"
3
+ require "models/book"
4
+
5
+ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
6
+ fixtures :citations
7
+
8
+ def test_batch_preloading_too_many_ids
9
+ in_clause_length = 10_000
10
+
11
+ # We Monkey patch Preloader to work with batches of 10_000 records.
12
+ # Expect: N Books queries + Citation query
13
+ expected_query_count = (Citation.count / in_clause_length.to_f).ceil + 1
14
+ assert_queries_count(expected_query_count) do
15
+ Citation.preload(:reference_of).to_a.size
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+
5
+ class EnumTestSQLServer < ActiveRecord::TestCase
6
+
7
+ # Check that enums are supported for all string types.
8
+ # For each type we check: cast, serialize, and update by declaration.
9
+ # We create a custom class for each type to test.
10
+ %w[char_10 varchar_50 varchar_max text nchar_10 nvarchar_50 nvarchar_max ntext].each do |col_name|
11
+ describe "support #{col_name} enums" do
12
+ let(:klass) do
13
+ Class.new(ActiveRecord::Base) do
14
+ self.table_name = 'sst_datatypes'
15
+
16
+ enum col_name, { alpha: "A", beta: "B" }
17
+ end
18
+ end
19
+
20
+ it "type.cast" do
21
+ type = klass.type_for_attribute(col_name)
22
+
23
+ assert_equal "alpha", type.cast('A')
24
+ assert_equal "beta", type.cast('B')
25
+ end
26
+
27
+ it "type.serialize" do
28
+ type = klass.type_for_attribute(col_name)
29
+
30
+ assert_equal 'A', type.serialize('A')
31
+ assert_equal 'B', type.serialize('B')
32
+
33
+ assert_equal 'A', type.serialize(:alpha)
34
+ assert_equal 'B', type.serialize(:beta)
35
+ end
36
+
37
+ it "update by declaration" do
38
+ r = klass.new
39
+
40
+ r.alpha!
41
+ assert_predicate r, :alpha?
42
+
43
+ r.beta!
44
+ assert_not_predicate r, :alpha?
45
+ assert_predicate r, :beta?
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+
5
+ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase
6
+ it "execute a simple procedure" do
7
+ tables = ActiveRecord::Base.execute_procedure :sp_tables
8
+ assert_instance_of Array, tables
9
+ assert tables.first.respond_to?(:keys)
10
+ end
11
+
12
+ it "take parameter arguments" do
13
+ tables = ActiveRecord::Base.execute_procedure :sp_tables, "sst_datatypes"
14
+ table_info = tables.first
15
+ assert_equal 1, tables.size
16
+ assert_equal (ENV["ARUNIT_DB_NAME"] || "activerecord_unittest"), table_info["TABLE_QUALIFIER"], "Table Info: #{table_info.inspect}"
17
+ assert_equal "TABLE", table_info["TABLE_TYPE"], "Table Info: #{table_info.inspect}"
18
+ end
19
+
20
+ it "allow multiple result sets to be returned" do
21
+ results1, results2 = ActiveRecord::Base.execute_procedure("sp_helpconstraint", "accounts")
22
+ assert_instance_of Array, results1
23
+ assert results1.first.respond_to?(:keys)
24
+ assert results1.first["Object Name"]
25
+ assert_instance_of Array, results2
26
+ assert results2.first.respond_to?(:keys)
27
+ assert results2.first["constraint_name"]
28
+ assert results2.first["constraint_type"]
29
+ end
30
+
31
+ it "take named parameter arguments" do
32
+ tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: "tables", table_owner: "sys"
33
+ table_info = tables.first
34
+ assert_equal 1, tables.size
35
+ assert_equal (ENV["ARUNIT_DB_NAME"] || "activerecord_unittest"), table_info["TABLE_QUALIFIER"], "Table Info: #{table_info.inspect}"
36
+ assert_equal "VIEW", table_info["TABLE_TYPE"], "Table Info: #{table_info.inspect}"
37
+ end
38
+
39
+ it "uses the proper timezone" do
40
+ date_proc = connection.execute_procedure("my_getutcdate").first["utcdate"]
41
+ date_base = connection.select_value("select GETUTCDATE()")
42
+ assert_equal date_base.change(usec: 0), date_proc.change(usec: 0)
43
+ end
44
+
45
+ def transaction_with_procedure_and_return
46
+ ActiveRecord::Base.transaction do
47
+ connection.execute_procedure("my_getutcdate")
48
+ return
49
+ end
50
+ end
51
+
52
+ it 'test deprecation with transaction return when executing procedure' do
53
+ assert_not_deprecated(ActiveRecord.deprecator) do
54
+ transaction_with_procedure_and_return
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "models/book"
5
+
6
+ class FetchTestSqlserver < ActiveRecord::TestCase
7
+ let(:books) { @books }
8
+
9
+ before { create_10_books }
10
+
11
+ it "work with fully qualified table and columns in select" do
12
+ books = Book.select("books.id, books.name").limit(3).offset(5)
13
+ assert_equal Book.all[5, 3].map(&:id), books.map(&:id)
14
+ end
15
+
16
+ describe "count" do
17
+ it "gauntlet" do
18
+ books[0].destroy
19
+ books[1].destroy
20
+ books[2].destroy
21
+ assert_equal 7, Book.count
22
+ assert_equal 1, Book.limit(1).offset(1).count
23
+ assert_equal 1, Book.limit(1).offset(5).count
24
+ assert_equal 1, Book.limit(1).offset(6).count
25
+ assert_equal 0, Book.limit(1).offset(7).count
26
+ assert_equal 3, Book.limit(3).offset(4).count
27
+ assert_equal 2, Book.limit(3).offset(5).count
28
+ assert_equal 1, Book.limit(3).offset(6).count
29
+ assert_equal 0, Book.limit(3).offset(7).count
30
+ assert_equal 0, Book.limit(3).offset(8).count
31
+ end
32
+ end
33
+
34
+ describe "order" do
35
+ it "gauntlet" do
36
+ Book.where(name: "Name-10").delete_all
37
+ _(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ["Name-2"]
38
+ _(Book.order(:name).limit(2).offset(2).map(&:name)).must_equal ["Name-3", "Name-4"]
39
+ _(Book.order(:name).limit(2).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"]
40
+ _(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"]
41
+ _(Book.order(:name).limit(3).offset(9).map(&:name)).must_equal []
42
+ end
43
+ end
44
+
45
+ describe "FROM subquery" do
46
+ let(:from_sql) { "(SELECT [books].* FROM [books]) [books]" }
47
+
48
+ it "SQL generated correctly for FROM subquery if order provided" do
49
+ query = Book.from(from_sql).order(:id).limit(5)
50
+
51
+ assert_equal query.to_sql, "SELECT [books].* FROM (SELECT [books].* FROM [books]) [books] ORDER BY [books].[id] ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY"
52
+ assert_equal query.to_a.count, 5
53
+ end
54
+
55
+ it "exception thrown if FROM subquery is provided without an order" do
56
+ query = Book.from(from_sql).limit(5)
57
+
58
+ assert_raise(ActiveRecord::StatementInvalid) do
59
+ query.to_sql
60
+ end
61
+ end
62
+ end
63
+
64
+ protected
65
+
66
+ def create_10_books
67
+ Book.delete_all
68
+ @books = (1..10).map { |i| Book.create! name: "Name-#{i}" }
69
+ end
70
+ end
71
+
72
+ class DeterministicFetchWithCompositePkTestSQLServer < ActiveRecord::TestCase
73
+ it "orders by the identity column if table has one" do
74
+ SSCompositePkWithIdentity.delete_all
75
+ SSCompositePkWithIdentity.create(pk_col_two: 2)
76
+ SSCompositePkWithIdentity.create(pk_col_two: 1)
77
+
78
+ _(SSCompositePkWithIdentity.take(1).map(&:pk_col_two)).must_equal [2]
79
+ end
80
+
81
+ it "orders by the first column if table has no identity column" do
82
+ SSCompositePkWithoutIdentity.delete_all
83
+ SSCompositePkWithoutIdentity.create(pk_col_one: 2, pk_col_two: 2)
84
+ SSCompositePkWithoutIdentity.create(pk_col_one: 1, pk_col_two: 1)
85
+
86
+ _(SSCompositePkWithoutIdentity.take(1).map(&:pk_col_two)).must_equal [1]
87
+ end
88
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+
5
+ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
6
+ describe "local server" do
7
+ it "should use table name in select projections" do
8
+ table = Arel::Table.new(:table)
9
+ expected_sql = "SELECT [table].[name] FROM [table]"
10
+ assert_equal expected_sql, table.project(table[:name]).to_sql
11
+ end
12
+ end
13
+
14
+ describe "remote server" do
15
+ before do
16
+ connection_options[:database_prefix] = "[my.server].db.schema."
17
+ end
18
+
19
+ after do
20
+ connection_options.delete :database_prefix
21
+ end
22
+
23
+ it "should use fully qualified table name in select from clause" do
24
+ table = Arel::Table.new(:table)
25
+ expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]"
26
+ assert_equal expected_sql, table.project(Arel.star).to_sql
27
+ end
28
+
29
+ it "should not use fully qualified table name in select projections" do
30
+ table = Arel::Table.new(:table)
31
+ expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]"
32
+ assert_equal expected_sql, table.project(table[:name]).to_sql
33
+ end
34
+
35
+ it "should not use fully qualified table name in where clause" do
36
+ table = Arel::Table.new(:table)
37
+ expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
38
+ quietly { assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql }
39
+ end
40
+
41
+ it "should not use fully qualified table name in order clause" do
42
+ table = Arel::Table.new(:table)
43
+ expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]"
44
+ assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql
45
+ end
46
+
47
+ it "should use fully qualified table name in insert statement" do
48
+ manager = Arel::InsertManager.new
49
+ manager.into Arel::Table.new(:table)
50
+ manager.values = manager.create_values [Arel.sql("*")]
51
+ expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)"
52
+ quietly { assert_equal expected_sql, manager.to_sql }
53
+ end
54
+
55
+ it "should use fully qualified table name in update statement" do
56
+ table = Arel::Table.new(:table)
57
+ manager = Arel::UpdateManager.new
58
+ manager.table(table).where(table[:id].eq(42))
59
+ manager.set([[table[:name], "Bob"]])
60
+ expected_sql = "UPDATE [my.server].[db].[schema].[table] SET [name] = N'Bob' WHERE [table].[id] = 42"
61
+ quietly { assert_equal expected_sql, manager.to_sql }
62
+ end
63
+
64
+ it "should use fully qualified table name in delete statement" do
65
+ table = Arel::Table.new(:table)
66
+ manager = Arel::DeleteManager.new
67
+ manager.from(table).where(table[:id].eq(42))
68
+ expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
69
+ quietly { assert_equal expected_sql, manager.to_sql }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "support/paths_sqlserver"
4
+ require "bundler/setup"
5
+ Bundler.require :default, :development
6
+ require "pry"
7
+ require "support/core_ext/query_cache"
8
+ require "support/minitest_sqlserver"
9
+ require "support/test_in_memory_oltp"
10
+ require "support/table_definition_sqlserver"
11
+ require "cases/helper"
12
+ require "support/load_schema_sqlserver"
13
+ require "support/coerceable_test_sqlserver"
14
+ require "support/connection_reflection"
15
+ require "support/query_assertions"
16
+ require "mocha/minitest"
17
+
18
+ module ActiveSupport
19
+ class TestCase < ::Minitest::Test
20
+ include ARTest::SQLServer::CoerceableTest
21
+ end
22
+ end
23
+
24
+ module ActiveRecord
25
+ class TestCase < ActiveSupport::TestCase
26
+ SQLServer = ActiveRecord::ConnectionAdapters::SQLServer
27
+
28
+ include ARTest::SQLServer::ConnectionReflection,
29
+ ActiveSupport::Testing::Stream,
30
+ ARTest::SQLServer::QueryAssertions
31
+
32
+ let(:logger) { ActiveRecord::Base.logger }
33
+
34
+ setup :ensure_clean_rails_env
35
+ setup :remove_backtrace_silencers
36
+
37
+ private
38
+
39
+ def ensure_clean_rails_env
40
+ Rails.instance_variable_set(:@_env, nil) if defined?(::Rails)
41
+ end
42
+
43
+ def remove_backtrace_silencers
44
+ Rails.backtrace_cleaner.remove_silencers!
45
+ end
46
+
47
+ def host_windows?
48
+ RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
49
+ end
50
+
51
+ def with_use_output_inserted_disabled
52
+ klass = ActiveRecord::ConnectionAdapters::SQLServerAdapter
53
+ klass.use_output_inserted = false
54
+ yield
55
+ ensure
56
+ klass.use_output_inserted = true
57
+ end
58
+ end
59
+ end
60
+
61
+ Dir["#{ARTest::SQLServer.test_root_sqlserver}/models/**/*.rb"].each { |f| require f }