activerecord-sqlserver-adapter 6.1.0.0 → 7.0.0.0.rc1

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -1
  3. data/CHANGELOG.md +12 -23
  4. data/Gemfile +1 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +31 -16
  7. data/VERSION +1 -1
  8. data/activerecord-sqlserver-adapter.gemspec +2 -2
  9. data/appveyor.yml +4 -6
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +2 -0
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -1
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +2 -0
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -0
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +7 -13
  15. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +15 -6
  16. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +4 -5
  17. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +28 -9
  18. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +14 -5
  19. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +3 -1
  20. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +3 -2
  21. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +1 -1
  22. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -1
  23. data/lib/active_record/connection_adapters/sqlserver/utils.rb +16 -1
  24. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +99 -76
  25. data/lib/active_record/connection_adapters/sqlserver_column.rb +74 -35
  26. data/lib/arel/visitors/sqlserver.rb +17 -2
  27. data/test/cases/adapter_test_sqlserver.rb +10 -2
  28. data/test/cases/coerced_tests.rb +314 -85
  29. data/test/cases/column_test_sqlserver.rb +62 -58
  30. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  31. data/test/cases/fetch_test_sqlserver.rb +18 -0
  32. data/test/cases/rake_test_sqlserver.rb +36 -0
  33. data/test/cases/schema_dumper_test_sqlserver.rb +2 -2
  34. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +1 -1
  35. data/test/models/sqlserver/composite_pk.rb +9 -0
  36. data/test/schema/sqlserver_specific_schema.rb +18 -0
  37. data/test/support/coerceable_test_sqlserver.rb +4 -4
  38. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  39. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  40. data/test/support/rake_helpers.rb +3 -1
  41. metadata +18 -15
  42. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -28
  43. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  44. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
@@ -277,8 +277,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
277
277
  _(col.sql_type).must_equal "date"
278
278
  _(col.type).must_equal :date
279
279
  _(col.null).must_equal true
280
- _(col.default).must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : "0001-01-01"
281
- _(obj.date).must_equal Date.civil(0001, 1, 1)
280
+ _(col.default).must_equal connection_dblib_73? ? Date.civil(1, 1, 1) : "0001-01-01"
281
+ _(obj.date).must_equal Date.civil(1, 1, 1)
282
282
  _(col.default_function).must_be_nil
283
283
  type = connection.lookup_cast_type_from_column(col)
284
284
  _(type).must_be_instance_of Type::Date
@@ -287,20 +287,22 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
287
287
  _(type.scale).must_be_nil
288
288
  # Can cast strings. SQL Server format.
289
289
  obj.date = "04-01-0001"
290
- _(obj.date).must_equal Date.civil(0001, 4, 1)
290
+ _(obj.date).must_equal Date.civil(1, 4, 1)
291
291
  obj.save!
292
- _(obj.date).must_equal Date.civil(0001, 4, 1)
292
+ _(obj.date).must_equal Date.civil(1, 4, 1)
293
293
  obj.reload
294
- _(obj.date).must_equal Date.civil(0001, 4, 1)
294
+ _(obj.date).must_equal Date.civil(1, 4, 1)
295
295
  # Can cast strings. ISO format.
296
296
  obj.date = "0001-04-01"
297
- _(obj.date).must_equal Date.civil(0001, 4, 1)
297
+ _(obj.date).must_equal Date.civil(1, 4, 1)
298
298
  obj.save!
299
- _(obj.date).must_equal Date.civil(0001, 4, 1)
299
+ _(obj.date).must_equal Date.civil(1, 4, 1)
300
300
  obj.reload
301
- _(obj.date).must_equal Date.civil(0001, 4, 1)
301
+ _(obj.date).must_equal Date.civil(1, 4, 1)
302
+ # Can filter by date range
303
+ _(obj).must_equal obj.class.where(date: obj.date..Date::Infinity.new).first
302
304
  # Can keep and return assigned date.
303
- assert_obj_set_and_save :date, Date.civil(1972, 04, 14)
305
+ assert_obj_set_and_save :date, Date.civil(1972, 4, 14)
304
306
  # Can accept and cast time objects.
305
307
  obj.date = Time.utc(2010, 4, 14, 12, 34, 56, 3000)
306
308
  _(obj.date).must_equal Date.civil(2010, 4, 14)
@@ -313,7 +315,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
313
315
  _(col.sql_type).must_equal "datetime"
