activerecord-sqlserver-adapter 6.1.2.1 → 7.2.4

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +30 -0
  3. data/.devcontainer/boot.sh +22 -0
  4. data/.devcontainer/devcontainer.json +38 -0
  5. data/.devcontainer/docker-compose.yml +42 -0
  6. data/.github/workflows/ci.yml +7 -4
  7. data/.gitignore +3 -1
  8. data/CHANGELOG.md +19 -42
  9. data/Dockerfile.ci +3 -3
  10. data/Gemfile +6 -1
  11. data/MIT-LICENSE +1 -1
  12. data/README.md +113 -27
  13. data/RUNNING_UNIT_TESTS.md +27 -14
  14. data/Rakefile +2 -6
  15. data/VERSION +1 -1
  16. data/activerecord-sqlserver-adapter.gemspec +3 -3
  17. data/appveyor.yml +4 -6
  18. data/docker-compose.ci.yml +2 -1
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb +20 -0
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +6 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -23
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +10 -7
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +12 -2
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +24 -16
  26. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -31
  27. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +143 -155
  28. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +5 -5
  29. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +57 -56
  30. data/lib/active_record/connection_adapters/sqlserver/savepoints.rb +26 -0
  31. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -12
  32. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +11 -0
  33. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +213 -57
  34. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +3 -3
  35. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +13 -2
  36. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +4 -6
  37. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +19 -1
  38. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +1 -1
  39. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +1 -1
  40. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -1
  41. data/lib/active_record/connection_adapters/sqlserver/utils.rb +21 -10
  42. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +187 -187
  43. data/lib/active_record/connection_adapters/sqlserver_column.rb +1 -0
  44. data/lib/active_record/tasks/sqlserver_database_tasks.rb +42 -33
  45. data/lib/arel/visitors/sqlserver.rb +77 -34
  46. data/test/cases/active_schema_test_sqlserver.rb +127 -0
  47. data/test/cases/adapter_test_sqlserver.rb +114 -26
  48. data/test/cases/coerced_tests.rb +1121 -340
  49. data/test/cases/column_test_sqlserver.rb +67 -64
  50. data/test/cases/connection_test_sqlserver.rb +3 -6
  51. data/test/cases/dbconsole.rb +19 -0
  52. data/test/cases/disconnected_test_sqlserver.rb +8 -5
  53. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  54. data/test/cases/enum_test_sqlserver.rb +49 -0
  55. data/test/cases/execute_procedure_test_sqlserver.rb +9 -5
  56. data/test/cases/fetch_test_sqlserver.rb +19 -0
  57. data/test/cases/helper_sqlserver.rb +11 -5
  58. data/test/cases/index_test_sqlserver.rb +8 -6
  59. data/test/cases/json_test_sqlserver.rb +1 -1
  60. data/test/cases/lateral_test_sqlserver.rb +2 -2
  61. data/test/cases/migration_test_sqlserver.rb +19 -1
  62. data/test/cases/optimizer_hints_test_sqlserver.rb +21 -12
  63. data/test/cases/pessimistic_locking_test_sqlserver.rb +8 -7
  64. data/test/cases/primary_keys_test_sqlserver.rb +2 -2
  65. data/test/cases/rake_test_sqlserver.rb +10 -5
  66. data/test/cases/schema_dumper_test_sqlserver.rb +155 -109
  67. data/test/cases/schema_test_sqlserver.rb +64 -1
  68. data/test/cases/showplan_test_sqlserver.rb +7 -7
  69. data/test/cases/specific_schema_test_sqlserver.rb +17 -13
  70. data/test/cases/transaction_test_sqlserver.rb +13 -8
  71. data/test/cases/trigger_test_sqlserver.rb +20 -0
  72. data/test/cases/utils_test_sqlserver.rb +2 -2
  73. data/test/cases/uuid_test_sqlserver.rb +8 -0
  74. data/test/cases/view_test_sqlserver.rb +58 -0
  75. data/test/config.yml +1 -2
  76. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +1 -1
  77. data/test/models/sqlserver/alien.rb +5 -0
  78. data/test/models/sqlserver/table_with_spaces.rb +5 -0
  79. data/test/models/sqlserver/trigger.rb +8 -0
  80. data/test/schema/sqlserver_specific_schema.rb +54 -6
  81. data/test/support/coerceable_test_sqlserver.rb +4 -4
  82. data/test/support/connection_reflection.rb +3 -9
  83. data/test/support/core_ext/query_cache.rb +7 -1
  84. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  85. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  86. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump +0 -0
  87. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump +0 -0
  88. data/test/support/query_assertions.rb +49 -0
  89. data/test/support/rake_helpers.rb +3 -1
  90. data/test/support/table_definition_sqlserver.rb +24 -0
  91. data/test/support/test_in_memory_oltp.rb +2 -2
  92. metadata +41 -17
  93. data/lib/active_record/sqlserver_base.rb +0 -18
  94. data/test/cases/scratchpad_test_sqlserver.rb +0 -8
  95. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  96. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
  97. data/test/support/sql_counter_sqlserver.rb +0 -29
