activerecord-sqlserver-adapter 5.2.1 → 7.0.0.0

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 (164) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.github/workflows/ci.yml +29 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +29 -0
  7. data/CHANGELOG.md +17 -27
  8. data/{Dockerfile → Dockerfile.ci} +1 -1
  9. data/Gemfile +49 -41
  10. data/Guardfile +9 -8
  11. data/MIT-LICENSE +1 -1
  12. data/README.md +65 -42
  13. data/RUNNING_UNIT_TESTS.md +3 -0
  14. data/Rakefile +14 -16
  15. data/VERSION +1 -1
  16. data/activerecord-sqlserver-adapter.gemspec +25 -14
  17. data/appveyor.yml +22 -17
  18. data/docker-compose.ci.yml +7 -5
  19. data/guides/RELEASING.md +11 -0
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +5 -4
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +10 -14
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +12 -5
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -7
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +30 -0
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -4
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +117 -52
  29. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
  30. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
  31. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +51 -14
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +40 -6
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +18 -10
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +235 -167
  35. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
  36. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
  37. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
  38. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +36 -7
  39. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -45
  40. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +8 -10
  41. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
  42. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
  43. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
  44. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
  45. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -3
  46. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +7 -5
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
  48. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
  51. data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  53. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  54. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  55. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  56. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  57. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  58. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  59. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  62. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  63. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  64. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  65. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  70. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  71. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  72. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  73. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  74. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  75. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  76. data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
  77. data/lib/active_record/connection_adapters/sqlserver/utils.rb +26 -12
  78. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
  79. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +271 -180
  80. data/lib/active_record/connection_adapters/sqlserver_column.rb +76 -16
  81. data/lib/active_record/sqlserver_base.rb +11 -9
  82. data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -39
  83. data/lib/activerecord-sqlserver-adapter.rb +3 -1
  84. data/lib/arel/visitors/sqlserver.rb +177 -56
  85. data/lib/arel_sqlserver.rb +4 -2
  86. data/test/appveyor/dbsetup.ps1 +4 -4
  87. data/test/cases/active_schema_test_sqlserver.rb +55 -0
  88. data/test/cases/adapter_test_sqlserver.rb +258 -173
  89. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  90. data/test/cases/change_column_null_test_sqlserver.rb +14 -12
  91. data/test/cases/coerced_tests.rb +1421 -397
  92. data/test/cases/column_test_sqlserver.rb +321 -315
  93. data/test/cases/connection_test_sqlserver.rb +17 -20
  94. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  95. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  96. data/test/cases/execute_procedure_test_sqlserver.rb +28 -19
  97. data/test/cases/fetch_test_sqlserver.rb +33 -21
  98. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  99. data/test/cases/helper_sqlserver.rb +15 -15
  100. data/test/cases/in_clause_test_sqlserver.rb +63 -0
  101. data/test/cases/index_test_sqlserver.rb +15 -15
  102. data/test/cases/json_test_sqlserver.rb +25 -25
  103. data/test/cases/lateral_test_sqlserver.rb +35 -0
  104. data/test/cases/migration_test_sqlserver.rb +74 -27
  105. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  106. data/test/cases/order_test_sqlserver.rb +59 -53
  107. data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
  108. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  109. data/test/cases/rake_test_sqlserver.rb +70 -45
  110. data/test/cases/schema_dumper_test_sqlserver.rb +124 -109
  111. data/test/cases/schema_test_sqlserver.rb +20 -26
  112. data/test/cases/scratchpad_test_sqlserver.rb +4 -4
  113. data/test/cases/showplan_test_sqlserver.rb +28 -35
  114. data/test/cases/specific_schema_test_sqlserver.rb +68 -65
  115. data/test/cases/transaction_test_sqlserver.rb +18 -20
  116. data/test/cases/trigger_test_sqlserver.rb +14 -13
  117. data/test/cases/utils_test_sqlserver.rb +70 -70
  118. data/test/cases/uuid_test_sqlserver.rb +13 -14
  119. data/test/debug.rb +8 -6
  120. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  121. data/test/migrations/create_clients_and_change_column_null.rb +3 -1
  122. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
  123. data/test/models/sqlserver/booking.rb +3 -1
  124. data/test/models/sqlserver/composite_pk.rb +9 -0
  125. data/test/models/sqlserver/customers_view.rb +3 -1
  126. data/test/models/sqlserver/datatype.rb +2 -0
  127. data/test/models/sqlserver/datatype_migration.rb +2 -0
  128. data/test/models/sqlserver/dollar_table_name.rb +3 -1
  129. data/test/models/sqlserver/edge_schema.rb +3 -3
  130. data/test/models/sqlserver/fk_has_fk.rb +3 -1
  131. data/test/models/sqlserver/fk_has_pk.rb +3 -1
  132. data/test/models/sqlserver/natural_pk_data.rb +4 -2
  133. data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
  134. data/test/models/sqlserver/no_pk_data.rb +3 -1
  135. data/test/models/sqlserver/object_default.rb +3 -1
  136. data/test/models/sqlserver/quoted_table.rb +4 -2
  137. data/test/models/sqlserver/quoted_view_1.rb +3 -1
  138. data/test/models/sqlserver/quoted_view_2.rb +3 -1
  139. data/test/models/sqlserver/sst_memory.rb +3 -1
  140. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  141. data/test/models/sqlserver/string_default.rb +3 -1
  142. data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
  143. data/test/models/sqlserver/string_defaults_view.rb +3 -1
  144. data/test/models/sqlserver/tinyint_pk.rb +3 -1
  145. data/test/models/sqlserver/trigger.rb +4 -2
  146. data/test/models/sqlserver/trigger_history.rb +3 -1
  147. data/test/models/sqlserver/upper.rb +3 -1
  148. data/test/models/sqlserver/uppered.rb +3 -1
  149. data/test/models/sqlserver/uuid.rb +3 -1
  150. data/test/schema/sqlserver_specific_schema.rb +56 -21
  151. data/test/support/coerceable_test_sqlserver.rb +19 -13
  152. data/test/support/connection_reflection.rb +3 -2
  153. data/test/support/core_ext/query_cache.rb +4 -1
  154. data/test/support/load_schema_sqlserver.rb +5 -5
  155. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  156. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  157. data/test/support/minitest_sqlserver.rb +3 -1
  158. data/test/support/paths_sqlserver.rb +11 -11
  159. data/test/support/rake_helpers.rb +15 -10
  160. data/test/support/sql_counter_sqlserver.rb +16 -15
  161. data/test/support/test_in_memory_oltp.rb +9 -7
  162. metadata +47 -13
  163. data/.travis.yml +0 -25
  164. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -26
