activerecord-sqlserver-adapter 7.0.2.0 → 7.0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c5135f809d1709197eef056bbac22e09adefd4ead979cc32e72a2e12c52453a
4
- data.tar.gz: 6a0f0b78026468b273926d259a93c7edb38dc8de176f88e32d8bd64c1667c90c
3
+ metadata.gz: 4a8fb3b90e33355b2f91c6b6cd6e7494f822a0f348881f385bbaee8fbe3b2eee
4
+ data.tar.gz: a1a24523b114d48249c543c445d15c77b50239c39aa022468f9f06efe378efeb
5
5
  SHA512:
6
- metadata.gz: 299fe79b88ebbfb2edfac1145c4969c3cde3e7eec25bf5415e671b8bc5e3564cf66cca398a1ca7285cc1b4950a0a0326162860b359a41e018c4bae8c058921ba
7
- data.tar.gz: 13566655cfaba356a7b1a1f63e95ad2a38ab0a95a6f85eaf9af4571e04ee1db03e8b2cbd4a9d58599de289c3769046e27ee23ef3898219ecc55fd24f7ec31faf
6
+ metadata.gz: 813a2f3bf0752a8c9f310997d01e1ffb7a4216381bcd58cb082c811370f36f81e2c8c17a84b2aafc23493afcda7e86af499cc609721e59f0e0bd4b2931709b90
7
+ data.tar.gz: d99ed863a56ab7d9036e433521b64fb04d0e8f2764c01d1109d6767a9f62beb46adbacbb36709d6702dc27f31afd0cdbf0adec148cdd5952a1a343f6392bf90d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## v7.0.3.0
2
+
3
+ #### Fixed
4
+
5
+ - [#1052](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1052) Ignore casing of VALUES clause when inserting records using SQL
6
+ - [#1053](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1053) Fix insertion of records to non-default schema table using raw SQL
7
+ - [#1059](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1059) Fix enums defined on string columns
8
+
9
+ #### Changed
10
+
11
+ - [#1057](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1057) Set precision 6 by default for timestamps.
12
+
1
13
  ## v7.0.2.0
2
14
 
3
15
  [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.1.0...v7.0.2.0)
data/README.md CHANGED
@@ -13,9 +13,9 @@ Interested in older versions? We follow a rational versioning policy that tracks
13
13
 
14
14
  | Adapter Version | Rails Version | Support |
15
15
  |-----------------| ------------- | ------------------------------------------------------------------------------------------- |
16
- | `7.0.1.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
16
+ | `7.0.3.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
17
17
  | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) |
18
- | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) |
18
+ | `6.0.3` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) |
19
19
  | `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) |
20
20
  | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) |
21
21
  | `4.2.18` | `4.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) |
@@ -29,7 +29,11 @@ We support every data type supported by FreeTDS. All simplified Rails types in m
29
29
 
30
30
  The following types (`date`, `datetime2`, `datetimeoffset`, `time`) all require TDS version `7.3` with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to `7.3`. The adapter also sets TinyTDS's `tds_version` to this as well if non is specified.
31
31
 
32
- The Rails v5 adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. Using a precision with the `:datetime` type will signal the adapter to use the `datetime2` type under the hood.
32
+ The adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported.
33
+
34
+ By default, precision 6 is used for `:datetime` types if precision is not specified. Any non-nil precision will tell
35
+ the adapter to use the `datetime2` column type. To create a `datetime` column using a migration a precision of `nil`
36
+ should be specified, otherwise the precision will default to 6 and a `datetime2` column will be created.
33
37
 
34
38
 
35
39
  #### Identity Inserts with Triggers
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.0.2.0
1
+ 7.0.3.0
@@ -278,11 +278,11 @@ module ActiveRecord
278
278
  id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted
279
279
  <<~SQL.squish
280
280
  DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type});
281
- #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"}
281
+ #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"}
282
282
  SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable
283
283
  SQL
284
284
  else
285
- sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
285
+ sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT INSERTED.#{quoted_pk}"
286
286
  end
287
287
  else
288
288
  "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
@@ -392,7 +392,7 @@ module ActiveRecord
392
392
  self.class.exclude_output_inserted_table_names[table_name]
393
393
  end
394
394
 
395
- def exec_insert_requires_identity?(sql, pk, binds)
395
+ def exec_insert_requires_identity?(sql, _pk, _binds)
396
396
  query_requires_identity_insert?(sql)
397
397
  end
398
398
 
@@ -402,7 +402,7 @@ module ActiveRecord
402
402
  raw_table_name = get_raw_table_name(sql)
403
403
  id_column = identity_columns(raw_table_name).first
404
404
 
