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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/Gemfile +9 -0
  4. data/README.md +40 -26
  5. data/VERSION +1 -0
  6. data/activerecord-sqlserver-adapter.gemspec +0 -10
  7. data/appveyor.yml +15 -3
  8. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +3 -3
  9. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +35 -11
  10. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +4 -16
  11. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +12 -2
  12. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +8 -0
  13. data/lib/active_record/connection_adapters/sqlserver/type.rb +3 -1
  14. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +9 -0
  15. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +18 -12
  16. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  17. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +31 -0
  18. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +6 -6
  19. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +13 -29
  20. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +72 -0
  21. data/lib/active_record/connection_adapters/sqlserver/utils.rb +12 -12
  22. data/lib/active_record/connection_adapters/sqlserver/version.rb +1 -1
  23. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +41 -5
  24. data/lib/active_record/sqlserver_base.rb +2 -4
  25. data/lib/arel/visitors/sqlserver.rb +19 -0
  26. data/test/cases/adapter_test_sqlserver.rb +24 -0
  27. data/test/cases/coerced_tests.rb +14 -0
  28. data/test/cases/column_test_sqlserver.rb +120 -47
  29. data/test/cases/connection_test_sqlserver.rb +3 -3
  30. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  31. data/test/cases/helper_sqlserver.rb +24 -16
  32. data/test/cases/migration_test_sqlserver.rb +0 -5
  33. data/test/cases/rake_test_sqlserver.rb +29 -10
  34. data/test/cases/schema_dumper_test_sqlserver.rb +28 -11
  35. data/test/cases/transaction_test_sqlserver.rb +2 -2
  36. data/test/cases/utils_test_sqlserver.rb +44 -14
  37. data/test/config.yml +2 -0
  38. data/test/debug.rb +14 -0
  39. data/test/schema/datatypes/2012.sql +8 -18
  40. data/test/schema/sqlserver_specific_schema.rb +14 -12
  41. data/test/support/connection_reflection.rb +37 -0
  42. metadata +13 -143
  43. data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +0 -32
@@ -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 '0001-01-01' # TODO: None type casted default. Really want Date.civil(0001, 1, 1).
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, 000)
316
- obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 000)
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 .003 seconds and return again.
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 :datetime
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 ActiveRecord's 2000-01-01 convention too.
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.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0)
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.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.reload.time_2.usec}> vs <30000>"
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.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.reload.time_2.usec}> vs <0>"
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 sqlserver_azure?
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 connection_mode_odbc?
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 connection.instance_variable_get(:@connection_options)[:mode]
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, :test
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
- def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end
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 connection
32
- ActiveRecord::Base.connection
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.use_output_inserted = false
29
+ klass = ActiveRecord::ConnectionAdapters::SQLServerAdapter
30
+ klass.use_output_inserted = false
37
31
  yield
38
32
  ensure
39
- ActiveRecord::ConnectionAdapters::SQLServerAdapter.use_output_inserted = true
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
@@ -58,9 +58,4 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
58
58
 
59
59
  end
60
60
 
61
-
62
- def quietly
63
- silence_stream(STDOUT) { silence_stream(STDERR) { yield } }
64
- end
65
-
66
61
  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 do
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
- after do
17
- ActiveRecord::Base.establish_connection(default_configuration)
18
- connection.drop_database(new_database) rescue nil
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
- # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk
124
- skip if host_windows?
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
- # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk
135
- skip if host_windows?
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'