@@ -1,9 +1,10 @@
1
- require 'cases/helper_sqlserver'
2
- require 'models/reply'
3
- require 'models/topic'
1
+ # frozen_string_literal: true
4
2
 
5
- class ConnectionTestSQLServer < ActiveRecord::TestCase
3
+ require "cases/helper_sqlserver"
4
+ require "models/reply"
5
+ require "models/topic"
6
6
 
7
+ class ConnectionTestSQLServer < ActiveRecord::TestCase
7
8
  self.use_transactional_tests = false
8
9
 
9
10
  fixtures :topics, :accounts
@@ -13,7 +14,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
13
14
  assert connection.active?
14
15
  end
15
16
 
16
- it 'affect rows' do
17
+ it "affect rows" do
17
18
  topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
18
19
  updated = Topic.update(topic_data.keys, topic_data.values)
19
20
  assert_equal 2, updated.size
@@ -22,43 +23,40 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
22
23
  assert_equal 2, Topic.delete([1, 2])
23
24
  end
24
25
 
25
- it 'allow usage of :database connection option to remove setting from dsn' do
26
- assert_equal 'activerecord_unittest', connection.current_database
26
+ it "allow usage of :database connection option to remove setting from dsn" do
27
+ assert_equal "activerecord_unittest", connection.current_database
27
28
  begin
28
- connection.use_database('activerecord_unittest2')
29
- assert_equal 'activerecord_unittest2', connection.current_database
29
+ connection.use_database("activerecord_unittest2")
30
+ assert_equal "activerecord_unittest2", connection.current_database
30
31
  ensure
31
32
  connection.use_database
32
- assert_equal 'activerecord_unittest', connection.current_database, 'Would default back to connection options'
33
+ assert_equal "activerecord_unittest", connection.current_database, "Would default back to connection options"
33
34
  end
34
35
  end unless connection_sqlserver_azure?
35
36
 
36
- describe 'Connection management' do
37
-
38
- it 'set spid on connect' do
39
- _(['Fixnum', 'Integer']).must_include connection.spid.class.name
37
+ describe "Connection management" do
38
+ it "set spid on connect" do
39
+ _(["Fixnum", "Integer"]).must_include connection.spid.class.name
40
40
  end