405
- id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? SQLServer::Utils.extract_identifiers(raw_table_name).object : false
405
+ id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? SQLServer::Utils.extract_identifiers(raw_table_name).quoted : false
406
406
  end
407
407
 
408
408
  def insert_sql?(sql)
@@ -346,7 +346,7 @@ module ActiveRecord
346
346
  datetime2: { name: "datetime2" },
347
347
  datetimeoffset: { name: "datetimeoffset" },
348
348
  smalldatetime: { name: "smalldatetime" },
349
- timestamp: { name: "datetime" },
349
+ timestamp: { name: "datetime2(6)" },
350
350
  time: { name: "time" },
351
351
  char: { name: "char" },
352
352
  varchar: { name: "varchar", limit: 8000 },
@@ -101,11 +101,16 @@ module ActiveRecord
101
101
 
102
102
  def new_column_definition(name, type, **options)
103
103
  case type
104
- when :datetime
105
- type = :datetime2 if options[:precision]
104
+ when :datetime, :timestamp
105
+ # If no precision then default it to 6.
106
+ options[:precision] = 6 unless options.key?(:precision)
107
+
108
+ # If there is precision then column must be of type 'datetime2'.
109
+ type = :datetime2 unless options[:precision].nil?
106
110
  when :primary_key
107
111
  options[:is_identity] = true
108
112
  end
113
+
109
114
  super
110
115
  end
111
116
  end
@@ -27,6 +27,12 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  def eql?(other)
30
+ # Support comparing `Type::Char`, `Type::Varchar` and `VarcharMax` with strings.
31
+ # This happens when we use enum with string columns.
32
+ if other.is_a?(::String)
33
+ return type.is_a?(ActiveRecord::ConnectionAdapters::SQLServer::Type::String) && value == other
34
+ end
35
+
30
36
  self.class == other.class && value == other.value
31
37
  end
32
38
  alias :== :eql?
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  QUOTED_STRING_PREFIX = "N"
10
10
 
11
11
  # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL
12
- # Inspiried from Rails PostgreSQL::Name adapter object in their own Utils.
12
+ # Inspired from Rails PostgreSQL::Name adapter object in their own Utils.
13
13
  #
14
14
  class Name
15
15
  SEPARATOR = "."
@@ -193,17 +193,31 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
193
193
  @identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')"
194
194
  @identity_insert_sql_unordered = "INSERT INTO [funny_jokes] ([name],[id]) VALUES('Knock knock',420)"
195
195
  @identity_insert_sql_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([id],[name]) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'"
196
- @identity_insert_sql_unquoted_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'"
196
+ @identity_insert_sql_unquoted_sp = "EXEC sp_executesql N'INSERT INTO funny_jokes (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'"
197
197
  @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420"
198
+
199
+ @identity_insert_sql_non_dbo = "INSERT INTO [test].[aliens] ([id],[name]) VALUES(420,'Mork')"
200
+ @identity_insert_sql_non_dbo_unquoted = "INSERT INTO test.aliens ([id],[name]) VALUES(420,'Mork')"
201
+ @identity_insert_sql_non_dbo_unordered = "INSERT INTO [test].[aliens] ([name],[id]) VALUES('Mork',420)"
202
+ @identity_insert_sql_non_dbo_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([id],[name]) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'"
203
+ @identity_insert_sql_non_dbo_unquoted_sp = "EXEC sp_executesql N'INSERT INTO test.aliens (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'"
204
+ @identity_insert_sql_non_dbo_unordered_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Mork', @1 = 420"
198
205
  end
199
206
 
200
- it "return unquoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do
201
- assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql)
202
- assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted)
203
- assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered)
204
- assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_sp)
205
- assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted_sp)
206
- assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered_sp)
207
+ it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do
208
+ assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql)
209
+ assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted)
210
+ assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered)
211
+ assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_sp)
212
+ assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted_sp)
213
+ assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered_sp)
214
+
215
+ assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo)
216
+ assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted)
217
+ assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered)
218
+ assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_sp)
219
+ assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted_sp)
220
+ assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered_sp)
207
221
  end
208
222
 
209
223
  it "return false to #query_requires_identity_insert? for normal SQL" do
@@ -532,4 +546,26 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
532
546
  end
533
547
  end
534
548
  end
