activerecord-sqlserver-adapter 4.2.6 → 4.2.8
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/CHANGELOG.md +34 -0
- data/Gemfile +9 -0
- data/README.md +40 -26
- data/VERSION +1 -0
- data/activerecord-sqlserver-adapter.gemspec +0 -10
- data/appveyor.yml +15 -3
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +35 -11
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +4 -16
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +12 -2
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +8 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +9 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +18 -12
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +31 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +6 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +13 -29
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +72 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +12 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +41 -5
- data/lib/active_record/sqlserver_base.rb +2 -4
- data/lib/arel/visitors/sqlserver.rb +19 -0
- data/test/cases/adapter_test_sqlserver.rb +24 -0
- data/test/cases/coerced_tests.rb +14 -0
- data/test/cases/column_test_sqlserver.rb +120 -47
- data/test/cases/connection_test_sqlserver.rb +3 -3
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
- data/test/cases/helper_sqlserver.rb +24 -16
- data/test/cases/migration_test_sqlserver.rb +0 -5
- data/test/cases/rake_test_sqlserver.rb +29 -10
- data/test/cases/schema_dumper_test_sqlserver.rb +28 -11
- data/test/cases/transaction_test_sqlserver.rb +2 -2
- data/test/cases/utils_test_sqlserver.rb +44 -14
- data/test/config.yml +2 -0
- data/test/debug.rb +14 -0
- data/test/schema/datatypes/2012.sql +8 -18
- data/test/schema/sqlserver_specific_schema.rb +14 -12
- data/test/support/connection_reflection.rb +37 -0
- metadata +13 -143
- data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +0 -32
data/test/cases/coerced_tests.rb
CHANGED
@@ -601,6 +601,20 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
601
601
|
# This is a poorly written test and really does not catch the bottom'ness it is meant too. Ours throw it off.
|
602
602
|
coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues
|
603
603
|
|
604
|
+
# Fall through false positive with no filter.
|
605
|
+
coerce_tests! :test_schema_dumps_partial_indices
|
606
|
+
def test_schema_dumps_partial_indices_coerced
|
607
|
+
index_definition = standard_dump.split(/\n/).grep(/add_index.*company_partial_index/).first.strip
|
608
|
+
assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", where: "([rating]>(10))"', index_definition
|
609
|
+
end
|
610
|
+
|
611
|
+
end
|
612
|
+
|
613
|
+
class SchemaDumperDefaultsTest < ActiveRecord::TestCase
|
614
|
+
|
615
|
+
# These date formats do not match ours. We got these covered in our dumper tests.
|
616
|
+
coerce_tests! :test_schema_dump_defaults_with_universally_supported_types
|
617
|
+
|
604
618
|
end
|
605
619
|
|
606
620
|
|
@@ -284,7 +284,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
284
284
|
col = column('date')
|
285
285
|
col.sql_type.must_equal 'date'
|
286
286
|
col.null.must_equal true
|
287
|
-
col.default.must_equal
|
287
|
+
col.default.must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01'
|
288
288
|
obj.date.must_equal Date.civil(0001, 1, 1)
|
289
289
|
col.default_function.must_equal nil
|
290
290
|
type = col.cast_type
|
@@ -312,8 +312,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
312
312
|
col = column('datetime')
|
313
313
|
col.sql_type.must_equal 'datetime'
|
314
314
|
col.null.must_equal true
|
315
|
-
col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00,
|
316
|
-
obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00,
|
315
|
+
col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{col.default.usec}> vs <123000>"
|
316
|
+
obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{obj.datetime.usec}> vs <123000>"
|
317
317
|
col.default_function.must_equal nil
|
318
318
|
type = col.cast_type
|
319
319
|
type.must_be_instance_of Type::DateTime
|
@@ -322,7 +322,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
322
322
|
type.limit.must_equal nil
|
323
323
|
type.precision.must_equal nil
|
324
324
|
type.scale.must_equal nil
|
325
|
-
# Can save
|
325
|
+
# Can save to proper accuracy and return again.
|
326
326
|
obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 3000)
|
327
327
|
obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>"
|
328
328
|
obj.save!
|
@@ -334,6 +334,80 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
334
334
|
obj.reload.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>"
|
335
335
|
end
|
336
336
|
|
337
|
+
it 'datetime2' do
|
338
|
+
skip 'datetime2 not supported in this protocal version' unless connection_dblib_73?
|
339
|
+
col = column('datetime2_7')
|
340
|
+
col.sql_type.must_equal 'datetime2(7)'
|
341
|
+
col.null.must_equal true
|
342
|
+
col.default.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <999999900>"
|
343
|
+
obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>"
|
344
|
+
col.default_function.must_equal nil
|
345
|
+
type = col.cast_type
|
346
|
+
type.must_be_instance_of Type::DateTime2
|
347
|
+
type.type.must_equal :datetime2
|
348
|
+
type.wont_be :number?
|
349
|
+
type.limit.must_equal nil
|
350
|
+
type.precision.must_equal 7
|
351
|
+
type.scale.must_equal nil
|
352
|
+
# Can save 100 nanosecond precisoins and return again.
|
353
|
+
obj.datetime2_7 = Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456755, 1000))
|
354
|
+
obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>"
|
355
|
+
obj.save!
|
356
|
+
obj.reload.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>"
|
357
|
+
# With other precisions.
|
358
|
+
time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000)
|
359
|
+
col = column('datetime2_3')
|
360
|
+
col.cast_type.precision.must_equal 3
|
361
|
+
obj.datetime2_3 = time
|
362
|
+
obj.datetime2_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>"
|
363
|
+
obj.save! ; obj.reload
|
364
|
+
obj.datetime2_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>"
|
365
|
+
col = column('datetime2_1')
|
366
|
+
col.cast_type.precision.must_equal 1
|
367
|
+
obj.datetime2_1 = time
|
368
|
+
obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>"
|
369
|
+
obj.save! ; obj.reload
|
370
|
+
obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>"
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'datetimeoffset' do
|
374
|
+
skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73?
|
375
|
+
col = column('datetimeoffset_7')
|
376
|
+
col.sql_type.must_equal 'datetimeoffset(7)'
|
377
|
+
col.null.must_equal true
|
378
|
+
col.default.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>"
|
379
|
+
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>"
|
380
|
+
col.default_function.must_equal nil
|
381
|
+
type = col.cast_type
|
382
|
+
type.must_be_instance_of Type::DateTimeOffset
|
383
|
+
type.type.must_equal :datetimeoffset
|
384
|
+
type.wont_be :number?
|
385
|
+
type.limit.must_equal nil
|
386
|
+
type.precision.must_equal 7
|
387
|
+
type.scale.must_equal nil
|
388
|
+
# Can save 100 nanosecond precisoins and return again.
|
389
|
+
obj.datetimeoffset_7 = Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456755)
|
390
|
+
obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
|
391
|
+
obj.save! ; obj.reload
|
392
|
+
obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>"
|
393
|
+
# With other precisions.
|
394
|
+
time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000)
|
395
|
+
col = column('datetimeoffset_3')
|
396
|
+
col.cast_type.precision.must_equal 3
|
397
|
+
obj.datetimeoffset_3 = time
|
398
|
+
obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>"
|
399
|
+
# TODO: FreeTDS date bug fixed: https://github.com/FreeTDS/freetds/issues/44
|
400
|
+
return
|
401
|
+
obj.save! ; obj.reload
|
402
|
+
obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>"
|
403
|
+
col = column('datetime2_1')
|
404
|
+
col.cast_type.precision.must_equal 1
|
405
|
+
obj.datetime2_1 = time
|
406
|
+
obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>"
|
407
|
+
obj.save! ; obj.reload
|
408
|
+
obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>"
|
409
|
+
end
|
410
|
+
|
337
411
|
it 'smalldatetime' do
|
338
412
|
col = column('smalldatetime')
|
339
413
|
col.sql_type.must_equal 'smalldatetime'
|
@@ -343,7 +417,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
343
417
|
col.default_function.must_equal nil
|
344
418
|
type = col.cast_type
|
345
419
|
type.must_be_instance_of Type::SmallDateTime
|
346
|
-
type.type.must_equal :
|
420
|
+
type.type.must_equal :smalldatetime
|
347
421
|
type.wont_be :number?
|
348
422
|
type.limit.must_equal nil
|
349
423
|
type.precision.must_equal nil
|
@@ -355,7 +429,41 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
355
429
|
obj.reload.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>"
|
356
430
|
end
|
357
431
|
|
432
|
+
it 'time(7)' do
|
433
|
+
skip 'time() not supported in this protocal version' unless connection_dblib_73?
|
434
|
+
col = column('time_7')
|
435
|
+
col.sql_type.must_equal 'time(7)'
|
436
|
+
col.null.must_equal true
|
437
|
+
col.default.must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>"
|
438
|
+
col.default_function.must_equal nil
|
439
|
+
type = col.cast_type
|
440
|
+
type.must_be_instance_of Type::Time
|
441
|
+
type.type.must_equal :time
|
442
|
+
type.wont_be :number?
|
443
|
+
type.limit.must_equal nil
|
444
|
+
type.precision.must_equal 7
|
445
|
+
type.scale.must_equal nil
|
446
|
+
# Time's #usec precision (low micro)
|
447
|
+
obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300)
|
448
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>"
|
449
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>"
|
450
|
+
obj.save! ; obj.reload
|
451
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>"
|
452
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>"
|
453
|
+
# Time's #usec precision (high micro)
|
454
|
+
obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567)
|
455
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
|
456
|
+
obj.save! ; obj.reload
|
457
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
|
458
|
+
# Time's #usec precision (high nano rounded)
|
459
|
+
obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000))
|
460
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
|
461
|
+
obj.save! ; obj.reload
|
462
|
+
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>"
|
463
|
+
end
|
464
|
+
|
358
465
|
it 'time(2)' do
|
466
|
+
skip 'time() not supported in this protocal version' unless connection_dblib_73?
|
359
467
|
col = column('time_2')
|
360
468
|
col.sql_type.must_equal 'time(2)'
|
361
469
|
col.null.must_equal true
|
@@ -368,56 +476,21 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
368
476
|
type.limit.must_equal nil
|
369
477
|
type.precision.must_equal 2
|
370
478
|
type.scale.must_equal nil
|
371
|
-
# Always uses
|
479
|
+
# Always uses TinyTDS/Windows 2000-01-01 convention too.
|
372
480
|
obj.time_2 = Time.utc(2015, 01, 10, 15, 45, 00, 0)
|
373
481
|
obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0)
|
374
|
-
obj.save!
|
375
|
-
obj.
|
376
|
-
# Midnight the beggining of the day.
|
377
|
-
obj.time_2 = Time.utc(2000, 01, 01).midnight.change(usec: 0)
|
378
|
-
obj.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0)
|
379
|
-
obj.save!
|
380
|
-
obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0)
|
381
|
-
# The end of day.
|
382
|
-
obj.time_2 = Time.utc(2000, 01, 01).end_of_day.change(usec: 0)
|
383
|
-
obj.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0)
|
384
|
-
obj.save!
|
385
|
-
obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0)
|
482
|
+
obj.save! ; obj.reload
|
483
|
+
obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0)
|
386
484
|
# Time's #usec precision (barely in 2 precision equal to 0.03 seconds)
|
387
485
|
obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 30000)
|
388
486
|
obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
|
389
|
-
obj.save!
|
390
|
-
obj.
|
487
|
+
obj.save! ; obj.reload
|
488
|
+
obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>"
|
391
489
|
# Time's #usec precision (below 2 precision)
|
392
490
|
obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 4000)
|
393
491
|
obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
|
394
|
-
obj.save!
|
395
|
-
obj.
|
396
|
-
end
|
397
|
-
|
398
|
-
it 'time(7)' do
|
399
|
-
col = column('time_7')
|
400
|
-
col.sql_type.must_equal 'time(7)'
|
401
|
-
col.null.must_equal true
|
402
|
-
col.default.must_equal nil
|
403
|
-
col.default_function.must_equal nil
|
404
|
-
type = col.cast_type
|
405
|
-
type.must_be_instance_of Type::Time
|
406
|
-
type.type.must_equal :time
|
407
|
-
type.wont_be :number?
|
408
|
-
type.limit.must_equal nil
|
409
|
-
type.precision.must_equal nil, 'so it is clean in schema dumper'
|
410
|
-
type.scale.must_equal nil
|
411
|
-
# Time's #usec precision (low)
|
412
|
-
obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300)
|
413
|
-
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <300>"
|
414
|
-
obj.save!
|
415
|
-
obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.reload.time_7.usec}> vs <300>"
|
416
|
-
# Time's #usec precision (high)
|
417
|
-
obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567)
|
418
|
-
obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>"
|
419
|
-
obj.save!
|
420
|
-
obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.reload.time_7.usec}> vs <234567>"
|
492
|
+
obj.save! ; obj.reload
|
493
|
+
obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>"
|
421
494
|
end
|
422
495
|
|
423
496
|
# Character Strings
|
@@ -29,7 +29,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
29
29
|
connection.use_database
|
30
30
|
assert_equal 'activerecord_unittest', connection.current_database, 'Would default back to connection options'
|
31
31
|
end
|
32
|
-
end unless
|
32
|
+
end unless connection_sqlserver_azure?
|
33
33
|
|
34
34
|
describe 'ODBC connection management' do
|
35
35
|
|
@@ -83,7 +83,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
end if
|
86
|
+
end if connection_odbc?
|
87
87
|
|
88
88
|
|
89
89
|
describe 'Connection management' do
|
@@ -115,7 +115,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
115
115
|
private
|
116
116
|
|
117
117
|
def disconnect_raw_connection!
|
118
|
-
case
|
118
|
+
case connection_options[:mode]
|
119
119
|
when :dblib
|
120
120
|
connection.raw_connection.close rescue nil
|
121
121
|
when :odbc
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
|
3
|
+
class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
|
4
|
+
|
5
|
+
describe 'local server' do
|
6
|
+
|
7
|
+
it 'should use table name in select projections' do
|
8
|
+
table = Arel::Table.new(:table)
|
9
|
+
expected_sql = "SELECT [table].[name] FROM [table]"
|
10
|
+
assert_equal expected_sql, table.project(table[:name]).to_sql
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'remote server' do
|
16
|
+
|
17
|
+
before do
|
18
|
+
connection_options[:database_prefix] = "[my.server].db.schema."
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
connection_options.delete :database_prefix
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should use fully qualified table name in select from clause' do
|
26
|
+
table = Arel::Table.new(:table)
|
27
|
+
expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]"
|
28
|
+
assert_equal expected_sql, table.project(Arel.star).to_sql
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should not use fully qualified table name in select projections' do
|
32
|
+
table = Arel::Table.new(:table)
|
33
|
+
expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]"
|
34
|
+
assert_equal expected_sql, table.project(table[:name]).to_sql
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should not use fully qualified table name in where clause' do
|
38
|
+
table = Arel::Table.new(:table)
|
39
|
+
expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
|
40
|
+
assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should not use fully qualified table name in order clause' do
|
44
|
+
table = Arel::Table.new(:table)
|
45
|
+
expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]"
|
46
|
+
assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should use fully qualified table name in insert statement' do
|
50
|
+
manager = Arel::InsertManager.new(Arel::Table.engine)
|
51
|
+
manager.into Arel::Table.new(:table)
|
52
|
+
manager.values = manager.create_values [Arel.sql('*')], %w{ a }
|
53
|
+
expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)"
|
54
|
+
assert_equal expected_sql, manager.to_sql
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should use fully qualified table name in update statement' do
|
58
|
+
table = Arel::Table.new(:table)
|
59
|
+
manager = Arel::UpdateManager.new(Arel::Table.engine)
|
60
|
+
manager.table(table).where(table[:id].eq(42))
|
61
|
+
manager.set([[table[:name], "Bob"]])
|
62
|
+
expected_sql = "UPDATE [my.server].[db].[schema].[table] SET [name] = N'Bob' WHERE [table].[id] = 42"
|
63
|
+
assert_equal expected_sql, manager.to_sql
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should use fully qualified table name in delete statement' do
|
67
|
+
table = Arel::Table.new(:table)
|
68
|
+
manager = Arel::DeleteManager.new(Arel::Table.engine)
|
69
|
+
manager.from(table).where(table[:id].eq(42))
|
70
|
+
expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
|
71
|
+
assert_equal expected_sql, manager.to_sql
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
|
-
require 'bundler' ; Bundler.require :default, :development
|
1
|
+
require 'bundler/setup' ; Bundler.require :default, :development
|
2
2
|
require 'support/paths_sqlserver'
|
3
3
|
require 'support/minitest_sqlserver'
|
4
4
|
require 'cases/helper'
|
5
5
|
require 'support/load_schema_sqlserver'
|
6
6
|
require 'support/coerceable_test_sqlserver'
|
7
7
|
require 'support/sql_counter_sqlserver'
|
8
|
+
require 'support/connection_reflection'
|
8
9
|
require 'mocha/mini_test'
|
9
10
|
|
10
11
|
module ActiveRecord
|
@@ -12,31 +13,38 @@ module ActiveRecord
|
|
12
13
|
|
13
14
|
SQLServer = ActiveRecord::ConnectionAdapters::SQLServer
|
14
15
|
|
15
|
-
include ARTest::SQLServer::CoerceableTest
|
16
|
+
include ARTest::SQLServer::CoerceableTest,
|
17
|
+
ARTest::SQLServer::ConnectionReflection
|
16
18
|
|
17
19
|
let(:logger) { ActiveRecord::Base.logger }
|
18
20
|
|
19
|
-
class << self
|
20
|
-
def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end
|
21
|
-
def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :odbc ; end
|
22
|
-
def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end
|
23
|
-
def host_windows? ; RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ; end
|
24
|
-
end
|
25
21
|
|
26
|
-
|
27
|
-
def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end
|
28
|
-
def sqlserver_azure? ; self.class.sqlserver_azure? ; end
|
29
|
-
def host_windows? ; self.class.host_windows? ; end
|
22
|
+
private
|
30
23
|
|
31
|
-
def
|
32
|
-
|
24
|
+
def host_windows?
|
25
|
+
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
33
26
|
end
|
34
27
|
|
35
28
|
def with_use_output_inserted_disabled
|
36
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter
|
29
|
+
klass = ActiveRecord::ConnectionAdapters::SQLServerAdapter
|
30
|
+
klass.use_output_inserted = false
|
37
31
|
yield
|
38
32
|
ensure
|
39
|
-
|
33
|
+
klass.use_output_inserted = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def silence_stream(stream)
|
37
|
+
old_stream = stream.dup
|
38
|
+
stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
|
39
|
+
stream.sync = true
|
40
|
+
yield
|
41
|
+
ensure
|
42
|
+
stream.reopen(old_stream)
|
43
|
+
old_stream.close
|
44
|
+
end
|
45
|
+
|
46
|
+
def quietly
|
47
|
+
silence_stream(STDOUT) { silence_stream(STDERR) { yield } }
|
40
48
|
end
|
41
49
|
|
42
50
|
end
|
@@ -4,24 +4,43 @@ class SQLServerRakeTest < ActiveRecord::TestCase
|
|
4
4
|
|
5
5
|
self.use_transactional_fixtures = false
|
6
6
|
|
7
|
+
cattr_accessor :azure_skip
|
8
|
+
self.azure_skip = connection_sqlserver_azure?
|
9
|
+
|
7
10
|
let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks }
|
8
11
|
let(:new_database) { 'activerecord_unittest_tasks' }
|
9
12
|
let(:default_configuration) { ARTest.connection_config['arunit'] }
|
10
13
|
let(:configuration) { default_configuration.merge('database' => new_database) }
|
11
14
|
|
12
|
-
before
|
15
|
+
before { skip 'on azure' if azure_skip }
|
16
|
+
before { disconnect! unless azure_skip }
|
17
|
+
after { reconnect unless azure_skip }
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def disconnect!
|
13
22
|
connection.disconnect!
|
14
23
|
end
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
25
|
+
def reconnect
|
26
|
+
config = default_configuration
|
27
|
+
if connection_sqlserver_azure?
|
28
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => 'master'))
|
29
|
+
connection.drop_database(new_database) rescue nil
|
30
|
+
disconnect!
|
31
|
+
ActiveRecord::Base.establish_connection(config)
|
32
|
+
else
|
33
|
+
ActiveRecord::Base.establish_connection(config)
|
34
|
+
connection.drop_database(new_database) rescue nil
|
35
|
+
end
|
19
36
|
end
|
20
37
|
|
21
38
|
end
|
22
39
|
|
23
40
|
class SQLServerRakeCreateTest < SQLServerRakeTest
|
24
41
|
|
42
|
+
self.azure_skip = false
|
43
|
+
|
25
44
|
it 'establishes connection to database after create ' do
|
26
45
|
db_tasks.create configuration
|
27
46
|
connection.current_database.must_equal(new_database)
|
@@ -47,6 +66,8 @@ end
|
|
47
66
|
|
48
67
|
class SQLServerRakeDropTest < SQLServerRakeTest
|
49
68
|
|
69
|
+
self.azure_skip = false
|
70
|
+
|
50
71
|
it 'drops database and uses master' do
|
51
72
|
db_tasks.create configuration
|
52
73
|
db_tasks.drop configuration
|
@@ -120,9 +141,8 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest
|
|
120
141
|
end
|
121
142
|
|
122
143
|
it 'dumps structure and accounts for defncopy oddities' do
|
123
|
-
|
124
|
-
|
125
|
-
db_tasks.structure_dump configuration, filename
|
144
|
+
skip 'debug defncopy on windows later' if host_windows?
|
145
|
+
quietly { db_tasks.structure_dump configuration, filename }
|
126
146
|
filedata.wont_match %r{\AUSE.*\z}
|
127
147
|
filedata.wont_match %r{\AGO.*\z}
|
128
148
|
filedata.must_match %r{email\s+nvarchar\(4000\)}
|
@@ -131,9 +151,8 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest
|
|
131
151
|
end
|
132
152
|
|
133
153
|
it 'can load dumped structure' do
|
134
|
-
|
135
|
-
|
136
|
-
db_tasks.structure_dump configuration, filename
|
154
|
+
skip 'debug defncopy on windows later' if host_windows?
|
155
|
+
quietly { db_tasks.structure_dump configuration, filename }
|
137
156
|
filedata.must_match %r{CREATE TABLE dbo\.users}
|
138
157
|
db_tasks.purge(configuration)
|
139
158
|
connection.tables.wont_include 'users'
|