activerecord-sqlserver-adapter 6.1.2.1 → 7.0.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -1
  3. data/CHANGELOG.md +11 -56
  4. data/Gemfile +1 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +17 -8
  7. data/VERSION +1 -1
  8. data/activerecord-sqlserver-adapter.gemspec +2 -2
  9. data/appveyor.yml +4 -6
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +7 -15
  11. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +15 -6
  12. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +2 -4
  13. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +27 -8
  14. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +3 -1
  15. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +1 -1
  16. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +1 -1
  17. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -1
  18. data/lib/active_record/connection_adapters/sqlserver/utils.rb +16 -1
  19. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +82 -72
  20. data/lib/arel/visitors/sqlserver.rb +2 -0
  21. data/test/cases/coerced_tests.rb +301 -85
  22. data/test/cases/column_test_sqlserver.rb +58 -58
  23. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  24. data/test/cases/rake_test_sqlserver.rb +2 -1
  25. data/test/cases/schema_dumper_test_sqlserver.rb +2 -2
  26. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +1 -1
  27. data/test/support/coerceable_test_sqlserver.rb +4 -4
  28. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  29. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  30. data/test/support/rake_helpers.rb +3 -1
  31. metadata +15 -13
  32. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  33. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cbe90476efcde83329c5afc117491b77898865135cca90e25f44eed91b54e87
4
- data.tar.gz: fd41bbb523fdf9281e18120788dd0ff49653a31ff189dceb55ab49aba816d197
3
+ metadata.gz: 9983757834cfcc185a0a292ec422cc8e090a5cdff995be814738bb82b0620373
4
+ data.tar.gz: 527f419f372081cd743fcd0898c16183136ac36eaf36dd14a521bb455a5147cc
5
5
  SHA512:
6
- metadata.gz: b6555a063590ffe22617da22910c8f131935582dd366e76bb9f1b953670ef462416ca8cd28f290277d799449cd7e298e3abb5159578b074e59c66b18fd30cedb
7
- data.tar.gz: 8c5a255f1d37d1961c1dd5957535513370510316ace2266c1f91359a2c85700b9c94bf30aa47bb482d9e928c5399fa54fbb2415c8e52b65ebbec16c7fd38abad
6
+ metadata.gz: 4e64604834556a9bf9bd9be39e3aff2cc267729c6d32f28c697d293a6b9be5f5a44c924bbe6fae48f528b88a6c24c2c2d6337a5658aa295284764cb9a66a46c4
7
+ data.tar.gz: c09d354e4af8a5dbb9eb4ef4ab3fdf3b3a527f3ed779895995aac6d60c6335e0698d57cae2509b43e5e3e2825a724bcc41ed2424822b6f067ae6fddfd26ea9bb
@@ -13,7 +13,10 @@ jobs:
13
13
  strategy:
14
14
  fail-fast: false
15
15
  matrix:
16
- ruby: [2.5.9, 2.6.7, 2.7.3, 3.0.1]
16
+ ruby:
17
+ - 2.7.5
18
+ - 3.0.3
19
+ - 3.1.0
17
20
 
18
21
  steps:
19
22
  - name: Checkout code
data/CHANGELOG.md CHANGED
@@ -1,63 +1,18 @@
1
- ## v6.1.2.1
1
+ ## v7.0.0.0.rc1
2
2
 