data/Rakefile CHANGED
@@ -9,6 +9,8 @@ task test: ["test:dblib"]
9
9
  task default: [:test]
10
10
 
11
11
  namespace :test do
12
+ ENV["ARCONN"] = "sqlserver"
13
+
12
14
  %w(dblib).each do |mode|
13
15
  Rake::TestTask.new(mode) do |t|
14
16
  t.libs = ARTest::SQLServer.test_load_paths
@@ -17,14 +19,8 @@ namespace :test do
17
19
  t.verbose = false
18
20
  end
19
21
  end
20
-
21
- task "dblib:env" do
22
- ENV["ARCONN"] = "dblib"
23
- end
24
22
  end
25
23
 
26
- task "test:dblib" => "test:dblib:env"
27
-
28
24
  namespace :profile do
29
25
  ["dblib"].each do |mode|
30
26
  namespace mode.to_sym do
data/VERSION CHANGED
@@ -1 +1 @@
1
- 6.1.2.1
1
+ 7.2.4
@@ -7,10 +7,10 @@ Gem::Specification.new do |spec|
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.version = version
9
9
 
10
- spec.required_ruby_version = ">= 2.5.0"
10
+ spec.required_ruby_version = ">= 3.1.0"
11
11
 
12
12
  spec.license = "MIT"
13
- spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward"]
13
+ spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward", "Aidan Haran"]
14
14
  spec.email = ["ken@metaskills.net", "will@wbond.net"]
15
15
  spec.homepage = "http://github.com/rails-sqlserver/activerecord-sqlserver-adapter"
16
16
  spec.summary = "ActiveRecord SQL Server Adapter."
@@ -27,6 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
28
28
  spec.require_paths = ["lib"]
29
29
 
30
- spec.add_dependency "activerecord", "~> 6.1.0"
30
+ spec.add_dependency "activerecord", "~> 7.2.0"
31
31
  spec.add_dependency "tiny_tds"
32
32
  end
data/appveyor.yml CHANGED
@@ -5,10 +5,10 @@ build: off
5
5
  matrix:
6
6
  fast_finish: true
7
7
  allow_failures:
8
- - ruby_version: "25"
9
- - ruby_version: "26"
10
8
  - ruby_version: "27"
11
9
  - ruby_version: "27-x64"
10
+ - ruby_version: "30"
11
+ - ruby_version: "30-x64"
12
12
  services:
13
13
  - mssql2014
14
14
 
@@ -38,9 +38,7 @@ environment:
38
38
  CI_AZURE_PASS:
39
39
  secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w=
40
40
  matrix:
41
- - ruby_version: "25-x64"
42
- - ruby_version: "25"
43
- - ruby_version: "26-x64"
44
- - ruby_version: "26"
45
41
  - ruby_version: "27-x64"
46
42
  - ruby_version: "27"
43
+ - ruby_version: "30-x64"
44
+ - ruby_version: "30"
@@ -1,10 +1,11 @@
1
1
  version: "2.2"
2
2
  services:
3
3
  sqlserver:
4
- image: metaskills/mssql-server-linux-rails
4
+ image: ghcr.io/rails-sqlserver/mssql-server-linux-rails
5
5
  ci:
6
6
  environment:
7
7
  - ACTIVERECORD_UNITTEST_HOST=sqlserver
8
+ - RAILS_BRANCH=7-2-stable
8
9
  build:
9
10
  context: .
10
11
  dockerfile: Dockerfile.ci
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLServer
6
+ module CoreExt
7
+ module AbstractAdapter
8
+ def sqlserver?
9
+ false
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ ActiveSupport.on_load(:active_record) do
18
+ mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::AbstractAdapter
19
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(mod)
20
+ end
@@ -10,11 +10,13 @@ module ActiveRecord
10
10
  private
11
11
 
12
12
  def attributes_for_update(attribute_names)
13
- return super unless self.class.connection.adapter_name == "SQLServer"
13
+ self.class.with_connection do |connection|
14
+ return super(attribute_names) unless connection.sqlserver?
14
15
 
15
- super.reject do |name|
16
- column = self.class.columns_hash[name]
17
- column && column.respond_to?(:is_identity?) && column.is_identity?
16
+ super(attribute_names).reject do |name|
17
+ column = self.class.columns_hash[name]
18
+ column && column.respond_to?(:is_identity?) && column.is_identity?
19
+ end
18
20
  end
19
21
  end
20
22
  end
@@ -8,32 +8,14 @@ module ActiveRecord
8
8
  module SQLServer
9
9
  module CoreExt
10
10
  module Calculations
11
- # Same as original except we don't perform PostgreSQL hack that removes ordering.
12
- def calculate(operation, column_name)
13
- return super unless klass.connection.adapter_name == "SQLServer"
14
-
15
- if has_include?(column_name)
16
- relation = apply_join_dependency
17
-
18
- if operation.to_s.downcase == "count"
19
- unless distinct_value || distinct_select?(column_name || select_for_count)
20
- relation.distinct!
21
- relation.select_values = [klass.primary_key || table[Arel.star]]
22
- end
23
- end
24
-
25
- relation.calculate(operation, column_name)
26
- else
27
- perform_calculation(operation, column_name)
28
- end
29
- end
30
11
 
31
12
  private
32
-
13
+
33
14
  def build_count_subquery(relation, column_name, distinct)
34
- return super unless klass.connection.adapter_name == "SQLServer"
35
-
36
- super(relation.unscope(:order), column_name, distinct)
15
+ klass.with_connection do |connection|
16
+ relation = relation.unscope(:order) if connection.sqlserver?
17
+ super(relation, column_name, distinct)
18
+ end
37
19
  end
38
20
  end
39
21
  end
@@ -8,20 +8,23 @@ module ActiveRecord
8
8
  SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql "
9
9
  SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/
10
10
 
11
- def exec_explain(queries)
12
- return super unless connection.adapter_name == "SQLServer"
11
+ def exec_explain(queries, options = [])
12
+ with_connection do |connection|
13
+ return super(queries, options) unless connection.sqlserver?
13
14
 
14
- unprepared_queries = queries.map do |(sql, binds)|
15
- [unprepare_sqlserver_statement(sql, binds), binds]
15
+ unprepared_queries = queries.map do |(sql, binds)|
16
+ [unprepare_sqlserver_statement(sql, binds), binds]
17
+ end
18
+
19
+ super(unprepared_queries, options)
16
20
  end
17
- super(unprepared_queries)
18
21
  end
19
22
 
20
23
  private
21
24
 
22
- # This is somewhat hacky, but it should reliably reformat our prepared sql statment
25
+ # This is somewhat hacky, but it should reliably reformat our prepared sql statement
23
26
  # which uses sp_executesql to just the first argument, then unquote it. Likewise our
24
- # `sp_executesql` method should substitude the @n args with the quoted values.
27
+ # `sp_executesql` method should substitute the @n args with the quoted values.
25
28
  def unprepare_sqlserver_statement(sql, binds)
26
29
  return sql unless sql.start_with?(SQLSERVER_STATEMENT_PREFIX)