314
316
  _(col.type).must_equal :datetime
315
317
  _(col.null).must_equal true
316
- time = Time.utc 1753, 01, 01, 00, 00, 00, 123000
318
+ time = Time.utc 1753, 1, 1, 0, 0, 0, 123000
317
319
  _(col.default).must_equal time, "Microseconds were <#{col.default.usec}> vs <123000>"
318
320
  _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <123000>"
319
321
  _(col.default_function).must_be_nil
@@ -325,7 +327,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
325
327
  obj.save!
326
328
  _(obj).must_equal obj.class.where(datetime: time).first
327
329
  # Can save to proper accuracy and return again.
328
- time = Time.utc 2010, 04, 01, 12, 34, 56, 3000
330
+ time = Time.utc 2010, 4, 1, 12, 34, 56, 3000
329
331
  obj.datetime = time
330
332
  _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>"
331
333
  obj.save!
@@ -333,9 +335,11 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
333
335
  obj.reload
334
336
  _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>"
335
337
  _(obj).must_equal obj.class.where(datetime: time).first
338
+ # Can filter by datetime range
339
+ _(obj).must_equal obj.class.where(datetime: time..DateTime::Infinity.new).first
336
340
  # Will cast to true DB value on attribute write, save and return again.
337
- time = Time.utc 2010, 04, 01, 12, 34, 56, 234567
338
- time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000
341
+ time = Time.utc 2010, 4, 1, 12, 34, 56, 234567
342
+ time2 = Time.utc 2010, 4, 1, 12, 34, 56, 233000
339
343
  obj.datetime = time
340
344
  _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>"
341
345
  obj.save!
@@ -423,8 +427,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
423
427
  _(col.sql_type).must_equal "datetimeoffset(7)"
424
428
  _(col.type).must_equal :datetimeoffset
425
429
  _(col.null).must_equal true
426
- _(col.default).must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>"
427
- _(obj.datetimeoffset_7).must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>"
430
+ _(col.default).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>"
431
+ _(obj.datetimeoffset_7).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>"
428
432
  _(col.default_function).must_be_nil
429
433
  type = connection.lookup_cast_type_from_column(col)
430
434
  _(type).must_be_instance_of Type::DateTimeOffset
@@ -432,12 +436,12 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
432
436
  _(type.precision).must_equal 7
433
437
  _(type.scale).must_be_nil
434
438
  # Can save 100 nanosecond precisoins and return again.
435
- obj.datetimeoffset_7 = Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456755)
436
- _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
439
+ obj.datetimeoffset_7 = Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456755)
440
+ _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
437
441
  obj.save!
438
- _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
442
+ _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
439
443
  obj.reload
440
- _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
444
+ _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
441
445
  # Maintains the timezone
442
446
  time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000)
443
447
  obj.datetimeoffset_7 = time
@@ -466,8 +470,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
466
470
  _(col.sql_type).must_equal "smalldatetime"
467
471
  _(col.type).must_equal :smalldatetime
468
472
  _(col.null).must_equal true
469
- _(col.default).must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000)
470
- _(obj.smalldatetime).must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000)
473
+ _(col.default).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0)
474
+ _(obj.smalldatetime).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0)
471
475
  _(col.default_function).must_be_nil
472
476
  type = connection.lookup_cast_type_from_column(col)
473
477
  _(type).must_be_instance_of Type::SmallDateTime
@@ -475,12 +479,12 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
475
479
  _(type.precision).must_be_nil
476
480
  _(type.scale).must_be_nil
477
481
  # Will remove fractional seconds and return again.
478
- obj.smalldatetime = Time.utc(2078, 06, 05, 4, 20, 00, 3000)
479
- _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>"
482
+ obj.smalldatetime = Time.utc(2078, 6, 5, 4, 20, 0, 3000)
483
+ _(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>"
480
484
  obj.save!
481
- _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>"
485
+ _(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>"
482
486
  obj.reload
483
- _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>"
487
+ _(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>"
484
488
  end
485
489
 
486
490
  it "time(7)" do
@@ -489,7 +493,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
489
493
  _(col.sql_type).must_equal "time(7)"
490
494
  _(col.type).must_equal :time
491
495
  _(col.null).must_equal true
492
- _(col.default).must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>"
496
+ _(col.default).must_equal Time.utc(1900, 1, 1, 4, 20, 0, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>"
493
497
  _(col.default_function).must_be_nil
494
498
  type = connection.lookup_cast_type_from_column(col)
495
499
  _(type).must_be_instance_of Type::Time
@@ -497,22 +501,22 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
497
501
  _(type.precision).must_equal 7
498
502
  _(type.scale).must_be_nil
499
503
  # Time's #usec precision (low micro)
500
- obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300)
501
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>"
502
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>"
504
+ obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, 300)
505
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>"
506
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>"
503
507
  obj.save!; obj.reload
504
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>"
505
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>"
508
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>"
509
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>"
506
510
  # Time's #usec precision (high micro)
507
- obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567)
508
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
511
+ obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, 234567)
512
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
509
513
  obj.save!; obj.reload
