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.
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'