27
30
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/kernel/reporting"
4
+
3
5
  ActiveSupport.on_load(:active_record) do
4
6
  silence_warnings do
5
7
  # Already defined in Rails
@@ -10,18 +10,28 @@ module ActiveRecord
10
10
  module FinderMethods
11
11
  private
12
12
 
13
- # Same as original except we order by values in distinct select if present.
14
13
  def construct_relation_for_exists(conditions)
15
- return super unless klass.connection.adapter_name == "SQLServer"
14
+ klass.with_connection do |connection|
15
+ if connection.sqlserver?
16
+ _construct_relation_for_exists(conditions)
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
16
22
 
23
+ # Same as original except we order by values in distinct select if present.
24
+ def _construct_relation_for_exists(conditions)
17
25
  conditions = sanitize_forbidden_attributes(conditions)
18
26
 
19
27
  if distinct_value && offset_value
28
+ # Start of monkey-patch
20
29
  if select_values.present?
21
30
  relation = order(*select_values).limit!(1)
22
31
  else
23
32
  relation = except(:order).limit!(1)
24
33
  end
34
+ # End of monkey-patch
25
35
  else
26
36
  relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1)
27
37
  end
@@ -6,20 +6,28 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module SQLServer
8
8
  module CoreExt
9
- module Preloader
10
- private
11
-
12
- def records_for(ids)
13
- return super unless klass.connection.adapter_name == "SQLServer"
14
-
15
- ids.each_slice(in_clause_length).flat_map do |slice|
16
- scope.where(association_key_name => slice).load do |record|
17
- # Processing only the first owner
18
- # because the record is modified but not an owner
19
- owner = owners_by_key[convert_key(record[association_key_name])].first
20
- association = owner.association(reflection.name)
21
- association.set_inverse_instance(record)
22
- end.records
9
+ module LoaderQuery
10
+ def load_records_for_keys(keys, &block)
11
+ scope.with_connection do |connection|
12
+ return super unless connection.sqlserver?
13
+
14
+ return [] if keys.empty?
15
+
16
+ if association_key_name.is_a?(Array)
17
+ query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new }
18
+
19
+ keys.each_with_object(query_constraints) do |values_set, constraints|
20
+ association_key_name.zip(values_set).each do |key_name, value|
21
+ constraints[key_name] << value
22
+ end
23
+ end
24
+
25
+ scope.where(query_constraints).load(&block)
26
+ else
27
+ keys.each_slice(in_clause_length).flat_map do |slice|
28
+ scope.where(association_key_name => slice).load(&block).records
29
+ end
30
+ end
23
31
  end
24
32
  end
25
33
 
@@ -33,6 +41,6 @@ module ActiveRecord
33
41
  end
34
42
 
35
43
  ActiveSupport.on_load(:active_record) do
36
- mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Preloader
37
- ActiveRecord::Associations::Preloader::Association.prepend(mod)
44
+ mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::LoaderQuery
45
+ ActiveRecord::Associations::Preloader::Association::LoaderQuery.prepend(mod)
38
46
  end
@@ -8,45 +8,14 @@ module ActiveRecord
8
8
  128
9
9
  end
10
10
 
11
- def column_name_length
12
- 128
13
- end
14
- deprecate :column_name_length
15
-
16
11
  def table_name_length
17
12
  128
18
13
  end
19
- deprecate :table_name_length
20
14
 
21
15
  def index_name_length
22
16
  128
23
17
  end
24
18
 
25
- def columns_per_table
26
- 1024
27
- end
28
- deprecate :columns_per_table
29
-
30
- def indexes_per_table
31
- 999
32
- end
33
- deprecate :indexes_per_table
34
-
35
- def columns_per_multicolumn_index
36
- 16
37
- end
38
- deprecate :columns_per_multicolumn_index
39
-
40
- def sql_query_length
41
- 65_536 * 4_096
42
- end
43
- deprecate :sql_query_length
44
-
45
- def joins_per_query
46
- 256
47
- end
48
- deprecate :joins_per_query
49
-
50
19
  private
51
20
 
52
21
  # The max number of binds is 2100, but because sp_executesql takes