510
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
514
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
511
515
  # Time's #usec precision (high nano rounded)
512
- obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000))
513
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
516
+ obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321545, 1000))
517
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
514
518
  obj.save!; obj.reload
515
- _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
519
+ _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
516
520
  end
517
521
 
518
522
  it "time(2)" do
@@ -529,20 +533,20 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
529
533
  _(type.precision).must_equal 2
530
534
  _(type.scale).must_be_nil
531
535
  # Always uses TinyTDS/Windows 2000-01-01 convention too.
532
- obj.time_2 = Time.utc(2015, 01, 10, 15, 45, 00, 0)
533
- _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0)
536
+ obj.time_2 = Time.utc(2015, 1, 10, 15, 45, 0, 0)
537
+ _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0)
534
538
  obj.save!; obj.reload
535
- _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0)
539
+ _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0)
536
540
  # Time's #usec precision (barely in 2 precision equal to 0.03 seconds)
537
- obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 30000)
538
- _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
541
+ obj.time_2 = Time.utc(2000, 1, 1, 15, 45, 0, 30000)
542
+ _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
539
543
  obj.save!; obj.reload
540
- _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
544
+ _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
541
545
  # Time's #usec precision (below 2 precision)
542
- obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 4000)
543
- _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
546
+ obj.time_2 = Time.utc(2000, 1, 1, 15, 45, 0, 4000)
547
+ _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
544
548
  obj.save!; obj.reload
545
- _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
549
+ _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
546
550
  end
547
551
 
548
552
  it "time using default precision" do
@@ -551,7 +555,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
551
555
  _(col.sql_type).must_equal "time(7)"
552
556
  _(col.type).must_equal :time
553
557
  _(col.null).must_equal true
554
- _(col.default).must_equal Time.utc(1900, 01, 01, 15, 03, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>"
558
+ _(col.default).must_equal Time.utc(1900, 1, 1, 15, 3, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>"
555
559
  _(col.default_function).must_be_nil
556
560
  type = connection.lookup_cast_type_from_column(col)
557
561
  _(type).must_be_instance_of Type::Time
@@ -559,22 +563,22 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
559
563
  _(type.precision).must_equal 7
560
564
  _(type.scale).must_be_nil
561
565
  # Time's #usec precision (low micro)
562
- obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, 300)
563
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>"
564
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>"
566
+ obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, 300)
567
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>"
568
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>"
565
569
  obj.save!; obj.reload
566
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>"
567
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>"
570
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>"
571
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>"
568
572
  # Time's #usec precision (high micro)
569
- obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, 234567)
570
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>"
573
+ obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, 234567)
574
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>"
571
575
  obj.save!; obj.reload
572
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>"
576
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>"
573
577
  # Time's #usec precision (high nano rounded)
574
- obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000))
575
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>"
578
+ obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321545, 1000))
579
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>"
576
580
  obj.save!; obj.reload
577
- _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>"
581
+ _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>"
578
582
  end
579
583
 
580
584
  # Character Strings
@@ -0,0 +1,18 @@
1
+ require "cases/helper_sqlserver"
2
+ require "models/citation"
3
+ require "models/book"
4
+
5
+ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
6
+ fixtures :citations
7
+
8
+ def test_batch_preloading_too_many_ids
9
+ in_clause_length = 10_000
10
+
11
+ # We Monkey patch Preloader to work with batches of 10_000 records.
12
+ # Expect: N Books queries + Citation query
13
+ expected_query_count = (Citation.count / in_clause_length.to_f).ceil + 1
14
+ assert_queries(expected_query_count) do
15
+ Citation.preload(:reference_of).to_a.size
16
+ end
17
+ end
18
+ end
@@ -49,3 +49,21 @@ class FetchTestSqlserver < ActiveRecord::TestCase
49
49
  @books = (1..10).map { |i| Book.create! name: "Name-#{i}" }
