activerecord-sqlserver-adapter 6.1.2.1 → 7.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +30 -0
- data/.devcontainer/boot.sh +22 -0
- data/.devcontainer/devcontainer.json +38 -0
- data/.devcontainer/docker-compose.yml +42 -0
- data/.github/workflows/ci.yml +7 -4
- data/.gitignore +3 -1
- data/CHANGELOG.md +19 -42
- data/Dockerfile.ci +3 -3
- data/Gemfile +6 -1
- data/MIT-LICENSE +1 -1
- data/README.md +113 -27
- data/RUNNING_UNIT_TESTS.md +27 -14
- data/Rakefile +2 -6
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +3 -3
- data/appveyor.yml +4 -6
- data/docker-compose.ci.yml +2 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -23
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +10 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +12 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +24 -16
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -31
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +143 -155
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +57 -56
- data/lib/active_record/connection_adapters/sqlserver/savepoints.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -12
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +213 -57
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +13 -2
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +19 -1
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +21 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +187 -187
- data/lib/active_record/connection_adapters/sqlserver_column.rb +1 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +42 -33
- data/lib/arel/visitors/sqlserver.rb +77 -34
- data/test/cases/active_schema_test_sqlserver.rb +127 -0
- data/test/cases/adapter_test_sqlserver.rb +114 -26
- data/test/cases/coerced_tests.rb +1121 -340
- data/test/cases/column_test_sqlserver.rb +67 -64
- data/test/cases/connection_test_sqlserver.rb +3 -6
- data/test/cases/dbconsole.rb +19 -0
- data/test/cases/disconnected_test_sqlserver.rb +8 -5
- data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
- data/test/cases/enum_test_sqlserver.rb +49 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +9 -5
- data/test/cases/fetch_test_sqlserver.rb +19 -0
- data/test/cases/helper_sqlserver.rb +11 -5
- data/test/cases/index_test_sqlserver.rb +8 -6
- data/test/cases/json_test_sqlserver.rb +1 -1
- data/test/cases/lateral_test_sqlserver.rb +2 -2
- data/test/cases/migration_test_sqlserver.rb +19 -1
- data/test/cases/optimizer_hints_test_sqlserver.rb +21 -12
- data/test/cases/pessimistic_locking_test_sqlserver.rb +8 -7
- data/test/cases/primary_keys_test_sqlserver.rb +2 -2
- data/test/cases/rake_test_sqlserver.rb +10 -5
- data/test/cases/schema_dumper_test_sqlserver.rb +155 -109
- data/test/cases/schema_test_sqlserver.rb +64 -1
- data/test/cases/showplan_test_sqlserver.rb +7 -7
- data/test/cases/specific_schema_test_sqlserver.rb +17 -13
- data/test/cases/transaction_test_sqlserver.rb +13 -8
- data/test/cases/trigger_test_sqlserver.rb +20 -0
- data/test/cases/utils_test_sqlserver.rb +2 -2
- data/test/cases/uuid_test_sqlserver.rb +8 -0
- data/test/cases/view_test_sqlserver.rb +58 -0
- data/test/config.yml +1 -2
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +1 -1
- data/test/models/sqlserver/alien.rb +5 -0
- data/test/models/sqlserver/table_with_spaces.rb +5 -0
- data/test/models/sqlserver/trigger.rb +8 -0
- data/test/schema/sqlserver_specific_schema.rb +54 -6
- data/test/support/coerceable_test_sqlserver.rb +4 -4
- data/test/support/connection_reflection.rb +3 -9
- data/test/support/core_ext/query_cache.rb +7 -1
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump +0 -0
- data/test/support/query_assertions.rb +49 -0
- data/test/support/rake_helpers.rb +3 -1
- data/test/support/table_definition_sqlserver.rb +24 -0
- data/test/support/test_in_memory_oltp.rb +2 -2
- metadata +41 -17
- data/lib/active_record/sqlserver_base.rb +0 -18
- data/test/cases/scratchpad_test_sqlserver.rb +0 -8
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
- data/test/support/sql_counter_sqlserver.rb +0 -29
@@ -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
|
281
|
-
_(obj.date).must_equal Date.civil(
|
280
|
+
_(col.default).must_equal connection_tds_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,22 +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(
|
290
|
+
_(obj.date).must_equal Date.civil(1, 4, 1)
|
291
291
|
obj.save!
|
292
|
-
_(obj.date).must_equal Date.civil(
|
292
|
+
_(obj.date).must_equal Date.civil(1, 4, 1)
|
293
293
|
obj.reload
|
294
|
-
_(obj.date).must_equal Date.civil(
|
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(
|
297
|
+
_(obj.date).must_equal Date.civil(1, 4, 1)
|
298
298
|
obj.save!
|
299
|
-
_(obj.date).must_equal Date.civil(
|
299
|
+
_(obj.date).must_equal Date.civil(1, 4, 1)
|
300
300
|
obj.reload
|
301
|
-
_(obj.date).must_equal Date.civil(
|
301
|
+
_(obj.date).must_equal Date.civil(1, 4, 1)
|
302
302
|
# Can filter by date range
|
303
303
|
_(obj).must_equal obj.class.where(date: obj.date..Date::Infinity.new).first
|
304
304
|
# Can keep and return assigned date.
|
305
|
-
assert_obj_set_and_save :date, Date.civil(1972,
|
305
|
+
assert_obj_set_and_save :date, Date.civil(1972, 4, 14)
|
306
306
|
# Can accept and cast time objects.
|
307
307
|
obj.date = Time.utc(2010, 4, 14, 12, 34, 56, 3000)
|
308
308
|
_(obj.date).must_equal Date.civil(2010, 4, 14)
|
@@ -315,7 +315,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
315
315
|
_(col.sql_type).must_equal "datetime"
|
316
316
|
_(col.type).must_equal :datetime
|
317
317
|
_(col.null).must_equal true
|
318
|
-
time = Time.utc 1753,
|
318
|
+
time = Time.utc 1753, 1, 1, 0, 0, 0, 123000
|
319
319
|
_(col.default).must_equal time, "Microseconds were <#{col.default.usec}> vs <123000>"
|
320
320
|
_(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <123000>"
|
321
321
|
_(col.default_function).must_be_nil
|
@@ -327,7 +327,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
327
327
|
obj.save!
|
328
328
|
_(obj).must_equal obj.class.where(datetime: time).first
|
329
329
|
# Can save to proper accuracy and return again.
|
330
|
-
time = Time.utc 2010,
|
330
|
+
time = Time.utc 2010, 4, 1, 12, 34, 56, 3000
|
331
331
|
obj.datetime = time
|
332
332
|
_(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>"
|
333
333
|
obj.save!
|
@@ -338,8 +338,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
338
338
|
# Can filter by datetime range
|
339
339
|
_(obj).must_equal obj.class.where(datetime: time..DateTime::Infinity.new).first
|
340
340
|
# Will cast to true DB value on attribute write, save and return again.
|
341
|
-
time = Time.utc 2010,
|
342
|
-
time2 = Time.utc 2010,
|
341
|
+
time = Time.utc 2010, 4, 1, 12, 34, 56, 234567
|
342
|
+
time2 = Time.utc 2010, 4, 1, 12, 34, 56, 233000
|
343
343
|
obj.datetime = time
|
344
344
|
_(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>"
|
345
345
|
obj.save!
|
@@ -357,7 +357,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
357
357
|
end
|
358
358
|
|
359
359
|
it "datetime2" do
|
360
|
-
skip "datetime2 not supported in this protocol version" unless
|
360
|
+
skip "datetime2 not supported in this protocol version" unless connection_tds_73
|
361
361
|
col = column("datetime2_7")
|
362
362
|
_(col.sql_type).must_equal "datetime2(7)"
|
363
363
|
_(col.type).must_equal :datetime
|
@@ -422,26 +422,28 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
422
422
|
end
|
423
423
|
|
424
424
|
it "datetimeoffset" do
|
425
|
-
skip "datetimeoffset not supported in this protocol version" unless
|
425
|
+
skip "datetimeoffset not supported in this protocol version" unless connection_tds_73
|
426
426
|
col = column("datetimeoffset_7")
|
427
427
|
_(col.sql_type).must_equal "datetimeoffset(7)"
|
428
428
|
_(col.type).must_equal :datetimeoffset
|
429
429
|
_(col.null).must_equal true
|
430
|
-
_(col.default).must_equal Time.new(1984,
|
431
|
-
_(obj.datetimeoffset_7).must_equal Time.new(1984,
|
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>"
|
432
432
|
_(col.default_function).must_be_nil
|
433
433
|
type = connection.lookup_cast_type_from_column(col)
|
434
434
|
_(type).must_be_instance_of Type::DateTimeOffset
|
435
435
|
_(type.limit).must_be_nil
|
436
436
|
_(type.precision).must_equal 7
|
437
437
|
_(type.scale).must_be_nil
|
438
|
-
|
439
|
-
|
440
|
-
|
438
|
+
|
439
|
+
# Can save 100 nanosecond precisions and return again.
|
440
|
+
obj.datetimeoffset_7 = Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456755)
|
441
|
+
_(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
442
|
obj.save!
|
442
|
-
_(obj.datetimeoffset_7).must_equal Time.new(2010,
|
443
|
+
_(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>"
|
443
444
|
obj.reload
|
444
|
-
_(obj.datetimeoffset_7).must_equal Time.new(2010,
|
445
|
+
_(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>"
|
446
|
+
|
445
447
|
# Maintains the timezone
|
446
448
|
time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000)
|
447
449
|
obj.datetimeoffset_7 = time
|
@@ -449,6 +451,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
449
451
|
obj.save!
|
450
452
|
_(obj.datetimeoffset_7).must_equal time
|
451
453
|
_(obj.reload.datetimeoffset_7).must_equal time
|
454
|
+
|
452
455
|
# With other precisions.
|
453
456
|
time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000)
|
454
457
|
col = column("datetimeoffset_3")
|
@@ -470,8 +473,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
470
473
|
_(col.sql_type).must_equal "smalldatetime"
|
471
474
|
_(col.type).must_equal :smalldatetime
|
472
475
|
_(col.null).must_equal true
|
473
|
-
_(col.default).must_equal Time.utc(1901,
|
474
|
-
_(obj.smalldatetime).must_equal Time.utc(1901,
|
476
|
+
_(col.default).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0)
|
477
|
+
_(obj.smalldatetime).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0)
|
475
478
|
_(col.default_function).must_be_nil
|
476
479
|
type = connection.lookup_cast_type_from_column(col)
|
477
480
|
_(type).must_be_instance_of Type::SmallDateTime
|
@@ -479,21 +482,21 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
479
482
|
_(type.precision).must_be_nil
|
480
483
|
_(type.scale).must_be_nil
|
481
484
|
# Will remove fractional seconds and return again.
|
482
|
-
obj.smalldatetime = Time.utc(2078,
|
483
|
-
_(obj.smalldatetime).must_equal Time.utc(2078,
|
485
|
+
obj.smalldatetime = Time.utc(2078, 6, 5, 4, 20, 0, 3000)
|
486
|
+
_(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>"
|
484
487
|
obj.save!
|
485
|
-
_(obj.smalldatetime).must_equal Time.utc(2078,
|
488
|
+
_(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>"
|
486
489
|
obj.reload
|
487
|
-
_(obj.smalldatetime).must_equal Time.utc(2078,
|
490
|
+
_(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>"
|
488
491
|
end
|
489
492
|
|
490
493
|
it "time(7)" do
|
491
|
-
skip "time() not supported in this protocol version" unless
|
494
|
+
skip "time() not supported in this protocol version" unless connection_tds_73
|
492
495
|
col = column("time_7")
|
493
496
|
_(col.sql_type).must_equal "time(7)"
|
494
497
|
_(col.type).must_equal :time
|
495
498
|
_(col.null).must_equal true
|
496
|
-
_(col.default).must_equal Time.utc(1900,
|
499
|
+
_(col.default).must_equal Time.utc(1900, 1, 1, 4, 20, 0, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>"
|
497
500
|
_(col.default_function).must_be_nil
|
498
501
|
type = connection.lookup_cast_type_from_column(col)
|
499
502
|
_(type).must_be_instance_of Type::Time
|
@@ -501,26 +504,26 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
501
504
|
_(type.precision).must_equal 7
|
502
505
|
_(type.scale).must_be_nil
|
503
506
|
# Time's #usec precision (low micro)
|
504
|
-
obj.time_7 = Time.utc(2000,
|
505
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
506
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
507
|
+
obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, 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>"
|
507
510
|
obj.save!; obj.reload
|
508
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
509
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
511
|
+
_(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>"
|
512
|
+
_(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>"
|
510
513
|
# Time's #usec precision (high micro)
|
511
|
-
obj.time_7 = Time.utc(2000,
|
512
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
514
|
+
obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, 234567)
|
515
|
+
_(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
|
513
516
|
obj.save!; obj.reload
|
514
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
517
|
+
_(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
|
515
518
|
# Time's #usec precision (high nano rounded)
|
516
|
-
obj.time_7 = Time.utc(2000,
|
517
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
519
|
+
obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321545, 1000))
|
520
|
+
_(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
|
518
521
|
obj.save!; obj.reload
|
519
|
-
_(obj.time_7).must_equal Time.utc(2000,
|
522
|
+
_(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
|
520
523
|
end
|
521
524
|
|
522
525
|
it "time(2)" do
|
523
|
-
skip "time() not supported in this protocol version" unless
|
526
|
+
skip "time() not supported in this protocol version" unless connection_tds_73
|
524
527
|
col = column("time_2")
|
525
528
|
_(col.sql_type).must_equal "time(2)"
|
526
529
|
_(col.type).must_equal :time
|
@@ -533,29 +536,29 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
533
536
|
_(type.precision).must_equal 2
|
534
537
|
_(type.scale).must_be_nil
|
535
538
|
# Always uses TinyTDS/Windows 2000-01-01 convention too.
|
536
|
-
obj.time_2 = Time.utc(2015,
|
537
|
-
_(obj.time_2).must_equal Time.utc(2000,
|
539
|
+
obj.time_2 = Time.utc(2015, 1, 10, 15, 45, 0, 0)
|
540
|
+
_(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0)
|
538
541
|
obj.save!; obj.reload
|
539
|
-
_(obj.time_2).must_equal Time.utc(2000,
|
542
|
+
_(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0)
|
540
543
|
# Time's #usec precision (barely in 2 precision equal to 0.03 seconds)
|
541
|
-
obj.time_2 = Time.utc(2000,
|
542
|
-
_(obj.time_2).must_equal Time.utc(2000,
|
544
|
+
obj.time_2 = Time.utc(2000, 1, 1, 15, 45, 0, 30000)
|
545
|
+
_(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
|
543
546
|
obj.save!; obj.reload
|
544
|
-
_(obj.time_2).must_equal Time.utc(2000,
|
547
|
+
_(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
|
545
548
|
# Time's #usec precision (below 2 precision)
|
546
|
-
obj.time_2 = Time.utc(2000,
|
547
|
-
_(obj.time_2).must_equal Time.utc(2000,
|
549
|
+
obj.time_2 = Time.utc(2000, 1, 1, 15, 45, 0, 4000)
|
550
|
+
_(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
|
548
551
|
obj.save!; obj.reload
|
549
|
-
_(obj.time_2).must_equal Time.utc(2000,
|
552
|
+
_(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
|
550
553
|
end
|
551
554
|
|
552
555
|
it "time using default precision" do
|
553
|
-
skip "time() not supported in this protocol version" unless
|
556
|
+
skip "time() not supported in this protocol version" unless connection_tds_73
|
554
557
|
col = column("time_default")
|
555
558
|
_(col.sql_type).must_equal "time(7)"
|
556
559
|
_(col.type).must_equal :time
|
557
560
|
_(col.null).must_equal true
|
558
|
-
_(col.default).must_equal Time.utc(1900,
|
561
|
+
_(col.default).must_equal Time.utc(1900, 1, 1, 15, 3, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>"
|
559
562
|
_(col.default_function).must_be_nil
|
560
563
|
type = connection.lookup_cast_type_from_column(col)
|
561
564
|
_(type).must_be_instance_of Type::Time
|
@@ -563,22 +566,22 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
563
566
|
_(type.precision).must_equal 7
|
564
567
|
_(type.scale).must_be_nil
|
565
568
|
# Time's #usec precision (low micro)
|
566
|
-
obj.time_default = Time.utc(2000,
|
567
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
568
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
569
|
+
obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, 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>"
|
569
572
|
obj.save!; obj.reload
|
570
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
571
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
573
|
+
_(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>"
|
574
|
+
_(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>"
|
572
575
|
# Time's #usec precision (high micro)
|
573
|
-
obj.time_default = Time.utc(2000,
|
574
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
576
|
+
obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, 234567)
|
577
|
+
_(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>"
|
575
578
|
obj.save!; obj.reload
|
576
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
579
|
+
_(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>"
|
577
580
|
# Time's #usec precision (high nano rounded)
|
578
|
-
obj.time_default = Time.utc(2000,
|
579
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
581
|
+
obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321545, 1000))
|
582
|
+
_(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>"
|
580
583
|
obj.save!; obj.reload
|
581
|
-
_(obj.time_default).must_equal Time.utc(2000,
|
584
|
+
_(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>"
|
582
585
|
end
|
583
586
|
|
584
587
|
# Character Strings
|
@@ -44,9 +44,9 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
44
44
|
assert connection.spid.nil?
|
45
45
|
end
|
46
46
|
|
47
|
-
it "reset
|
47
|
+
it "reset raw connection on disconnect!" do
|
48
48
|
connection.disconnect!
|
49
|
-
_(connection.raw_connection).must_be_nil
|
49
|
+
_(connection.instance_variable_get(:@raw_connection)).must_be_nil
|
50
50
|
end
|
51
51
|
|
52
52
|
it "be able to disconnect and reconnect at will" do
|
@@ -60,9 +60,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
60
60
|
private
|
61
61
|
|
62
62
|
def disconnect_raw_connection!
|
63
|
-
|
64
|
-
when :dblib
|
65
|
-
connection.raw_connection.close rescue nil
|
66
|
-
end
|
63
|
+
connection.raw_connection.close rescue nil
|
67
64
|
end
|
68
65
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DbConsole < ActiveRecord::TestCase
|
4
|
+
subject { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter }
|
5
|
+
|
6
|
+
it "uses sqlplus to connect to database" do
|
7
|
+
subject.expects(:find_cmd_and_exec).with("sqlcmd", "-d", "db", "-U", "user", "-P", "secret", "-S", "tcp:localhost,1433")
|
8
|
+
|
9
|
+
config = make_db_config(adapter: "sqlserver", database: "db", username: "user", password: "secret", host: "localhost", port: 1433)
|
10
|
+
|
11
|
+
subject.dbconsole(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def make_db_config(config)
|
17
|
+
ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", config)
|
18
|
+
end
|
19
|
+
end
|
@@ -5,8 +5,9 @@ require "cases/helper_sqlserver"
|
|
5
5
|
class TestDisconnectedAdapter < ActiveRecord::TestCase
|
6
6
|
self.use_transactional_tests = false
|
7
7
|
|
8
|
+
undef_method :setup
|
8
9
|
def setup
|
9
|
-
@connection = ActiveRecord::Base.
|
10
|
+
@connection = ActiveRecord::Base.lease_connection
|
10
11
|
end
|
11
12
|
|
12
13
|
teardown do
|
@@ -15,15 +16,16 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase
|
|
15
16
|
ActiveRecord::Base.establish_connection(db_config)
|
16
17
|
end
|
17
18
|
|
18
|
-
test "
|
19
|
+
test "execute procedure after disconnect reconnects" do
|
19
20
|
@connection.execute_procedure :sp_tables, "sst_datatypes"
|
20
21
|
@connection.disconnect!
|
21
|
-
|
22
|
+
|
23
|
+
assert_nothing_raised do
|
22
24
|
@connection.execute_procedure :sp_tables, "sst_datatypes"
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
test "
|
28
|
+
test "execute query after disconnect reconnects" do
|
27
29
|
sql = "SELECT count(*) from products WHERE id IN(@0, @1)"
|
28
30
|
binds = [
|
29
31
|
ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new),
|
@@ -32,7 +34,8 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase
|
|
32
34
|
|
33
35
|
@connection.exec_query sql, "TEST", binds
|
34
36
|
@connection.disconnect!
|
35
|
-
|
37
|
+
|
38
|
+
assert_nothing_raised do
|
36
39
|
@connection.exec_query sql, "TEST", binds
|
37
40
|
end
|
38
41
|
end
|
@@ -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_count(expected_query_count) do
|
15
|
+
Citation.preload(:reference_of).to_a.size
|
16
|
+
end
|
17
|
+
end
|
18
|
+
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
|
@@ -42,12 +42,16 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase
|
|
42
42
|
assert_equal date_base.change(usec: 0), date_proc.change(usec: 0)
|
43
43
|
end
|
44
44
|
|
45
|
+
def transaction_with_procedure_and_return
|
46
|
+
ActiveRecord::Base.transaction do
|
47
|
+
connection.execute_procedure("my_getutcdate")
|
48
|
+
return
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
45
52
|
it 'test deprecation with transaction return when executing procedure' do
|
46
|
-
|
47
|
-
|
48
|
-
connection.execute_procedure("my_getutcdate")
|
49
|
-
return
|
50
|
-
end
|
53
|
+
assert_not_deprecated(ActiveRecord.deprecator) do
|
54
|
+
transaction_with_procedure_and_return
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
@@ -42,6 +42,25 @@ class FetchTestSqlserver < ActiveRecord::TestCase
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe "FROM subquery" do
|
46
|
+
let(:from_sql) { "(SELECT [books].* FROM [books]) [books]" }
|
47
|
+
|
48
|
+
it "SQL generated correctly for FROM subquery if order provided" do
|
49
|
+
query = Book.from(from_sql).order(:id).limit(5)
|
50
|
+
|
51
|
+
assert_equal query.to_sql, "SELECT [books].* FROM (SELECT [books].* FROM [books]) [books] ORDER BY [books].[id] ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY"
|
52
|
+
assert_equal query.to_a.count, 5
|
53
|
+
end
|
54
|
+
|
55
|
+
it "exception thrown if FROM subquery is provided without an order" do
|
56
|
+
query = Book.from(from_sql).limit(5)
|
57
|
+
|
58
|
+
assert_raise(ActiveRecord::StatementInvalid) do
|
59
|
+
query.to_sql
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
45
64
|
protected
|
46
65
|
|
47
66
|
def create_10_books
|
@@ -7,21 +7,27 @@ require "pry"
|
|
7
7
|
require "support/core_ext/query_cache"
|
8
8
|
require "support/minitest_sqlserver"
|
9
9
|
require "support/test_in_memory_oltp"
|
10
|
+
require "support/table_definition_sqlserver"
|
10
11
|
require "cases/helper"
|
11
12
|
require "support/load_schema_sqlserver"
|
12
13
|
require "support/coerceable_test_sqlserver"
|
13
|
-
require "support/sql_counter_sqlserver"
|
14
14
|
require "support/connection_reflection"
|
15
|
+
require "support/query_assertions"
|
15
16
|
require "mocha/minitest"
|
16
17
|
|
18
|
+
module ActiveSupport
|
19
|
+
class TestCase < ::Minitest::Test
|
20
|
+
include ARTest::SQLServer::CoerceableTest
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
17
24
|
module ActiveRecord
|
18
25
|
class TestCase < ActiveSupport::TestCase
|
19
26
|
SQLServer = ActiveRecord::ConnectionAdapters::SQLServer
|
20
27
|
|
21
|
-
include ARTest::SQLServer::
|
22
|
-
|
23
|
-
ARTest::SQLServer::
|
24
|
-
ActiveSupport::Testing::Stream
|
28
|
+
include ARTest::SQLServer::ConnectionReflection,
|
29
|
+
ActiveSupport::Testing::Stream,
|
30
|
+
ARTest::SQLServer::QueryAssertions
|
25
31
|
|
26
32
|
let(:logger) { ActiveRecord::Base.logger }
|
27
33
|
|
@@ -19,29 +19,31 @@ class IndexTestSQLServer < ActiveRecord::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
it "add index with order" do
|
22
|
-
|
22
|
+
assert_queries_match(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do
|
23
23
|
connection.add_index "testings", ["last_name"], order: { last_name: :desc }
|
24
24
|
connection.remove_index "testings", ["last_name"]
|
25
25
|
end
|
26
|
-
|
26
|
+
assert_queries_match(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do
|
27
27
|
connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc }
|
28
28
|
connection.remove_index "testings", ["last_name", "first_name"]
|
29
29
|
end
|
30
|
-
|
30
|
+
assert_queries_match(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\] ASC\)/i) do
|
31
31
|
connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc, first_name: :asc }
|
32
32
|
connection.remove_index "testings", ["last_name", "first_name"]
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
it "add index with where" do
|
37
|
-
|
37
|
+
assert_queries_match(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do
|
38
38
|
connection.add_index "testings", "last_name", where: "[first_name] = N'john doe'"
|
39
39
|
connection.remove_index "testings", "last_name"
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
it "add index with expression" do
|
44
|
-
|
45
|
-
|
44
|
+
assert_nothing_raised do
|
45
|
+
connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])"
|
46
|
+
connection.add_index "testings", "first_name_upper"
|
47
|
+
end
|
46
48
|
end
|
47
49
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "cases/helper_sqlserver"
|
4
4
|
|
5
|
-
if ActiveRecord::Base.
|
5
|
+
if ActiveRecord::Base.lease_connection.supports_json?
|
6
6
|
class JsonTestSQLServer < ActiveRecord::TestCase
|
7
7
|
before do
|
8
8
|
@o1 = SSTestDatatypeMigrationJson.create! json_col: { "a" => "a", "b" => "b", "c" => "c" }
|
@@ -16,7 +16,7 @@ class LateralTestSQLServer < ActiveRecord::TestCase
|
|
16
16
|
eq = Arel::Nodes::Equality.new(one, one)
|
17
17
|
|
18
18
|
sql = author.project(Arel.star).where(author[:name].matches("David")).outer_join(subselect.lateral.as("bar")).on(eq).to_sql
|
19
|
-
results = ActiveRecord::Base.
|
19
|
+
results = ActiveRecord::Base.lease_connection.exec_query sql
|
20
20
|
assert_equal sql, "SELECT * FROM [authors] OUTER APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'"
|
21
21
|
assert_equal results.length, 1
|
22
22
|
end
|
@@ -27,7 +27,7 @@ class LateralTestSQLServer < ActiveRecord::TestCase
|
|
27
27
|
subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42))
|
28
28
|
|
29
29
|
sql = author.project(Arel.star).where(author[:name].matches("David")).join(subselect.lateral.as("bar")).to_sql
|
30
|
-
results = ActiveRecord::Base.
|
30
|
+
results = ActiveRecord::Base.lease_connection.exec_query sql
|
31
31
|
|
32
32
|
assert_equal sql, "SELECT * FROM [authors] CROSS APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'"
|
33
33
|
assert_equal results.length, 0
|
@@ -20,7 +20,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
|
|
20
20
|
it "not create a tables if error in migrations" do
|
21
21
|
begin
|
22
22
|
migrations_dir = File.join ARTest::SQLServer.migrations_root, "transaction_table"
|
23
|
-
quietly { ActiveRecord::MigrationContext.new(migrations_dir
|
23
|
+
quietly { ActiveRecord::MigrationContext.new(migrations_dir).up }
|
24
24
|
rescue Exception => e
|
25
25
|
assert_match %r|this and all later migrations canceled|, e.message
|
26
26
|
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
|