549
+
550
+ describe 'table is in non-dbo schema' do
551
+ it "records can be created successfully" do
552
+ Alien.create!(name: 'Trisolarans')
553
+ end
554
+
555
+ it 'records can be inserted using SQL' do
556
+ Alien.connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')")
557
+ end
558
+ end
559
+
560
+ describe "exec_insert" do
561
+ it 'values clause should be case-insensitive' do
562
+ assert_difference("Post.count", 4) do
563
+ first_insert = connection.exec_insert("INSERT INTO [posts] ([id],[title],[body]) VALUES(100, 'Title', 'Body'), (102, 'Title', 'Body')")
564
+ second_insert = connection.exec_insert("INSERT INTO [posts] ([id],[title],[body]) values(113, 'Body', 'Body'), (114, 'Body', 'Body')")
565
+
566
+ assert_equal first_insert.rows.map(&:first), [100, 102]
567
+ assert_equal second_insert.rows.map(&:first), [113, 114]
568
+ end
569
+ end
570
+ end
535
571
  end
@@ -450,7 +450,7 @@ class CalculationsTest < ActiveRecord::TestCase
450
450
  FROM companies
451
451
  INNER JOIN accounts ON companies.id = accounts.firm_id
452
452
  WHERE companies.id = ?
453
- GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description
453
+ GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description, companies.status
454
454
  ORDER BY companies.id
455
455
  OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
456
456
  SQL
@@ -476,7 +476,7 @@ class CalculationsTest < ActiveRecord::TestCase
476
476
  .select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit")
477
477
  .where(id: rails_core)
478
478
  .joins(:account)
479
- .group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description)
479
+ .group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description, :status)
480
480
  .take!
481
481
 
482
482
  # all the DependentFirm attributes should be present
@@ -543,7 +543,7 @@ module ActiveRecord
543
543
  assert_equal "hello", five.default
544
544
  end
545
545
 
546
- # Rails adds precision 6 by default, sql server uses datetime2 for datetimes with precision
546
+ # Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision.
547
547
  coerce_tests! :test_add_column_with_postgresql_datetime_type
548
548
  def test_add_column_with_postgresql_datetime_type_coerced
549
549
  connection.create_table :testings do |t|
@@ -556,7 +556,7 @@ module ActiveRecord
556
556
  assert_equal "datetime2(6)", column.sql_type
557
557
  end
558
558
 
559
- # timestamp is datetime with default limit
559
+ # Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision.
560
560
  coerce_tests! :test_change_column_with_timestamp_type
561
561
  def test_change_column_with_timestamp_type_coerced
562
562
  connection.create_table :testings do |t|
@@ -568,7 +568,20 @@ module ActiveRecord
568
568
  column = connection.columns(:testings).find { |c| c.name == "foo" }
569
569
 
570
570
  assert_equal :datetime, column.type
571
- assert_equal "datetime", column.sql_type
571
+ assert_equal "datetime2(6)", column.sql_type
572
+ end
573
+
574
+ # Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision.
575
+ coerce_tests! :test_add_column_with_timestamp_type
576
+ def test_add_column_with_timestamp_type_coerced
577
+ connection.create_table :testings do |t|
578
+ t.column :foo, :timestamp
579
+ end
580
+
581
+ column = connection.columns(:testings).find { |c| c.name == "foo" }
582
+
583
+ assert_equal :datetime, column.type
584
+ assert_equal "datetime2(6)", column.sql_type
572
585
  end
573
586
  end
574
587
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+
5
+ class EnumTestSQLServer < ActiveRecord::TestCase
6
+
7
+ # Check that enums are supported for all string types.
8
+ # For each type we check: cast, serialize, and update by declaration.
9
+ # We create a custom class for each type to test.
10
+ %w[char_10 varchar_50 varchar_max text nchar_10 nvarchar_50 nvarchar_max ntext].each do |col_name|
11
+ describe "support #{col_name} enums" do
12
+ let(:klass) do
13
+ Class.new(ActiveRecord::Base) do
14
+ self.table_name = 'sst_datatypes'
15
+
16
+ enum col_name => { alpha: "A", beta: "B" }
17
+ end
18
+ end
19
+
20
+ it "type.cast" do
21
+ type = klass.type_for_attribute(col_name)
22
+
23
+ assert_equal "alpha", type.cast('A')
24
+ assert_equal "beta", type.cast('B')
25
+ end
26
+
27
+ it "type.serialize" do
28
+ type = klass.type_for_attribute(col_name)
29
+
30
+ assert_equal 'A', type.serialize('A')
31
+ assert_equal 'B', type.serialize('B')
32
+
33
+ assert_equal 'A', type.serialize(:alpha)
34
+ assert_equal 'B', type.serialize(:beta)
35
+ end
36
+
37
+ it "update by declaration" do
38
+ r = klass.new
39
+
40
+ r.alpha!
41
+ assert_predicate r, :alpha?
42
+
43
+ r.beta!
44
+ assert_not_predicate r, :alpha?
45
+ assert_predicate r, :beta?
46
+ end
47
+ end
48
+ end
49
+ end
@@ -10,122 +10,130 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
10
10
 
