activerecord-sqlserver-adapter 5.0.5 → 5.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|