activerecord-sqlserver-adapter 7.0.2.0 → 7.0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c5135f809d1709197eef056bbac22e09adefd4ead979cc32e72a2e12c52453a
4
- data.tar.gz: 6a0f0b78026468b273926d259a93c7edb38dc8de176f88e32d8bd64c1667c90c
3
+ metadata.gz: 41b4642838dbc7178a170c4b002937e5481bf0c7154446d43cff2dae7800a938
4
+ data.tar.gz: fa20040c55e79a8417ec58c414b5ad6617acaa2fe808c5b48fc44c6e3187c5d7
5
5
  SHA512:
6
- metadata.gz: 299fe79b88ebbfb2edfac1145c4969c3cde3e7eec25bf5415e671b8bc5e3564cf66cca398a1ca7285cc1b4950a0a0326162860b359a41e018c4bae8c058921ba
7
- data.tar.gz: 13566655cfaba356a7b1a1f63e95ad2a38ab0a95a6f85eaf9af4571e04ee1db03e8b2cbd4a9d58599de289c3769046e27ee23ef3898219ecc55fd24f7ec31faf
6
+ metadata.gz: b7786aeaaf3f90af8a69208e76901c1263e20ef1c4f410948319108531e128ff2d5247838efb10cbcda0fe6b035d1b130576647824e027a668b0fc3df1988305
7
+ data.tar.gz: 3d9db5b28f4b8b8468b71049b14b7000e25c7aa7afcac55f6fec0ec234f0e4cdf76c4158069328da464ad0a718f45ee9b45b2b6f19f00fc058b7d4ab87071308
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## v7.0.4.0
2
+
3
+ #### Changed
4
+
5
+ - [#1073](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1073) Improve performance of view default function lookup
6
+
7
+ #### Fixed
8
+
9
+ - [#1088](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1088) Fix creation of stored procedures that contain insert statements
10
+ - [#1089](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1089) When changing columns set date-time columns to datetime(6) by default
11
+
12
+ ## v7.0.3.0
13
+
14
+ #### Fixed
15
+
16
+ - [#1052](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1052) Ignore casing of VALUES clause when inserting records using SQL
17
+ - [#1053](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1053) Fix insertion of records to non-default schema table using raw SQL
18
+ - [#1059](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1059) Fix enums defined on string columns
19
+
20
+ #### Changed
21
+
22
+ - [#1057](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1057) Set precision 6 by default for timestamps.
23
+
1
24
  ## v7.0.2.0
2
25
 
3
26
  [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.1.0...v7.0.2.0)
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008-2022
1
+ Copyright (c) Ken Collins
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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.4.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
@@ -186,6 +190,7 @@ Many many people have contributed. If you do not see your name here and it shoul
186
190
 
187
191
  You can see an up-to-date list of contributors here: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors
188
192
 
193
+
189
194
  ## License
190
195
 
191
- Copyright © 2008-2022. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file.
196
+ ActiveRecord SQL Server Adapter is released under the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.0.2.0
1
+ 7.0.4.0
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.required_ruby_version = ">= 2.7.0"
11
11
 
12
12
  spec.license = "MIT"
13
- spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward"]
13
+ spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward", "Aidan Haran"]
14
14
  spec.email = ["ken@metaskills.net", "will@wbond.net"]
15
15
  spec.homepage = "http://github.com/rails-sqlserver/activerecord-sqlserver-adapter"
16
16
  spec.summary = "ActiveRecord SQL Server Adapter."
@@ -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,11 +402,11 @@ 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)
409
- !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
409
+ !(sql =~ /\A\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
410
410
  end
411
411
 
412
412
  def identity_columns(table_name)
@@ -143,6 +143,15 @@ module ActiveRecord
143
143
  def change_column(table_name, column_name, type, options = {})
144
144
  sql_commands = []
145
145
  indexes = []
146
+
147
+ if type == :datetime
148
+ # If no precision then default it to 6.
149
+ options[:precision] = 6 unless options.key?(:precision)
150
+
151
+ # If there is precision then column must be of type 'datetime2'.
152
+ type = :datetime2 unless options[:precision].nil?
153
+ end
154
+
146
155
  column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s }
147
156
  without_constraints = options.key?(:default) || options.key?(:limit)
148
157
  default = if !options.key?(:default) && column_object
@@ -150,24 +159,29 @@ module ActiveRecord
150
159
  else
151
160
  options[:default]
152
161
  end
162
+
153
163
  if without_constraints || (column_object && column_object.type != type.to_sym)
154
164
  remove_default_constraint(table_name, column_name)
155
165
  indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }
156
166
  remove_indexes(table_name, column_name)
157
167
  end
168
+
158
169
  sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
159
170
  alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
160
171
  alter_command += " COLLATE #{options[:collation]}" if options[:collation].present?
161
172
  alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false
162
173
  sql_commands << alter_command
174
+
163
175
  if without_constraints
164
176
  default = quote_default_expression(default, column_object || column_for(table_name, column_name))
165
177
  sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}"
166
178
  end
179
+
167
180
  # Add any removed indexes back
168
181
  indexes.each do |index|
169
182
  sql_commands << "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index.columns.map { |c| quote_column_name(c) }.join(', ')})"
170
183
  end
184
+
171
185
  sql_commands.each { |c| do_execute(c) }
172
186
  clear_cache!
173
187
  end
@@ -229,6 +243,7 @@ module ActiveRecord
229
243
  def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
230
244
  type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
231
245
  limit = nil unless type_limitable
246
+
232
247
  case type.to_s
233
248
  when "integer"
234
249
  case limit
@@ -346,7 +361,7 @@ module ActiveRecord
346
361
  datetime2: { name: "datetime2" },
347
362
  datetimeoffset: { name: "datetimeoffset" },
348
363
  smalldatetime: { name: "smalldatetime" },
349
- timestamp: { name: "datetime" },
364
+ timestamp: { name: "datetime2(6)" },
350
365
  time: { name: "time" },
351
366
  char: { name: "char" },
352
367
  varchar: { name: "varchar", limit: 8000 },
@@ -371,6 +386,15 @@ module ActiveRecord
371
386
  view_exists = view_exists?(table_name)
372
387
  view_tblnm = view_table_name(table_name) if view_exists
373
388
 
389
+ if view_exists
390
+ results = sp_executesql %{
391
+ SELECT c.COLUMN_NAME AS [name], c.COLUMN_DEFAULT AS [default]
392
+ FROM #{database}.INFORMATION_SCHEMA.COLUMNS c
393
+ WHERE c.TABLE_NAME = #{quote(view_tblnm)}
394
+ }.squish, "SCHEMA", []
395
+ default_functions = results.each.with_object({}) {|row, out| out[row["name"]] = row["default"] }.compact
396
+ end
397
+
374
398
  sql = column_definitions_sql(database, identifier)
375
399
 
376
400
  binds = []
@@ -402,13 +426,8 @@ module ActiveRecord
402
426
  ci[:default_function] = begin
403
427
  default = ci[:default_value]
404
428
  if default.nil? && view_exists
405
- default = select_value %{
406
- SELECT c.COLUMN_DEFAULT
407
- FROM #{database}.INFORMATION_SCHEMA.COLUMNS c
408
- WHERE
409
- c.TABLE_NAME = '#{view_tblnm}'
410
- AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'
411
- }.squish, "SCHEMA"
429
+ view_column = views_real_column_name(table_name, ci[:name])
430
+ default = default_functions[view_column] if view_column.present?
412
431
  end
413
432
  case default
414
433
  when nil
@@ -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
@@ -1297,9 +1310,26 @@ class RelationTest < ActiveRecord::TestCase
1297
1310
  # We are not doing order duplicate removal anymore.
1298
1311
  coerce_tests! :test_default_scope_order_with_scope_order
1299
1312
 
1300
- # Leave it up to users to format selects/functions so HAVING works correctly.
1313
+ # Order column must be in the GROUP clause.
1301
1314
  coerce_tests! :test_multiple_where_and_having_clauses
1315
+ def test_multiple_where_and_having_clauses_coerced
1316
+ post = Post.first
1317
+ having_then_where = Post.having(id: post.id).where(title: post.title)
1318
+ .having(id: post.id).where(title: post.title).group(:id).select(:id)
1319
+
1320
+ assert_equal [post], having_then_where
1321
+ end
1322
+
1323
+ # Order column must be in the GROUP clause.
1302
1324
  coerce_tests! :test_having_with_binds_for_both_where_and_having
1325
+ def test_having_with_binds_for_both_where_and_having
1326
+ post = Post.first
1327
+ having_then_where = Post.having(id: post.id).where(title: post.title).group(:id).select(:id)
1328
+ where_then_having = Post.where(title: post.title).having(id: post.id).group(:id).select(:id)
1329
+
1330
+ assert_equal [post], having_then_where
1331
+ assert_equal [post], where_then_having
1332
+ end
1303
1333
 
1304
1334
  # Find any limit via our expression.
1305
1335
  coerce_tests! %r{relations don't load all records in #inspect}
@@ -1309,10 +1339,18 @@ class RelationTest < ActiveRecord::TestCase
1309
1339
  end
1310
1340
  end
1311
1341
 
1312
- # I wanted to add `.order("author_id")` scope to avoid error: Column "posts.id" is invalid in the ORDER BY
1313
- # However, this pull request on Rails core drops order on exists relation. https://github.com/rails/rails/pull/28699
1314
- # so we are skipping all together.
1342
+ # Order column must be in the GROUP clause.
1315
1343
  coerce_tests! :test_empty_complex_chained_relations
1344
+ def test_empty_complex_chained_relations_coerced
1345
+ posts = Post.select("comments_count").where("id is not null").group("author_id", "id").where("legacy_comments_count > 0")
1346
+
1347
+ assert_queries(1) { assert_equal false, posts.empty? }
1348
+ assert_not_predicate posts, :loaded?
1349
+
1350
+ no_posts = posts.where(title: "")
1351
+ assert_queries(1) { assert_equal true, no_posts.empty? }
1352
+ assert_not_predicate no_posts, :loaded?
1353
+ end
1316
1354
 
1317
1355
  # Can't apply offset without ORDER
1318
1356
  coerce_tests! %r{using a custom table affects the wheres}
@@ -2107,6 +2145,17 @@ class FieldOrderedValuesTest < ActiveRecord::TestCase
2107
2145
  Book.where(author_id: nil, name: nil).delete_all
2108
2146
  Book.connection.add_index(:books, [:author_id, :name], unique: true)
2109
2147
  end
2148
+
2149
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2150
+ coerce_tests! :test_in_order_of_with_nil
2151
+ def test_in_order_of_with_nil_coerced
2152
+ Book.connection.remove_index(:books, column: [:author_id, :name])
2153
+
2154
+ original_test_in_order_of_with_nil
2155
+ ensure
2156
+ Book.where(author_id: nil, name: nil).delete_all
2157
+ Book.connection.add_index(:books, [:author_id, :name], unique: true)
2158
+ end
2110
2159
  end
2111
2160
 
2112
2161
  require "models/dashboard"
@@ -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
@@ -115,4 +115,22 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
115
115
  refute_includes schemas, { "name" => "some schema" }
116
116
  end
117
117
  end
118
+
119
+ describe 'creating stored procedure' do
120
+ it 'stored procedure contains inserts are created successfully' do
121
+ sql = <<-SQL
122
+ CREATE OR ALTER PROCEDURE do_some_task
123
+ AS
124
+ IF NOT EXISTS(SELECT * FROM sys.objects WHERE type = 'U' AND name = 'SomeTableName')
125
+ BEGIN
126
+ CREATE TABLE SomeTableName (SomeNum int PRIMARY KEY CLUSTERED);
127
+ INSERT INTO SomeTableName(SomeNum) VALUES(1);
128
+ END
129
+ SQL
130
+
131
+ assert_nothing_raised { connection.execute(sql) }
132
+ ensure
133
+ connection.execute("DROP PROCEDURE IF EXISTS dbo.do_some_task;")
134
+ end
135
+ end
118
136
  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.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Collins
@@ -11,10 +11,11 @@ authors:
11
11
  - Shawn Balestracci
12
12
  - Joe Rafaniello
13
13
  - Tom Ward
14
+ - Aidan Haran
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
- date: 2023-04-18 00:00:00.000000000 Z
18
+ date: 2023-09-19 00:00:00.000000000 Z
18
19
  dependencies:
19
20
  - !ruby/object:Gem::Dependency
20
21
  name: activerecord
@@ -152,6 +153,7 @@ files:
152
153
  - test/cases/dbconsole.rb
153
154
  - test/cases/disconnected_test_sqlserver.rb
154
155
  - test/cases/eager_load_too_many_ids_test_sqlserver.rb
156
+ - test/cases/enum_test_sqlserver.rb
155
157
  - test/cases/execute_procedure_test_sqlserver.rb
156
158
  - test/cases/fetch_test_sqlserver.rb
157
159
  - test/cases/fully_qualified_identifier_test_sqlserver.rb
@@ -228,8 +230,8 @@ licenses:
228
230
  - MIT
229
231
  metadata:
230
232
  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
233
+ changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.0.4.0/CHANGELOG.md
234
+ source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.0.4.0
233
235
  post_install_message:
234
236
  rdoc_options: []
235
237
  require_paths:
@@ -245,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
245
247
  - !ruby/object:Gem::Version
246
248
  version: '0'
247
249
  requirements: []
248
- rubygems_version: 3.2.33
250
+ rubygems_version: 3.4.10
249
251
  signing_key:
250
252
  specification_version: 4
251
253
  summary: ActiveRecord SQL Server Adapter.
@@ -265,6 +267,7 @@ test_files:
265
267
  - test/cases/dbconsole.rb
266
268
  - test/cases/disconnected_test_sqlserver.rb
267
269
  - test/cases/eager_load_too_many_ids_test_sqlserver.rb
270
+ - test/cases/enum_test_sqlserver.rb
268
271
  - test/cases/execute_procedure_test_sqlserver.rb
269
272
  - test/cases/fetch_test_sqlserver.rb
270
273
  - test/cases/fully_qualified_identifier_test_sqlserver.rb