50
50
  end
51
51
  end
52
+
53
+ class DeterministicFetchWithCompositePkTestSQLServer < ActiveRecord::TestCase
54
+ it "orders by the identity column if table has one" do
55
+ SSCompositePkWithIdentity.delete_all
56
+ SSCompositePkWithIdentity.create(pk_col_two: 2)
57
+ SSCompositePkWithIdentity.create(pk_col_two: 1)
58
+
59
+ _(SSCompositePkWithIdentity.take(1).map(&:pk_col_two)).must_equal [2]
60
+ end
61
+
62
+ it "orders by the first column if table has no identity column" do
63
+ SSCompositePkWithoutIdentity.delete_all
64
+ SSCompositePkWithoutIdentity.create(pk_col_one: 2, pk_col_two: 2)
65
+ SSCompositePkWithoutIdentity.create(pk_col_one: 1, pk_col_two: 1)
66
+
67
+ _(SSCompositePkWithoutIdentity.take(1).map(&:pk_col_two)).must_equal [1]
68
+ end
69
+ end
@@ -156,3 +156,39 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest
156
156
  _(connection.tables).must_include "users"
157
157
  end
158
158
  end
159
+
160
+ class SQLServerRakeSchemaCacheDumpLoadTest < SQLServerRakeTest
161
+ let(:filename) { File.join ARTest::SQLServer.test_root_sqlserver, "schema_cache.yml" }
162
+ let(:filedata) { File.read(filename) }
163
+
164
+ before do
165
+ quietly { db_tasks.create(configuration) }
166
+
167
+ connection.create_table :users, force: true do |t|
168
+ t.string :name, null: false
169
+ end
170
+ end
171
+
172
+ after do
173
+ FileUtils.rm_rf(filename)
174
+ end
175
+
176
+ it "dumps schema cache with SQL Server metadata" do
177
+ quietly { db_tasks.dump_schema_cache connection, filename }
178
+
179
+ filedata = File.read(filename)
180
+ schema_cache = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(filedata) : YAML.load(filedata)
181
+
182
+ col_id, col_name = connection.schema_cache.columns("users")
183
+
184
+ assert col_id.is_identity
185
+ assert col_id.is_primary
186
+ assert_equal col_id.ordinal_position, 1
187
+ assert_equal col_id.table_name, "users"
188
+
189
+ assert_not col_name.is_identity
190
+ assert_not col_name.is_primary
191
+ assert_equal col_name.ordinal_position, 2
192
+ assert_equal col_name.table_name, "users"
193
+ end
194
+ end
@@ -67,7 +67,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
67
67
  _(columns["float_col"].sql_type).must_equal "float"
68
68
  _(columns["string_col"].sql_type).must_equal "nvarchar(4000)"
69
69
  _(columns["text_col"].sql_type).must_equal "nvarchar(max)"
70
- _(columns["datetime_col"].sql_type).must_equal "datetime"
70
+ _(columns["datetime_col"].sql_type).must_equal "datetime2(6)"
71
71
  _(columns["timestamp_col"].sql_type).must_equal "datetime"
72
72
  _(columns["time_col"].sql_type).must_equal "time(7)"
73
73
  _(columns["date_col"].sql_type).must_equal "date"
@@ -79,7 +79,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
79
79
  assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil
80
80
  assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil
81
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
82
+ assert_line :datetime_col, type: "datetime", limit: nil, precision: 6, scale: nil, default: nil
83
83
  assert_line :timestamp_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil
84
84
  assert_line :time_col, type: "time", limit: nil, precision: 7, scale: nil, default: nil
85
85
  assert_line :date_col, type: "date", limit: nil, precision: nil, scale: nil, default: nil
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class TableWillNeverBeCreated < ActiveRecord::Migration
3
+ class TableWillNeverBeCreated < ActiveRecord::Migration[5.2]
4
4
  def self.up
5
5
  create_table(:sqlserver_trans_table1) {}
