activerecord-sqlserver-adapter 5.0.5 → 5.0.6
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/.travis.yml +1 -1
- data/CHANGELOG.md +46 -0
- data/RAILS5-TODO.md +1 -32
- data/README.md +1 -0
- data/VERSION +1 -1
- data/appveyor.yml +1 -1
- data/circle.yml +1 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +25 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +3 -22
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +10 -10
- data/test/bin/setup.sh +1 -1
- data/test/cases/adapter_test_sqlserver.rb +9 -15
- data/test/cases/coerced_tests.rb +10 -0
- data/test/cases/connection_test_sqlserver.rb +1 -1
- data/test/cases/helper_sqlserver.rb +1 -0
- data/test/cases/index_test_sqlserver.rb +47 -0
- data/test/cases/json_test_sqlserver.rb +32 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +4 -0
- data/test/cases/scratchpad_test_sqlserver.rb +0 -1
- data/test/cases/specific_schema_test_sqlserver.rb +1 -1
- data/test/models/sqlserver/datatype_migration.rb +5 -0
- data/test/models/sqlserver/sst_memory.rb +3 -0
- data/test/schema/enable-in-memory-oltp.sql +81 -0
- data/test/schema/sqlserver_specific_schema.rb +15 -0
- data/test/support/test_in_memory_oltp.rb +15 -0
- metadata +14 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c87b967854fdb250bb0ca9159ca183a6193ae29a
|
4
|
+
data.tar.gz: 9b39e726bac55f07636ff11e23090512a7d35e96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa2739f0f136f581c72b66fa9207793204c837ff9ce7173f37ba4e0227b7378d9a71b44d877bcd889b4415fda0d3197a4008f9e8a614a88d71716879bad3481f
|
7
|
+
data.tar.gz: 30f4417f5f319feb26b1188fe579407bc8b3b3124c8c40ce62aed4f16e7225e95b0caa820f70ef0ee17b6779bd08a0bc5ec98a3c47cb70720ddb776aed1b13c2
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,49 @@
|
|
1
|
+
## v5.0.6
|
2
|
+
|
3
|
+
#### Fixed
|
4
|
+
|
5
|
+
* Performance w/inserts. Check binds & use schema cache for id inserts.
|
6
|
+
Fixes #572. Thanks @noelr.
|
7
|
+
* Add smalldatetime type for migrations. Fixes #507
|
8
|
+
|
9
|
+
#### Changed
|
10
|
+
|
11
|
+
* Misc index enhancements or testing. Fixes #570
|
12
|
+
Enable `supports_index_sort_order?`, test `supports_partial_index?`, test how expression indexes work.
|
13
|
+
|
14
|
+
#### Added
|
15
|
+
|
16
|
+
* New `primary_key_nonclustered` type for easy In-Memory table creation.
|
17
|
+
* Examples for an In-Memory table.
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
create_table :in_memory_table, id: false,
|
21
|
+
options: 'WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)' do |t|
|
22
|
+
t.primary_key_nonclustered :id
|
23
|
+
t.string :name
|
24
|
+
t.timestamps
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
* Enable supports_json? Fixes #577.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
create_table :users do |t|
|
32
|
+
t.string :name, :email
|
33
|
+
t.json :data # Creates a nvarchar(max) column.
|
34
|
+
end
|
35
|
+
|
36
|
+
class Users < ActiveRecord::Base
|
37
|
+
attribute :data, ActiveRecord::Type::SQLServer::Json.new
|
38
|
+
end
|
39
|
+
|
40
|
+
User.create! name: 'Ken Collins', data: { 'admin' => true, 'foo' => 'bar' }
|
41
|
+
|
42
|
+
admin = User.where("JSON_VALUE(data, '$.admin') = CAST(1 AS BIT)").first
|
43
|
+
admin.data['foo'] # => "bar"
|
44
|
+
```
|
45
|
+
|
46
|
+
|
1
47
|
## v5.0.5
|
2
48
|
|
3
49
|
#### Changed
|
data/RAILS5-TODO.md
CHANGED
@@ -1,36 +1,5 @@
|
|
1
1
|
|
2
|
-
##
|
2
|
+
## Rails v5.1
|
3
3
|
|
4
|
-
Misc remidners while in the heat of adapting the adpater.
|
5
|
-
|
6
|
-
|
7
|
-
## LONG TERM
|
8
|
-
|
9
|
-
After we get some tests passing
|
10
|
-
|
11
|
-
* Check `sql_for_insert` can do without the table regular expresion.
|
12
|
-
* Do we need the `query_requires_identity_insert` check in `execute`?
|
13
|
-
* Does the schema cache serialize properly since we conform to that now?
|
14
|
-
* What does `supports_materialized_views?` means for SQL Server
|
15
|
-
- http://michaeljswart.com/2014/12/materialized-views-in-sql-server/
|
16
|
-
- https://blogs.msdn.microsoft.com/ssma/2011/06/20/migrating-oracle-materialized-view-to-sql-server/
|
17
|
-
- http://stackoverflow.com/questions/3986366/how-to-create-materialized-views-in-sql-server
|
18
4
|
* BIGINT PK support. https://github.com/rails/rails/pull/26266
|
19
|
-
* Can we use `OPTIMIZE FOR UNKNOWN`
|
20
|
-
- http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx
|
21
|
-
- http://stackoverflow.com/questions/24016199/sql-server-stored-procedure-become-very-slow-raw-sql-query-is-still-very-fast
|
22
|
-
- https://blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature/
|
23
|
-
* Re-visit all `current_adapter?(:PostgreSQLAdapter)` checks and find ones we can play in.
|
24
|
-
|
25
|
-
|
26
|
-
#### Does Find By SQL Work?
|
27
|
-
|
28
|
-
With binds and prepareable?
|
29
5
|
|
30
|
-
```ruby
|
31
|
-
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
|
32
|
-
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
|
33
|
-
#
|
34
|
-
def find_by_sql(sql, binds = [], preparable: nil)
|
35
|
-
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
|
36
|
-
```
|
data/README.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
5.0.
|
1
|
+
5.0.6
|
data/appveyor.yml
CHANGED
data/circle.yml
CHANGED
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def exec_insert(sql, name, binds, pk = nil, _sequence_name = nil)
|
23
|
-
if
|
23
|
+
if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds)
|
24
24
|
with_identity_insert_enabled(id_insert_table_name) { exec_query(sql, name, binds) }
|
25
25
|
else
|
26
26
|
exec_query(sql, name, binds)
|
@@ -281,6 +281,30 @@ module ActiveRecord
|
|
281
281
|
@update_sql = false
|
282
282
|
end
|
283
283
|
|
284
|
+
# === SQLServer Specific (Identity Inserts) ===================== #
|
285
|
+
|
286
|
+
def exec_insert_requires_identity?(sql, pk, binds)
|
287
|
+
query_requires_identity_insert?(sql) if pk && binds.map(&:name).include?(pk)
|
288
|
+
end
|
289
|
+
|
290
|
+
def query_requires_identity_insert?(sql)
|
291
|
+
if insert_sql?(sql)
|
292
|
+
table_name = get_table_name(sql)
|
293
|
+
id_column = identity_columns(table_name).first
|
294
|
+
id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
|
295
|
+
else
|
296
|
+
false
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def insert_sql?(sql)
|
301
|
+
!(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
|
302
|
+
end
|
303
|
+
|
304
|
+
def identity_columns(table_name)
|
305
|
+
schema_cache.columns(table_name).select(&:is_identity?)
|
306
|
+
end
|
307
|
+
|
284
308
|
# === SQLServer Specific (Selecting) ============================ #
|
285
309
|
|
286
310
|
def raw_select(sql, name = 'SQL', binds = [], options = {})
|
@@ -253,6 +253,7 @@ module ActiveRecord
|
|
253
253
|
def initialize_native_database_types
|
254
254
|
{
|
255
255
|
primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
|
256
|
+
primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED',
|
256
257
|
integer: { name: 'int', limit: 4 },
|
257
258
|
bigint: { name: 'bigint' },
|
258
259
|
boolean: { name: 'bit' },
|
@@ -280,7 +281,8 @@ module ActiveRecord
|
|
280
281
|
varbinary: { name: 'varbinary', limit: 8000 },
|
281
282
|
binary: { name: 'varbinary(max)' },
|
282
283
|
uuid: { name: 'uniqueidentifier' },
|
283
|
-
ss_timestamp: { name: 'timestamp' }
|
284
|
+
ss_timestamp: { name: 'timestamp' },
|
285
|
+
json: { name: 'nvarchar(max)' }
|
284
286
|
}
|
285
287
|
end
|
286
288
|
|
@@ -496,27 +498,6 @@ module ActiveRecord
|
|
496
498
|
match_data ? match_data[1] : column_name
|
497
499
|
end
|
498
500
|
|
499
|
-
# === SQLServer Specific (Identity Inserts) ===================== #
|
500
|
-
|
501
|
-
def query_requires_identity_insert?(sql)
|
502
|
-
if insert_sql?(sql)
|
503
|
-
table_name = get_table_name(sql)
|
504
|
-
id_column = identity_columns(table_name).first
|
505
|
-
id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
|
506
|
-
else
|
507
|
-
false
|
508
|
-
end
|
509
|
-
end
|
510
|
-
|
511
|
-
def insert_sql?(sql)
|
512
|
-
!(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
|
513
|
-
end
|
514
|
-
|
515
|
-
def identity_columns(table_name)
|
516
|
-
columns(table_name).select(&:is_identity?)
|
517
|
-
end
|
518
|
-
|
519
|
-
|
520
501
|
private
|
521
502
|
|
522
503
|
def create_table_definition(*args)
|
@@ -11,6 +11,10 @@ module ActiveRecord
|
|
11
11
|
column name, type, options
|
12
12
|
end
|
13
13
|
|
14
|
+
def primary_key_nonclustered(*args, **options)
|
15
|
+
args.each { |name| column(name, :primary_key_nonclustered, options) }
|
16
|
+
end
|
17
|
+
|
14
18
|
def real(*args, **options)
|
15
19
|
args.each { |name| column(name, :real, options) }
|
16
20
|
end
|
@@ -19,6 +23,10 @@ module ActiveRecord
|
|
19
23
|
args.each { |name| column(name, :money, options) }
|
20
24
|
end
|
21
25
|
|
26
|
+
def smalldatetime(*args, **options)
|
27
|
+
args.each { |name| column(name, :smalldatetime, options) }
|
28
|
+
end
|
29
|
+
|
22
30
|
def datetime(*args, **options)
|
23
31
|
args.each do |name|
|
24
32
|
if options[:precision]
|
@@ -81,6 +89,10 @@ module ActiveRecord
|
|
81
89
|
args.each { |name| column(name, :ss_timestamp, options) }
|
82
90
|
end
|
83
91
|
|
92
|
+
def json(*args, **options)
|
93
|
+
args.each { |name| column(name, :text, options) }
|
94
|
+
end
|
95
|
+
|
84
96
|
end
|
85
97
|
|
86
98
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
@@ -40,6 +40,7 @@ require 'active_record/connection_adapters/sqlserver/type/varbinary_max'
|
|
40
40
|
# Other Data Types
|
41
41
|
require 'active_record/connection_adapters/sqlserver/type/uuid'
|
42
42
|
require 'active_record/connection_adapters/sqlserver/type/timestamp'
|
43
|
+
require 'active_record/connection_adapters/sqlserver/type/json'
|
43
44
|
|
44
45
|
module ActiveRecord
|
45
46
|
module Type
|
@@ -97,10 +97,6 @@ module ActiveRecord
|
|
97
97
|
true
|
98
98
|
end
|
99
99
|
|
100
|
-
def supports_index_sort_order?
|
101
|
-
false
|
102
|
-
end
|
103
|
-
|
104
100
|
def supports_partial_index?
|
105
101
|
true
|
106
102
|
end
|
@@ -134,7 +130,7 @@ module ActiveRecord
|
|
134
130
|
end
|
135
131
|
|
136
132
|
def supports_json?
|
137
|
-
|
133
|
+
@version_year >= 2016
|
138
134
|
end
|
139
135
|
|
140
136
|
def supports_comments?
|
@@ -145,6 +141,10 @@ module ActiveRecord
|
|
145
141
|
false
|
146
142
|
end
|
147
143
|
|
144
|
+
def supports_in_memory_oltp?
|
145
|
+
@version_year >= 2014
|
146
|
+
end
|
147
|
+
|
148
148
|
def disable_referential_integrity
|
149
149
|
tables = tables_with_referential_integrity
|
150
150
|
tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" }
|
@@ -304,6 +304,7 @@ module ActiveRecord
|
|
304
304
|
register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
|
305
305
|
m.alias_type 'string', 'nvarchar(4000)'
|
306
306
|
m.register_type 'nvarchar(max)', SQLServer::Type::UnicodeVarcharMax.new
|
307
|
+
m.register_type 'nvarchar(max)', SQLServer::Type::UnicodeVarcharMax.new
|
307
308
|
m.register_type 'ntext', SQLServer::Type::UnicodeText.new
|
308
309
|
# Binary Strings
|
309
310
|
register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
|
@@ -368,17 +369,15 @@ module ActiveRecord
|
|
368
369
|
).tap do |client|
|
369
370
|
if config[:azure]
|
370
371
|
client.execute('SET ANSI_NULLS ON').do
|
371
|
-
client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
|
372
372
|
client.execute('SET ANSI_NULL_DFLT_ON ON').do
|
373
|
-
client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
|
374
373
|
client.execute('SET ANSI_PADDING ON').do
|
375
|
-
client.execute('SET QUOTED_IDENTIFIER ON').do
|
376
374
|
client.execute('SET ANSI_WARNINGS ON').do
|
377
375
|
else
|
378
376
|
client.execute('SET ANSI_DEFAULTS ON').do
|
379
|
-
client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
|
380
|
-
client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
|
381
377
|
end
|
378
|
+
client.execute('SET QUOTED_IDENTIFIER ON').do
|
379
|
+
client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
|
380
|
+
client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
|
382
381
|
client.execute('SET TEXTSIZE 2147483647').do
|
383
382
|
client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do
|
384
383
|
end
|
@@ -420,6 +419,7 @@ module ActiveRecord
|
|
420
419
|
|
421
420
|
def version_year
|
422
421
|
vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
|
422
|
+
return 2016 if vstring =~ /vNext/
|
423
423
|
/SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i
|
424
424
|
rescue Exception => e
|
425
425
|
2016
|
data/test/bin/setup.sh
CHANGED
@@ -290,21 +290,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
290
290
|
|
291
291
|
end
|
292
292
|
|
293
|
-
describe 'indexes' do
|
294
|
-
|
295
|
-
let(:desc_index_name) { 'idx_credit_limit_test_desc' }
|
296
|
-
|
297
|
-
it 'have indexes with descending order' do
|
298
|
-
begin
|
299
|
-
connection.execute "CREATE INDEX [#{desc_index_name}] ON [accounts] (credit_limit DESC)"
|
300
|
-
assert connection.indexes('accounts').find { |i| i.name == desc_index_name }
|
301
|
-
ensure
|
302
|
-
connection.execute "DROP INDEX [#{desc_index_name}] ON [accounts]"
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
end
|
307
|
-
|
308
293
|
describe 'views' do
|
309
294
|
|
310
295
|
# Using connection.views
|
@@ -431,5 +416,14 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
431
416
|
|
432
417
|
end
|
433
418
|
|
419
|
+
it 'in_memory_oltp' do
|
420
|
+
if ENV['IN_MEMORY_OLTP'] && connection.supports_in_memory_oltp?
|
421
|
+
SSTMemory.primary_key.must_equal 'id'
|
422
|
+
SSTMemory.columns_hash['id'].must_be :is_identity?
|
423
|
+
else
|
424
|
+
skip 'supports_in_memory_oltp? => false'
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
434
428
|
end
|
435
429
|
|
data/test/cases/coerced_tests.rb
CHANGED
@@ -806,3 +806,13 @@ module ActiveRecord
|
|
806
806
|
coerce_tests! %r{with offset which return 0 rows}
|
807
807
|
end
|
808
808
|
end
|
809
|
+
|
810
|
+
|
811
|
+
|
812
|
+
|
813
|
+
module ActiveRecord
|
814
|
+
class StatementCacheTest < ActiveRecord::TestCase
|
815
|
+
# Getting random failures.
|
816
|
+
coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed
|
817
|
+
end
|
818
|
+
end
|
@@ -36,7 +36,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
36
36
|
describe 'Connection management' do
|
37
37
|
|
38
38
|
it 'set spid on connect' do
|
39
|
-
|
39
|
+
['Fixnum', 'Integer'].must_include connection.spid.class.name
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'reset spid on disconnect!' do
|
@@ -3,6 +3,7 @@ require 'bundler/setup'
|
|
3
3
|
Bundler.require :default, :development
|
4
4
|
require 'pry'
|
5
5
|
require 'support/minitest_sqlserver'
|
6
|
+
require 'support/test_in_memory_oltp'
|
6
7
|
require 'cases/helper'
|
7
8
|
require 'support/load_schema_sqlserver'
|
8
9
|
require 'support/coerceable_test_sqlserver'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
|
3
|
+
class IndexTestSQLServer < ActiveRecord::TestCase
|
4
|
+
|
5
|
+
before do
|
6
|
+
connection.create_table(:testings) do |t|
|
7
|
+
t.column :foo, :string, limit: 100
|
8
|
+
t.column :bar, :string, limit: 100
|
9
|
+
t.string :first_name
|
10
|
+
t.string :last_name, limit: 100
|
11
|
+
t.string :key, limit: 100
|
12
|
+
t.boolean :administrator
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
connection.drop_table :testings rescue nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'add index with order' do
|
21
|
+
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do
|
22
|
+
connection.add_index 'testings', ['last_name'], order: { last_name: :desc }
|
23
|
+
connection.remove_index 'testings', ['last_name']
|
24
|
+
end
|
25
|
+
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do
|
26
|
+
connection.add_index 'testings', ['last_name', 'first_name'], order: { last_name: :desc }
|
27
|
+
connection.remove_index 'testings', ['last_name', 'first_name']
|
28
|
+
end
|
29
|
+
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\] ASC\)/i) do
|
30
|
+
connection.add_index 'testings', ['last_name', 'first_name'], order: { last_name: :desc, first_name: :asc }
|
31
|
+
connection.remove_index 'testings', ['last_name', 'first_name']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'add index with where' do
|
36
|
+
assert_sql(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do
|
37
|
+
connection.add_index 'testings', 'last_name', where: "[first_name] = N'john doe'"
|
38
|
+
connection.remove_index 'testings', 'last_name'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'add index with expression' do
|
43
|
+
connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])"
|
44
|
+
connection.add_index 'testings', 'first_name_upper'
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
|
3
|
+
if ActiveRecord::Base.connection.supports_json?
|
4
|
+
class JsonTestSQLServer < ActiveRecord::TestCase
|
5
|
+
|
6
|
+
before do
|
7
|
+
@o1 = SSTestDatatypeMigrationJson.create! json_col: { 'a' => 'a', 'b' => 'b', 'c' => 'c' }
|
8
|
+
@o2 = SSTestDatatypeMigrationJson.create! json_col: { 'a' => nil, 'b' => 'b', 'c' => 'c' }
|
9
|
+
@o3 = SSTestDatatypeMigrationJson.create! json_col: { 'x' => 1, 'y' => 2, 'z' => 3 }
|
10
|
+
@o4 = SSTestDatatypeMigrationJson.create! json_col: { 'array' => [1, 2, 3] }
|
11
|
+
@o5 = SSTestDatatypeMigrationJson.create! json_col: nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'can return and save JSON data' do
|
15
|
+
SSTestDatatypeMigrationJson.find(@o1.id).json_col.must_equal({ 'a' => 'a', 'b' => 'b', 'c' => 'c' })
|
16
|
+
@o1.json_col = { 'a' => 'a' }
|
17
|
+
@o1.json_col.must_equal({ 'a' => 'a' })
|
18
|
+
@o1.save!
|
19
|
+
@o1.reload.json_col.must_equal({ 'a' => 'a' })
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'can use ISJSON function' do
|
23
|
+
SSTestDatatypeMigrationJson.where('ISJSON(json_col) > 0').count.must_equal 4
|
24
|
+
SSTestDatatypeMigrationJson.where('ISJSON(json_col) IS NULL').count.must_equal 1
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can use JSON_VALUE function' do
|
28
|
+
SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count.must_equal 2
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -85,6 +85,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
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'
|
88
|
+
columns['smalldatetime_col'].sql_type.must_equal 'smalldatetime'
|
88
89
|
columns['datetime2_col'].sql_type.must_equal 'datetime2(7)'
|
89
90
|
columns['datetimeoffset'].sql_type.must_equal 'datetimeoffset(7)'
|
90
91
|
columns['smallmoney_col'].sql_type.must_equal 'smallmoney'
|
@@ -97,8 +98,10 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
97
98
|
columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)'
|
98
99
|
columns['uuid_col'].sql_type.must_equal 'uniqueidentifier'
|
99
100
|
columns['sstimestamp_col'].sql_type.must_equal 'timestamp'
|
101
|
+
columns['json_col'].sql_type.must_equal 'nvarchar(max)'
|
100
102
|
assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil
|
101
103
|
assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil
|
104
|
+
assert_line :smalldatetime_col, type: 'smalldatetime', limit: nil, precision: nil, scale: nil, default: nil
|
102
105
|
assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil
|
103
106
|
assert_line :datetimeoffset, type: 'datetimeoffset', limit: nil, precision: 7, scale: nil, default: nil
|
104
107
|
assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil
|
@@ -111,6 +114,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
111
114
|
assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil
|
112
115
|
assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
|
113
116
|
assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
|
117
|
+
assert_line :json_col, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: nil
|
114
118
|
end
|
115
119
|
|
116
120
|
# Special Cases
|
@@ -17,7 +17,7 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase
|
|
17
17
|
|
18
18
|
it 'models can use tinyint pk tables' do
|
19
19
|
obj = SSTestTinyintPk.create! name: '1'
|
20
|
-
obj.id.
|
20
|
+
['Fixnum', 'Integer'].must_include obj.id.class.name
|
21
21
|
SSTestTinyintPk.find(obj.id).must_equal obj
|
22
22
|
end
|
23
23
|
|
@@ -1,3 +1,8 @@
|
|
1
1
|
class SSTestDatatypeMigration < ActiveRecord::Base
|
2
2
|
self.table_name = :sst_datatypes_migration
|
3
3
|
end
|
4
|
+
|
5
|
+
class SSTestDatatypeMigrationJson < ActiveRecord::Base
|
6
|
+
self.table_name = :sst_datatypes_migration
|
7
|
+
attribute :json_col, ActiveRecord::Type::SQLServer::Json.new
|
8
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
-- https://msdn.microsoft.com/en-us/library/mt694156.aspx
|
2
|
+
-- https://raw.githubusercontent.com/Microsoft/sql-server-samples/master/samples/features/in-memory/t-sql-scripts/enable-in-memory-oltp.sql
|
3
|
+
--
|
4
|
+
-- The below scipt enables the use of In-Memory OLTP in the current database,
|
5
|
+
-- provided it is supported in the edition / pricing tier of the database.
|
6
|
+
-- It does the following:
|
7
|
+
-- 1. Validate that In-Memory OLTP is supported.
|
8
|
+
-- 2. In SQL Server, it will add a MEMORY_OPTIMIZED_DATA filegroup to the database
|
9
|
+
-- and create a container within the filegroup in the default data folder.
|
10
|
+
-- 3. Change the database compatibility level to 130 (needed for parallel queries
|
11
|
+
-- and auto-update of statistics).
|
12
|
+
-- 4. Enables the database option MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT to avoid the
|
13
|
+
-- need to use the WITH (SNAPSHOT) hint for ad hoc queries accessing memory-optimized
|
14
|
+
-- tables.
|
15
|
+
--
|
16
|
+
-- Applies To: SQL Server 2016 (or higher); Azure SQL Database
|
17
|
+
-- Author: Jos de Bruijn (Microsoft)
|
18
|
+
-- Last Updated: 2016-05-02
|
19
|
+
|
20
|
+
SET NOCOUNT ON;
|
21
|
+
SET XACT_ABORT ON;
|
22
|
+
|
23
|
+
-- 1. validate that In-Memory OLTP is supported
|
24
|
+
IF SERVERPROPERTY(N'IsXTPSupported') = 0
|
25
|
+
BEGIN
|
26
|
+
PRINT N'Error: In-Memory OLTP is not supported for this server edition or database pricing tier.';
|
27
|
+
END
|
28
|
+
IF DB_ID() < 5
|
29
|
+
BEGIN
|
30
|
+
PRINT N'Error: In-Memory OLTP is not supported in system databases. Connect to a user database.';
|
31
|
+
END
|
32
|
+
ELSE
|
33
|
+
BEGIN
|
34
|
+
BEGIN TRY;
|
35
|
+
-- 2. add MEMORY_OPTIMIZED_DATA filegroup when not using Azure SQL DB
|
36
|
+
IF SERVERPROPERTY('EngineEdition') != 5
|
37
|
+
BEGIN
|
38
|
+
DECLARE @SQLDataFolder nvarchar(max) = cast(SERVERPROPERTY('InstanceDefaultDataPath') as nvarchar(max))
|
39
|
+
DECLARE @MODName nvarchar(max) = DB_NAME() + N'_mod';
|
40
|
+
DECLARE @MemoryOptimizedFilegroupFolder nvarchar(max) = @SQLDataFolder + @MODName;
|
41
|
+
|
42
|
+
DECLARE @SQL nvarchar(max) = N'';
|
43
|
+
|
44
|
+
-- add filegroup
|
45
|
+
IF NOT EXISTS (SELECT 1 FROM sys.filegroups WHERE type = N'FX')
|
46
|
+
BEGIN
|
47
|
+
SET @SQL = N'
|
48
|
+
ALTER DATABASE CURRENT
|
49
|
+
ADD FILEGROUP ' + QUOTENAME(@MODName) + N' CONTAINS MEMORY_OPTIMIZED_DATA;';
|
50
|
+
EXECUTE (@SQL);
|
51
|
+
|
52
|
+
END;
|
53
|
+
|
54
|
+
-- add container in the filegroup
|
55
|
+
IF NOT EXISTS (SELECT * FROM sys.database_files WHERE data_space_id IN (SELECT data_space_id FROM sys.filegroups WHERE type = N'FX'))
|
56
|
+
BEGIN
|
57
|
+
SET @SQL = N'
|
58
|
+
ALTER DATABASE CURRENT
|
59
|
+
ADD FILE (name = N''' + @MODName + ''', filename = '''
|
60
|
+
+ @MemoryOptimizedFilegroupFolder + N''')
|
61
|
+
TO FILEGROUP ' + QUOTENAME(@MODName);
|
62
|
+
EXECUTE (@SQL);
|
63
|
+
END
|
64
|
+
END
|
65
|
+
|
66
|
+
-- 3. set compat level to 130 if it is lower
|
67
|
+
IF (SELECT compatibility_level FROM sys.databases WHERE database_id=DB_ID()) < 130
|
68
|
+
ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 130
|
69
|
+
|
70
|
+
-- 4. enable MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT for the database
|
71
|
+
ALTER DATABASE CURRENT SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = ON;
|
72
|
+
|
73
|
+
|
74
|
+
END TRY
|
75
|
+
BEGIN CATCH
|
76
|
+
PRINT N'Error enabling In-Memory OLTP';
|
77
|
+
IF XACT_STATE() != 0
|
78
|
+
ROLLBACK;
|
79
|
+
THROW;
|
80
|
+
END CATCH;
|
81
|
+
END;
|
@@ -21,6 +21,7 @@ ActiveRecord::Schema.define do
|
|
21
21
|
# Our type methods.
|
22
22
|
t.real :real_col
|
23
23
|
t.money :money_col
|
24
|
+
t.smalldatetime :smalldatetime_col
|
24
25
|
t.datetime2 :datetime2_col
|
25
26
|
t.datetimeoffset :datetimeoffset
|
26
27
|
t.smallmoney :smallmoney_col
|
@@ -33,10 +34,24 @@ ActiveRecord::Schema.define do
|
|
33
34
|
t.varbinary :varbinary_col
|
34
35
|
t.uuid :uuid_col
|
35
36
|
t.ss_timestamp :sstimestamp_col
|
37
|
+
if supports_json?
|
38
|
+
t.json :json_col
|
39
|
+
else
|
40
|
+
t.text :json_col
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
44
|
# Edge Cases
|
39
45
|
|
46
|
+
if ENV['IN_MEMORY_OLTP'] && supports_in_memory_oltp?
|
47
|
+
create_table 'sst_memory', force: true, id: false,
|
48
|
+
options: 'WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)' do |t|
|
49
|
+
t.primary_key_nonclustered :id
|
50
|
+
t.string :name
|
51
|
+
t.timestamps
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
40
55
|
create_table 'sst_bookings', force: true do |t|
|
41
56
|
t.string :name
|
42
57
|
t.datetime2 :created_at, null: false
|
@@ -0,0 +1,15 @@
|
|
1
|
+
if ENV['IN_MEMORY_OLTP']
|
2
|
+
require 'config'
|
3
|
+
require 'active_record'
|
4
|
+
require 'support/config'
|
5
|
+
require 'support/connection'
|
6
|
+
|
7
|
+
ARTest.connect
|
8
|
+
|
9
|
+
if ActiveRecord::Base.connection.supports_in_memory_oltp?
|
10
|
+
puts 'Configuring In-Memory OLTP...'
|
11
|
+
inmem_file = ARTest::SQLServer.test_root_sqlserver, 'schema', 'enable-in-memory-oltp.sql'
|
12
|
+
inmem_sql = File.read File.join(inmem_file)
|
13
|
+
ActiveRecord::Base.connection.execute(inmem_sql)
|
14
|
+
end
|
15
|
+
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: 5.0.
|
4
|
+
version: 5.0.6
|
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-03-22 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: activerecord
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- lib/active_record/connection_adapters/sqlserver/type/decimal.rb
|
99
99
|
- lib/active_record/connection_adapters/sqlserver/type/float.rb
|
100
100
|
- lib/active_record/connection_adapters/sqlserver/type/integer.rb
|
101
|
+
- lib/active_record/connection_adapters/sqlserver/type/json.rb
|
101
102
|
- lib/active_record/connection_adapters/sqlserver/type/money.rb
|
102
103
|
- lib/active_record/connection_adapters/sqlserver/type/real.rb
|
103
104
|
- lib/active_record/connection_adapters/sqlserver/type/small_integer.rb
|
@@ -142,6 +143,8 @@ files:
|
|
142
143
|
- test/cases/fetch_test_sqlserver.rb
|
143
144
|
- test/cases/fully_qualified_identifier_test_sqlserver.rb
|
144
145
|
- test/cases/helper_sqlserver.rb
|
146
|
+
- test/cases/index_test_sqlserver.rb
|
147
|
+
- test/cases/json_test_sqlserver.rb
|
145
148
|
- test/cases/migration_test_sqlserver.rb
|
146
149
|
- test/cases/order_test_sqlserver.rb
|
147
150
|
- test/cases/pessimistic_locking_test_sqlserver.rb
|
@@ -174,6 +177,7 @@ files:
|
|
174
177
|
- test/models/sqlserver/quoted_table.rb
|
175
178
|
- test/models/sqlserver/quoted_view_1.rb
|
176
179
|
- test/models/sqlserver/quoted_view_2.rb
|
180
|
+
- test/models/sqlserver/sst_memory.rb
|
177
181
|
- test/models/sqlserver/string_default.rb
|
178
182
|
- test/models/sqlserver/string_defaults_big_view.rb
|
179
183
|
- test/models/sqlserver/string_defaults_view.rb
|
@@ -182,6 +186,7 @@ files:
|
|
182
186
|
- test/models/sqlserver/uppered.rb
|
183
187
|
- test/models/sqlserver/uuid.rb
|
184
188
|
- test/schema/datatypes/2012.sql
|
189
|
+
- test/schema/enable-in-memory-oltp.sql
|
185
190
|
- test/schema/sqlserver_specific_schema.rb
|
186
191
|
- test/support/coerceable_test_sqlserver.rb
|
187
192
|
- test/support/connection_reflection.rb
|
@@ -190,6 +195,7 @@ files:
|
|
190
195
|
- test/support/paths_sqlserver.rb
|
191
196
|
- test/support/rake_helpers.rb
|
192
197
|
- test/support/sql_counter_sqlserver.rb
|
198
|
+
- test/support/test_in_memory_oltp.rb
|
193
199
|
homepage: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter
|
194
200
|
licenses:
|
195
201
|
- MIT
|
@@ -210,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
210
216
|
version: '0'
|
211
217
|
requirements: []
|
212
218
|
rubyforge_project:
|
213
|
-
rubygems_version: 2.6.
|
219
|
+
rubygems_version: 2.6.8
|
214
220
|
signing_key:
|
215
221
|
specification_version: 4
|
216
222
|
summary: ActiveRecord SQL Server Adapter.
|
@@ -229,6 +235,8 @@ test_files:
|
|
229
235
|
- test/cases/fetch_test_sqlserver.rb
|
230
236
|
- test/cases/fully_qualified_identifier_test_sqlserver.rb
|
231
237
|
- test/cases/helper_sqlserver.rb
|
238
|
+
- test/cases/index_test_sqlserver.rb
|
239
|
+
- test/cases/json_test_sqlserver.rb
|
232
240
|
- test/cases/migration_test_sqlserver.rb
|
233
241
|
- test/cases/order_test_sqlserver.rb
|
234
242
|
- test/cases/pessimistic_locking_test_sqlserver.rb
|
@@ -261,6 +269,7 @@ test_files:
|
|
261
269
|
- test/models/sqlserver/quoted_table.rb
|
262
270
|
- test/models/sqlserver/quoted_view_1.rb
|
263
271
|
- test/models/sqlserver/quoted_view_2.rb
|
272
|
+
- test/models/sqlserver/sst_memory.rb
|
264
273
|
- test/models/sqlserver/string_default.rb
|
265
274
|
- test/models/sqlserver/string_defaults_big_view.rb
|
266
275
|
- test/models/sqlserver/string_defaults_view.rb
|
@@ -269,6 +278,7 @@ test_files:
|
|
269
278
|
- test/models/sqlserver/uppered.rb
|
270
279
|
- test/models/sqlserver/uuid.rb
|
271
280
|
- test/schema/datatypes/2012.sql
|
281
|
+
- test/schema/enable-in-memory-oltp.sql
|
272
282
|
- test/schema/sqlserver_specific_schema.rb
|
273
283
|
- test/support/coerceable_test_sqlserver.rb
|
274
284
|
- test/support/connection_reflection.rb
|
@@ -277,3 +287,4 @@ test_files:
|
|
277
287
|
- test/support/paths_sqlserver.rb
|
278
288
|
- test/support/rake_helpers.rb
|
279
289
|
- test/support/sql_counter_sqlserver.rb
|
290
|
+
- test/support/test_in_memory_oltp.rb
|