activerecord-sqlserver-adapter 7.2.2 → 8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dba9ea79e6287b945d1c8fba8894559510999b58272b968f69953630d94eb484
4
- data.tar.gz: 9abcab548b00c1c906e26ebbb5f1d18982529c605452d447fba3b0a081f5046d
3
+ metadata.gz: 926f65915d98bb3c271370883fa89891fc575b5c6801cd8c235386c078d34bf5
4
+ data.tar.gz: 0a932a9d5edf0d3932e3f0ba44328ed77a033a1223f05699e8f4caa79116ac56
5
5
  SHA512:
6
- metadata.gz: cba19ac223013b55bc0d67fade1dc8b8d0a80dc05175bebcc5ba209e28a3980d271f8d9240a92ec8d2cebba2ae33049581d21c0a30c5f9e188ca9150909d5bb3
7
- data.tar.gz: e4feae1a11d2f000c1f75b81e6e5600a5abb0c08ea3220b26bf8d681aa881e34ebafb212ad0ae8a85280ae3d72401f27855e2af0e79f42521e6cdb5b56334907
6
+ metadata.gz: ee45540a8d2ae57dc69a35048f4ccfbf01bcfc91e177e57405b19ccb8a3279eef7b752c22dee20c4171c8bb4e2cb0fcaadb99cd6c25d139fd2ef53577a5cb23a
7
+ data.tar.gz: 9184a7041e5de445103bcd47832352614897865f0ef49e83067b553786d089be73eb73c34dfaf3f102e02804ee7d8d40803d5582430ebfeecbe95f568b0b7cb7
@@ -1,6 +1,12 @@
1
1
  name: CI
2
2
 
3
- on: [push, pull_request]
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+ schedule:
9
+ - cron: '0 4 * * 1'
4
10
 
5
11
  jobs:
6
12
  test:
@@ -14,9 +20,8 @@ jobs:
14
20
  fail-fast: false
15
21
  matrix:
16
22
  ruby:
17
- - 3.1.6
18
- - 3.2.4
19
- - 3.3.2
23
+ - 3.3.4
24
+ - 3.2.5
20
25
 
21
26
  steps:
22
27
  - name: Checkout code
data/CHANGELOG.md CHANGED
@@ -1,28 +1,12 @@
1
- ## v7.2.2
1
+ ## v8.0.0
2
2
 
