activerecord-sqlserver-adapter 5.0.8 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/BACKERS.md +32 -0
- data/CHANGELOG.md +3 -111
- data/RAILS5-TODO.md +21 -1
- data/README.md +7 -3
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +10 -14
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +69 -51
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +15 -7
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +29 -18
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +2 -2
- data/lib/arel/visitors/sqlserver.rb +2 -1
- data/test/cases/adapter_test_sqlserver.rb +8 -12
- data/test/cases/coerced_tests.rb +86 -11
- data/test/cases/column_test_sqlserver.rb +1 -1
- data/test/cases/helper_sqlserver.rb +5 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +0 -11
- data/test/cases/schema_dumper_test_sqlserver.rb +10 -10
- data/test/schema/sqlserver_specific_schema.rb +1 -1
- data/test/support/sql_counter_sqlserver.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13700d5c59ccddfde7189f1bf0dfac4bbd276777
|
4
|
+
data.tar.gz: e9cafb310dfe9904c39dbf77ccc5bb86687f231a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2cbb63f122e3ad2657532e7649c33728a0790f945043a12afdb078d171122970636a81dfa621485aa6a8abe7d30b9ab3892d2fef76fefd0a7ed6019fbda543dd
|
7
|
+
data.tar.gz: 220bc1f5a936151978885af18c38f7a25d7e684f0d7199c64d21d30922c4b33b8312e2a0db924f59d3040b0b147bbc6fa4dc564b5e1fbe6bb190daa522b322b4
|
data/BACKERS.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Backers
|
2
|
+
|
3
|
+
You can join in supporting TinyTDS and the Rails SQL Server Adapter development by [pledging on Patreon](https://www.patreon.com/metaskills)! Backers in the same pledge level appear in the order of pledge date.
|
4
|
+
|
5
|
+
### $2000
|
6
|
+
|
7
|
+
[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611218)
|
8
|
+
|
9
|
+
|
10
|
+
### $500
|
11
|
+
|
12
|
+
[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611209)
|
13
|
+
|
14
|
+
|
15
|
+
### $250
|
16
|
+
|
17
|
+
[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611199)
|
18
|
+
|
19
|
+
|
20
|
+
### $100
|
21
|
+
|
22
|
+
[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611196)
|
23
|
+
|
24
|
+
|
25
|
+
### $50+
|
26
|
+
|
27
|
+
[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611186)
|
28
|
+
|
29
|
+
|
30
|
+
### $10+
|
31
|
+
|
32
|
+
[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611149)
|
data/CHANGELOG.md
CHANGED
@@ -1,120 +1,12 @@
|
|
1
|
-
## v5.0
|
2
|
-
|
3
|
-
* Memorized SQL Server Version to prevent a flood of SELECT @@version queries. Fixes #632
|
4
|
-
|
5
|
-
## v5.0.7
|
6
|
-
|
7
|
-
* Add `WITH NO_INFOMSGS` to `user_options` method. Fixes #580
|
8
|
-
|
9
|
-
|
10
|
-
## v5.0.6
|
11
|
-
|
12
|
-
#### Fixed
|
13
|
-
|
14
|
-
* Performance w/inserts. Check binds & use schema cache for id inserts.
|
15
|
-
Fixes #572. Thanks @noelr.
|
16
|
-
* Add smalldatetime type for migrations. Fixes #507
|
17
|
-
|
18
|
-
#### Changed
|
19
|
-
|
20
|
-
* Misc index enhancements or testing. Fixes #570
|
21
|
-
Enable `supports_index_sort_order?`, test `supports_partial_index?`, test how expression indexes work.
|
22
|
-
|
23
|
-
#### Added
|
24
|
-
|
25
|
-
* New `primary_key_nonclustered` type for easy In-Memory table creation.
|
26
|
-
* Examples for an In-Memory table.
|
27
|
-
|
28
|
-
```ruby
|
29
|
-
create_table :in_memory_table, id: false,
|
30
|
-
options: 'WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)' do |t|
|
31
|
-
t.primary_key_nonclustered :id
|
32
|
-
t.string :name
|
33
|
-
t.timestamps
|
34
|
-
end
|
35
|
-
```
|
36
|
-
|
37
|
-
* Enable supports_json? Fixes #577.
|
38
|
-
|
39
|
-
```ruby
|
40
|
-
create_table :users do |t|
|
41
|
-
t.string :name, :email
|
42
|
-
t.json :data # Creates a nvarchar(max) column.
|
43
|
-
end
|
44
|
-
|
45
|
-
class Users < ActiveRecord::Base
|
46
|
-
attribute :data, ActiveRecord::Type::SQLServer::Json.new
|
47
|
-
end
|
48
|
-
|
49
|
-
User.create! name: 'Ken Collins', data: { 'admin' => true, 'foo' => 'bar' }
|
50
|
-
|
51
|
-
admin = User.where("JSON_VALUE(data, '$.admin') = CAST(1 AS BIT)").first
|
52
|
-
admin.data['foo'] # => "bar"
|
53
|
-
```
|
54
|
-
|
55
|
-
|
56
|
-
## v5.0.5
|
57
|
-
|
58
|
-
#### Changed
|
59
|
-
|
60
|
-
* Add TinyTDS as a runtime dependency.
|
61
|
-
|
62
|
-
|
63
|
-
## v5.0.4
|
1
|
+
## v5.1.0
|
64
2
|
|
65
3
|
#### Fixed
|
66
4
|
|
67
|
-
* Allow `datetimeoffset` to be used in migrations and represented in schema.
|
68
|
-
* Using transactions and resetting isolation level correctly when `READ_COMMITTED_SNAPSHOT` is set to `ON` Fixes #520
|
69
|
-
|
70
|
-
|
71
|
-
## v5.0.3
|
72
|
-
|
73
|
-
#### Changed
|
74
|
-
|
75
|
-
* Reduce view information reflection to per table vs. column. Fixes #552
|
76
|
-
* The `user_options` parsing. Works for hash/array. Fixes #535
|
77
|
-
* Pass the `:contained` option to TinyTDS. Fixes #527
|
78
|
-
|
79
|
-
|
80
|
-
## v5.0.2
|
81
|
-
|
82
|
-
#### Fixed
|
83
|
-
|
84
|
-
* Filter table constraints with matching table schema to column. Fixes #478
|
85
|
-
|
86
|
-
|
87
|
-
## v5.0.1
|
88
|
-
|
89
5
|
#### Changed
|
90
6
|
|
91
|
-
*
|
92
|
-
|
93
|
-
#### Fixed
|
94
|
-
|
95
|
-
* Support 2014, 2012 drop table statement.
|
96
|
-
|
97
|
-
|
98
|
-
## v5.0.0
|
7
|
+
* The `drop_table` with force cascade option now mimics in via pure SQL for us.
|
99
8
|
|
100
9
|
#### Added
|
101
10
|
|
102
|
-
* Support
|
103
|
-
* Support for `unprepared_statement` blocks on the connection.
|
104
|
-
|
105
|
-
#### Changed
|
106
|
-
|
107
|
-
* Major refactoring of all type objects. Especially time types.
|
108
|
-
|
109
|
-
#### Deprecated
|
110
|
-
|
111
|
-
* Support for a handful of standard Rails deprecations in 5-0-stable suite.
|
112
|
-
|
113
|
-
#### Removed
|
114
|
-
|
115
|
-
* ODBC connection mode. Not been maintained since Rails 4.0.
|
116
|
-
* View table name detection in `with_identity_insert_enabled` method for fixtures. Perf hit.
|
117
|
-
|
118
|
-
#### Fixed
|
11
|
+
* Support MismatchedForeignKey exception.
|
119
12
|
|
120
|
-
* Do not output column collation in schema when same as database.
|
data/RAILS5-TODO.md
CHANGED
@@ -2,4 +2,24 @@
|
|
2
2
|
## Rails v5.1
|
3
3
|
|
4
4
|
* BIGINT PK support. https://github.com/rails/rails/pull/26266
|
5
|
-
|
5
|
+
* Raise `ActiveRecord::NotNullViolation` when a record cannot be inserted
|
6
|
+
or updated because it would violate a not null constraint.
|
7
|
+
* Raise `ActiveRecord::RangeError` when values that executed are out of range.
|
8
|
+
* Allow passing extra flags to `db:structure:load` and `db:structure:dump`
|
9
|
+
Introduces `ActiveRecord::Tasks::DatabaseTasks.structure_(load|dump)_flags` to customize the
|
10
|
+
eventual commands run against the database, e.g. mysqldump/pg_dump.
|
11
|
+
* Set `:time` as a timezone aware type and remove deprecation when
|
12
|
+
`config.active_record.time_zone_aware_types` is not explicitly set.
|
13
|
+
* Remove deprecated support to passing a column to `#quote`.
|
14
|
+
* `#tables` and `#table_exists?` return only tables and not views.
|
15
|
+
All the deprecations on those methods were removed.
|
16
|
+
* Remove deprecated `original_exception` argument in `ActiveRecord::StatementInvalid#initialize`
|
17
|
+
and `ActiveRecord::StatementInvalid#original_exception`.
|
18
|
+
* Remove deprecated tasks: `db:test:clone`, `db:test:clone_schema`, `db:test:clone_structure`.
|
19
|
+
* Make `table_name=` reset current statement cache,
|
20
|
+
so queries are not run against the previous table name.
|
21
|
+
* Deprecate using `#quoted_id` in quoting.
|
22
|
+
* Deprecate `supports_migrations?` on connection adapters.
|
23
|
+
* Dig moving `Column#sqlserver_options` to `sql_type_metadata` delegate.
|
24
|
+
* Should we do like PG and add `options[:collation]` before `#add_column_options!`?
|
25
|
+
* Translated exceptions: `SerializationFailure` and `RangeError`.
|
data/README.md
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
[![Support via Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.3.0/dist/gratipay.svg)](https://gratipay.com/metaskills/)
|
2
|
-
|
3
1
|
# ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher.
|
4
2
|
|
5
3
|
* [![TravisCI](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter.svg?branch=master)](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter) - TravisCI
|
@@ -9,6 +7,12 @@
|
|
9
7
|
* [![Dependency Status](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter/badge)](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter) - Dependency Status
|
10
8
|
* [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community
|
11
9
|
|
10
|
+
## Supporting TinyTDS/Adapter
|
11
|
+
|
12
|
+
Both TinyTDS and the Rails SQL Server Adapter are MIT-licensed open source projects. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/rails-sqlserver/tiny_tds/blob/master/BACKERS.md). If you'd like to join them, check out our [Patreon Campaign](https://www.patreon.com/metaskills).
|
13
|
+
|
14
|
+
|
15
|
+
## About The Adapter
|
12
16
|
|
13
17
|
The SQL Server adapter for ActiveRecord v5.0 using SQL Server 2012 or higher.
|
14
18
|
|
@@ -129,7 +133,7 @@ If you would like to contribute a feature or bugfix, thanks! To make sure your f
|
|
129
133
|
Many many people have contributed. If you do not see your name here and it should be let us know. Also, many thanks go out to those that have pledged financial contributions.
|
130
134
|
|
131
135
|
|
132
|
-
##
|
136
|
+
## Contributors
|
133
137
|
|
134
138
|
Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors
|
135
139
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
5.0
|
1
|
+
5.1.0
|
@@ -16,6 +16,6 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ['lib']
|
19
|
-
spec.add_dependency 'activerecord', '~> 5.
|
19
|
+
spec.add_dependency 'activerecord', '~> 5.1.0'
|
20
20
|
spec.add_dependency 'tiny_tds'
|
21
21
|
end
|
@@ -3,10 +3,6 @@ module ActiveRecord
|
|
3
3
|
module SQLServer
|
4
4
|
module DatabaseStatements
|
5
5
|
|
6
|
-
def select_rows(sql, name = nil, binds = [])
|
7
|
-
sp_executesql sql, name, binds, fetch: :rows
|
8
|
-
end
|
9
|
-
|
10
6
|
def execute(sql, name = nil)
|
11
7
|
if id_insert_table_name = query_requires_identity_insert?(sql)
|
12
8
|
with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) }
|
@@ -19,22 +15,22 @@ module ActiveRecord
|
|
19
15
|
sp_executesql(sql, name, binds, prepare: prepare)
|
20
16
|
end
|
21
17
|
|
22
|
-
def exec_insert(sql, name, binds, pk = nil, _sequence_name = nil)
|
18
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil)
|
23
19
|
if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds)
|
24
|
-
with_identity_insert_enabled(id_insert_table_name) {
|
20
|
+
with_identity_insert_enabled(id_insert_table_name) { super(sql, name, binds, pk) }
|
25
21
|
else
|
26
|
-
|
22
|
+
super(sql, name, binds, pk)
|
27
23
|
end
|
28
24
|
end
|
29
25
|
|
30
26
|
def exec_delete(sql, name, binds)
|
31
|
-
sql << '; SELECT @@ROWCOUNT AS AffectedRows'
|
32
|
-
super.rows.first.first
|
27
|
+
sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows'
|
28
|
+
super(sql, name, binds).rows.first.first
|
33
29
|
end
|
34
30
|
|
35
31
|
def exec_update(sql, name, binds)
|
36
|
-
sql << '; SELECT @@ROWCOUNT AS AffectedRows'
|
37
|
-
super.rows.first.first
|
32
|
+
sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows'
|
33
|
+
super(sql, name, binds).rows.first.first
|
38
34
|
end
|
39
35
|
|
40
36
|
def supports_statement_cache?
|
@@ -80,7 +76,7 @@ module ActiveRecord
|
|
80
76
|
end
|
81
77
|
|
82
78
|
def case_sensitive_comparison(table, attribute, column, value)
|
83
|
-
if
|
79
|
+
if column.collation && !column.case_sensitive?
|
84
80
|
table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
|
85
81
|
else
|
86
82
|
super
|
@@ -198,7 +194,7 @@ module ActiveRecord
|
|
198
194
|
end
|
199
195
|
sql = if pk && self.class.use_output_inserted && !database_prefix_remote_server?
|
200
196
|
quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
|
201
|
-
sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
|
197
|
+
sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
|
202
198
|
else
|
203
199
|
"#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
204
200
|
end
|
@@ -261,7 +257,7 @@ module ActiveRecord
|
|
261
257
|
if name == 'EXPLAIN'
|
262
258
|
params.each.with_index do |param, index|
|
263
259
|
substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values.
|
264
|
-
sql.sub
|
260
|
+
sql = sql.sub substitute_at_finder, param.to_s
|
265
261
|
end
|
266
262
|
else
|
267
263
|
types = quote(types.join(', '))
|
@@ -16,6 +16,20 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
def add_column_options!(sql, options)
|
20
|
+
sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
|
21
|
+
if options[:null] == false
|
22
|
+
sql << " NOT NULL"
|
23
|
+
end
|
24
|
+
if options[:is_identity] == true
|
25
|
+
sql << " IDENTITY(1,1)"
|
26
|
+
end
|
27
|
+
if options[:primary_key] == true
|
28
|
+
sql << " PRIMARY KEY"
|
29
|
+
end
|
30
|
+
sql
|
31
|
+
end
|
32
|
+
|
19
33
|
def action_sql(action, dependency)
|
20
34
|
case dependency
|
21
35
|
when :restrict
|
@@ -28,6 +42,14 @@ module ActiveRecord
|
|
28
42
|
end
|
29
43
|
end
|
30
44
|
|
45
|
+
def options_include_default?(options)
|
46
|
+
super || options_primary_key_with_nil_default?(options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def options_primary_key_with_nil_default?(options)
|
50
|
+
options[:primary_key] && options.include?(:default) && options[:default].nil?
|
51
|
+
end
|
52
|
+
|
31
53
|
end
|
32
54
|
end
|
33
55
|
end
|
@@ -3,13 +3,34 @@ module ActiveRecord
|
|
3
3
|
module SQLServer
|
4
4
|
module SchemaDumper
|
5
5
|
|
6
|
+
SQLSEVER_NO_LIMIT_TYPES = [
|
7
|
+
'text',
|
8
|
+
'ntext',
|
9
|
+
'varchar(max)',
|
10
|
+
'nvarchar(max)',
|
11
|
+
'varbinary(max)'
|
12
|
+
].freeze
|
13
|
+
|
6
14
|
private
|
7
15
|
|
16
|
+
def explicit_primary_key_default?(column)
|
17
|
+
column.is_primary? && !column.is_identity?
|
18
|
+
end
|
19
|
+
|
20
|
+
def schema_limit(column)
|
21
|
+
return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
8
25
|
def schema_collation(column)
|
9
26
|
return unless column.collation
|
10
27
|
column.collation if column.collation != collation
|
11
28
|
end
|
12
29
|
|
30
|
+
def default_primary_key?(column)
|
31
|
+
super && column.is_primary? && column.is_identity?
|
32
|
+
end
|
33
|
+
|
13
34
|
end
|
14
35
|
end
|
15
36
|
end
|
@@ -7,35 +7,6 @@ module ActiveRecord
|
|
7
7
|
@native_database_types ||= initialize_native_database_types.freeze
|
8
8
|
end
|
9
9
|
|
10
|
-
def tables(name = nil)
|
11
|
-
ActiveSupport::Deprecation.warn 'Passing arguments to #tables is deprecated without replacement.' if name
|
12
|
-
tables_sql('BASE TABLE')
|
13
|
-
end
|
14
|
-
|
15
|
-
def table_exists?(table_name)
|
16
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
17
|
-
#table_exists? currently checks both tables and views.
|
18
|
-
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
19
|
-
Use #data_source_exists? instead.
|
20
|
-
MSG
|
21
|
-
data_source_exists?(table_name)
|
22
|
-
end
|
23
|
-
|
24
|
-
def data_source_exists?(table_name)
|
25
|
-
return false if table_name.blank?
|
26
|
-
unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object
|
27
|
-
super(unquoted_table_name)
|
28
|
-
end
|
29
|
-
|
30
|
-
def views
|
31
|
-
tables_sql('VIEW')
|
32
|
-
end
|
33
|
-
|
34
|
-
def view_exists?(table_name)
|
35
|
-
identifier = SQLServer::Utils.extract_identifiers(table_name)
|
36
|
-
super(identifier.object)
|
37
|
-
end
|
38
|
-
|
39
10
|
def create_table(table_name, comment: nil, **options)
|
40
11
|
res = super
|
41
12
|
clear_cache!
|
@@ -43,6 +14,17 @@ module ActiveRecord
|
|
43
14
|
end
|
44
15
|
|
45
16
|
def drop_table(table_name, options = {})
|
17
|
+
# Mimic CASCADE option as best we can.
|
18
|
+
if options[:force] == :cascade
|
19
|
+
execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
|
20
|
+
fktable = fkdata['FKTABLE_NAME']
|
21
|
+
fkcolmn = fkdata['FKCOLUMN_NAME']
|
22
|
+
pktable = fkdata['PKTABLE_NAME']
|
23
|
+
pkcolmn = fkdata['PKCOLUMN_NAME']
|
24
|
+
remove_foreign_key fktable, name: fkdata['FK_NAME']
|
25
|
+
do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )"
|
26
|
+
end
|
27
|
+
end
|
46
28
|
if options[:if_exists] && @version_year != 2016
|
47
29
|
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
|
48
30
|
else
|
@@ -103,10 +85,34 @@ module ActiveRecord
|
|
103
85
|
end
|
104
86
|
|
105
87
|
def primary_keys(table_name)
|
106
|
-
primaries =
|
88
|
+
primaries = primary_keys_select(table_name)
|
107
89
|
primaries.present? ? primaries : identity_columns(table_name).map(&:name)
|
108
90
|
end
|
109
91
|
|
92
|
+
def primary_keys_select(table_name)
|
93
|
+
identifier = database_prefix_identifier(table_name)
|
94
|
+
database = identifier.fully_qualified_database_quoted
|
95
|
+
sql = %{
|
96
|
+
SELECT KCU.COLUMN_NAME AS [name]
|
97
|
+
FROM #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
|
98
|
+
LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
99
|
+
ON KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
|
100
|
+
AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
|
101
|
+
AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
|
102
|
+
AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
|
103
|
+
AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
|
104
|
+
WHERE KCU.TABLE_NAME = #{prepared_statements ? '@0' : quote(identifier.object)}
|
105
|
+
AND KCU.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))}
|
106
|
+
AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
|
107
|
+
ORDER BY KCU.ORDINAL_POSITION ASC
|
108
|
+
}.gsub(/[[:space:]]/, ' ')
|
109
|
+
binds = []
|
110
|
+
nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
|
111
|
+
binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128)
|
112
|
+
binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank?
|
113
|
+
sp_executesql(sql, 'SCHEMA', binds).map { |r| r['name'] }
|
114
|
+
end
|
115
|
+
|
110
116
|
def rename_table(table_name, new_name)
|
111
117
|
do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
|
112
118
|
rename_table_indexes(table_name, new_name)
|
@@ -130,9 +136,11 @@ module ActiveRecord
|
|
130
136
|
remove_indexes(table_name, column_name)
|
131
137
|
end
|
132
138
|
sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
|
133
|
-
sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
134
|
-
sql_commands
|
135
|
-
if
|
139
|
+
sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
|
140
|
+
sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false
|
141
|
+
if options.key?(:default) && default_constraint_name(table_name, column_name).present?
|
142
|
+
change_column_default(table_name, column_name, options[:default])
|
143
|
+
elsif options_include_default?(options)
|
136
144
|
sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}"
|
137
145
|
end
|
138
146
|
# Add any removed indexes back
|
@@ -140,6 +148,7 @@ module ActiveRecord
|
|
140
148
|
sql_commands << "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index.columns.map { |c| quote_column_name(c) }.join(', ')})"
|
141
149
|
end
|
142
150
|
sql_commands.each { |c| do_execute(c) }
|
151
|
+
clear_cache!
|
143
152
|
end
|
144
153
|
|
145
154
|
def change_column_default(table_name, column_name, default_or_changes)
|
@@ -194,7 +203,7 @@ module ActiveRecord
|
|
194
203
|
end
|
195
204
|
end
|
196
205
|
|
197
|
-
def type_to_sql(type, limit
|
206
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
|
198
207
|
type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
|
199
208
|
limit = nil unless type_limitable
|
200
209
|
case type.to_s
|
@@ -240,19 +249,40 @@ module ActiveRecord
|
|
240
249
|
if !allow_null.nil? && allow_null == false && !default.nil?
|
241
250
|
do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
|
242
251
|
end
|
243
|
-
sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, column.limit, column.precision, column.scale}"
|
252
|
+
sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}"
|
244
253
|
sql << ' NOT NULL' if !allow_null.nil? && allow_null == false
|
245
254
|
do_execute sql
|
246
255
|
end
|
247
256
|
|
257
|
+
private
|
248
258
|
|
249
|
-
|
259
|
+
def data_source_sql(name = nil, type: nil)
|
260
|
+
scope = quoted_scope name, type: type
|
261
|
+
table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
|
262
|
+
sql = "SELECT #{table_name}"
|
263
|
+
sql << ' FROM INFORMATION_SCHEMA.TABLES'
|
264
|
+
sql << ' WHERE TABLE_CATALOG = DB_NAME()'
|
265
|
+
sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
|
266
|
+
sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
|
267
|
+
sql << " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
|
268
|
+
sql << " ORDER BY #{table_name}"
|
269
|
+
sql
|
270
|
+
end
|
271
|
+
|
272
|
+
def quoted_scope(name = nil, type: nil)
|
273
|
+
identifier = SQLServer::Utils.extract_identifiers(name)
|
274
|
+
{}.tap do |scope|
|
275
|
+
scope[:schema] = identifier.schema || 'dbo'
|
276
|
+
scope[:name] = identifier.object if identifier.object
|
277
|
+
scope[:type] = type if type
|
278
|
+
end
|
279
|
+
end
|
250
280
|
|
251
281
|
# === SQLServer Specific ======================================== #
|
252
282
|
|
253
283
|
def initialize_native_database_types
|
254
284
|
{
|
255
|
-
primary_key: '
|
285
|
+
primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
|
256
286
|
primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED',
|
257
287
|
integer: { name: 'int', limit: 4 },
|
258
288
|
bigint: { name: 'bigint' },
|
@@ -286,18 +316,8 @@ module ActiveRecord
|
|
286
316
|
}
|
287
317
|
end
|
288
318
|
|
289
|
-
def tables_sql(type)
|
290
|
-
tn = lowercase_schema_reflection_sql 'TABLE_NAME'
|
291
|
-
sql = "SELECT #{tn} FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = '#{type}' ORDER BY TABLE_NAME"
|
292
|
-
select_values sql, 'SCHEMA'
|
293
|
-
end
|
294
|
-
|
295
319
|
def column_definitions(table_name)
|
296
|
-
identifier
|
297
|
-
SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}")
|
298
|
-
else
|
299
|
-
SQLServer::Utils.extract_identifiers(table_name)
|
300
|
-
end
|
320
|
+
identifier = database_prefix_identifier(table_name)
|
301
321
|
database = identifier.fully_qualified_database_quoted
|
302
322
|
view_exists = view_exists?(table_name)
|
303
323
|
view_tblnm = view_table_name(table_name) if view_exists
|
@@ -475,7 +495,7 @@ module ActiveRecord
|
|
475
495
|
@view_information ||= {}
|
476
496
|
@view_information[table_name] ||= begin
|
477
497
|
identifier = SQLServer::Utils.extract_identifiers(table_name)
|
478
|
-
view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME =
|
498
|
+
view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA'
|
479
499
|
if view_info
|
480
500
|
view_info = view_info.with_indifferent_access
|
481
501
|
if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
|
@@ -498,8 +518,6 @@ module ActiveRecord
|
|
498
518
|
match_data ? match_data[1] : column_name
|
499
519
|
end
|
500
520
|
|
501
|
-
private
|
502
|
-
|
503
521
|
def create_table_definition(*args)
|
504
522
|
SQLServer::TableDefinition.new(*args)
|
505
523
|
end
|
@@ -5,10 +5,13 @@ module ActiveRecord
|
|
5
5
|
module ColumnMethods
|
6
6
|
|
7
7
|
def primary_key(name, type = :primary_key, **options)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
if [:integer, :bigint].include?(type)
|
9
|
+
options[:is_identity] = true unless options.key?(:default)
|
10
|
+
elsif type == :uuid
|
11
|
+
options[:default] = options.fetch(:default, 'NEWID()')
|
12
|
+
options[:primary_key] = true
|
13
|
+
end
|
14
|
+
super
|
12
15
|
end
|
13
16
|
|
14
17
|
def primary_key_nonclustered(*args, **options)
|
@@ -98,9 +101,14 @@ module ActiveRecord
|
|
98
101
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
99
102
|
include ColumnMethods
|
100
103
|
|
101
|
-
def new_column_definition(name, type, options)
|
102
|
-
|
103
|
-
|
104
|
+
def new_column_definition(name, type, **options)
|
105
|
+
case type
|
106
|
+
when :datetime
|
107
|
+
type = :datetime2 if options[:precision]
|
108
|
+
when :primary_key
|
109
|
+
options[:is_identity] = true
|
110
|
+
end
|
111
|
+
super
|
104
112
|
end
|
105
113
|
end
|
106
114
|
|
@@ -73,14 +73,6 @@ module ActiveRecord
|
|
73
73
|
SQLServer::SchemaCreation.new self
|
74
74
|
end
|
75
75
|
|
76
|
-
def supports_migrations?
|
77
|
-
true
|
78
|
-
end
|
79
|
-
|
80
|
-
def supports_primary_key?
|
81
|
-
true
|
82
|
-
end
|
83
|
-
|
84
76
|
def supports_ddl_transactions?
|
85
77
|
true
|
86
78
|
end
|
@@ -147,10 +139,10 @@ module ActiveRecord
|
|
147
139
|
|
148
140
|
def disable_referential_integrity
|
149
141
|
tables = tables_with_referential_integrity
|
150
|
-
tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" }
|
142
|
+
tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" }
|
151
143
|
yield
|
152
144
|
ensure
|
153
|
-
tables.each { |t| do_execute "ALTER TABLE #{t} CHECK CONSTRAINT ALL" }
|
145
|
+
tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} CHECK CONSTRAINT ALL" }
|
154
146
|
end
|
155
147
|
|
156
148
|
# === Abstract Adapter (Connection Management) ================== #
|
@@ -230,6 +222,14 @@ module ActiveRecord
|
|
230
222
|
@connection_options[:database_prefix]
|
231
223
|
end
|
232
224
|
|
225
|
+
def database_prefix_identifier(name)
|
226
|
+
if database_prefix_remote_server?
|
227
|
+
SQLServer::Utils.extract_identifiers("#{database_prefix}#{name}")
|
228
|
+
else
|
229
|
+
SQLServer::Utils.extract_identifiers(name)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
233
|
def version
|
234
234
|
self.class::VERSION
|
235
235
|
end
|
@@ -327,6 +327,20 @@ module ActiveRecord
|
|
327
327
|
NoDatabaseError.new(message)
|
328
328
|
when /data would be truncated/
|
329
329
|
ValueTooLong.new(message)
|
330
|
+
when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/
|
331
|
+
pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2)
|
332
|
+
MismatchedForeignKey.new(
|
333
|
+
self,
|
334
|
+
message: message,
|
335
|
+
table: fk_id.schema,
|
336
|
+
foreign_key: fk_id.object,
|
337
|
+
target_table: pk_id.schema,
|
338
|
+
primary_key: pk_id.object
|
339
|
+
)
|
340
|
+
when /Cannot insert the value NULL into column.*does not allow nulls/
|
341
|
+
NotNullViolation.new(message)
|
342
|
+
when /Arithmetic overflow error/
|
343
|
+
RangeError.new(message)
|
330
344
|
else
|
331
345
|
super
|
332
346
|
end
|
@@ -418,14 +432,11 @@ module ActiveRecord
|
|
418
432
|
end
|
419
433
|
|
420
434
|
def version_year
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
rescue Exception => e
|
427
|
-
2016
|
428
|
-
end
|
435
|
+
vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
|
436
|
+
return 2016 if vstring =~ /vNext/
|
437
|
+
/SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i
|
438
|
+
rescue Exception => e
|
439
|
+
2016
|
429
440
|
end
|
430
441
|
|
431
442
|
end
|
@@ -48,7 +48,7 @@ module ActiveRecord
|
|
48
48
|
create true
|
49
49
|
end
|
50
50
|
|
51
|
-
def structure_dump(filename)
|
51
|
+
def structure_dump(filename, extra_flags)
|
52
52
|
command = [
|
53
53
|
"defncopy",
|
54
54
|
"-S #{Shellwords.escape(configuration['host'])}",
|
@@ -71,7 +71,7 @@ module ActiveRecord
|
|
71
71
|
File.open(filename, "w") { |file| file.puts dump }
|
72
72
|
end
|
73
73
|
|
74
|
-
def structure_load(filename)
|
74
|
+
def structure_load(filename, extra_flags)
|
75
75
|
connection.execute File.read(filename)
|
76
76
|
end
|
77
77
|
|
@@ -190,7 +190,8 @@ module Arel
|
|
190
190
|
|
191
191
|
def primary_Key_From_Table t
|
192
192
|
return unless t
|
193
|
-
column_name = schema_cache.primary_keys(t.name) ||
|
193
|
+
column_name = @connection.schema_cache.primary_keys(t.name) ||
|
194
|
+
@connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
|
194
195
|
column_name ? t[column_name] : nil
|
195
196
|
end
|
196
197
|
|
@@ -37,10 +37,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
37
37
|
assert_equal 'SQLServer', connection.adapter_name
|
38
38
|
end
|
39
39
|
|
40
|
-
it 'supports migrations' do
|
41
|
-
assert connection.supports_migrations?
|
42
|
-
end
|
43
|
-
|
44
40
|
it 'support DDL in transactions' do
|
45
41
|
assert connection.supports_ddl_transactions?
|
46
42
|
end
|
@@ -265,23 +261,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
265
261
|
end
|
266
262
|
|
267
263
|
it 'create integers when limit is 4' do
|
268
|
-
assert_equal 'integer', connection.type_to_sql(:integer, 4)
|
264
|
+
assert_equal 'integer', connection.type_to_sql(:integer, limit: 4)
|
269
265
|
end
|
270
266
|
|
271
267
|
it 'create integers when limit is 3' do
|
272
|
-
assert_equal 'integer', connection.type_to_sql(:integer, 3)
|
268
|
+
assert_equal 'integer', connection.type_to_sql(:integer, limit: 3)
|
273
269
|
end
|
274
270
|
|
275
271
|
it 'create smallints when limit is less than 3' do
|
276
|
-
assert_equal 'smallint', connection.type_to_sql(:integer, 2)
|
277
|
-
assert_equal 'smallint', connection.type_to_sql(:integer, 1)
|
272
|
+
assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2)
|
273
|
+
assert_equal 'smallint', connection.type_to_sql(:integer, limit: 1)
|
278
274
|
end
|
279
275
|
|
280
276
|
it 'create bigints when limit is greateer than 4' do
|
281
|
-
assert_equal 'bigint', connection.type_to_sql(:integer, 5)
|
282
|
-
assert_equal 'bigint', connection.type_to_sql(:integer, 6)
|
283
|
-
assert_equal 'bigint', connection.type_to_sql(:integer, 7)
|
284
|
-
assert_equal 'bigint', connection.type_to_sql(:integer, 8)
|
277
|
+
assert_equal 'bigint', connection.type_to_sql(:integer, limit: 5)
|
278
|
+
assert_equal 'bigint', connection.type_to_sql(:integer, limit: 6)
|
279
|
+
assert_equal 'bigint', connection.type_to_sql(:integer, limit: 7)
|
280
|
+
assert_equal 'bigint', connection.type_to_sql(:integer, limit: 8)
|
285
281
|
end
|
286
282
|
|
287
283
|
it 'create floats when no limit supplied' do
|
data/test/cases/coerced_tests.rb
CHANGED
@@ -31,6 +31,9 @@ end
|
|
31
31
|
require 'models/event'
|
32
32
|
module ActiveRecord
|
33
33
|
class AdapterTest < ActiveRecord::TestCase
|
34
|
+
# I really dont think we can support legacy binds.
|
35
|
+
coerce_tests! :test_select_all_with_legacy_binds
|
36
|
+
|
34
37
|
# As far as I can tell, SQL Server does not support null bytes in strings.
|
35
38
|
coerce_tests! :test_update_prepared_statement
|
36
39
|
|
@@ -52,14 +55,14 @@ end
|
|
52
55
|
|
53
56
|
require 'models/topic'
|
54
57
|
class AttributeMethodsTest < ActiveRecord::TestCase
|
55
|
-
coerce_tests!
|
58
|
+
coerce_tests! %r{typecast attribute from select to false}
|
56
59
|
def test_typecast_attribute_from_select_to_false_coerced
|
57
60
|
Topic.create(:title => 'Budget')
|
58
61
|
topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 2, 1, 0) as is_test").first
|
59
62
|
assert !topic.is_test?
|
60
63
|
end
|
61
64
|
|
62
|
-
coerce_tests!
|
65
|
+
coerce_tests! %r{typecast attribute from select to true}
|
63
66
|
def test_typecast_attribute_from_select_to_true_coerced
|
64
67
|
Topic.create(:title => 'Budget')
|
65
68
|
topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first
|
@@ -118,6 +121,15 @@ end
|
|
118
121
|
|
119
122
|
|
120
123
|
class CalculationsTest < ActiveRecord::TestCase
|
124
|
+
# This fails randomly due to schema cache being lost?
|
125
|
+
coerce_tests! :test_offset_is_kept
|
126
|
+
def test_offset_is_kept_coerced
|
127
|
+
Account.first
|
128
|
+
queries = assert_sql { Account.offset(1).count }
|
129
|
+
assert_equal 1, queries.length
|
130
|
+
assert_match(/OFFSET/, queries.first)
|
131
|
+
end
|
132
|
+
|
121
133
|
# Are decimal, not integer.
|
122
134
|
coerce_tests! :test_should_return_decimal_average_of_integer_field
|
123
135
|
def test_should_return_decimal_average_of_integer_field_coerced
|
@@ -161,6 +173,29 @@ end
|
|
161
173
|
|
162
174
|
|
163
175
|
|
176
|
+
module ActiveRecord
|
177
|
+
module ConnectionAdapters
|
178
|
+
class QuoteARBaseTest < ActiveRecord::TestCase
|
179
|
+
|
180
|
+
# Use our date format.
|
181
|
+
coerce_tests! :test_quote_ar_object
|
182
|
+
def test_quote_ar_object_coerced
|
183
|
+
value = DatetimePrimaryKey.new(id: @time)
|
184
|
+
assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Use our date format.
|
188
|
+
coerce_tests! :test_type_cast_ar_object
|
189
|
+
def test_type_cast_ar_object_coerced
|
190
|
+
value = DatetimePrimaryKey.new(id: @time)
|
191
|
+
assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value)
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
|
164
199
|
|
165
200
|
module ActiveRecord
|
166
201
|
class Migration
|
@@ -289,6 +324,10 @@ end
|
|
289
324
|
|
290
325
|
|
291
326
|
module ActiveRecord
|
327
|
+
class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase
|
328
|
+
# Skip this test with /tmp/my_schema_cache.yml path on Windows.
|
329
|
+
coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
330
|
+
end
|
292
331
|
class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
|
293
332
|
# We extend `local_database?` so that common VM IPs can be used.
|
294
333
|
coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
|
@@ -375,12 +414,6 @@ class FinderTest < ActiveRecord::TestCase
|
|
375
414
|
end
|
376
415
|
end
|
377
416
|
|
378
|
-
coerce_tests! :test_string_sanitation
|
379
|
-
def test_string_sanitation_coerced
|
380
|
-
assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
|
381
|
-
assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table")
|
382
|
-
end
|
383
|
-
|
384
417
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
385
418
|
def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
|
386
419
|
assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
|
@@ -576,6 +609,15 @@ end
|
|
576
609
|
|
577
610
|
|
578
611
|
|
612
|
+
class PrimaryKeysTest < ActiveRecord::TestCase
|
613
|
+
# Gonna trust Rails core for this. We end up with 2 querys vs 3 asserted
|
614
|
+
# but as far as I can tell, this is only one for us anyway.
|
615
|
+
coerce_tests! :test_create_without_primary_key_no_extra_query
|
616
|
+
end
|
617
|
+
|
618
|
+
|
619
|
+
|
620
|
+
|
579
621
|
require 'models/task'
|
580
622
|
class QueryCacheTest < ActiveRecord::TestCase
|
581
623
|
coerce_tests! :test_cache_does_not_wrap_string_results_in_arrays
|
@@ -619,6 +661,26 @@ class RelationTest < ActiveRecord::TestCase
|
|
619
661
|
# Leave it up to users to format selects/functions so HAVING works correctly.
|
620
662
|
coerce_tests! :test_multiple_where_and_having_clauses
|
621
663
|
coerce_tests! :test_having_with_binds_for_both_where_and_having
|
664
|
+
|
665
|
+
# Find any limit via our expression.
|
666
|
+
coerce_tests! %r{relations don't load all records in #inspect}
|
667
|
+
def test_relations_dont_load_all_records_in_inspect_coerced
|
668
|
+
assert_sql(/NEXT @0 ROWS.*@0 = \d+/) do
|
669
|
+
Post.all.inspect
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
# I wanted to add `.order("author_id")` scope to avoid error: Column "posts.id" is invalid in the ORDER BY
|
674
|
+
# However, this pull request on Rails core drops order on exists relation. https://github.com/rails/rails/pull/28699
|
675
|
+
# so we are skipping all together.
|
676
|
+
coerce_tests! :test_empty_complex_chained_relations
|
677
|
+
|
678
|
+
# Use LEN() vs length() function.
|
679
|
+
coerce_tests! :test_reverse_arel_assoc_order_with_function
|
680
|
+
def test_reverse_arel_assoc_order_with_function_coerced
|
681
|
+
topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order
|
682
|
+
assert_equal topics(:second).title, topics.first.title
|
683
|
+
end
|
622
684
|
end
|
623
685
|
|
624
686
|
|
@@ -650,9 +712,6 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
650
712
|
assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38}, output
|
651
713
|
end
|
652
714
|
|
653
|
-
# This accidently returns the wrong number because of our tables too.
|
654
|
-
coerce_tests! :test_types_line_up
|
655
|
-
|
656
715
|
# This is a poorly written test and really does not catch the bottom'ness it is meant too. Ours throw it off.
|
657
716
|
coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues
|
658
717
|
|
@@ -816,3 +875,19 @@ module ActiveRecord
|
|
816
875
|
coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed
|
817
876
|
end
|
818
877
|
end
|
878
|
+
|
879
|
+
|
880
|
+
|
881
|
+
|
882
|
+
module ActiveRecord
|
883
|
+
module ConnectionAdapters
|
884
|
+
class SchemaCacheTest < ActiveRecord::TestCase
|
885
|
+
private
|
886
|
+
# We need to give the full path for this to work.
|
887
|
+
def schema_dump_path
|
888
|
+
File.join ARTest::SQLServer.root_activerecord, 'test/assets/schema_dump_5_1.yml'
|
889
|
+
end
|
890
|
+
end
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
@@ -36,7 +36,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
36
36
|
it 'bigint(8)' do
|
37
37
|
col = column('bigint')
|
38
38
|
col.sql_type.must_equal 'bigint(8)'
|
39
|
-
col.type.must_equal :
|
39
|
+
col.type.must_equal :integer
|
40
40
|
col.null.must_equal true
|
41
41
|
col.default.must_equal 42
|
42
42
|
obj.bigint.must_equal 42
|
@@ -23,9 +23,14 @@ module ActiveRecord
|
|
23
23
|
|
24
24
|
let(:logger) { ActiveRecord::Base.logger }
|
25
25
|
|
26
|
+
setup :ensure_clean_rails_env
|
26
27
|
|
27
28
|
private
|
28
29
|
|
30
|
+
def ensure_clean_rails_env
|
31
|
+
Rails.instance_variable_set(:@_env, nil) if defined?(::Rails)
|
32
|
+
end
|
33
|
+
|
29
34
|
def host_windows?
|
30
35
|
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
31
36
|
end
|
@@ -46,17 +46,6 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
it 'reload with lock when #lock! called' do
|
50
|
-
assert_nothing_raised do
|
51
|
-
Person.transaction do
|
52
|
-
person = Person.find 1
|
53
|
-
old, person.first_name = person.first_name, 'fooman'
|
54
|
-
person.lock!
|
55
|
-
assert_equal old, person.first_name
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
49
|
it 'can add a custom lock directive' do
|
61
50
|
assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do
|
62
51
|
Person.lock('WITH(HOLDLOCK, ROWLOCK)').load
|
@@ -38,17 +38,17 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
38
38
|
# Character Strings
|
39
39
|
assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil
|
40
40
|
assert_line :varchar_50, type: 'varchar', limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil
|
41
|
-
assert_line :varchar_max, type: 'varchar_max', limit:
|
42
|
-
assert_line :text, type: 'text_basic', limit:
|
41
|
+
assert_line :varchar_max, type: 'varchar_max', limit: nil, precision: nil, scale: nil, default: "test varchar_max", collation: nil
|
42
|
+
assert_line :text, type: 'text_basic', limit: nil, precision: nil, scale: nil, default: "test text", collation: nil
|
43
43
|
# Unicode Character Strings
|
44
44
|
assert_line :nchar_10, type: 'nchar', limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: nil
|
45
45
|
assert_line :nvarchar_50, type: 'string', limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil
|
46
|
-
assert_line :nvarchar_max, type: 'text', limit:
|
47
|
-
assert_line :ntext, type: 'ntext', limit:
|
46
|
+
assert_line :nvarchar_max, type: 'text', limit: nil, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil
|
47
|
+
assert_line :ntext, type: 'ntext', limit: nil, precision: nil, scale: nil, default: "test ntext åå", collation: nil
|
48
48
|
# Binary Strings
|
49
49
|
assert_line :binary_49, type: 'binary_basic', limit: 49, precision: nil, scale: nil, default: nil
|
50
50
|
assert_line :varbinary_49, type: 'varbinary', limit: 49, precision: nil, scale: nil, default: nil
|
51
|
-
assert_line :varbinary_max, type: 'binary', limit:
|
51
|
+
assert_line :varbinary_max, type: 'binary', limit: nil, precision: nil, scale: nil, default: nil
|
52
52
|
# Other Data Types
|
53
53
|
assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: -> { "newid()" }
|
54
54
|
assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
|
@@ -76,12 +76,12 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
76
76
|
assert_line :decimal_col, type: 'decimal', limit: nil, precision: 18, scale: 0, default: nil
|
77
77
|
assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil
|
78
78
|
assert_line :string_col, type: 'string', limit: nil, precision: nil, scale: nil, default: nil
|
79
|
-
assert_line :text_col, type: 'text', limit:
|
79
|
+
assert_line :text_col, type: 'text', limit: nil, precision: nil, scale: nil, default: nil
|
80
80
|
assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
|
81
81
|
assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
|
82
82
|
assert_line :time_col, type: 'time', limit: nil, precision: 7, scale: nil, default: nil
|
83
83
|
assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil
|
84
|
-
assert_line :binary_col, type: 'binary', limit:
|
84
|
+
assert_line :binary_col, type: 'binary', limit: nil, precision: nil, scale: nil, default: nil
|
85
85
|
# Our type methods.
|
86
86
|
columns['real_col'].sql_type.must_equal 'real'
|
87
87
|
columns['money_col'].sql_type.must_equal 'money'
|
@@ -107,14 +107,14 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
107
107
|
assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil
|
108
108
|
assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil
|
109
109
|
assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil
|
110
|
-
assert_line :text_basic_col, type: 'text_basic', limit:
|
110
|
+
assert_line :text_basic_col, type: 'text_basic', limit: nil, precision: nil, scale: nil, default: nil
|
111
111
|
assert_line :nchar_col, type: 'nchar', limit: 1, precision: nil, scale: nil, default: nil
|
112
|
-
assert_line :ntext_col, type: 'ntext', limit:
|
112
|
+
assert_line :ntext_col, type: 'ntext', limit: nil, precision: nil, scale: nil, default: nil
|
113
113
|
assert_line :binary_basic_col, type: 'binary_basic', limit: 1, precision: nil, scale: nil, default: nil
|
114
114
|
assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil
|
115
115
|
assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
|
116
116
|
assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
|
117
|
-
assert_line :json_col, type: 'text', limit:
|
117
|
+
assert_line :json_col, type: 'text', limit: nil, precision: nil, scale: nil, default: nil
|
118
118
|
end
|
119
119
|
|
120
120
|
# Special Cases
|
@@ -148,7 +148,7 @@ ActiveRecord::Schema.define do
|
|
148
148
|
|
149
149
|
# Constraints
|
150
150
|
|
151
|
-
create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :
|
151
|
+
create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :bigint, null: false) }
|
152
152
|
create_table(:sst_has_pks, force: true) { }
|
153
153
|
execute <<-ADDFKSQL
|
154
154
|
ALTER TABLE sst_has_fks
|
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: 5.0
|
4
|
+
version: 5.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ken Collins
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2017-
|
17
|
+
date: 2017-05-29 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: activerecord
|
@@ -22,14 +22,14 @@ dependencies:
|
|
22
22
|
requirements:
|
23
23
|
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: 5.
|
25
|
+
version: 5.1.0
|
26
26
|
type: :runtime
|
27
27
|
prerelease: false
|
28
28
|
version_requirements: !ruby/object:Gem::Requirement
|
29
29
|
requirements:
|
30
30
|
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 5.
|
32
|
+
version: 5.1.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: tiny_tds
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -54,6 +54,7 @@ extra_rdoc_files: []
|
|
54
54
|
files:
|
55
55
|
- ".gitignore"
|
56
56
|
- ".travis.yml"
|
57
|
+
- BACKERS.md
|
57
58
|
- CHANGELOG.md
|
58
59
|
- CODE_OF_CONDUCT.md
|
59
60
|
- Gemfile
|
@@ -288,3 +289,4 @@ test_files:
|
|
288
289
|
- test/support/rake_helpers.rb
|
289
290
|
- test/support/sql_counter_sqlserver.rb
|
290
291
|
- test/support/test_in_memory_oltp.rb
|
292
|
+
has_rdoc:
|