11
11
  it "sst_datatypes" do
12
12
  generate_schema_for_table "sst_datatypes"
13
- assert_line :bigint, type: "bigint", limit: nil, precision: nil, scale: nil, default: 42
14
- assert_line :int, type: "integer", limit: nil, precision: nil, scale: nil, default: 42
15
- assert_line :smallint, type: "integer", limit: 2, precision: nil, scale: nil, default: 42
16
- assert_line :tinyint, type: "integer", limit: 1, precision: nil, scale: nil, default: 42
17
- assert_line :bit, type: "boolean", limit: nil, precision: nil, scale: nil, default: true
18
- assert_line :decimal_9_2, type: "decimal", limit: nil, precision: 9, scale: 2, default: 12345.01
19
- assert_line :numeric_18_0, type: "decimal", limit: nil, precision: 18, scale: nil, default: 191
20
- assert_line :numeric_36_2, type: "decimal", limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01
21
- assert_line :money, type: "money", limit: nil, precision: 19, scale: 4, default: 4.2
22
- assert_line :smallmoney, type: "smallmoney", limit: nil, precision: 10, scale: 4, default: 4.2
13
+
14
+ assert_line :bigint, type: "bigint", default: 42
15
+ assert_line :int, type: "integer", default: 42
16
+ assert_line :smallint, type: "integer", limit: 2, default: 42
17
+ assert_line :tinyint, type: "integer", limit: 1, default: 42
18
+ assert_line :bit, type: "boolean", default: true
19
+ assert_line :decimal_9_2, type: "decimal", precision: 9, scale: 2, default: 12345.01
20
+ assert_line :numeric_18_0, type: "decimal", precision: 18, default: 191
21
+ assert_line :numeric_36_2, type: "decimal", precision: 36, scale: 2, default: 12345678901234567890.01
22
+ assert_line :money, type: "money", precision: 19, scale: 4, default: 4.2
23
+ assert_line :smallmoney, type: "smallmoney", precision: 10, scale: 4, default: 4.2
23
24
  # Approximate Numerics
24
- assert_line :float, type: "float", limit: nil, precision: nil, scale: nil, default: 123.00000001
25
- assert_line :real, type: "real", limit: nil, precision: nil, scale: nil, default: 123.45
25
+ assert_line :float, type: "float", default: 123.00000001
26
+ assert_line :real, type: "real", default: 123.45
26
27
  # Date and Time
27
- assert_line :date, type: "date", limit: nil, precision: nil, scale: nil, default: "01-01-0001"
28
- assert_line :datetime, type: "datetime", limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123"
28
+ assert_line :date, type: "date", default: "01-01-0001"
29
+ assert_line :datetime, type: "datetime", precision: nil, default: "01-01-1753 00:00:00.123"
29
30
  if connection_dblib_73?
30
- assert_line :datetime2_7, type: "datetime", limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999"
31
- assert_line :datetime2_3, type: "datetime", limit: nil, precision: 3, scale: nil, default: nil
32
- assert_line :datetime2_1, type: "datetime", limit: nil, precision: 1, scale: nil, default: nil
31
+ assert_line :datetime2_7, type: "datetime", precision: 7, default: "12-31-9999 23:59:59.9999999"
32
+ assert_line :datetime2_3, type: "datetime", precision: 3
33
+ assert_line :datetime2_1, type: "datetime", precision: 1
33
34
  end
34
- assert_line :smalldatetime, type: "smalldatetime", limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0"
35
+ assert_line :smalldatetime, type: "smalldatetime", default: "01-01-1901 15:45:00.0"
35
36
  if connection_dblib_73?
36
- assert_line :time_7, type: "time", limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215"
37
- assert_line :time_2, type: "time", limit: nil, precision: 2, scale: nil, default: nil
38
- assert_line :time_default, type: "time", limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978"
37
+ assert_line :time_7, type: "time", precision: 7, default: "04:20:00.2883215"
38
+ assert_line :time_2, type: "time", precision: 2
39
+ assert_line :time_default, type: "time", precision: 7, default: "15:03:42.0621978"
39
40
  end
40
41
  # Character Strings
41
- assert_line :char_10, type: "char", limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil
42
- assert_line :varchar_50, type: "varchar", limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil
43
- assert_line :varchar_max, type: "varchar_max", limit: nil, precision: nil, scale: nil, default: "test varchar_max", collation: nil
44
- assert_line :text, type: "text_basic", limit: nil, precision: nil, scale: nil, default: "test text", collation: nil
42
+ assert_line :char_10, type: "char", limit: 10, default: "1234567890"
43
+ assert_line :varchar_50, type: "varchar", limit: 50, default: "test varchar_50"
44
+ assert_line :varchar_max, type: "varchar_max", default: "test varchar_max"
45
+ assert_line :text, type: "text_basic", default: "test text"
45
46
  # Unicode Character Strings