3
- #### Fixed
4
-
5
- - [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation
6
- - [#1247](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1247) Fix queries with date and date-time placeholder conditions
7
- - [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable
8
- - [#1255](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1255) Fixed the ordering of optimizer hints in the generated SQL
3
+ #### Changed
9
4
 
10
- ## v7.2.1
5
+ - [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter
6
+ - [#1225](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1225) Drop support to Ruby 3.1
11
7
 
12
8
  #### Fixed
13
9
 
14
- - [#1231](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1231) Enable identity insert on view's base table
15
-
16
- ## v7.2.0
17
-
18
- #### Added
19
-
20
- - [#1178](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1178) Support encrypting binary columns
21
-
22
- #### Changed
23
-
24
- - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+
25
- - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter
26
-
10
+ - [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors
27
11
 
28
- Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes.
12
+ Please check [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-2-stable/CHANGELOG.md) for previous changes.
data/Dockerfile.ci CHANGED
@@ -9,6 +9,6 @@ WORKDIR $WORKDIR
9
9
 
10
10
  COPY . $WORKDIR
11
11
 
12
- RUN RAILS_BRANCH=7-2-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3
12
+ RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3
13
13
 
14
14
  CMD ["sh"]
data/Gemfile CHANGED
@@ -18,6 +18,8 @@ if ENV["RAILS_SOURCE"]
18
18
  gemspec path: ENV["RAILS_SOURCE"]
19
19
  elsif ENV["RAILS_BRANCH"]
20
20
  gem "rails", github: "rails/rails", branch: ENV["RAILS_BRANCH"]
21
+ elsif ENV["RAILS_COMMIT"]
22
+ gem "rails", github: "rails/rails", ref: ENV["RAILS_COMMIT"]
21
23
  else
22
24
  # Need to get rails source because the gem doesn't include tests
23
25
  version = ENV["RAILS_VERSION"] || begin
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher.
1
+ # ActiveRecord SQL Server Adapter
2
2
 
3
3
  * [![CI](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml/badge.svg)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml) - CI
4
4
  * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version
@@ -8,15 +8,18 @@
8
8
 
9
9
  The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher.
10
10
 
11
- Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version
12
- of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you
13
- are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails
14
- version. We also have stable branches for each major/minor release of ActiveRecord. For older versions, please check
15
- their stable branches.
11
+ We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only
12
+ for the latest 7.x version of Rails. We also have stable branches for each major/minor release of ActiveRecord.
13
+
14
+ We support the versions of the adapter that are in the Rails [Bug Fixes](https://rubyonrails.org/maintenance)
15
+ maintenance group.
16
+
17
+ See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release.
16
18
 
17
19
  | Adapter Version | Rails Version | Support | Branch |
18
20
  |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------|
19
- | `7.2.x` | `7.2.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
21
+ | `8.0.0` | `8.0.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
22
+ | `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) |
20
23
  | `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) |
21
24
  | `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) |
22
25
  | `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) |
@@ -26,7 +29,6 @@ their stable branches.
26
29
  | `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) |
27
30
  | `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) |
28
31
 
29
- See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release.
30
32
 
31
33
  #### Native Data Type Support
32
34
 
@@ -103,16 +105,14 @@ configuration then implement the `configure_connection` method in an initializer
103
105
  example we are setting the `TEXTSIZE` to 64 megabytes.
104
106
 
105
107
  ```ruby
106
- module ActiveRecord
107
- module ConnectionAdapters
108
- class SQLServerAdapter < AbstractAdapter
109
- def configure_connection
110
- super
111
- @raw_connection.execute("SET TEXTSIZE #{64.megabytes}").do
112
- end
108
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.prepend(
109
+ Module.new do
110
+ def configure_connection
111
+ super
112
+ @raw_connection.execute("SET TEXTSIZE #{64.megabytes}").do
113
113
  end
114
114
  end
115
- end
115
+ )
116
116
  ```
117
117
 
118
118
  #### Configure Application Name
@@ -262,7 +262,6 @@ Many many people have contributed. If you do not see your name here and it shoul
262
262
 
263
263
  You can see an up-to-date list of contributors here: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors
264
264
 
265
-
266
265
  ## License
267
266
 
268
267
  ActiveRecord SQL Server Adapter is released under the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.2.2
1
+ 8.0.0
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.version = version
9
9
 
10
- spec.required_ruby_version = ">= 3.1.0"
10
+ spec.required_ruby_version = ">= 3.2.0"
11
11
 
12
12
  spec.license = "MIT"
13
13
  spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward", "Aidan Haran"]
@@ -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", "~> 7.2.0"
30
+ spec.add_dependency "activerecord", "~> 8.0.0"
31
31
  spec.add_dependency "tiny_tds"
32
32
  end
@@ -5,7 +5,6 @@ services:
5
5
  ci:
6
6
  environment:
7
7
  - ACTIVERECORD_UNITTEST_HOST=sqlserver
8
- - RAILS_BRANCH=7-2-stable
9
8
  build:
10
9
  context: .
11
10
  dockerfile: Dockerfile.ci
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  private
12
12
 
13
13
  def construct_relation_for_exists(conditions)
14
- klass.with_connection do |connection|
14
+ model.with_connection do |connection|
15
15
  if connection.sqlserver?
16
16
  _construct_relation_for_exists(conditions)
17
17
  else
@@ -13,50 +13,42 @@ module ActiveRecord
13
13
  !READ_QUERY.match?(sql.b)
14
14
  end
15
15
 
16
- def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
17
- log(sql, name, async: async) do |notification_payload|
18
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
19
- result = if id_insert_table_name = query_requires_identity_insert?(sql)
20
- with_identity_insert_enabled(id_insert_table_name, conn) { internal_raw_execute(sql, conn, perform_do: true) }
21
- else
22
- internal_raw_execute(sql, conn, perform_do: true)
23
- end
24
- verified!
25
- notification_payload[:row_count] = result
26
- result
27
- end
16
+ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:)
17
+ result = if id_insert_table_name = query_requires_identity_insert?(sql)
18
+ # If the table name is a view, we need to get the base table name for enabling identity insert.
19
+ id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name)
20
+
21
+ with_identity_insert_enabled(id_insert_table_name, raw_connection) do
22
+ internal_exec_sql_query(sql, raw_connection)
23
+ end
24
+ else
25
+ internal_exec_sql_query(sql, raw_connection)
26
+ end
27
+
28
+ verified!
29
+ notification_payload[:row_count] = result.count
30
+ result
31
+ end
32
+
33
+ def cast_result(raw_result)
34
+ if raw_result.columns.empty?
35
+ ActiveRecord::Result.empty
36
+ else
37
+ ActiveRecord::Result.new(raw_result.columns, raw_result.rows)
28
38
  end
29
39
  end
30
40
 
31
- def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false)
32
- sql = transform_query(sql)
33
-
34
- check_if_write_query(sql)
35
- mark_transaction_written_if_write(sql)
41
+ def affected_rows(raw_result)
42
+ raw_result.first['AffectedRows']
43
+ end
36
44
 
37
- unless without_prepared_statement?(binds)
45
+ def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
46
+ unless binds.nil? || binds.empty?
38
47
  types, params = sp_executesql_types_and_parameters(binds)
39
48
  sql = sp_executesql_sql(sql, types, params, name)
40
49
  end
41
50
 
42
- log(sql, name, binds, async: async) do |notification_payload|
43
- with_raw_connection do |conn|
44
- result = if id_insert_table_name = query_requires_identity_insert?(sql)
45
- # If the table name is a view, we need to get the base table name for enabling identity insert.
46
- id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name)
47
-
48
- with_identity_insert_enabled(id_insert_table_name, conn) do
49
- internal_exec_sql_query(sql, conn)
50
- end
51
- else
52
- internal_exec_sql_query(sql, conn)
53
- end
54
-
55
- verified!
56
- notification_payload[:row_count] = result.count
57
- result
58
- end
59
- end
51
+ super
60
52
  end
61
53
 
62
54
  def internal_exec_sql_query(sql, conn)
@@ -66,14 +58,14 @@ module ActiveRecord
66
58
  finish_statement_handle(handle)
67
59
  end
68
60
 
69
- def exec_delete(sql, name, binds)
61
+ def exec_delete(sql, name = nil, binds = [])
70
62
  sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows"
71
- super(sql, name, binds).rows.first.first
63
+ super(sql, name, binds)
72
64
  end
73
65
 
74
- def exec_update(sql, name, binds)
66
+ def exec_update(sql, name = nil, binds = [])
75
67
  sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows"
76
- super(sql, name, binds).rows.first.first
68
+ super(sql, name, binds)
77
69
  end
78
70
 
79
71
  def begin_db_transaction
@@ -156,7 +148,7 @@ module ActiveRecord
156
148
  returning_sql = if returning.is_a?(String)
157
149
  returning
158
150
  else
159
- returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
151
+ Array(returning).map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
160
152
  end
161
153
  sql << " OUTPUT #{returning_sql}"
162
154
  end
@@ -332,10 +324,13 @@ module ActiveRecord
332
324
 
333
325
  def sp_executesql_sql_type(attr)
334
326
  if attr.respond_to?(:type)
335
- return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type)
327
+ type = attr.type.is_a?(ActiveRecord::Normalization::NormalizedValueType) ? attr.type.cast_type : attr.type
328
+ type = type.subtype if type.serialized?
329
+
330
+ return type.sqlserver_type if type.respond_to?(:sqlserver_type)
336
331
 
337
- if attr.type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) && attr.type.instance_variable_get(:@cast_type).respond_to?(:sqlserver_type)
338
- return attr.type.instance_variable_get(:@cast_type).sqlserver_type
332
+ if type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) && type.instance_variable_get(:@cast_type).respond_to?(:sqlserver_type)
333
+ return type.instance_variable_get(:@cast_type).sqlserver_type
339
334
  end
