activerecord-sqlserver-adapter 7.2.3 → 8.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +1 -1
- data/.github/workflows/ci.yml +10 -5
- data/CHANGELOG.md +7 -23
- data/Dockerfile.ci +1 -1
- data/Gemfile +2 -0
- data/README.md +16 -17
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +2 -2
- data/{docker-compose.ci.yml → compose.ci.yaml} +0 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +43 -48
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +119 -97
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +3 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +11 -21
- data/test/cases/adapter_test_sqlserver.rb +26 -4
- data/test/cases/coerced_tests.rb +56 -23
- data/test/cases/optimizer_hints_test_sqlserver.rb +0 -1
- metadata +8 -9
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +0 -29
- /data/.devcontainer/{docker-compose.yml → compose.yaml} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c99a1de9708c420caf70ef674f8030ad285cd514110ddac415855918dba0312d
|
4
|
+
data.tar.gz: 54a9fd23dc6c22d61f8c7bedbc369ac727b198068e1c33a5937c6440d426c649
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c8dc920900865712ca82fa73de5294f5016aa381d5a2c56e2d920d5c2127904576408eaaf3dda6f5a190c4eceb1fb23a3266ed59560703146ca9217e1e0abbf
|
7
|
+
data.tar.gz: 266b7a4e3cf8e44fdc9302dac49bc4a53647fe6555769d130ff180b89c50c89e702aa7c6fba7ccc362bf287b6231a63ad5cc16832b19150ec16b1438065fe86c
|
@@ -1,7 +1,7 @@
|
|
1
1
|
// For format details, see https://aka.ms/devcontainer.json.
|
2
2
|
{
|
3
3
|
"name": "ActiveRecord SQL Server Adapter project development",
|
4
|
-
"dockerComposeFile": "
|
4
|
+
"dockerComposeFile": "compose.yaml",
|
5
5
|
"service": "activerecord-sqlserver-adapter",
|
6
6
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
7
7
|
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
name: CI
|
2
2
|
|
3
|
-
on:
|
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:
|
@@ -8,15 +14,14 @@ jobs:
|
|
8
14
|
runs-on: ubuntu-20.04 # TODO: Change back to 'ubuntu-latest' when https://github.com/microsoft/mssql-docker/issues/899 resolved.
|
9
15
|
|
10
16
|
env:
|
11
|
-
COMPOSE_FILE:
|
17
|
+
COMPOSE_FILE: compose.ci.yaml
|
12
18
|
|
13
19
|
strategy:
|
14
20
|
fail-fast: false
|
15
21
|
matrix:
|
16
22
|
ruby:
|
17
|
-
- 3.
|
18
|
-
- 3.2.
|
19
|
-
- 3.3.2
|
23
|
+
- 3.3.6
|
24
|
+
- 3.2.6
|
20
25
|
|
21
26
|
steps:
|
22
27
|
- name: Checkout code
|
data/CHANGELOG.md
CHANGED
@@ -1,34 +1,18 @@
|
|
1
|
-
##
|
1
|
+
## v8.0.1
|
2
2
|
|
3
3
|
#### Fixed
|
4
4
|
|
5
5
|
- [#1262](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1262) Fix distinct alias when multiple databases used.
|
6
6
|
|
7
|
-
##
|
7
|
+
## v8.0.0
|
8
8
|
|
9
|
-
####
|
10
|
-
|
11
|
-
- [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation
|
12
|
-
- [#1247](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1247) Fix queries with date and date-time placeholder conditions
|
13
|
-
- [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable
|
14
|
-
- [#1255](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1255) Fixed the ordering of optimizer hints in the generated SQL
|
9
|
+
#### Changed
|
15
10
|
|
16
|
-
|
11
|
+
- [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter.
|
12
|
+
- [#1225](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1225) Drop support to Ruby 3.1.
|
17
13
|
|
18
14
|
#### Fixed
|
19
15
|
|
20
|
-
- [#
|
21
|
-
|
22
|
-
## v7.2.0
|
23
|
-
|
24
|
-
#### Added
|
25
|
-
|
26
|
-
- [#1178](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1178) Support encrypting binary columns
|
27
|
-
|
28
|
-
#### Changed
|
29
|
-
|
30
|
-
- [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+
|
31
|
-
- [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter
|
32
|
-
|
16
|
+
- [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors.
|
33
17
|
|
34
|
-
Please check [7-
|
18
|
+
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
|
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
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
| `
|
21
|
+
| `8.0.x` | `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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
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
|
-
|
1
|
+
8.0.1
|
@@ -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.
|
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", "~>
|
30
|
+
spec.add_dependency "activerecord", "~> 8.0.0"
|
31
31
|
spec.add_dependency "tiny_tds"
|
32
32
|
end
|
@@ -13,50 +13,42 @@ module ActiveRecord
|
|
13
13
|
!READ_QUERY.match?(sql.b)
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
32
|
-
|
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
|
-
|
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
|
-
|
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)
|
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)
|
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
|
-
|
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
|
338
|
-
return
|
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,
|
456
|
-
result =
|
457
|
-
|
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(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
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[
|
46
|
-
unique = index[
|
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[
|
51
|
+
index['index_keys'].split(",").each do |column|
|
52
52
|
column.strip!
|
53
53
|
|
54
54
|
if column.end_with?("(-)")
|
@@ -484,16 +484,15 @@ module ActiveRecord
|
|
484
484
|
end
|
485
485
|
|
486
486
|
def column_definitions(table_name)
|
487
|
-
identifier
|
488
|
-
database
|
489
|
-
view_exists
|
490
|
-
view_tblnm = view_table_name(table_name) if view_exists
|
487
|
+
identifier = database_prefix_identifier(table_name)
|
488
|
+
database = identifier.fully_qualified_database_quoted
|
489
|
+
view_exists = view_exists?(table_name)
|
491
490
|
|
492
491
|
if view_exists
|
493
492
|
sql = <<~SQL
|
494
493
|
SELECT LOWER(c.COLUMN_NAME) AS [name], c.COLUMN_DEFAULT AS [default]
|
495
494
|
FROM #{database}.INFORMATION_SCHEMA.COLUMNS c
|
496
|
-
WHERE c.TABLE_NAME = #{quote(
|
495
|
+
WHERE c.TABLE_NAME = #{quote(view_table_name(table_name))}
|
497
496
|
SQL
|
498
497
|
results = internal_exec_query(sql, "SCHEMA")
|
499
498
|
default_functions = results.each.with_object({}) { |row, out| out[row["name"]] = row["default"] }.compact
|
@@ -502,71 +501,93 @@ module ActiveRecord
|
|
502
501
|
sql = column_definitions_sql(database, identifier)
|
503
502
|
|
504
503
|
binds = []
|
505
|
-
nv128 = SQLServer::Type::UnicodeVarchar.new
|
504
|
+
nv128 = SQLServer::Type::UnicodeVarchar.new(limit: 128)
|
506
505
|
binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
|
507
506
|
binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
|
507
|
+
|
508
508
|
results = internal_exec_query(sql, "SCHEMA", binds)
|
509
|
+
raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if results.empty?
|
509
510
|
|
510
511
|
columns = results.map do |ci|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
when nil
|
539
|
-
[nil, nil]
|
540
|
-
when /\A\((\w+\(\))\)\Z/
|
541
|
-
default_function = Regexp.last_match[1]
|
542
|
-
[nil, default_function]
|
543
|
-
when /\A\(N'(.*)'\)\Z/m
|
544
|
-
string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1])
|
545
|
-
[string_literal, nil]
|
546
|
-
when /CREATE DEFAULT/mi
|
547
|
-
[nil, nil]
|
548
|
-
else
|
549
|
-
type = case ci[:type]
|
550
|
-
when /smallint|int|bigint/ then ci[:_type]
|
551
|
-
else ci[:type]
|
552
|
-
end
|
553
|
-
value = default.match(/\A\((.*)\)\Z/m)[1]
|
554
|
-
value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
|
555
|
-
[value, nil]
|
556
|
-
end
|
512
|
+
col = {
|
513
|
+
name: ci["name"],
|
514
|
+
numeric_scale: ci["numeric_scale"],
|
515
|
+
numeric_precision: ci["numeric_precision"],
|
516
|
+
datetime_precision: ci["datetime_precision"],
|
517
|
+
collation: ci["collation"],
|
518
|
+
ordinal_position: ci["ordinal_position"],
|
519
|
+
length: ci["length"]
|
520
|
+
}
|
521
|
+
|
522
|
+
col[:table_name] = view_exists ? view_table_name(table_name) : table_name
|
523
|
+
col[:type] = column_type(ci: ci)
|
524
|
+
col[:default_value], col[:default_function] = default_value_and_function(default: ci['default_value'],
|
525
|
+
name: ci['name'],
|
526
|
+
type: col[:type],
|
527
|
+
original_type: ci['type'],
|
528
|
+
view_exists: view_exists,
|
529
|
+
table_name: table_name,
|
530
|
+
default_functions: default_functions)
|
531
|
+
|
532
|
+
col[:null] = ci['is_nullable'].to_i == 1
|
533
|
+
col[:is_primary] = ci['is_primary'].to_i == 1
|
534
|
+
|
535
|
+
if [true, false].include?(ci['is_identity'])
|
536
|
+
col[:is_identity] = ci['is_identity']
|
537
|
+
else
|
538
|
+
col[:is_identity] = ci['is_identity'].to_i == 1
|
557
539
|
end
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
540
|
+
|
541
|
+
col
|
542
|
+
end
|
543
|
+
|
544
|
+
columns
|
545
|
+
end
|
546
|
+
|
547
|
+
def default_value_and_function(default:, name:, type:, original_type:, view_exists:, table_name:, default_functions:)
|
548
|
+
if default.nil? && view_exists
|
549
|
+
view_column = views_real_column_name(table_name, name).downcase
|
550
|
+
default = default_functions[view_column] if view_column.present?
|
563
551
|
end
|
564
552
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
553
|
+
case default
|
554
|
+
when nil
|
555
|
+
[nil, nil]
|
556
|
+
when /\A\((\w+\(\))\)\Z/
|
557
|
+
default_function = Regexp.last_match[1]
|
558
|
+
[nil, default_function]
|
559
|
+
when /\A\(N'(.*)'\)\Z/m
|
560
|
+
string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1])
|
561
|
+
[string_literal, nil]
|
562
|
+
when /CREATE DEFAULT/mi
|
563
|
+
[nil, nil]
|
564
|
+
else
|
565
|
+
type = case type
|
566
|
+
when /smallint|int|bigint/ then original_type
|
567
|
+
else type
|
568
|
+
end
|
569
|
+
value = default.match(/\A\((.*)\)\Z/m)[1]
|
570
|
+
value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
|
571
|
+
[value, nil]
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
def column_type(ci:)
|
576
|
+
case ci['type']
|
577
|
+
when /^bit|image|text|ntext|datetime$/
|
578
|
+
ci['type']
|
579
|
+
when /^datetime2|datetimeoffset$/i
|
580
|
+
"#{ci['type']}(#{ci['datetime_precision']})"
|
581
|
+
when /^time$/i
|
582
|
+
"#{ci['type']}(#{ci['datetime_precision']})"
|
583
|
+
when /^numeric|decimal$/i
|
584
|
+
"#{ci['type']}(#{ci['numeric_precision']},#{ci['numeric_scale']})"
|
585
|
+
when /^float|real$/i
|
586
|
+
"#{ci['type']}"
|
587
|
+
when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/
|
588
|
+
ci['length'].to_i == -1 ? "#{ci['type']}(max)" : "#{ci['type']}(#{ci['length']})"
|
589
|
+
else
|
590
|
+
ci['type']
|
570
591
|
end
|
571
592
|
end
|
572
593
|
|
@@ -706,25 +727,26 @@ module ActiveRecord
|
|
706
727
|
|
707
728
|
def view_table_name(table_name)
|
708
729
|
view_info = view_information(table_name)
|
709
|
-
view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
|
730
|
+
view_info.present? ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
|
710
731
|
end
|
711
732
|
|
712
733
|
def view_information(table_name)
|
713
734
|
@view_information ||= {}
|
735
|
+
|
714
736
|
@view_information[table_name] ||= begin
|
715
737
|
identifier = SQLServer::Utils.extract_identifiers(table_name)
|
716
738
|
information_query_table = identifier.database.present? ? "[#{identifier.database}].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]"
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
if view_info[
|
722
|
-
view_info[
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
739
|
+
|
740
|
+
view_info = select_one("SELECT * FROM #{information_query_table} WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA").to_h
|
741
|
+
|
742
|
+
if view_info.present?
|
743
|
+
if view_info['VIEW_DEFINITION'].blank? || view_info['VIEW_DEFINITION'].length == 4000
|
744
|
+
view_info['VIEW_DEFINITION'] = begin
|
745
|
+
select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
|
746
|
+
rescue
|
747
|
+
warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
|
748
|
+
nil
|
749
|
+
end
|
728
750
|
end
|
729
751
|
end
|
730
752
|
|
@@ -733,8 +755,8 @@ module ActiveRecord
|
|
733
755
|
end
|
734
756
|
|
735
757
|
def views_real_column_name(table_name, column_name)
|
736
|
-
view_definition = view_information(table_name)[
|
737
|
-
return column_name
|
758
|
+
view_definition = view_information(table_name)['VIEW_DEFINITION']
|
759
|
+
return column_name if view_definition.blank?
|
738
760
|
|
739
761
|
# Remove "CREATE VIEW ... AS SELECT ..." and then match the column name.
|
740
762
|
match_data = view_definition.sub(/CREATE\s+VIEW.*AS\s+SELECT\s/, '').match(/([\w-]*)\s+AS\s+#{column_name}\W/im)
|
@@ -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
|
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(
|
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
|
-
|
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
|
467
|
+
# That have more than 4000 chars for their definition
|
468
468
|
|
469
|
-
it "cope with null returned for the
|
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
|
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
|
data/test/cases/coerced_tests.rb
CHANGED
@@ -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.
|
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.
|
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.
|
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.
|
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.
|
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:
|
4
|
+
version: 8.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ken Collins
|
@@ -23,14 +23,14 @@ dependencies:
|
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
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:
|
33
|
+
version: 8.0.0
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: tiny_tds
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,8 +55,8 @@ extra_rdoc_files: []
|
|
55
55
|
files:
|
56
56
|
- ".devcontainer/Dockerfile"
|
57
57
|
- ".devcontainer/boot.sh"
|
58
|
+
- ".devcontainer/compose.yaml"
|
58
59
|
- ".devcontainer/devcontainer.json"
|
59
|
-
- ".devcontainer/docker-compose.yml"
|
60
60
|
- ".editorconfig"
|
61
61
|
- ".github/issue_template.md"
|
62
62
|
- ".github/workflows/ci.yml"
|
@@ -74,12 +74,11 @@ files:
|
|
74
74
|
- VERSION
|
75
75
|
- activerecord-sqlserver-adapter.gemspec
|
76
76
|
- appveyor.yml
|
77
|
-
-
|
77
|
+
- compose.ci.yaml
|
78
78
|
- guides/RELEASING.md
|
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/
|
243
|
-
source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/
|
241
|
+
changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v8.0.1/CHANGELOG.md
|
242
|
+
source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v8.0.1
|
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.
|
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
|
File without changes
|