41
41
 
42
- it 'reset spid on disconnect!' do
42
+ it "reset spid on disconnect!" do
43
43
  connection.disconnect!
44
44
  assert connection.spid.nil?
45
45
  end
46
46
 
47
- it 'reset the connection' do
47
+ it "reset the connection" do
48
48
  connection.disconnect!
49
49
  _(connection.raw_connection).must_be_nil
50
50
  end
51
51
 
52
- it 'be able to disconnect and reconnect at will' do
52
+ it "be able to disconnect and reconnect at will" do
53
53
  disconnect_raw_connection!
54
54
  assert !connection.active?
55
55
  connection.reconnect!
56
56
  assert connection.active?
57
57
  end
58
-
59
58
  end
60
59
 
61
-
62
60
  private
63
61
 
64
62
  def disconnect_raw_connection!
@@ -67,5 +65,4 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
67
65
  connection.raw_connection.close rescue nil
68
66
  end
69
67
  end
70
-
71
68
  end
@@ -0,0 +1,39 @@
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
+ def setup
9
+ @connection = ActiveRecord::Base.connection
10
+ end
11
+
12
+ teardown do
13
+ return if in_memory_db?
14
+ db_config = ActiveRecord::Base.connection_db_config
15
+ ActiveRecord::Base.establish_connection(db_config)
16
+ end
17
+
18
+ test "can't execute procedures while disconnected" do
19
+ @connection.execute_procedure :sp_tables, "sst_datatypes"
20
+ @connection.disconnect!
21
+ assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
22
+ @connection.execute_procedure :sp_tables, "sst_datatypes"
23
+ end
24
+ end
25
+
26
+ test "can't execute query while disconnected" do
27
+ sql = "SELECT count(*) from products WHERE id IN(@0, @1)"
28
+ binds = [
29
+ ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new),
30
+ ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new)
31
+ ]
32
+
33
+ @connection.exec_query sql, "TEST", binds
34
+ @connection.disconnect!
35
+ assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
36
+ @connection.exec_query sql, "TEST", binds
37
+ end
38
+ end
39
+ 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(expected_query_count) do
15
+ Citation.preload(:reference_of).to_a.size
16
+ end
17
+ end
18
+ end
@@ -1,44 +1,53 @@
1
- require 'cases/helper_sqlserver'
1
+ # frozen_string_literal: true
2
2
 
3
- class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase
3
+ require "cases/helper_sqlserver"
4
4
 
5
- it 'execute a simple procedure' do
5
+ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase
6
+ it "execute a simple procedure" do
6
7
  tables = ActiveRecord::Base.execute_procedure :sp_tables
7
8
  assert_instance_of Array, tables
8
9
  assert tables.first.respond_to?(:keys)
9
10
  end
10
11
 
11
- it 'take parameter arguments' do
12
- tables = ActiveRecord::Base.execute_procedure :sp_tables, 'sst_datatypes'
12
+ it "take parameter arguments" do
13
+ tables = ActiveRecord::Base.execute_procedure :sp_tables, "sst_datatypes"
13
14
  table_info = tables.first
14
15
  assert_equal 1, tables.size
15
- assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}"
16
- assert_equal 'TABLE', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}"
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}"
17
18
  end
18
19
 
19
- it 'allow multiple result sets to be returned' do
20
- results1, results2 = ActiveRecord::Base.execute_procedure('sp_helpconstraint','accounts')
20
+ it "allow multiple result sets to be returned" do
21
+ results1, results2 = ActiveRecord::Base.execute_procedure("sp_helpconstraint", "accounts")
21
22
  assert_instance_of Array, results1
22
23
  assert results1.first.respond_to?(:keys)
23
- assert results1.first['Object Name']
24
+ assert results1.first["Object Name"]
24
25
  assert_instance_of Array, results2
25
26
  assert results2.first.respond_to?(:keys)
26
- assert results2.first['constraint_name']
27
- assert results2.first['constraint_type']
27
+ assert results2.first["constraint_name"]
28
+ assert results2.first["constraint_type"]
28
29
  end
29
30
 
30
- it 'take named parameter arguments' do
31
- tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: 'tables', table_owner: 'sys'
31
+ it "take named parameter arguments" do
32
+ tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: "tables", table_owner: "sys"
32
33
  table_info = tables.first