340
335
  end
341
336
 
@@ -375,6 +370,7 @@ module ActiveRecord
375
370
  sql = "EXEC sp_executesql #{quote(sql)}"
376
371
  sql += ", #{types}, #{params}" unless params.empty?
377
372
  end
373
+
378
374
  sql.freeze
379
375
  end
380
376
 
@@ -452,10 +448,9 @@ module ActiveRecord
452
448
  # TinyTDS returns false instead of raising an exception if connection fails.
453
449
  # Getting around this by raising an exception ourselves while PR
454
450
  # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
455
- def internal_raw_execute(sql, conn, perform_do: false)
456
- result = conn.execute(sql).tap do |_result|
457
- raise TinyTds::Error, "failed to execute statement" if _result.is_a?(FalseClass)
458
- end
451
+ def internal_raw_execute(sql, raw_connection, perform_do: false)
452
+ result = raw_connection.execute(sql)
453
+ raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass)
459
454
 
460
455
  perform_do ? result.do : result
461
456
  end
@@ -14,22 +14,24 @@ module ActiveRecord
14
14
  res
15
15
  end
16
16
 
17
- def drop_table(table_name, **options)
18
- # Mimic CASCADE option as best we can.
19
- if options[:force] == :cascade
20
- execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
21
- fktable = fkdata["FKTABLE_NAME"]
22
- fkcolmn = fkdata["FKCOLUMN_NAME"]
23
- pktable = fkdata["PKTABLE_NAME"]
24
- pkcolmn = fkdata["PKCOLUMN_NAME"]
25
- remove_foreign_key fktable, name: fkdata["FK_NAME"]
26
- execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )"
17
+ def drop_table(*table_names, **options)
18
+ table_names.each do |table_name|
19
+ # Mimic CASCADE option as best we can.
20
+ if options[:force] == :cascade
21
+ execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
22
+ fktable = fkdata["FKTABLE_NAME"]
23
+ fkcolmn = fkdata["FKCOLUMN_NAME"]
24
+ pktable = fkdata["PKTABLE_NAME"]
25
+ pkcolmn = fkdata["PKCOLUMN_NAME"]
26
+ remove_foreign_key fktable, name: fkdata["FK_NAME"]
27
+ execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )"
28
+ end
29
+ end
30
+ if options[:if_exists] && version_year < 2016
31
+ execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA"
32
+ else
33
+ super
27
34
  end
28
- end
29
- if options[:if_exists] && version_year < 2016
30
- execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA"
31
- else
32
- super
33
35
  end
34
36
  end
35
37
 
@@ -37,18 +39,16 @@ module ActiveRecord
37
39
  data = select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") rescue []
38
40
 
39
41
  data.reduce([]) do |indexes, index|