6
6
  create_table(:sqlserver_trans_table2) { raise("HELL") }
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SSCompositePkWithoutIdentity < ActiveRecord::Base
4
+ self.table_name = :sst_composite_without_identity
5
+ end
6
+
7
+ class SSCompositePkWithIdentity < ActiveRecord::Base
8
+ self.table_name = :sst_composite_with_identity
9
+ end
@@ -294,4 +294,22 @@ ActiveRecord::Schema.define do
294
294
  CONSTRAINT PK_UNIQUE_KEY PRIMARY KEY (id)
295
295
  );
296
296
  SQLSERVERUNIQUEKEYS
297
+
298
+ execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_composite_without_identity') DROP TABLE sst_composite_without_identity"
299
+ execute <<-COMPOSITE_WITHOUT_IDENTITY
300
+ CREATE TABLE sst_composite_without_identity (
301
+ pk_col_one int NOT NULL,
302
+ pk_col_two int NOT NULL,
303
+ CONSTRAINT PK_sst_composite_without_identity PRIMARY KEY (pk_col_one, pk_col_two)
304
+ );
305
+ COMPOSITE_WITHOUT_IDENTITY
306
+
307
+ execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_composite_with_identity') DROP TABLE sst_composite_with_identity"
308
+ execute <<-COMPOSITE_WITH_IDENTITY
309
+ CREATE TABLE sst_composite_with_identity (
310
+ pk_col_one int IDENTITY NOT NULL,
311
+ pk_col_two int NOT NULL,
312
+ CONSTRAINT PK_sst_composite_with_identity PRIMARY KEY (pk_col_one, pk_col_two)
313
+ );
314
+ COMPOSITE_WITH_IDENTITY
297
315
  end
@@ -13,7 +13,7 @@ module ARTest
13
13
  module ClassMethods
14
14
  def coerce_tests!(*methods)
15
15
  methods.each do |method|
16
- self.coerced_tests.push(method)
16
+ coerced_tests.push(method)
17
17
  coerced_test_warning(method)
18
18
  end
19
19
  end
@@ -24,7 +24,7 @@ module ARTest
24
24
 
25
25
  undef_method(method)
26
26
  end
27
- STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{self.name}"
27
+ STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{name}"
28
28
  end
29
29
 
30
30
  private
@@ -43,9 +43,9 @@ module ARTest
43
43
  end
44
44
 
45
45
  if result.blank?
46
- STDOUT.puts "🐳 Unfound coerced test: #{self.name}##{m}"
46
+ STDOUT.puts "🐳 Unfound coerced test: #{name}##{m}"
47
47
  else
48
- STDOUT.puts "🐵 Undefined coerced test: #{self.name}##{m}"
48
+ STDOUT.puts "🐵 Undefined coerced test: #{name}##{m}"
49
49
  end
50
50
  end
51
51
  end
@@ -25,7 +25,9 @@ end
25
25
 
26
26
  def ar_cases
27
27
  @ar_cases ||= begin