33
34
  assert_equal 1, tables.size
34
- assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}"
35
- assert_equal 'VIEW', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}"
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}"
36
37
  end
37
38
 
38
- it 'uses the proper timezone' do
39
- date_proc = connection.execute_procedure('my_getutcdate').first['utcdate']
40
- date_base = connection.select_value('select GETUTCDATE()')
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()")
41
42
  assert_equal date_base.change(usec: 0), date_proc.change(usec: 0)
42
43
  end
43
44
 
45
+ it 'test deprecation with transaction return when executing procedure' do
46
+ assert_deprecated do
47
+ ActiveRecord::Base.transaction do
48
+ connection.execute_procedure("my_getutcdate")
49
+ return
50
+ end
51
+ end
52
+ end
44
53
  end
@@ -1,20 +1,20 @@
1
- require 'cases/helper_sqlserver'
2
- require 'models/book'
1
+ # frozen_string_literal: true
3
2
 
4
- class FetchTestSqlserver < ActiveRecord::TestCase
3
+ require "cases/helper_sqlserver"
4
+ require "models/book"
5
5
 
6
+ class FetchTestSqlserver < ActiveRecord::TestCase
6
7
  let(:books) { @books }
7
8
 
8
9
  before { create_10_books }
9
10
 
10
- it 'work with fully qualified table and columns in select' do
11
- books = Book.select('books.id, books.name').limit(3).offset(5)
12
- assert_equal Book.all[5,3].map(&:id), books.map(&:id)
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)
13
14
  end
14
15
 
15
- describe 'count' do
16
-
17
- it 'gauntlet' do
16
+ describe "count" do
17
+ it "gauntlet" do
18
18
  books[0].destroy
19
19
  books[1].destroy
20
20
  books[2].destroy
@@ -29,29 +29,41 @@ class FetchTestSqlserver < ActiveRecord::TestCase
29
29
  assert_equal 0, Book.limit(3).offset(7).count
30
30
  assert_equal 0, Book.limit(3).offset(8).count
31
31
  end
32
-
33
32
  end
34
33
 
35
- describe 'order' do
36
-
37
- it 'gauntlet' do
38
- Book.where(name:'Name-10').delete_all
39
- _(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ['Name-2']
40
- _(Book.order(:name).limit(2).offset(2).map(&:name)).must_equal ['Name-3', 'Name-4']
41
- _(Book.order(:name).limit(2).offset(7).map(&:name)).must_equal ['Name-8', 'Name-9']
42
- _(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ['Name-8', 'Name-9']
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"]
43
41
  _(Book.order(:name).limit(3).offset(9).map(&:name)).must_equal []
44
42
  end
45
-
46
43
  end
47
44
 
48
-
49
45
  protected
50
46
 
51
47
  def create_10_books
52
48
  Book.delete_all
53
49
  @books = (1..10).map { |i| Book.create! name: "Name-#{i}" }
54
50
  end
55
-
56
51
  end
57
52
 
53
+ class DeterministicFetchWithCompositePkTestSQLServer < ActiveRecord::TestCase
54
+ it "orders by the identity column if table has one" do
55
+ SSCompositePkWithIdentity.delete_all
56
+ SSCompositePkWithIdentity.create(pk_col_two: 2)
57
+ SSCompositePkWithIdentity.create(pk_col_two: 1)
58
+
59
+ _(SSCompositePkWithIdentity.take(1).map(&:pk_col_two)).must_equal [2]
60
+ end
61
+
62
+ it "orders by the first column if table has no identity column" do
63
+ SSCompositePkWithoutIdentity.delete_all
64
+ SSCompositePkWithoutIdentity.create(pk_col_one: 2, pk_col_two: 2)
65
+ SSCompositePkWithoutIdentity.create(pk_col_one: 1, pk_col_two: 1)
66
+
67
+ _(SSCompositePkWithoutIdentity.take(1).map(&:pk_col_two)).must_equal [1]
68
+ end
69
+ end
@@ -1,19 +1,17 @@
1
- require 'cases/helper_sqlserver'
1
+ # frozen_string_literal: true
2
2
 
3
- class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
4
-
5
- describe 'local server' do
3
+ require "cases/helper_sqlserver"
6
4
 
7
- it 'should use table name in select projections' do
5
+ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
6
+ describe "local server" do
7
+ it "should use table name in select projections" do
8
8
  table = Arel::Table.new(:table)
9
9
  expected_sql = "SELECT [table].[name] FROM [table]"
10
10
  assert_equal expected_sql, table.project(table[:name]).to_sql
11
11
  end
12
-
13
12
  end
14
13
 
15
- describe 'remote server' do
16
-
14
+ describe "remote server" do
17
15
  before do
18
16
  connection_options[:database_prefix] = "[my.server].db.schema."
19
17
  end
@@ -22,39 +20,39 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
22
20
  connection_options.delete :database_prefix
23
21
  end
24
22
 
25
- it 'should use fully qualified table name in select from clause' do
23
+ it "should use fully qualified table name in select from clause" do
26
24
  table = Arel::Table.new(:table)
27
25
  expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]"
28
26
  assert_equal expected_sql, table.project(Arel.star).to_sql
29
27
  end
30
28
 
31
- it 'should not use fully qualified table name in select projections' do
29
+ it "should not use fully qualified table name in select projections" do
32
30
  table = Arel::Table.new(:table)
33
31
  expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]"