40
- index = index.with_indifferent_access
41
-
42
- if index[:index_description].match?(/primary key/)
42
+ if index['index_description'].match?(/primary key/)
43
43
  indexes
44
44
  else
45
- name = index[:index_name]
46
- unique = index[:index_description].match?(/unique/)
45
+ name = index['index_name']
46
+ unique = index['index_description'].match?(/unique/)
47
47
  where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}", "SCHEMA")
48
48
  orders = {}
49
49
  columns = []
50
50
 
51
- index[:index_keys].split(",").each do |column|
51
+ index['index_keys'].split(",").each do |column|
52
52
  column.strip!
53
53
 
54
54
  if column.end_with?("(-)")
@@ -480,16 +480,15 @@ module ActiveRecord
480
480
  end
481
481
 
482
482
  def column_definitions(table_name)
483
- identifier = database_prefix_identifier(table_name)
484
- database = identifier.fully_qualified_database_quoted
485
- view_exists = view_exists?(table_name)
486
- view_tblnm = view_table_name(table_name) if view_exists
483
+ identifier = database_prefix_identifier(table_name)
484
+ database = identifier.fully_qualified_database_quoted
485
+ view_exists = view_exists?(table_name)
487
486
 
488
487
  if view_exists
489
488
  sql = <<~SQL
490
489
  SELECT LOWER(c.COLUMN_NAME) AS [name], c.COLUMN_DEFAULT AS [default]
491
490
  FROM #{database}.INFORMATION_SCHEMA.COLUMNS c
492
- WHERE c.TABLE_NAME = #{quote(view_tblnm)}
491
+ WHERE c.TABLE_NAME = #{quote(view_table_name(table_name))}
493
492
  SQL
494
493
  results = internal_exec_query(sql, "SCHEMA")
495
494
  default_functions = results.each.with_object({}) { |row, out| out[row["name"]] = row["default"] }.compact
@@ -498,71 +497,93 @@ module ActiveRecord
498
497
  sql = column_definitions_sql(database, identifier)
499
498
 
500
499
  binds = []
501
- nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
500
+ nv128 = SQLServer::Type::UnicodeVarchar.new(limit: 128)
502
501
  binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
503
502
  binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
503
+
504
504
  results = internal_exec_query(sql, "SCHEMA", binds)
505
+ raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if results.empty?
505
506
 
506
507
  columns = results.map do |ci|
507
- ci = ci.symbolize_keys
508
- ci[:_type] = ci[:type]
509
- ci[:table_name] = view_tblnm || table_name
510
- ci[:type] = case ci[:type]
511
- when /^bit|image|text|ntext|datetime$/
512
- ci[:type]
513
- when /^datetime2|datetimeoffset$/i
514
- "#{ci[:type]}(#{ci[:datetime_precision]})"
515
- when /^time$/i
516
- "#{ci[:type]}(#{ci[:datetime_precision]})"
517
- when /^numeric|decimal$/i
518
- "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})"
519
- when /^float|real$/i
520
- "#{ci[:type]}"
521
- when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/
522
- ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})"
523
- else
524
- ci[:type]
525
- end
526
- ci[:default_value],
527
- ci[:default_function] = begin
528
- default = ci[:default_value]
529
- if default.nil? && view_exists
530
- view_column = views_real_column_name(table_name, ci[:name]).downcase
531
- default = default_functions[view_column] if view_column.present?
532
- end
533
- case default
534
- when nil
535
- [nil, nil]
536
- when /\A\((\w+\(\))\)\Z/
537
- default_function = Regexp.last_match[1]
538
- [nil, default_function]
539
- when /\A\(N'(.*)'\)\Z/m
540
- string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1])
541
- [string_literal, nil]
542
- when /CREATE DEFAULT/mi
543
- [nil, nil]
544
- else
545
- type = case ci[:type]
546
- when /smallint|int|bigint/ then ci[:_type]
547
- else ci[:type]
548
- end
549
- value = default.match(/\A\((.*)\)\Z/m)[1]
550
- value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
551
- [value, nil]
552
- end
508
+ col = {
509
+ name: ci["name"],
510
+ numeric_scale: ci["numeric_scale"],
511
+ numeric_precision: ci["numeric_precision"],
512
+ datetime_precision: ci["datetime_precision"],
513
+ collation: ci["collation"],
514
+ ordinal_position: ci["ordinal_position"],
515
+ length: ci["length"]
516
+ }
517
+
518
+ col[:table_name] = view_exists ? view_table_name(table_name) : table_name
519
+ col[:type] = column_type(ci: ci)
520
+ col[:default_value], col[:default_function] = default_value_and_function(default: ci['default_value'],
521
+ name: ci['name'],
522
+ type: col[:type],
523
+ original_type: ci['type'],
524
+ view_exists: view_exists,
525
+ table_name: table_name,
526
+ default_functions: default_functions)
527
+
528
+ col[:null] = ci['is_nullable'].to_i == 1
529
+ col[:is_primary] = ci['is_primary'].to_i == 1
530
+
531
+ if [true, false].include?(ci['is_identity'])
532
+ col[:is_identity] = ci['is_identity']
533
+ else
534
+ col[:is_identity] = ci['is_identity'].to_i == 1
553
535
  end