3
- [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.2.0...v6.1.2.1)
4
-
5
- #### Fixed
6
-
7
- - [#943](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/943) Fix appname resolution when outside Rails context
8
-
9
- ## v6.1.2.0
10
-
11
- [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.1.0...v6.1.2.0)
12
-
13
- #### Fixed
14
-
15
- - [#940](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/940) Primary key violation should result in RecordNotUnique error
3
+ [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/6-1-stable...v7.0.0.0.rc1)
16
4
 
17
5
  #### Changed
18
6
 
19
- - [#941](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/941) No longer support configuring the application name by overriding the 'configure_application_name' method.
20
-
21
- ## v6.1.1.0
22
-
23
- [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.0.0...v6.1.1.0)
24
-
25
- #### Fixed
7
+ - [#968](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/968) Define adapter type maps statically
8
+ - [#983](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/983) Optimize remove_columns to use a single SQL statement
9
+ - [#984](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/984) Better handle SQL queries with invalid encoding
10
+ - [#988](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/988) Raise `ActiveRecord::StatementInvalid` when `columns` is called with a non-existing table (***breaking change***)
26
11
 
27
- - [#933](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/933) Conditionally apply SQL Server monkey patches to ActiveRecord so that it is safe to use this gem alongside other database adapters (e.g. PostgreSQL) in a multi-database Rails app
28
- - [#935](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/935) Fix schema cache generation
29
- (**breaking change**)
30
- - [#936](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/936) Fix deteministic fetch when table has a composite primary key
31
- - [#938](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/938) Fix date columns serialization for range values
32
-
33
- ## v6.1.0.0
34
-
35
- - No changes
36
-
37
- ## v6.1.0.0.rc1
38
-
39
- [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/6-0-stable...v6.1.0.0.rc1)
40
-
41
- #### Fixed
42
-
43
- - [#872](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/872) Use native String#start_with
44
- - [#876](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/876) Use native String#end_with
45
- - [#873](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/873) Various fixes to get the tests running for Rails 6.1
46
- - [#874](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/874) Deduplicate schema cache structures
47
- - [#875](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/875) Handle default boolean column values when deduplicating
48
- - [#879](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/879) Added visit method for HomogeneousIn
49
- - [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating
50
- - [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config
51
- - [#890](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/890) Fix removal of invalid ordering from select statements
52
- - [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change
53
- - [#891](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/891) Add support for if_not_exists to indexes
54
- - [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column
55
- - [#883](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix quoting of ActiveRecord::Relation::QueryAttribute and ActiveModel::Attributes
56
- - [#893](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/893) Add Active Record Marshal forward compatibility tests
57
- - [#903](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/903) Raise ActiveRecord::ConnectionNotEstablished on calls to execute with a disconnected connection
58
-
59
- #### Changed
12
+ #### Added
60
13
 
61
- - [#917](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/917) Refactored to use new_client connection pattern
14
+ - [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Support `ActiveRecord::QueryLogs`
15
+ - [#981](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/981) Support `find_by` an encrypted attribute
16
+ - [#985](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/985) Support string returning clause for `ActiveRecord#insert_all`
62
17
 
63
- Please check [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-0-stable/CHANGELOG.md) for previous changes.
18
+ Please check [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-1-stable/CHANGELOG.md) for previous changes.
data/Gemfile CHANGED
@@ -10,6 +10,7 @@ gem "bcrypt"
10
10
  gem "pg", ">= 0.18.0"
11
11
  gem "sqlite3", "~> 1.4"
12
12
  gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
13
+ gem "benchmark-ips"
13
14
 
14
15
  if ENV["RAILS_SOURCE"]
15
16
  gemspec path: ENV["RAILS_SOURCE"]
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008-2015
1
+ Copyright (c) 2008-2022
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -9,11 +9,12 @@
9
9
 
10
10
  The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher.
11
11
 
12
- Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 6.x version of the adapter is only for the latest 6.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you 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 version. We also have stable branches for each major/minor release of ActiveRecord.
12
+ Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version 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 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 version. We also have stable branches for each major/minor release of ActiveRecord.
13
13
 
14
14
  | Adapter Version | Rails Version | Support |
15
15
  | --------------- | ------------- | ------------------------------------------------------------------------------------------- |
16
- | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
16
+ | `7.0.0.0.rc1` | `7.0.x` | [unreleased](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
17
+ | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) |
17
18
  | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) |
18
19
  | `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) |
19
20
  | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) |
@@ -66,7 +67,7 @@ ActiveRecord::Base.table_name_prefix = 'dbo.'
66
67
  It's also possible to create/change/drop a schema in the migration file as in the example below:
67
68
 
68
69
  ```ruby
69
- class CreateFooSchema < ActiveRecord::Migration[6.0]
70
+ class CreateFooSchema < ActiveRecord::Migration[7.0]
70
71
  def up
71
72
  create_schema('foo')
72
73
 
@@ -107,7 +108,7 @@ Below shows how you might use the database.yml file to use the process ID in you
107
108
  ```yaml
108
109
  development:
109
110
  adapter: sqlserver
110
- appname: <%= myapp_#{Process.pid} %>
111
+ appname: <%= "myapp_#{Process.pid}" %>
111
112
  ```
112
113
 
113
114
  #### Executing Stored Procedures
@@ -152,16 +153,25 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_X
152
153
  **NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info.
153
154
 
154
155
 
156
+ ## New Rails Applications
157
+
158
+ When creating a new Rails application you can specify that you want to use the SQL Server adapter using the `database` option:
159
+
160
+ ```
161
+ rails new my_app --database=sqlserver
162
+ ```
163
+
164
+ To then connect the application to your SQL Server instance edit the `config/database.yml` file with the username, password and host of your SQL Server instance.
165
+
166
+
155
167
  ## Installation
156
168
 
157
169
  The adapter has no strict gem dependencies outside of `ActiveRecord`. You will have to pick a connection mode, the default is dblib which uses the `TinyTDS` gem. Just bundle the gem and the adapter will use it.
158
170
 
159
171
  ```ruby
160
- gem 'tiny_tds'
161
172
  gem 'activerecord-sqlserver-adapter'
162
173
  ```
163
174
 
164
-
165
175
  ## Contributing
166
176
 
167
177
  If you would like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Gitter, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the [`RUNNING_UNIT_TESTS`](RUNNING_UNIT_TESTS.md) file for the details of how to run the unit tests.
@@ -178,5 +188,4 @@ You can see an up-to-date list of contributors here: http://github.com/rails-sql
178
188
 
179
189
  ## License
180
190
 
181
- Copyright © 2008-2020. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file.
182
-
191
+ Copyright © 2008-2022. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 6.1.2.1
1
+ 7.0.0.0.rc1
@@ -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 = ">= 2.5.0"
10
+ spec.required_ruby_version = ">= 2.7.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"]
@@ -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.0.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"
@@ -6,20 +6,12 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module SQLServer
8
8
  module CoreExt
9
- module Preloader
10
- private
9
+ module LoaderQuery
10
+ def load_records_for_keys(keys, &block)
11
+ return super unless scope.connection.adapter_name == "SQLServer"
11
12
 
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
13
+ keys.each_slice(in_clause_length).flat_map do |slice|
14
+ scope.where(association_key_name => slice).load(&block).records
23
15
  end
24
16
  end
25
17
 
@@ -33,6 +25,6 @@ module ActiveRecord
33
25
  end
34
26
 
35
27
  ActiveSupport.on_load(:active_record) do
36
- mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Preloader
37
- ActiveRecord::Associations::Preloader::Association.prepend(mod)
28
+ mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::LoaderQuery
29
+ ActiveRecord::Associations::Preloader::Association::LoaderQuery.prepend(mod)
38
30
  end
@@ -9,9 +9,12 @@ module ActiveRecord
9
9
 
10
10
  def write_query?(sql) # :nodoc:
11
11
  !READ_QUERY.match?(sql)
12
+ rescue ArgumentError # Invalid encoding
13
+ !READ_QUERY.match?(sql.b)
12
14
  end
13
15
 
14
16
  def execute(sql, name = nil)
17
+ sql = transform_query(sql)
15
18
  if preventing_writes? && write_query?(sql)
16
19
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
17
20
  end
@@ -26,7 +29,8 @@ module ActiveRecord
26
29
  end
27
30
  end
28
31
 
29
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
32
+ def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false)
33
+ sql = transform_query(sql)
30
34
  if preventing_writes? && write_query?(sql)
31
35
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
32
36
  end
@@ -34,7 +38,7 @@ module ActiveRecord
34
38
  materialize_transactions
35
39
  mark_transaction_written_if_write(sql)
36
40
 
37
- sp_executesql(sql, name, binds, prepare: prepare)
41
+ sp_executesql(sql, name, binds, prepare: prepare, async: async)
38
42
  end
39
43
 
40
44
  def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil)
@@ -145,7 +149,12 @@ module ActiveRecord
145
149
  sql = +"INSERT #{insert.into}"
146
150
 
147
151
  if returning = insert.send(:insert_all).returning
148
- sql << " OUTPUT " << returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
152
+ returning_sql = if returning.is_a?(String)
153
+ returning
154
+ else
155
+ returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
156
+ end
157
+ sql << " OUTPUT #{returning_sql}"
149
158
  end
150
159
 
151
160
  sql << " #{insert.values_list}"
@@ -168,7 +177,7 @@ module ActiveRecord
168
177
  case @connection_options[:mode]
169
178
  when :dblib
170
179
  result = ensure_established_connection! { dblib_execute(sql) }
171
- options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc }
180
+ options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc }
172
181
  result.each(options) do |row|
173
182
  r = row.with_indifferent_access
174
183
  yield(r) if block_given?
@@ -409,7 +418,7 @@ module ActiveRecord
409
418
  # === SQLServer Specific (Selecting) ============================ #
410
419
 
411
420
  def raw_select(sql, name = "SQL", binds = [], options = {})
412
- log(sql, name, binds) { _raw_select(sql, options) }
421
+ log(sql, name, binds, async: options[:async]) { _raw_select(sql, options) }
413
422
  end
414
423
 
415
424
  def _raw_select(sql, options = {})
@@ -441,7 +450,7 @@ module ActiveRecord
441
450
 
442
451
  def handle_to_names_and_values_dblib(handle, options = {})
443
452
  query_options = {}.tap do |qo|
444
- qo[:timezone] = ActiveRecord::Base.default_timezone || :utc
453
+ qo[:timezone] = ActiveRecord.default_timezone || :utc
445
454
  qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash
446
455
  end
447
456
  results = handle.each(query_options)
@@ -109,9 +109,7 @@ module ActiveRecord
109
109
 
110
110
  private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
111
111
 
112
- private
113
-
114
- def _quote(value)
112
+ def quote(value)
115
113
  case value
116
114
  when Type::Binary::Data
117
115
  "0x#{value.hex}"
@@ -124,7 +122,7 @@ module ActiveRecord
124
122
  end
125
123
  end
126
124
 
127
- def _type_cast(value)
125
+ def type_cast(value)
128
126
  case value
129
127
  when ActiveRecord::Type::SQLServer::Data
130
128
  value.to_s
@@ -378,7 +378,7 @@ module ActiveRecord
378
378
  binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
379
379
  binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
380
380
  results = sp_executesql(sql, "SCHEMA", binds)
381
- results.map do |ci|
381
+ columns = results.map do |ci|
382
382
  ci = ci.symbolize_keys
383
383
  ci[:_type] = ci[:type]
384
384
  ci[:table_name] = view_tblnm || table_name
@@ -437,6 +437,13 @@ module ActiveRecord
437
437
  ci[:is_identity] = ci[:is_identity].to_i == 1 unless [TrueClass, FalseClass].include?(ci[:is_identity].class)
438
438
  ci
439
439
  end
440
+
441
+ # Since Rails 7, it's expected that all adapter raise error when table doesn't exists.
442
+ # I'm not aware of the possibility of tables without columns on SQL Server (postgres have those).
443
+ # Raise error if the method return an empty array
444
+ columns.tap do |result|
445
+ raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if result.empty?
446
+ end
440
447
  end
441
448
 
442
449
  def column_definitions_sql(database, identifier)
@@ -507,6 +514,13 @@ module ActiveRecord
507
514
  }.gsub(/[ \t\r\n]+/, " ").strip
508
515
  end
509
516
 
517
+ def remove_columns_for_alter(table_name, *column_names, **options)
518
+ first, *rest = column_names
519
+
520
+ # return an array like this [DROP COLUMN col_1, col_2, col_3]. Abstract adapter joins fragments with ", "
521
+ [remove_column_for_alter(table_name, first)] + rest.map { |column_name| quote_column_name(column_name) }
522
+ end
523
+
510
524
  def remove_check_constraints(table_name, column_name)
511
525
  constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", "SCHEMA"
512
526
  constraints.each do |constraint|
@@ -531,17 +545,22 @@ module ActiveRecord
531
545
 
532
546
  # === SQLServer Specific (Misc Helpers) ========================= #
533
547
 
548
+ # Parses just the table name from the SQL. Table name does not include database/schema/etc.
534
549
  def get_table_name(sql)
535
- tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
536
- Regexp.last_match[3] || Regexp.last_match[4]
537
- elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
538
- Regexp.last_match[1]
539
- else
540
- nil
541
- end
550
+ tn = get_raw_table_name(sql)
542
551
  SQLServer::Utils.extract_identifiers(tn).object
543
552
  end
544
553
 
554
+ # Parses the raw table name that is used in the SQL. Table name could include database/schema/etc.
555
+ def get_raw_table_name(sql)
556
+ case sql
557
+ when /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
558
+ Regexp.last_match[3] || Regexp.last_match[4]
559
+ when /FROM\s+([^\(\s]+)\s*/i
560
+ Regexp.last_match[1]
561
+ end
562
+ end
563
+
545
564
  def default_constraint_name(table_name, column_name)
546
565
  "DF_#{table_name}_#{column_name}"
547
566
  end
@@ -7,6 +7,8 @@ module ActiveRecord
7
7
  class Data
8
8
  attr_reader :value, :type
9
9
 
10
+ delegate :sub, to: :value
11
+
10
12
  def initialize(value, type)
11
13
  @value, @type = value, type
12
14
  end
@@ -25,7 +27,7 @@ module ActiveRecord
25
27
  end
26
28
 
27
29
  def eql?(other)
28
- self.class == other.class && self.value == other.value
30
+ self.class == other.class && value == other.value
29
31
  end
30
32
  alias :== :eql?
31
33
  end
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
  value = super
14
14
  return value unless value.acts_like?(:date)
15
15
 
16
- date = super(value).to_s(:_sqlserver_dateformat)
16
+ date = super(value).to_formatted_s(:_sqlserver_dateformat)
17
17
  Data.new date, self
18
18
  end
19
19
 
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  value = super
16
16
  return value unless value.acts_like?(:time)
17
17
 
18
- datetime = "#{value.to_s(:_sqlserver_datetime)}.#{quote_fractional(value)}"
18
+ datetime = "#{value.to_formatted_s(:_sqlserver_datetime)}.#{quote_fractional(value)}"
19
19
 
20
20
  Data.new datetime, self
21
21
  end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  value = super
12
12
  return value unless value.acts_like?(:time)
13
13
 
14
- time = "#{value.to_s(:_sqlserver_time)}.#{quote_fractional(value)}"
14
+ time = "#{value.to_formatted_s(:_sqlserver_time)}.#{quote_fractional(value)}"
15
15
 
16
16
  Data.new time, self
17
17
  end
@@ -46,7 +46,22 @@ module ActiveRecord
46
46
  end
47
47
 
48
48
  def fully_qualified?
49
- parts.compact.size == 4
49
+ qualified_level == :fully
50
+ end
51
+
52
+ def qualified_level
53
+ case parts.compact.size
54
+ when 4
55
+ :fully
56
+ when 3
57
+ :database
58
+ when 2
59
+ :schema
60
+ when 1
61
+ :table
62
+ else
63
+ :none
64
+ end
50
65
  end
51
66
 
52
67
  def to_s