34
32
  assert_equal expected_sql, table.project(table[:name]).to_sql
35
33
  end
36
34
 
37
- it 'should not use fully qualified table name in where clause' do
35
+ it "should not use fully qualified table name in where clause" do
38
36
  table = Arel::Table.new(:table)
39
37
  expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
40
38
  quietly { assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql }
41
39
  end
42
40
 
43
- it 'should not use fully qualified table name in order clause' do
41
+ it "should not use fully qualified table name in order clause" do
44
42
  table = Arel::Table.new(:table)
45
- expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]"
43
+ expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]"
46
44
  assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql
47
45
  end
48
46
 
49
- it 'should use fully qualified table name in insert statement' do
47
+ it "should use fully qualified table name in insert statement" do
50
48
  manager = Arel::InsertManager.new
51
49
  manager.into Arel::Table.new(:table)
52
- manager.values = manager.create_values [Arel.sql('*')], %w{ a }
50
+ manager.values = manager.create_values [Arel.sql("*")]
53
51
  expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)"
54
52
  quietly { assert_equal expected_sql, manager.to_sql }
55
53
  end
56
54
 
57
- it 'should use fully qualified table name in update statement' do
55
+ it "should use fully qualified table name in update statement" do
58
56
  table = Arel::Table.new(:table)
59
57
  manager = Arel::UpdateManager.new
60
58
  manager.table(table).where(table[:id].eq(42))
@@ -63,14 +61,12 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
63
61
  quietly { assert_equal expected_sql, manager.to_sql }
64
62
  end
65
63
 
66
- it 'should use fully qualified table name in delete statement' do
64
+ it "should use fully qualified table name in delete statement" do
67
65
  table = Arel::Table.new(:table)
68
66
  manager = Arel::DeleteManager.new
69
67
  manager.from(table).where(table[:id].eq(42))
70
68
  expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
71
69
  quietly { assert_equal expected_sql, manager.to_sql }
72
70
  end
73
-
74
71
  end
75
-
76
72
  end
@@ -1,20 +1,21 @@
1
- require 'support/paths_sqlserver'
2
- require 'bundler/setup'
1
+ # frozen_string_literal: true
2
+
3
+ require "support/paths_sqlserver"
4
+ require "bundler/setup"
3
5
  Bundler.require :default, :development
4
- require 'pry'
5
- require 'support/core_ext/query_cache'
6
- require 'support/minitest_sqlserver'
7
- require 'support/test_in_memory_oltp'
8
- require 'cases/helper'
9
- require 'support/load_schema_sqlserver'
10
- require 'support/coerceable_test_sqlserver'
11
- require 'support/sql_counter_sqlserver'
12
- require 'support/connection_reflection'
13
- require 'mocha/minitest'
6
+ require "pry"
7
+ require "support/core_ext/query_cache"
8
+ require "support/minitest_sqlserver"
9
+ require "support/test_in_memory_oltp"
10
+ require "cases/helper"
11
+ require "support/load_schema_sqlserver"
12
+ require "support/coerceable_test_sqlserver"
13
+ require "support/sql_counter_sqlserver"
14
+ require "support/connection_reflection"
15
+ require "mocha/minitest"
14
16
 
15
17
  module ActiveRecord
16
18
  class TestCase < ActiveSupport::TestCase