554
- ci[:null] = ci[:is_nullable].to_i == 1
555
- ci.delete(:is_nullable)
556
- ci[:is_primary] = ci[:is_primary].to_i == 1
557
- ci[:is_identity] = ci[:is_identity].to_i == 1 unless [TrueClass, FalseClass].include?(ci[:is_identity].class)
558
- ci
536
+
537
+ col
538
+ end
539
+
540
+ columns
541
+ end
542
+
543
+ def default_value_and_function(default:, name:, type:, original_type:, view_exists:, table_name:, default_functions:)
544
+ if default.nil? && view_exists
545
+ view_column = views_real_column_name(table_name, name).downcase
546
+ default = default_functions[view_column] if view_column.present?
559
547
  end
560
548
 
561
- # Since Rails 7, it's expected that all adapter raise error when table doesn't exists.
562
- # I'm not aware of the possibility of tables without columns on SQL Server (postgres have those).
563
- # Raise error if the method return an empty array
564
- columns.tap do |result|
565
- raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if result.empty?
549
+ case default
550
+ when nil
551
+ [nil, nil]
552
+ when /\A\((\w+\(\))\)\Z/
553
+ default_function = Regexp.last_match[1]
554
+ [nil, default_function]
555
+ when /\A\(N'(.*)'\)\Z/m
556
+ string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1])
557
+ [string_literal, nil]
558
+ when /CREATE DEFAULT/mi
559
+ [nil, nil]
560
+ else
561
+ type = case type
562
+ when /smallint|int|bigint/ then original_type
563
+ else type
564
+ end
565
+ value = default.match(/\A\((.*)\)\Z/m)[1]
566
+ value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
567
+ [value, nil]
568
+ end
569
+ end
570
+
571
+ def column_type(ci:)
572
+ case ci['type']
573
+ when /^bit|image|text|ntext|datetime$/
574
+ ci['type']
575
+ when /^datetime2|datetimeoffset$/i
576
+ "#{ci['type']}(#{ci['datetime_precision']})"
577
+ when /^time$/i
578
+ "#{ci['type']}(#{ci['datetime_precision']})"
579
+ when /^numeric|decimal$/i
580
+ "#{ci['type']}(#{ci['numeric_precision']},#{ci['numeric_scale']})"
581
+ when /^float|real$/i
582
+ "#{ci['type']}"
583
+ when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/
584
+ ci['length'].to_i == -1 ? "#{ci['type']}(max)" : "#{ci['type']}(#{ci['length']})"
585
+ else
586
+ ci['type']
566
587
  end
567
588
  end
568
589
 
@@ -702,25 +723,26 @@ module ActiveRecord
702
723
 
703
724
  def view_table_name(table_name)
704
725
  view_info = view_information(table_name)
705
- view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
726
+ view_info.present? ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
706
727
  end
707
728
 
708
729
  def view_information(table_name)
709
730
  @view_information ||= {}
731
+
710
732
  @view_information[table_name] ||= begin
711
733
  identifier = SQLServer::Utils.extract_identifiers(table_name)
712
734
  information_query_table = identifier.database.present? ? "[#{identifier.database}].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]"
713
- view_info = select_one "SELECT * FROM #{information_query_table} WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA"
714
-
715
- if view_info
716
- view_info = view_info.with_indifferent_access
717
- if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
718
- view_info[:VIEW_DEFINITION] = begin
719
- select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
720
- rescue
721
- warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
722
- nil
723
- end
735
+
736
+ view_info = select_one("SELECT * FROM #{information_query_table} WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA").to_h
737
+
738
+ if view_info.present?
739
+ if view_info['VIEW_DEFINITION'].blank? || view_info['VIEW_DEFINITION'].length == 4000
740
+ view_info['VIEW_DEFINITION'] = begin
741
+ select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
742
+ rescue
743
+ warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
744
+ nil
745
+ end
724
746
  end
725
747
  end
726
748
 
@@ -729,8 +751,8 @@ module ActiveRecord
729
751
  end
730
752
 
731
753
  def views_real_column_name(table_name, column_name)
732
- view_definition = view_information(table_name)[:VIEW_DEFINITION]
733
- return column_name unless view_definition
754
+ view_definition = view_information(table_name)['VIEW_DEFINITION']
755
+ return column_name if view_definition.blank?
734
756
 
735
757
  # Remove "CREATE VIEW ... AS SELECT ..." and then match the column name.