46
- assert_line :nchar_10, type: "nchar", limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: nil
47
- assert_line :nvarchar_50, type: "string", limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil
48
- assert_line :nvarchar_max, type: "text", limit: nil, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil
49
- assert_line :ntext, type: "ntext", limit: nil, precision: nil, scale: nil, default: "test ntext åå", collation: nil
47
+ assert_line :nchar_10, type: "nchar", limit: 10, default: "12345678åå"
48
+ assert_line :nvarchar_50, type: "string", limit: 50, default: "test nvarchar_50 åå"
49
+ assert_line :nvarchar_max, type: "text", default: "test nvarchar_max åå"
50
+ assert_line :ntext, type: "ntext", default: "test ntext åå"
50
51
  # Binary Strings
51
- assert_line :binary_49, type: "binary_basic", limit: 49, precision: nil, scale: nil, default: nil
52
- assert_line :varbinary_49, type: "varbinary", limit: 49, precision: nil, scale: nil, default: nil
53
- assert_line :varbinary_max, type: "binary", limit: nil, precision: nil, scale: nil, default: nil
52
+ assert_line :binary_49, type: "binary_basic", limit: 49
53
+ assert_line :varbinary_49, type: "varbinary", limit: 49
54
+ assert_line :varbinary_max, type: "binary"
54
55
  # Other Data Types
55
- assert_line :uniqueidentifier, type: "uuid", limit: nil, precision: nil, scale: nil, default: -> { "newid()" }
56
- assert_line :timestamp, type: "ss_timestamp", limit: nil, precision: nil, scale: nil, default: nil
56
+ assert_line :uniqueidentifier, type: "uuid", default: -> { "newid()" }
57
+ assert_line :timestamp, type: "ss_timestamp"
57
58
  end
58
59
 
59
60
  it "sst_datatypes_migration" do
60
61
  columns = SSTestDatatypeMigration.columns_hash
61
62
  generate_schema_for_table "sst_datatypes_migration"
63
+
62
64
  # Simple Rails conventions
63
- _(columns["integer_col"].sql_type).must_equal "int(4)"
64
- _(columns["bigint_col"].sql_type).must_equal "bigint(8)"
65
- _(columns["boolean_col"].sql_type).must_equal "bit"
66
- _(columns["decimal_col"].sql_type).must_equal "decimal(18,0)"
67
- _(columns["float_col"].sql_type).must_equal "float"
68
- _(columns["string_col"].sql_type).must_equal "nvarchar(4000)"
69
- _(columns["text_col"].sql_type).must_equal "nvarchar(max)"
70
- _(columns["datetime_col"].sql_type).must_equal "datetime2(6)"
71
- _(columns["timestamp_col"].sql_type).must_equal "datetime"
72
- _(columns["time_col"].sql_type).must_equal "time(7)"
73
- _(columns["date_col"].sql_type).must_equal "date"
74
- _(columns["binary_col"].sql_type).must_equal "varbinary(max)"
75
- assert_line :integer_col, type: "integer", limit: nil, precision: nil, scale: nil, default: nil
76
- assert_line :bigint_col, type: "bigint", limit: nil, precision: nil, scale: nil, default: nil
77
- assert_line :boolean_col, type: "boolean", limit: nil, precision: nil, scale: nil, default: nil
78
- assert_line :decimal_col, type: "decimal", limit: nil, precision: 18, scale: nil, default: nil
79
- assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil
80
- assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil
81
- assert_line :text_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil
82
- assert_line :datetime_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil
83
- assert_line :timestamp_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil
84
- assert_line :time_col, type: "time", limit: nil, precision: 7, scale: nil, default: nil
85
- assert_line :date_col, type: "date", limit: nil, precision: nil, scale: nil, default: nil
86
- assert_line :binary_col, type: "binary", limit: nil, precision: nil, scale: nil, default: nil
65
+ _(columns["integer_col"].sql_type).must_equal "int(4)"
66
+ _(columns["bigint_col"].sql_type).must_equal "bigint(8)"
67
+ _(columns["boolean_col"].sql_type).must_equal "bit"
68
+ _(columns["decimal_col"].sql_type).must_equal "decimal(18,0)"
69
+ _(columns["float_col"].sql_type).must_equal "float"
70
+ _(columns["string_col"].sql_type).must_equal "nvarchar(4000)"
71
+ _(columns["text_col"].sql_type).must_equal "nvarchar(max)"
72
+ _(columns["datetime_nil_precision_col"].sql_type).must_equal "datetime"
73
+ _(columns["datetime_col"].sql_type).must_equal "datetime2(6)"
74
+ _(columns["timestamp_col"].sql_type).must_equal "datetime2(6)"
75
+ _(columns["time_col"].sql_type).must_equal "time(7)"
76
+ _(columns["date_col"].sql_type).must_equal "date"
77
+ _(columns["binary_col"].sql_type).must_equal "varbinary(max)"
78
+
79
+ assert_line :integer_col, type: "integer"
80
+ assert_line :bigint_col, type: "bigint"
81
+ assert_line :boolean_col, type: "boolean"
82
+ assert_line :decimal_col, type: "decimal", precision: 18
83
+ assert_line :float_col, type: "float"
84
+ assert_line :string_col, type: "string"
85
+ assert_line :text_col, type: "text"
86
+ assert_line :datetime_nil_precision_col, type: "datetime", precision: nil
87
+ assert_line :datetime_col, type: "datetime"
88
+ assert_line :datetime_col, type: "datetime"
89
+ assert_line :timestamp_col, type: "datetime"
90
+ assert_line :time_col, type: "time", precision: 7
91
+ assert_line :date_col, type: "date"
92
+ assert_line :binary_col, type: "binary"
93
+
87
94
  # Our type methods.