28
- Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject { |x| x =~ /\/adapters\// }.sort
28
+ Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject {
29
+ |x| x.include?("/adapters/") || x.include?("/encryption/performance")
30
+ }.sort
29
31
  end
30
32
  end
31
33
 
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: 6.1.0.0
4
+ version: 7.0.0.0.rc1
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: 2021-06-04 00:00:00.000000000 Z
17
+ date: 2022-01-18 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activerecord
@@ -22,14 +22,14 @@ dependencies:
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 6.1.0
25
+ version: 7.0.0
26
26
  type: :runtime
27
27
  prerelease: false
28
28
  version_requirements: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 6.1.0
32
+ version: 7.0.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: tiny_tds
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -78,7 +78,6 @@ files:
78
78
  - lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb
79
79
  - lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb
80
80
  - lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb
81
- - lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb
82
81
  - lib/active_record/connection_adapters/sqlserver/database_limits.rb
83
82
  - lib/active_record/connection_adapters/sqlserver/database_statements.rb
84
83
  - lib/active_record/connection_adapters/sqlserver/database_tasks.rb
@@ -150,6 +149,7 @@ files:
150
149
  - test/cases/column_test_sqlserver.rb
151
150
  - test/cases/connection_test_sqlserver.rb
152
151
  - test/cases/disconnected_test_sqlserver.rb
152
+ - test/cases/eager_load_too_many_ids_test_sqlserver.rb
153
153
  - test/cases/execute_procedure_test_sqlserver.rb
154
154
  - test/cases/fetch_test_sqlserver.rb
155
155
  - test/cases/fully_qualified_identifier_test_sqlserver.rb
@@ -180,6 +180,7 @@ files:
180
180
  - test/migrations/create_clients_and_change_column_null.rb
181
181
  - test/migrations/transaction_table/1_table_will_never_be_created.rb
182
182
  - test/models/sqlserver/booking.rb
183
+ - test/models/sqlserver/composite_pk.rb
183
184
  - test/models/sqlserver/customers_view.rb
184
185
  - test/models/sqlserver/datatype.rb
185
186
  - test/models/sqlserver/datatype_migration.rb
@@ -212,8 +213,8 @@ files:
212
213
  - test/support/connection_reflection.rb
213
214
  - test/support/core_ext/query_cache.rb
214
215
  - test/support/load_schema_sqlserver.rb
215
- - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump
216
- - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump
216
+ - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump
217
+ - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump
217
218
  - test/support/minitest_sqlserver.rb
218
219
  - test/support/paths_sqlserver.rb
219
220
  - test/support/rake_helpers.rb
@@ -224,8 +225,8 @@ licenses:
224
225
  - MIT
225
226
  metadata:
226
227
  bug_tracker_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues
227
- changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v6.1.0.0/CHANGELOG.md
228
- source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v6.1.0.0
228
+ changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.0.0.0.rc1/CHANGELOG.md
229
+ source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.0.0.0.rc1
229
230
  post_install_message:
230
231
  rdoc_options: []
231
232
  require_paths:
@@ -234,14 +235,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
234
235
  requirements:
235
236
  - - ">="
236
237
  - !ruby/object:Gem::Version
237
- version: 2.5.0
238
+ version: 2.7.0
238
239
  required_rubygems_version: !ruby/object:Gem::Requirement
239
240
  requirements:
240
- - - ">="
241
+ - - ">"
241
242
  - !ruby/object:Gem::Version
242
- version: '0'
243
+ version: 1.3.1
243
244
  requirements: []
244
- rubygems_version: 3.2.3
245
+ rubygems_version: 3.2.22
245
246
  signing_key:
246
247
  specification_version: 4
247
248
  summary: ActiveRecord SQL Server Adapter.
@@ -258,6 +259,7 @@ test_files:
258
259
  - test/cases/column_test_sqlserver.rb
259
260
  - test/cases/connection_test_sqlserver.rb
260
261
  - test/cases/disconnected_test_sqlserver.rb
262
+ - test/cases/eager_load_too_many_ids_test_sqlserver.rb
261
263
  - test/cases/execute_procedure_test_sqlserver.rb
262
264
  - test/cases/fetch_test_sqlserver.rb
263
265
  - test/cases/fully_qualified_identifier_test_sqlserver.rb
@@ -288,6 +290,7 @@ test_files:
288
290
  - test/migrations/create_clients_and_change_column_null.rb
289
291
  - test/migrations/transaction_table/1_table_will_never_be_created.rb
290
292
  - test/models/sqlserver/booking.rb
293
+ - test/models/sqlserver/composite_pk.rb
291
294
  - test/models/sqlserver/customers_view.rb
292
295
  - test/models/sqlserver/datatype.rb
293
296
  - test/models/sqlserver/datatype_migration.rb
@@ -320,8 +323,8 @@ test_files:
320
323
  - test/support/connection_reflection.rb
321
324
  - test/support/core_ext/query_cache.rb
322
325
  - test/support/load_schema_sqlserver.rb
323
- - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump
324
- - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump
326
+ - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump
327
+ - test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump
325
328
  - test/support/minitest_sqlserver.rb
326
329
  - test/support/paths_sqlserver.rb
327
330
  - test/support/rake_helpers.rb
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_record/relation"
4
- require "active_record/version"
5
-
6
- module ActiveRecord
7
- module ConnectionAdapters
8
- module SQLServer
9
- module CoreExt
10
- module QueryMethods
11
- private
12
-
13
- # Copy of original from Rails master.
14
- # This patch can be removed when adapter supports Rails version greater than 6.0.2.2
15
- def table_name_matches?(from)
16
- table_name = Regexp.escape(table.name)
17
- quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
18
- /(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
25
-
26
- ActiveSupport.on_load(:active_record) do
27
- ActiveRecord::Relation.include(ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::QueryMethods)
28
- end