activerecord-sqlserver-adapter 7.2.5 → 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 +4 -4
- data/.github/workflows/ci.yml +9 -4
- data/CHANGELOG.md +6 -40
- 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 +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 +128 -120
- 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/utils.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +11 -21
- data/test/cases/adapter_test_sqlserver.rb +26 -13
- data/test/cases/coerced_tests.rb +56 -35
- data/test/cases/optimizer_hints_test_sqlserver.rb +0 -1
- data/test/cases/schema_test_sqlserver.rb +0 -6
- metadata +7 -10
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +0 -29
- data/test/cases/temporary_table_test_sqlserver.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 926f65915d98bb3c271370883fa89891fc575b5c6801cd8c235386c078d34bf5
|
4
|
+
data.tar.gz: 0a932a9d5edf0d3932e3f0ba44328ed77a033a1223f05699e8f4caa79116ac56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee45540a8d2ae57dc69a35048f4ccfbf01bcfc91e177e57405b19ccb8a3279eef7b752c22dee20c4171c8bb4e2cb0fcaadb99cd6c25d139fd2ef53577a5cb23a
|
7
|
+
data.tar.gz: 9184a7041e5de445103bcd47832352614897865f0ef49e83067b553786d089be73eb73c34dfaf3f102e02804ee7d8d40803d5582430ebfeecbe95f568b0b7cb7
|
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:
|
@@ -14,9 +20,8 @@ jobs:
|
|
14
20
|
fail-fast: false
|
15
21
|
matrix:
|
16
22
|
ruby:
|
17
|
-
- 3.
|
18
|
-
- 3.2.
|
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,46 +1,12 @@
|
|
1
|
-
##
|
1
|
+
## v8.0.0
|
2
2
|
|
3
|
-
####
|
4
|
-
|
5
|
-
- [#1308](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1308) Fix retrieval of temporary table's column information.
|
6
|
-
|
7
|
-
## v7.2.4
|
8
|
-
|
9
|
-
#### Fixed
|
10
|
-
|
11
|
-
- [#1270](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1270) Fix parsing of raw table name from SQL with extra parentheses
|
12
|
-
|
13
|
-
## v7.2.3
|
14
|
-
|
15
|
-
#### Fixed
|
16
|
-
|
17
|
-
- [#1262](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1262) Fix distinct alias when multiple databases used.
|
18
|
-
|
19
|
-
## v7.2.2
|
20
|
-
|
21
|
-
#### Fixed
|
22
|
-
|
23
|
-
- [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation
|
24
|
-
- [#1247](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1247) Fix queries with date and date-time placeholder conditions
|
25
|
-
- [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable
|
26
|
-
- [#1255](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1255) Fixed the ordering of optimizer hints in the generated SQL
|
3
|
+
#### Changed
|
27
4
|
|
28
|
-
|
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
|
29
7
|
|
30
8
|
#### Fixed
|
31
9
|
|
32
|
-
- [#
|
33
|
-
|
34
|
-
## v7.2.0
|
35
|
-
|
36
|
-
#### Added
|
37
|
-
|
38
|
-
- [#1178](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1178) Support encrypting binary columns
|
39
|
-
|
40
|
-
#### Changed
|
41
|
-
|
42
|
-
- [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+
|
43
|
-
- [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter
|
44
|
-
|
10
|
+
- [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors
|
45
11
|
|
46
|
-
Please check [7-
|
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
|
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
|
* [](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml) - CI
|
4
4
|
* [](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.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
|
-
|
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.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.
|
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
|
data/docker-compose.ci.yml
CHANGED
@@ -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?("(-)")
|
@@ -347,16 +347,12 @@ module ActiveRecord
|
|
347
347
|
|
348
348
|
def columns_for_distinct(columns, orders)
|
349
349
|
order_columns = orders.reject(&:blank?).map { |s|
|
350
|
-
s =
|
350
|
+
s = s.to_sql unless s.is_a?(String)
|
351
351
|
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
352
352
|
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
|
353
|
-
|
354
|
-
.reject(&:blank?)
|
355
|
-
.reject { |s| columns.include?(s) }
|
353
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
356
354
|
|
357
|
-
|
358
|
-
|
359
|
-
(order_columns_aliased << super).join(", ")
|
355
|
+
(order_columns << super).join(", ")
|
360
356
|
end
|
361
357
|
|
362
358
|
def update_table_definition(table_name, base)
|
@@ -484,16 +480,15 @@ module ActiveRecord
|
|
484
480
|
end
|
485
481
|
|
486
482
|
def column_definitions(table_name)
|
487
|
-
identifier
|
488
|
-
database
|
489
|
-
view_exists
|
490
|
-
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)
|
491
486
|
|
492
487
|
if view_exists
|
493
488
|
sql = <<~SQL
|
494
489
|
SELECT LOWER(c.COLUMN_NAME) AS [name], c.COLUMN_DEFAULT AS [default]
|
495
490
|
FROM #{database}.INFORMATION_SCHEMA.COLUMNS c
|
496
|
-
WHERE c.TABLE_NAME = #{quote(
|
491
|
+
WHERE c.TABLE_NAME = #{quote(view_table_name(table_name))}
|
497
492
|
SQL
|
498
493
|
results = internal_exec_query(sql, "SCHEMA")
|
499
494
|
default_functions = results.each.with_object({}) { |row, out| out[row["name"]] = row["default"] }.compact
|
@@ -502,91 +497,103 @@ module ActiveRecord
|
|
502
497
|
sql = column_definitions_sql(database, identifier)
|
503
498
|
|
504
499
|
binds = []
|
505
|
-
nv128 = SQLServer::Type::UnicodeVarchar.new
|
500
|
+
nv128 = SQLServer::Type::UnicodeVarchar.new(limit: 128)
|
506
501
|
binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
|
507
502
|
binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
|
503
|
+
|
508
504
|
results = internal_exec_query(sql, "SCHEMA", binds)
|
505
|
+
raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if results.empty?
|
509
506
|
|
510
507
|
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
|
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
|
557
535
|
end
|
558
|
-
ci[:null] = ci[:is_nullable].to_i == 1
|
559
|
-
ci.delete(:is_nullable)
|
560
|
-
ci[:is_primary] = ci[:is_primary].to_i == 1
|
561
|
-
ci[:is_identity] = ci[:is_identity].to_i == 1 unless [TrueClass, FalseClass].include?(ci[:is_identity].class)
|
562
|
-
ci
|
563
|
-
end
|
564
536
|
|
565
|
-
|
566
|
-
# I'm not aware of the possibility of tables without columns on SQL Server (postgres have those).
|
567
|
-
# Raise error if the method return an empty array
|
568
|
-
columns.tap do |result|
|
569
|
-
raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if result.empty?
|
537
|
+
col
|
570
538
|
end
|
539
|
+
|
540
|
+
columns
|
571
541
|
end
|
572
542
|
|
573
|
-
def
|
574
|
-
|
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?
|
547
|
+
end
|
575
548
|
|
576
|
-
|
577
|
-
|
578
|
-
|
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]
|
579
560
|
else
|
580
|
-
|
581
|
-
|
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]
|
582
568
|
end
|
569
|
+
end
|
583
570
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
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']
|
589
587
|
end
|
588
|
+
end
|
589
|
+
|
590
|
+
def column_definitions_sql(database, identifier)
|
591
|
+
object_name = prepared_statements ? "@0" : quote(identifier.object)
|
592
|
+
schema_name = if identifier.schema.blank?
|
593
|
+
"schema_name()"
|
594
|
+
else
|
595
|
+
prepared_statements ? "@1" : quote(identifier.schema)
|
596
|
+
end
|
590
597
|
|
591
598
|
%{
|
592
599
|
SELECT
|
@@ -641,7 +648,7 @@ module ActiveRecord
|
|
641
648
|
AND k.unique_index_id = ic.index_id
|
642
649
|
AND c.column_id = ic.column_id
|
643
650
|
WHERE
|
644
|
-
o.
|
651
|
+
o.name = #{object_name}
|
645
652
|
AND s.name = #{schema_name}
|
646
653
|
ORDER BY
|
647
654
|
c.column_id
|
@@ -663,7 +670,7 @@ module ActiveRecord
|
|
663
670
|
end
|
664
671
|
|
665
672
|
def remove_default_constraint(table_name, column_name)
|
666
|
-
# If
|
673
|
+
# If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case.
|
667
674
|
execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row|
|
668
675
|
row["constraint_type"] == "DEFAULT on column #{column_name}"
|
669
676
|
end.each do |row|
|
@@ -700,7 +707,7 @@ module ActiveRecord
|
|
700
707
|
elsif s.match?(/^\s*UPDATE\s+.*/i)
|
701
708
|
s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1]
|
702
709
|
else
|
703
|
-
s.match(/FROM
|
710
|
+
s.match(/FROM\s+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1]
|
704
711
|
end.strip
|
705
712
|
end
|
706
713
|
|
@@ -716,25 +723,26 @@ module ActiveRecord
|
|
716
723
|
|
717
724
|
def view_table_name(table_name)
|
718
725
|
view_info = view_information(table_name)
|
719
|
-
view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
|
726
|
+
view_info.present? ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
|
720
727
|
end
|
721
728
|
|
722
729
|
def view_information(table_name)
|
723
730
|
@view_information ||= {}
|
731
|
+
|
724
732
|
@view_information[table_name] ||= begin
|
725
733
|
identifier = SQLServer::Utils.extract_identifiers(table_name)
|
726
734
|
information_query_table = identifier.database.present? ? "[#{identifier.database}].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]"
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
if view_info[
|
732
|
-
view_info[
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
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
|
738
746
|
end
|
739
747
|
end
|
740
748
|
|
@@ -743,8 +751,8 @@ module ActiveRecord
|
|
743
751
|
end
|
744
752
|
|
745
753
|
def views_real_column_name(table_name, column_name)
|
746
|
-
view_definition = view_information(table_name)[
|
747
|
-
return column_name
|
754
|
+
view_definition = view_information(table_name)['VIEW_DEFINITION']
|
755
|
+
return column_name if view_definition.blank?
|
748
756
|
|
749
757
|
# Remove "CREATE VIEW ... AS SELECT ..." and then match the column name.
|
750
758
|
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
|
@@ -594,13 +616,4 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
594
616
|
assert_equal Task.where("starting < ?", DateTime.current).count, 1
|
595
617
|
end
|
596
618
|
end
|
597
|
-
|
598
|
-
describe "distinct select query" do
|
599
|
-
it "generated SQL does not contain unnecessary alias projection" do
|
600
|
-
sqls = capture_sql do
|
601
|
-
Post.includes(:comments).joins(:comments).first
|
602
|
-
end
|
603
|
-
assert_no_match(/AS alias_0/, sqls.first)
|
604
|
-
end
|
605
|
-
end
|
606
619
|
end
|
data/test/cases/coerced_tests.rb
CHANGED
@@ -376,18 +376,6 @@ module ActiveRecord
|
|
376
376
|
end
|
377
377
|
|
378
378
|
class CalculationsTest < ActiveRecord::TestCase
|
379
|
-
# SELECT columns must be in the GROUP clause.
|
380
|
-
coerce_tests! :test_should_count_with_group_by_qualified_name_on_loaded
|
381
|
-
def test_should_count_with_group_by_qualified_name_on_loaded_coerced
|
382
|
-
accounts = Account.group("accounts.id").select("accounts.id")
|
383
|
-
expected = { 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1 }
|
384
|
-
assert_not_predicate accounts, :loaded?
|
385
|
-
assert_equal expected, accounts.count
|
386
|
-
accounts.load
|
387
|
-
assert_predicate accounts, :loaded?
|
388
|
-
assert_equal expected, accounts.count(:id)
|
389
|
-
end
|
390
|
-
|
391
379
|
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
392
380
|
coerce_tests! :test_offset_is_kept
|
393
381
|
def test_offset_is_kept_coerced
|
@@ -532,6 +520,9 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
532
520
|
|
533
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.
|
534
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
|
535
526
|
end
|
536
527
|
|
537
528
|
module ActiveRecord
|
@@ -1349,6 +1340,20 @@ module ActiveRecord
|
|
1349
1340
|
def test_registering_new_handlers_for_association_coerced
|
1350
1341
|
assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: { title: /rails/ }).to_sql
|
1351
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
|
1352
1357
|
end
|
1353
1358
|
end
|
1354
1359
|
|
@@ -2180,17 +2185,6 @@ class EnumTest < ActiveRecord::TestCase
|
|
2180
2185
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
2181
2186
|
end
|
2182
2187
|
|
2183
|
-
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
2184
|
-
coerce_tests! %r{declare multiple enums at a time}
|
2185
|
-
test "declare multiple enums at a time coerced" do
|
2186
|
-
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
2187
|
-
|
2188
|
-
send(:'original_declare multiple enums at a time')
|
2189
|
-
ensure
|
2190
|
-
Book.where(author_id: nil, name: nil).delete_all
|
2191
|
-
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
2192
|
-
end
|
2193
|
-
|
2194
2188
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
2195
2189
|
coerce_tests! %r{serializable\? with large number label}
|
2196
2190
|
test "serializable? with large number label coerced" do
|
@@ -2262,14 +2256,6 @@ class LogSubscriberTest < ActiveRecord::TestCase
|
|
2262
2256
|
def test_verbose_query_logs_coerced
|
2263
2257
|
original_test_verbose_query_logs
|
2264
2258
|
end
|
2265
|
-
|
2266
|
-
# Bindings logged slightly differently.
|
2267
|
-
coerce_tests! :test_where_in_binds_logging_include_attribute_names
|
2268
|
-
def test_where_in_binds_logging_include_attribute_names_coerced
|
2269
|
-
Developer.where(id: [1, 2, 3, 4, 5]).load
|
2270
|
-
wait
|
2271
|
-
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)
|
2272
|
-
end
|
2273
2259
|
end
|
2274
2260
|
|
2275
2261
|
class ReloadModelsTest < ActiveRecord::TestCase
|
@@ -2431,7 +2417,9 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
2431
2417
|
# SQL requires double single-quotes.
|
2432
2418
|
coerce_tests! :test_sql_commenter_format
|
2433
2419
|
def test_sql_commenter_format_coerced
|
2434
|
-
ActiveRecord::QueryLogs.
|
2420
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
2421
|
+
ActiveRecord::QueryLogs.tags = [:application]
|
2422
|
+
|
2435
2423
|
assert_queries_match(%r{/\*application=''active_record''\*/}) do
|
2436
2424
|
Dashboard.first
|
2437
2425
|
end
|
@@ -2440,7 +2428,7 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
2440
2428
|
# SQL requires double single-quotes.
|
2441
2429
|
coerce_tests! :test_sqlcommenter_format_value
|
2442
2430
|
def test_sqlcommenter_format_value_coerced
|
2443
|
-
ActiveRecord::QueryLogs.
|
2431
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
2444
2432
|
|
2445
2433
|
ActiveRecord::QueryLogs.tags = [
|
2446
2434
|
:application,
|
@@ -2455,7 +2443,7 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
2455
2443
|
# SQL requires double single-quotes.
|
2456
2444
|
coerce_tests! :test_sqlcommenter_format_value_string_coercible
|
2457
2445
|
def test_sqlcommenter_format_value_string_coercible_coerced
|
2458
|
-
ActiveRecord::QueryLogs.
|
2446
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
2459
2447
|
|
2460
2448
|
ActiveRecord::QueryLogs.tags = [
|
2461
2449
|
:application,
|
@@ -2470,7 +2458,7 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
2470
2458
|
# SQL requires double single-quotes.
|
2471
2459
|
coerce_tests! :test_sqlcommenter_format_allows_string_keys
|
2472
2460
|
def test_sqlcommenter_format_allows_string_keys_coerced
|
2473
|
-
ActiveRecord::QueryLogs.
|
2461
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
2474
2462
|
|
2475
2463
|
ActiveRecord::QueryLogs.tags = [
|
2476
2464
|
:application,
|
@@ -2505,6 +2493,17 @@ class InsertAllTest < ActiveRecord::TestCase
|
|
2505
2493
|
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
2506
2494
|
assert_equal %w[ REWORK ], result.pluck("name")
|
2507
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
|
2508
2507
|
end
|
2509
2508
|
|
2510
2509
|
module ActiveRecord
|
@@ -2667,6 +2666,28 @@ module ActiveRecord
|
|
2667
2666
|
end
|
2668
2667
|
end
|
2669
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
|
+
|
2670
2691
|
module ActiveRecord
|
2671
2692
|
class TableMetadataTest < ActiveSupport::TestCase
|
2672
2693
|
# Adapter returns an object that is subclass of what is expected in the original test.
|
@@ -101,11 +101,5 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
|
|
101
101
|
assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "INSERT INTO [with].[select notation] SELECT * FROM [table_name]")
|
102
102
|
end
|
103
103
|
end
|
104
|
-
|
105
|
-
describe 'CREATE VIEW statements' do
|
106
|
-
it do
|
107
|
-
assert_equal "test_table_as", connection.send(:get_raw_table_name, "CREATE VIEW test_views ( test_table_a_id, test_table_b_id ) AS SELECT test_table_as.id as test_table_a_id, test_table_bs.id as test_table_b_id FROM (test_table_as with(nolock) LEFT JOIN test_table_bs with(nolock) ON (test_table_as.id = test_table_bs.test_table_a_id))")
|
108
|
-
end
|
109
|
-
end
|
110
104
|
end
|
111
105
|
end
|
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.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:
|
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:
|
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
|
@@ -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
|
@@ -177,7 +176,6 @@ files:
|
|
177
176
|
- test/cases/schema_test_sqlserver.rb
|
178
177
|
- test/cases/showplan_test_sqlserver.rb
|
179
178
|
- test/cases/specific_schema_test_sqlserver.rb
|
180
|
-
- test/cases/temporary_table_test_sqlserver.rb
|
181
179
|
- test/cases/transaction_test_sqlserver.rb
|
182
180
|
- test/cases/trigger_test_sqlserver.rb
|
183
181
|
- test/cases/utils_test_sqlserver.rb
|
@@ -240,8 +238,8 @@ licenses:
|
|
240
238
|
- MIT
|
241
239
|
metadata:
|
242
240
|
bug_tracker_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues
|
243
|
-
changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/
|
244
|
-
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.0/CHANGELOG.md
|
242
|
+
source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v8.0.0
|
245
243
|
post_install_message:
|
246
244
|
rdoc_options: []
|
247
245
|
require_paths:
|
@@ -250,7 +248,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
250
248
|
requirements:
|
251
249
|
- - ">="
|
252
250
|
- !ruby/object:Gem::Version
|
253
|
-
version: 3.
|
251
|
+
version: 3.2.0
|
254
252
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
255
253
|
requirements:
|
256
254
|
- - ">="
|
@@ -296,7 +294,6 @@ test_files:
|
|
296
294
|
- test/cases/schema_test_sqlserver.rb
|
297
295
|
- test/cases/showplan_test_sqlserver.rb
|
298
296
|
- test/cases/specific_schema_test_sqlserver.rb
|
299
|
-
- test/cases/temporary_table_test_sqlserver.rb
|
300
297
|
- test/cases/transaction_test_sqlserver.rb
|
301
298
|
- test/cases/trigger_test_sqlserver.rb
|
302
299
|
- test/cases/utils_test_sqlserver.rb
|
@@ -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
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "cases/helper_sqlserver"
|
4
|
-
|
5
|
-
class TemporaryTableSQLServer < ActiveRecord::TestCase
|
6
|
-
def test_insert_into_temporary_table
|
7
|
-
ActiveRecord::Base.with_connection do |conn|
|
8
|
-
conn.exec_query("CREATE TABLE #temp_users (id INT IDENTITY(1,1), name NVARCHAR(100))")
|
9
|
-
|
10
|
-
result = conn.exec_query("SELECT * FROM #temp_users")
|
11
|
-
assert_equal 0, result.count
|
12
|
-
|
13
|
-
conn.exec_query("INSERT INTO #temp_users (name) VALUES ('John'), ('Doe')")
|
14
|
-
|
15
|
-
result = conn.exec_query("SELECT * FROM #temp_users")
|
16
|
-
assert_equal 2, result.count
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|