activerecord-sqlserver-adapter 4.2.6 → 4.2.8
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|