17
-
18
19
  SQLServer = ActiveRecord::ConnectionAdapters::SQLServer
19
20
 
20
21
  include ARTest::SQLServer::CoerceableTest,
@@ -38,7 +39,7 @@ module ActiveRecord
38
39
  end
39
40
 
40
41
  def host_windows?
41
- RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
42
+ RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
42
43
  end
43
44
 
44
45
  def with_use_output_inserted_disabled
@@ -48,7 +49,6 @@ module ActiveRecord
48
49
  ensure
49
50
  klass.use_output_inserted = true
50
51
  end
51
-
52
52
  end
53
53
  end
54
54
 
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "models/post"
5
+ require "models/author"
6
+
7
+ class InClauseTestSQLServer < ActiveRecord::TestCase
8
+ fixtures :posts, :authors
9
+
10
+ it "removes ordering from subqueries" do
11
+ authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name)
12
+ posts = Post.where(author: authors_subquery)
13
+
14
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
15
+ assert_not_includes posts.to_sql, "ORDER BY [authors].[name]"
16
+ assert_equal 10, posts.length
17
+ end
18
+
19
+ it "does not remove ordering from subquery that includes a limit" do
20
+ authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name).limit(2)
21
+ posts = Post.where(author: authors_subquery)
22
+
23
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
24
+ assert_includes posts.to_sql, "ORDER BY [authors].[name]"
25
+ assert_equal 7, posts.length
26
+ end
27
+
28
+ it "does not remove ordering from subquery that includes an offset" do
29
+ authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name).offset(1)
30
+ posts = Post.where(author: authors_subquery)
31
+
32
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
33
+ assert_includes posts.to_sql, "ORDER BY [authors].[name]"
34
+ assert_equal 8, posts.length
35
+ end
36
+
37
+ it "removes ordering from 'not' subqueries" do
38
+ authors_subquery = Author.where.not(name: ["Mary", "Bob"]).order(:name)
39
+ posts = Post.where(author: authors_subquery)
40
+
41
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
42
+ assert_not_includes posts.to_sql, "ORDER BY [authors].[name]"
43
+ assert_equal 5, posts.length
44
+ end
45
+
46
+ it "does not remove ordering from 'not' subquery that includes a limit" do
47
+ authors_subquery = Author.where.not(name: ["Ronan", "Mary", "Bob"]).order(:name).limit(2)
48
+ posts = Post.where(author: authors_subquery)
49
+
50
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
51
+ assert_includes posts.to_sql, "ORDER BY [authors].[name]"
52
+ assert_equal 5, posts.length
53
+ end
54
+
55
+ it "does not remove ordering from 'not' subquery that includes an offset" do
56
+ authors_subquery = Author.where.not(name: ["David", "Ronan", "Cian"]).order(:name).offset(1)
57
+ posts = Post.where(author: authors_subquery)
58
+
59
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
60
+ assert_includes posts.to_sql, "ORDER BY [authors].[name]"
61
+ assert_equal 3, posts.length
62
+ end
63
+ end
@@ -1,7 +1,8 @@
1
- require 'cases/helper_sqlserver'
1
+ # frozen_string_literal: true
2
2
 
3
- class IndexTestSQLServer < ActiveRecord::TestCase
3
+ require "cases/helper_sqlserver"
4
4
 
5
+ class IndexTestSQLServer < ActiveRecord::TestCase
5
6
  before do
6
7
  connection.create_table(:testings) do |t|
7
8
  t.column :foo, :string, limit: 100
@@ -17,31 +18,30 @@ class IndexTestSQLServer < ActiveRecord::TestCase
17
18
  connection.drop_table :testings rescue nil
18
19
  end
19
20
 
20
- it 'add index with order' do
21
+ it "add index with order" do
21
22
  assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do
22
- connection.add_index 'testings', ['last_name'], order: { last_name: :desc }
23
- connection.remove_index 'testings', ['last_name']
23
+ connection.add_index "testings", ["last_name"], order: { last_name: :desc }
24
+ connection.remove_index "testings", ["last_name"]
24
25
  end
25
26
  assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do
26
- connection.add_index 'testings', ['last_name', 'first_name'], order: { last_name: :desc }
27
- connection.remove_index 'testings', ['last_name', 'first_name']
27
+ connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc }
28
+ connection.remove_index "testings", ["last_name", "first_name"]
28
29
  end