736
758
  match_data = view_definition.sub(/CREATE\s+VIEW.*AS\s+SELECT\s/, '').match(/([\w-]*)\s+AS\s+#{column_name}\W/im)
@@ -16,6 +16,7 @@ module ActiveRecord
16
16
  sql = to_sql(arel)
17
17
  result = with_showplan_on { internal_exec_query(sql, "EXPLAIN", binds) }
18
18
  printer = showplan_printer.new(result)
19
+
19
20
  printer.pp
20
21
  end
21
22
 
@@ -36,9 +36,10 @@ module ActiveRecord
36
36
 
37
37
  def cast_value(value)
38
38
  value = super
39
- return if value.blank?
40
39
 
41
- value = value.change year: 2000, month: 01, day: 01
40
+ return value unless value.is_a?(::Time)
41
+
42
+ value = value.change(year: 2000, month: 01, day: 01)
42
43
  apply_seconds_precision(value)
43
44
  end
44
45
 
@@ -4,9 +4,7 @@ require "tiny_tds"
4
4
  require "base64"
5
5
  require "active_record"
6
6
  require "arel_sqlserver"
7
- require "active_record/connection_adapters/abstract_adapter"
8
7
  require "active_record/connection_adapters/sqlserver/core_ext/active_record"
9
- require "active_record/connection_adapters/sqlserver/core_ext/calculations"
10
8
  require "active_record/connection_adapters/sqlserver/core_ext/explain"
11
9
  require "active_record/connection_adapters/sqlserver/core_ext/explain_subscriber"
12
10
  require "active_record/connection_adapters/sqlserver/core_ext/attribute_methods"
@@ -431,36 +429,28 @@ module ActiveRecord
431
429
  TYPE_MAP
432
430
  end
433
431
 
434
- def translate_exception(e, message:, sql:, binds:)
432
+ def translate_exception(exception, message:, sql:, binds:)
435
433
  case message
436
434
  when /(SQL Server client is not connected)|(failed to execute statement)/i
437
- ConnectionNotEstablished.new(message)
435
+ ConnectionNotEstablished.new(message, connection_pool: @pool)
438
436
  when /(cannot insert duplicate key .* with unique index) | (violation of (unique|primary) key constraint)/i
439
- RecordNotUnique.new(message, sql: sql, binds: binds)
437
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
440
438
  when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
441
- InvalidForeignKey.new(message, sql: sql, binds: binds)
439
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
442
440
  when /has been chosen as the deadlock victim/i
443
- DeadlockVictim.new(message, sql: sql, binds: binds)
441
+ DeadlockVictim.new(message, sql: sql, binds: binds, connection_pool: @pool)
444
442
  when /database .* does not exist/i
445
- NoDatabaseError.new(message)
443
+ NoDatabaseError.new(message, connection_pool: @pool)
446
444
  when /data would be truncated/
447
- ValueTooLong.new(message, sql: sql, binds: binds)
445
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
448
446
  when /connection timed out/
449
- StatementTimeout.new(message, sql: sql, binds: binds)
447
+ StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
450
448
  when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/
451
- pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2)
452
- MismatchedForeignKey.new(
453
- self,
454
- message: message,
455
- table: fk_id.schema,
456
- foreign_key: fk_id.object,
457
- target_table: pk_id.schema,
458
- primary_key: pk_id.object
459
- )
449
+ MismatchedForeignKey.new(message: message, connection_pool: @pool)
460
450
  when /Cannot insert the value NULL into column.*does not allow nulls/
461
- NotNullViolation.new(message, sql: sql, binds: binds)
451
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
462
452
  when /Arithmetic overflow error/
463
- RangeError.new(message, sql: sql, binds: binds)
453
+ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
464
454
  else
465
455
  super
466
456
  end
@@ -464,13 +464,13 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
464
464
  assert SSTestStringDefaultsView.lease_connection.data_source_exists?(SSTestStringDefaultsView.table_name)
465
465
  end
466
466
 
467
- # That have more than 4000 chars for their defintion
467
+ # That have more than 4000 chars for their definition
468
468
 
469
- it "cope with null returned for the defintion" do
469
+ it "cope with null returned for the definition" do
470
470
  assert_nothing_raised() { SSTestStringDefaultsBigView.columns }
471
471
  end
472
472
 
473
- it "using alternate view defintion still be able to find real default" do
473
+ it "using alternate view definition still be able to find real default" do
474
474
  assert_equal "null", SSTestStringDefaultsBigView.new.pretend_null,
475
475
  SSTestStringDefaultsBigView.columns_hash["pretend_null"].inspect
476
476
  end
@@ -542,7 +542,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
542
542
  @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
543
543
 
544
544
  ActiveRecord::Base.while_preventing_writes do
545
- assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
545
+ assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'").count
546
546
  end
547
547
  end
548
548
  end
@@ -581,6 +581,28 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
581
581
  end
582
582
  end
583
583
 
584
+ describe "mismatched foreign keys error" do
585
+ def setup
586
+ @conn = ActiveRecord::Base.lease_connection
587
+ end
588
+
589
+ it 'raises an error when the foreign key is mismatched' do
590
+ error = assert_raises(ActiveRecord::MismatchedForeignKey) do
591
+ @conn.add_reference :engines, :old_car
592
+ @conn.add_foreign_key :engines, :old_cars
593
+ end
594
+
595
+ assert_match(
596
+ %r/Column 'old_cars\.id' is not the same data type as referencing column 'engines\.old_car_id' in foreign key '.*'/,
597
+ error.message
598
+ )
599
+ assert_not_nil error.cause
600
+ assert_equal @conn.pool, error.connection_pool
601
+ ensure
602
+ @conn.execute("ALTER TABLE engines DROP COLUMN old_car_id") rescue nil
603
+ end
604
+ end
605
+
584
606
  describe "placeholder conditions" do
