activerecord-sqlserver-adapter 5.2.1 → 7.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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