29
30
  assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\] ASC\)/i) do
30
- connection.add_index 'testings', ['last_name', 'first_name'], order: { last_name: :desc, first_name: :asc }
31
- connection.remove_index 'testings', ['last_name', 'first_name']
31
+ connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc, first_name: :asc }
32
+ connection.remove_index "testings", ["last_name", "first_name"]
32
33
  end
33
34
  end
34
35
 
35
- it 'add index with where' do
36
+ it "add index with where" do
36
37
  assert_sql(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do
37
- connection.add_index 'testings', 'last_name', where: "[first_name] = N'john doe'"
38
- connection.remove_index 'testings', 'last_name'
38
+ connection.add_index "testings", "last_name", where: "[first_name] = N'john doe'"
39
+ connection.remove_index "testings", "last_name"
39
40
  end
40
41
  end
41
42
 
42
- it 'add index with expression' do
43
+ it "add index with expression" do
43
44
  connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])"
44
- connection.add_index 'testings', 'first_name_upper'
45
+ connection.add_index "testings", "first_name_upper"
45
46
  end
46
-
47
47
  end
@@ -1,32 +1,32 @@
1
- require 'cases/helper_sqlserver'
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
2
4
 
3
5
  if ActiveRecord::Base.connection.supports_json?
4
- class JsonTestSQLServer < ActiveRecord::TestCase
6
+ class JsonTestSQLServer < ActiveRecord::TestCase
7
+ before do
8
+ @o1 = SSTestDatatypeMigrationJson.create! json_col: { "a" => "a", "b" => "b", "c" => "c" }
9
+ @o2 = SSTestDatatypeMigrationJson.create! json_col: { "a" => nil, "b" => "b", "c" => "c" }
10
+ @o3 = SSTestDatatypeMigrationJson.create! json_col: { "x" => 1, "y" => 2, "z" => 3 }
11
+ @o4 = SSTestDatatypeMigrationJson.create! json_col: { "array" => [1, 2, 3] }
12
+ @o5 = SSTestDatatypeMigrationJson.create! json_col: nil
13
+ end
5
14
 
6
- before do
7
- @o1 = SSTestDatatypeMigrationJson.create! json_col: { 'a' => 'a', 'b' => 'b', 'c' => 'c' }
8
- @o2 = SSTestDatatypeMigrationJson.create! json_col: { 'a' => nil, 'b' => 'b', 'c' => 'c' }
9
- @o3 = SSTestDatatypeMigrationJson.create! json_col: { 'x' => 1, 'y' => 2, 'z' => 3 }
10
- @o4 = SSTestDatatypeMigrationJson.create! json_col: { 'array' => [1, 2, 3] }
11
- @o5 = SSTestDatatypeMigrationJson.create! json_col: nil
12
- end
15
+ it "can return and save JSON data" do
16
+ _(SSTestDatatypeMigrationJson.find(@o1.id).json_col).must_equal({ "a" => "a", "b" => "b", "c" => "c" })
17
+ @o1.json_col = { "a" => "a" }
18
+ _(@o1.json_col).must_equal({ "a" => "a" })
19
+ @o1.save!
20
+ _(@o1.reload.json_col).must_equal({ "a" => "a" })
21
+ end
13
22
 
14
- it 'can return and save JSON data' do
15
- _(SSTestDatatypeMigrationJson.find(@o1.id).json_col).must_equal({ 'a' => 'a', 'b' => 'b', 'c' => 'c' })
16
- @o1.json_col = { 'a' => 'a' }
17
- _(@o1.json_col).must_equal({ 'a' => 'a' })
18
- @o1.save!
19
- _(@o1.reload.json_col).must_equal({ 'a' => 'a' })
20
- end
23
+ it "can use ISJSON function" do
24
+ _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) > 0").count).must_equal 4
25
+ _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) IS NULL").count).must_equal 1
26
+ end
21
27
 
22
- it 'can use ISJSON function' do
23
- _(SSTestDatatypeMigrationJson.where('ISJSON(json_col) > 0').count).must_equal 4
24
- _(SSTestDatatypeMigrationJson.where('ISJSON(json_col) IS NULL').count).must_equal 1
28
+ it "can use JSON_VALUE function" do
29
+ _(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2
30
+ end
25
31
  end
26
-
27
- it 'can use JSON_VALUE function' do
28
- _(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2
29
- end
30
-
31
- end
32
32
  end