585
607
  it 'using time placeholder' do
586
608
  assert_equal Task.where("starting < ?", Time.now).count, 1
@@ -520,6 +520,9 @@ class CalculationsTest < ActiveRecord::TestCase
520
520
 
521
521
  # SELECT columns must be in the GROUP clause. Since since `ids` only selects the primary key you cannot perform this query in SQL Server.
522
522
  coerce_tests! :test_ids_with_includes_and_non_primary_key_order
523
+
524
+ # To limit the results in SQL Server we use `FETCH NEXT @0 ROWS ONLY` instead of `LIMIT @0`. To use `FETCH NEXT` an order must be provided.
525
+ coerce_tests! :test_no_order_by_when_counting_all
523
526
  end
524
527
 
525
528
  module ActiveRecord
@@ -1337,6 +1340,20 @@ module ActiveRecord
1337
1340
  def test_registering_new_handlers_for_association_coerced
1338
1341
  assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: { title: /rails/ }).to_sql
1339
1342
  end
1343
+
1344
+ # Same as original test except string has `N` prefix to indicate unicode string.
1345
+ coerce_tests! :test_registering_new_handlers_for_joins
1346
+ def test_registering_new_handlers_for_joins_coerced
1347
+ Reply.belongs_to :regexp_topic, -> { where(title: /rails/) }, class_name: "Topic", foreign_key: "parent_id"
1348
+
1349
+ assert_match %r{#{Regexp.escape(quote_table_name("regexp_topic.title"))} ~ N'rails'}i, Reply.joins(:regexp_topic).references(Arel.sql("regexp_topic")).to_sql
1350
+ end
1351
+
1352
+ private
1353
+
1354
+ def topic_title
1355
+ Topic.lease_connection.quote_table_name("topics.title")
1356
+ end
1340
1357
  end
1341
1358
  end
1342
1359
 
@@ -2168,17 +2185,6 @@ class EnumTest < ActiveRecord::TestCase
2168
2185
  Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2169
2186
  end
2170
2187
 
2171
- # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2172
- coerce_tests! %r{declare multiple enums at a time}
2173
- test "declare multiple enums at a time coerced" do
2174
- Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2175
-
2176
- send(:'original_declare multiple enums at a time')
2177
- ensure
2178
- Book.where(author_id: nil, name: nil).delete_all
2179
- Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2180
- end
2181
-
2182
2188
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2183
2189
  coerce_tests! %r{serializable\? with large number label}
2184
2190
  test "serializable? with large number label coerced" do
@@ -2250,14 +2256,6 @@ class LogSubscriberTest < ActiveRecord::TestCase
2250
2256
  def test_verbose_query_logs_coerced
2251
2257
  original_test_verbose_query_logs
2252
2258
  end
2253
-
2254
- # Bindings logged slightly differently.
2255
- coerce_tests! :test_where_in_binds_logging_include_attribute_names
2256
- def test_where_in_binds_logging_include_attribute_names_coerced
2257
- Developer.where(id: [1, 2, 3, 4, 5]).load
2258
- wait
2259
- assert_match(%{@0 = 1, @1 = 2, @2 = 3, @3 = 4, @4 = 5 [["id", nil], ["id", nil], ["id", nil], ["id", nil], ["id", nil]]}, @logger.logged(:debug).last)
2260
- end
2261
2259
  end
2262
2260
 
2263
2261
  class ReloadModelsTest < ActiveRecord::TestCase
@@ -2419,7 +2417,9 @@ class QueryLogsTest < ActiveRecord::TestCase
2419
2417
  # SQL requires double single-quotes.
2420
2418
  coerce_tests! :test_sql_commenter_format
2421
2419
  def test_sql_commenter_format_coerced
2422
- ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
2420
+ ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
2421
+ ActiveRecord::QueryLogs.tags = [:application]
2422
+
2423
2423
  assert_queries_match(%r{/\*application=''active_record''\*/}) do
2424
2424
  Dashboard.first
2425
2425
  end
@@ -2428,7 +2428,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2428
2428
  # SQL requires double single-quotes.
2429
2429
  coerce_tests! :test_sqlcommenter_format_value
2430
2430
  def test_sqlcommenter_format_value_coerced
2431
- ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
2431
+ ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
2432
2432
 
2433
2433
  ActiveRecord::QueryLogs.tags = [
2434
2434
  :application,
@@ -2443,7 +2443,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2443
2443
  # SQL requires double single-quotes.
2444
2444
  coerce_tests! :test_sqlcommenter_format_value_string_coercible
2445
2445
  def test_sqlcommenter_format_value_string_coercible_coerced
2446
- ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
2446
+ ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
2447
2447
 
2448
2448
  ActiveRecord::QueryLogs.tags = [
2449
2449
  :application,
@@ -2458,7 +2458,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2458
2458
  # SQL requires double single-quotes.
2459
2459
  coerce_tests! :test_sqlcommenter_format_allows_string_keys
2460
2460
  def test_sqlcommenter_format_allows_string_keys_coerced
2461
- ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
2461
+ ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
2462
2462
 
2463
2463
  ActiveRecord::QueryLogs.tags = [
2464
2464
  :application,
@@ -2493,6 +2493,17 @@ class InsertAllTest < ActiveRecord::TestCase
2493
2493
  result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
2494
2494
  assert_equal %w[ REWORK ], result.pluck("name")
2495
2495
  end
2496
+
2497
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2498
+ coerce_tests! :test_insert_with_type_casting_and_serialize_is_consistent
2499
+ def test_insert_with_type_casting_and_serialize_is_consistent_coerced
2500
+ connection.remove_index(:books, column: [:author_id, :name])
2501
+
2502
+ original_test_insert_with_type_casting_and_serialize_is_consistent
2503
+ ensure
2504
+ Book.where(author_id: nil, name: '["Array"]').delete_all
2505
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2506
+ end
2496
2507
  end
2497
2508
 
2498
2509
  module ActiveRecord
@@ -2655,6 +2666,28 @@ module ActiveRecord
2655
2666
  end
2656
2667
  end
2657
2668
 
2669
+ module ActiveRecord
2670
+ module ConnectionAdapters
2671
+ class RegistrationIsolatedTest < ActiveRecord::TestCase
2672
+ # SQL Server was not included in the list of available adapters in the error message.
2673
+ coerce_tests! %r{resolve raises if the adapter is using the pre 7.2 adapter registration API}
2674
+ def resolve_raises_if_the_adapter_is_using_the_pre_7_2_adapter_registration_API
2675
+ exception = assert_raises(ActiveRecord::AdapterNotFound) do
2676
+ ActiveRecord::ConnectionAdapters.resolve("fake_legacy")
2677
+ end
2678
+
2679
+ assert_equal(
2680
+ "Database configuration specifies nonexistent 'ridiculous' adapter. Available adapters are: abstract, fake, mysql2, postgresql, sqlite3, sqlserver, trilogy. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile if it's not in the list of available adapters.",
2681
+ exception.message
2682
+ )
2683
+ ensure
2684
+ ActiveRecord::ConnectionAdapters.instance_variable_get(:@adapters).delete("fake_legacy")
2685
+ end
2686
+ end
2687
+ end
2688
+ end
2689
+
2690
+
2658
2691
  module ActiveRecord
2659
2692
  class TableMetadataTest < ActiveSupport::TestCase
2660
2693
  # Adapter returns an object that is subclass of what is expected in the original test.
@@ -36,7 +36,6 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase
36
36
  end
37
37
  end
38
38
 
39
-
40
39
  it "support order" do
41
40
  assert_queries_match(%r{\ASELECT .+ FROM .+ ORDER .+ OPTION .+\z}) do
42
41
  companies = Company.optimizer_hints("LABEL='FindCompanies'")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-sqlserver-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.2
4
+ version: 8.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Collins
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2024-11-10 00:00:00.000000000 Z
18
+ date: 2024-11-11 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activerecord
@@ -23,14 +23,14 @@ dependencies:
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 7.2.0
26
+ version: 8.0.0
27
27
  type: :runtime
28
28
  prerelease: false
29
29
  version_requirements: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 7.2.0
33
+ version: 8.0.0
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: tiny_tds
36
36
  requirement: !ruby/object:Gem::Requirement
@@ -79,7 +79,6 @@ files:
79
79
  - lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb
80
80
  - lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb
81
81
  - lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb
82
- - lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb
83
82
  - lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb
84
83
  - lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb
85
84
  - lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb
@@ -239,8 +238,8 @@ licenses:
239
238
  - MIT
240
239
  metadata:
241
240
  bug_tracker_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues
242
- changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.2.2/CHANGELOG.md
243
- source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.2.2
241
+ changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v8.0.0/CHANGELOG.md
242
+ source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v8.0.0
244
243
  post_install_message:
245
244
  rdoc_options: []
246
245
  require_paths:
@@ -249,7 +248,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
249
248
  requirements:
250
249
  - - ">="
251
250
  - !ruby/object:Gem::Version
252
- version: 3.1.0
251
+ version: 3.2.0
253
252
  required_rubygems_version: !ruby/object:Gem::Requirement
254
253
  requirements:
255
254
  - - ">="
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_record/relation"
4
- require "active_record/version"
5
-
6
- module ActiveRecord
7
- module ConnectionAdapters
8
- module SQLServer
9
- module CoreExt
10
- module Calculations
11
-
12
- private
13
-
14
- def build_count_subquery(relation, 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
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
25
-
26
- ActiveSupport.on_load(:active_record) do
27
- mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Calculations
28
- ActiveRecord::Relation.include(mod)
29
- end