88
- _(columns["real_col"].sql_type).must_equal "real"
89
- _(columns["money_col"].sql_type).must_equal "money"
95
+ _(columns["real_col"].sql_type).must_equal "real"
96
+ _(columns["money_col"].sql_type).must_equal "money"
90
97
  _(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime"
91
- _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)"
92
- _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)"
93
- _(columns["smallmoney_col"].sql_type).must_equal "smallmoney"
94
- _(columns["char_col"].sql_type).must_equal "char(1)"
95
- _(columns["varchar_col"].sql_type).must_equal "varchar(8000)"
96
- _(columns["text_basic_col"].sql_type).must_equal "text"
97
- _(columns["nchar_col"].sql_type).must_equal "nchar(1)"
98
- _(columns["ntext_col"].sql_type).must_equal "ntext"
99
- _(columns["binary_basic_col"].sql_type).must_equal "binary(1)"
100
- _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)"
101
- _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier"
102
- _(columns["sstimestamp_col"].sql_type).must_equal "timestamp"
103
- _(columns["json_col"].sql_type).must_equal "nvarchar(max)"
104
- assert_line :real_col, type: "real", limit: nil, precision: nil, scale: nil, default: nil
105
- assert_line :money_col, type: "money", limit: nil, precision: 19, scale: 4, default: nil
106
- assert_line :smalldatetime_col, type: "smalldatetime", limit: nil, precision: nil, scale: nil, default: nil
107
- assert_line :datetime2_col, type: "datetime", limit: nil, precision: 7, scale: nil, default: nil
108
- assert_line :datetimeoffset, type: "datetimeoffset", limit: nil, precision: 7, scale: nil, default: nil
109
- assert_line :smallmoney_col, type: "smallmoney", limit: nil, precision: 10, scale: 4, default: nil
110
- assert_line :char_col, type: "char", limit: 1, precision: nil, scale: nil, default: nil
111
- assert_line :varchar_col, type: "varchar", limit: nil, precision: nil, scale: nil, default: nil
112
- assert_line :text_basic_col, type: "text_basic", limit: nil, precision: nil, scale: nil, default: nil
113
- assert_line :nchar_col, type: "nchar", limit: 1, precision: nil, scale: nil, default: nil
114
- assert_line :ntext_col, type: "ntext", limit: nil, precision: nil, scale: nil, default: nil
115
- assert_line :binary_basic_col, type: "binary_basic", limit: 1, precision: nil, scale: nil, default: nil
116
- assert_line :varbinary_col, type: "varbinary", limit: nil, precision: nil, scale: nil, default: nil
117
- assert_line :uuid_col, type: "uuid", limit: nil, precision: nil, scale: nil, default: nil
118
- assert_line :sstimestamp_col, type: "ss_timestamp", limit: nil, precision: nil, scale: nil, default: nil
119
- assert_line :json_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil
98
+ _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)"
99
+ _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)"
100
+ _(columns["smallmoney_col"].sql_type).must_equal "smallmoney"
101
+ _(columns["char_col"].sql_type).must_equal "char(1)"
102
+ _(columns["varchar_col"].sql_type).must_equal "varchar(8000)"
103
+ _(columns["text_basic_col"].sql_type).must_equal "text"
104
+ _(columns["nchar_col"].sql_type).must_equal "nchar(1)"
105
+ _(columns["ntext_col"].sql_type).must_equal "ntext"
106
+ _(columns["binary_basic_col"].sql_type).must_equal "binary(1)"
107
+ _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)"
108
+ _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier"
109
+ _(columns["sstimestamp_col"].sql_type).must_equal "timestamp"
110
+ _(columns["json_col"].sql_type).must_equal "nvarchar(max)"
111
+
112
+ assert_line :real_col, type: "real"
113
+ assert_line :money_col, type: "money", precision: 19, scale: 4
114
+ assert_line :smalldatetime_col, type: "smalldatetime"
115
+ assert_line :datetime2_col, type: "datetime", precision: 7
116
+ assert_line :datetimeoffset, type: "datetimeoffset", precision: 7
117
+ assert_line :smallmoney_col, type: "smallmoney", precision: 10, scale: 4
118
+ assert_line :char_col, type: "char", limit: 1
119
+ assert_line :varchar_col, type: "varchar"
120
+ assert_line :text_basic_col, type: "text_basic"
121
+ assert_line :nchar_col, type: "nchar", limit: 1
122
+ assert_line :ntext_col, type: "ntext"
123
+ assert_line :binary_basic_col, type: "binary_basic", limit: 1
124
+ assert_line :varbinary_col, type: "varbinary"
125
+ assert_line :uuid_col, type: "uuid"
126
+ assert_line :sstimestamp_col, type: "ss_timestamp", null: false
127
+ assert_line :json_col, type: "text"
120
128
  end
121
129
 
122
130
  it "dump column collation" do
123
131
  generate_schema_for_table('sst_string_collation')
124
132
 
125
- assert_line :string_without_collation, type: "string", limit: nil, default: nil, collation: nil
126
- assert_line :string_default_collation, type: "varchar", limit: nil, default: nil, collation: nil
127
- assert_line :string_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS"
128
- assert_line :varchar_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS"
133
+ assert_line :string_without_collation, type: "string"
134
+ assert_line :string_default_collation, type: "varchar"
135
+ assert_line :string_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS"
136
+ assert_line :varchar_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS"
129
137
  end
130
138
 
131
139
  # Special Cases
@@ -140,8 +148,9 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
140
148
 
141
149
  it "no id with model driven primary key" do
142
150
  output = generate_schema_for_table "sst_no_pk_data"
151
+
143
152
  _(output).must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do}
144
- assert_line :name, type: "string", limit: nil, default: nil, collation: nil
153
+ assert_line :name, type: "string"
145
154
  end
146
155
 
147
156
  it "dumps field with unique key constraints only once" do
@@ -154,6 +163,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
154
163
 
155
164
  def generate_schema_for_table(*table_names)
156
165
  require "stringio"
166
+
157
167
  stream = StringIO.new
158
168
  ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names
159
169
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
@@ -173,15 +183,19 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
173
183
  @schema_lines[column_name.to_s]
174
184
  end
175
185
 
176
- def assert_line(column_name, options = {})
186
+ def assert_line(column_name, expected_options = {})
177
187
  line = line(column_name)
178
188
  assert line, "Could not find line with column name: #{column_name.inspect} in schema:\n#{schema}"
179
189
 
180
- [:type, :limit, :precision, :scale, :collation, :default].each do |key|
181
- next unless options.key?(key)
190
+ # Check that the expected and actual option keys.
191
+ expected_options_keys = expected_options.keys
192
+ expected_options_keys.delete(:type)
193
+ _(expected_options_keys.sort).must_equal (line.options.keys.sort), "For column '#{column_name}' expected schema options and actual schema options do not match."
182
194
 
195
+ # Check the expected and actual option values.
196
+ expected_options.each do |key, expected|
183
197
  actual = key == :type ? line.send(:type_method) : line.send(key)
184
- expected = options[key]
198
+
185
199
  message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}"
186
200
 
187
201
  if expected.nil?
@@ -207,7 +221,13 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
207
221
  :options
208
222
 
209
223
  def self.option(method_name)
210
- define_method(method_name) { options.present? ? options[method_name.to_sym] : nil }
224
+ define_method(method_name) do
225
+ if options.key?(method_name.to_sym)
226
+ options[method_name.to_sym]
227
+ else
228
+ throw "Schema line does include the '#{method_name}' option!"
229
+ end
230
+ end
211
231
  end
212
232
 
213
233
  def initialize(line)
@@ -220,6 +240,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
220
240
  option :scale
221
241
  option :default
222
242
  option :collation
243
+ option :null
223
244
 
224
245
  def to_s
225
246
  line.squish
@@ -234,6 +255,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
234
255
  def parse_line
235
256
  _all, type_method, col_name, options = @line.match(LINE_PARSER).to_a
236
257
  options = parse_options(options)
258
+
237
259
  [type_method, col_name, options]
238
260
  end
239
261
 
@@ -243,8 +265,6 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
243
265
  else
244
266
  {}
245
267
  end
246
- rescue SyntaxError
247
- {}
248
268
  end
249
269
  end
250
270
  end
@@ -47,9 +47,5 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
47
47
  assert_equal 255, columns.find { |c| c.name == "n_name" }.limit
48
48
  assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit
49
49
  end
50
-
51
- it "creates new record" do
52
- Alien.create!(name: 'Trisolarans')
53
- end
54
50
  end
55
51
  end
@@ -77,7 +77,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase
77
77
  _(extract_identifiers(" ").object).must_be_nil
78
78
  end
79
79
 
80
- it "has a #quoted that returns a fully quoted name with all identifiers as orginially passed in" do
80
+ it "has a #quoted that returns a fully quoted name with all identifiers as originally passed in" do
81
81
  _(extract_identifiers("object").quoted).must_equal "[object]"
82
82
  _(extract_identifiers("server.database..object").quoted).must_equal "[server].[database]..[object]"
83
83
  _(extract_identifiers("[server]...[object]").quoted).must_equal "[server]...[object]"
@@ -92,7 +92,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase
92
92
  _(extract_identifiers("[obj.name].[foo]").quoted).must_equal "[obj.name].[foo]"
93
93
  end
94
94
 
95
- it "should indicate if a name is fully qualitified" do
95
+ it "should indicate if a name is fully qualified" do
96
96
  _(extract_identifiers("object").fully_qualified?).must_equal false
97
97
  _(extract_identifiers("schema.object").fully_qualified?).must_equal false
98
98
  _(extract_identifiers("database.schema.object").fully_qualified?).must_equal false
@@ -14,8 +14,9 @@ ActiveRecord::Schema.define do
14
14
  t.float :float_col
15
15
  t.string :string_col
16
16
  t.text :text_col
17
- t.datetime :datetime_col
18
- t.timestamp :timestamp_col
17
+ t.datetime :datetime_nil_precision_col, precision: nil
18
+ t.datetime :datetime_col # Precision defaults to 6
19
+ t.timestamp :timestamp_col # Precision defaults to 6
19
20
  t.time :time_col
20
21
  t.date :date_col
21
22
  t.binary :binary_col
@@ -316,6 +317,7 @@ ActiveRecord::Schema.define do
316
317
  execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'aliens' and TABLE_SCHEMA = 'test') DROP TABLE test.aliens"
317
318
  execute <<-TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL
318
319
  CREATE TABLE test.aliens(
320
+ id int IDENTITY NOT NULL primary key,
319
321
  name varchar(255)
320
322
  )
321
323
  TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL
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: 7.0.2.0
4
+ version: 7.0.3.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: 2023-04-18 00:00:00.000000000 Z
17
+ date: 2023-06-12 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activerecord
@@ -152,6 +152,7 @@ files:
152
152
  - test/cases/dbconsole.rb
153
153
  - test/cases/disconnected_test_sqlserver.rb
154
154
  - test/cases/eager_load_too_many_ids_test_sqlserver.rb
155
+ - test/cases/enum_test_sqlserver.rb
155
156
  - test/cases/execute_procedure_test_sqlserver.rb
156
157
  - test/cases/fetch_test_sqlserver.rb
157
158
  - test/cases/fully_qualified_identifier_test_sqlserver.rb
@@ -228,8 +229,8 @@ licenses:
228
229
  - MIT
229
230
  metadata:
230
231
  bug_tracker_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues
231
- changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.0.2.0/CHANGELOG.md
232
- source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.0.2.0
232
+ changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.0.3.0/CHANGELOG.md
233
+ source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.0.3.0
233
234
  post_install_message:
234
235
  rdoc_options: []
235
236
  require_paths:
@@ -245,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
245
246
  - !ruby/object:Gem::Version
246
247
  version: '0'
247
248
  requirements: []
248
- rubygems_version: 3.2.33
249
+ rubygems_version: 3.4.6
249
250
  signing_key:
250
251
  specification_version: 4
251
252
  summary: ActiveRecord SQL Server Adapter.
@@ -265,6 +266,7 @@ test_files:
265
266
  - test/cases/dbconsole.rb
266
267
  - test/cases/disconnected_test_sqlserver.rb
267
268
  - test/cases/eager_load_too_many_ids_test_sqlserver.rb
269
+ - test/cases/enum_test_sqlserver.rb
268
270
  - test/cases/execute_procedure_test_sqlserver.rb
269
271
  - test/cases/fetch_test_sqlserver.rb
270
272
  - test/cases/fully_qualified_identifier_test_sqlserver.rb