activerecord-sqlserver-adapter 4.2.18 → 5.0.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -223
  3. data/Gemfile +18 -17
  4. data/RAILS5-TODO.md +36 -0
  5. data/README.md +27 -8
  6. data/RUNNING_UNIT_TESTS.md +0 -17
  7. data/Rakefile +2 -7
  8. data/VERSION +1 -1
  9. data/activerecord-sqlserver-adapter.gemspec +1 -1
  10. data/appveyor.yml +0 -2
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +15 -8
  12. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +45 -97
  13. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +1 -2
  14. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +31 -10
  15. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +0 -18
  16. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -0
  17. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +101 -58
  18. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +7 -7
  19. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +20 -0
  20. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +56 -32
  21. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +1 -1
  22. data/lib/active_record/connection_adapters/sqlserver/type.rb +34 -32
  23. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +4 -0
  24. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +6 -0
  25. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -0
  26. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +9 -20
  27. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +30 -0
  28. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +28 -4
  29. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +28 -14
  30. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  31. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +4 -16
  32. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +9 -0
  33. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +4 -0
  34. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -0
  35. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +5 -1
  36. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +4 -0
  37. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -1
  38. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +5 -1
  39. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +8 -1
  40. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +4 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +20 -8
  42. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +25 -10
  43. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +4 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +6 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +4 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +7 -1
  48. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +5 -1
  49. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +15 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +7 -1
  51. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +5 -1
  52. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +7 -1
  53. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +5 -1
  54. data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -0
  55. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +71 -57
  56. data/lib/active_record/connection_adapters/sqlserver_column.rb +5 -30
  57. data/lib/active_record/sqlserver_base.rb +1 -5
  58. data/lib/arel/visitors/sqlserver.rb +11 -20
  59. data/test/bin/setup.sh +4 -6
  60. data/test/cases/adapter_test_sqlserver.rb +11 -20
  61. data/test/cases/coerced_tests.rb +233 -138
  62. data/test/cases/column_test_sqlserver.rb +244 -227
  63. data/test/cases/connection_test_sqlserver.rb +5 -76
  64. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +7 -7
  65. data/test/cases/helper_sqlserver.rb +4 -15
  66. data/test/cases/pessimistic_locking_test_sqlserver.rb +1 -1
  67. data/test/cases/rake_test_sqlserver.rb +20 -14
  68. data/test/cases/schema_dumper_test_sqlserver.rb +94 -63
  69. data/test/cases/schema_test_sqlserver.rb +2 -2
  70. data/test/cases/showplan_test_sqlserver.rb +1 -1
  71. data/test/cases/specific_schema_test_sqlserver.rb +7 -14
  72. data/test/cases/transaction_test_sqlserver.rb +1 -1
  73. data/test/cases/uuid_test_sqlserver.rb +0 -1
  74. data/test/config.yml +0 -10
  75. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +1 -1
  76. data/test/schema/sqlserver_specific_schema.rb +0 -16
  77. data/test/support/coerceable_test_sqlserver.rb +6 -2
  78. data/test/support/connection_reflection.rb +0 -4
  79. data/test/support/sql_counter_sqlserver.rb +17 -21
  80. metadata +9 -7
  81. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +0 -34
  82. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +0 -114
@@ -4,12 +4,14 @@ require 'models/topic'
4
4
 
5
5
  class ConnectionTestSQLServer < ActiveRecord::TestCase
6
6
 
7
- self.use_transactional_fixtures = false
7
+ self.use_transactional_tests = false
8
8
 
9
9
  fixtures :topics, :accounts
10
10
 
11
- before { assert connection.active? }
12
- after { connection.reconnect! }
11
+ before do
12
+ connection.reconnect!
13
+ assert connection.active?
14
+ end
13
15
 
14
16
  it 'affect rows' do
15
17
  topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
@@ -31,61 +33,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
31
33
  end
32
34
  end unless connection_sqlserver_azure?
33
35
 
34
- describe 'ODBC connection management' do
35
-
36
- it 'return finished ODBC statement handle from #execute without block' do
37
- assert_all_odbc_statements_used_are_closed do
38
- connection.execute('SELECT * FROM [topics]')
39
- end
40
- end
41
-
42
- it 'finish ODBC statement handle from #execute with block' do
43
- assert_all_odbc_statements_used_are_closed do
44
- connection.execute('SELECT * FROM [topics]') { }
45
- end
46
- end
47
-
48
- it 'finish connection from #raw_select' do
49
- assert_all_odbc_statements_used_are_closed do
50
- connection.send(:raw_select,'SELECT * FROM [topics]')
51
- end
52
- end
53
-
54
- it 'execute without block closes statement' do
55
- assert_all_odbc_statements_used_are_closed do
56
- connection.execute("SELECT 1")
57
- end
58
- end
59
-
60
- it 'execute with block closes statement' do
61
- assert_all_odbc_statements_used_are_closed do
62
- connection.execute("SELECT 1") do |sth|
63
- assert !sth.finished?, "Statement should still be alive within block"
64
- end
65
- end
66
- end
67
-
68
- it 'insert with identity closes statement' do
69
- assert_all_odbc_statements_used_are_closed do
70
- connection.exec_insert "INSERT INTO accounts ([id],[firm_id],[credit_limit]) VALUES (999, 1, 50)", "SQL", []
71
- end
72
- end
73
-
74
- it 'insert without identity closes statement' do
75
- assert_all_odbc_statements_used_are_closed do
76
- connection.exec_insert "INSERT INTO accounts ([firm_id],[credit_limit]) VALUES (1, 50)", "SQL", []
77
- end
78
- end
79
-
80
- it 'active closes statement' do
81
- assert_all_odbc_statements_used_are_closed do
82
- connection.active?
83
- end
84
- end
85
-
86
- end if connection_odbc?
87
-
88
-
89
36
  describe 'Connection management' do
90
37
 
91
38
  it 'set spid on connect' do
@@ -118,25 +65,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
118
65
  case connection_options[:mode]
119
66
  when :dblib
120
67
  connection.raw_connection.close rescue nil
121
- when :odbc
122
- connection.raw_connection.disconnect rescue nil
123
68
  end
124
69
  end
125
70
 
126
- def assert_all_odbc_statements_used_are_closed(&block)
127
- odbc = connection.raw_connection.class.parent
128
- existing_handles = []
129
- ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h }
130
- existing_handle_ids = existing_handles.map(&:object_id)
131
- assert existing_handles.all?(&:finished?), "Somewhere before the block some statements were not closed"
132
- GC.disable
133
- yield
134
- used_handles = []
135
- ObjectSpace.each_object(odbc::Statement) { |h| used_handles << h unless existing_handle_ids.include?(h.object_id) }
136
- assert used_handles.size > 0, "No statements were used within given block"
137
- assert used_handles.all?(&:finished?), "Statement should have been closed within given block"
138
- ensure
139
- GC.enable
140
- end
141
-
142
71
  end
@@ -37,7 +37,7 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
37
37
  it 'should not use fully qualified table name in where clause' do
38
38
  table = Arel::Table.new(:table)
39
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
40
+ quietly { assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql }
41
41
  end
42
42
 
43
43
  it 'should not use fully qualified table name in order clause' do
@@ -47,28 +47,28 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
47
47
  end
48
48
 
49
49
  it 'should use fully qualified table name in insert statement' do
50
- manager = Arel::InsertManager.new(Arel::Table.engine)
50
+ manager = Arel::InsertManager.new
51
51
  manager.into Arel::Table.new(:table)
52
52
  manager.values = manager.create_values [Arel.sql('*')], %w{ a }
53
53
  expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)"
54
- assert_equal expected_sql, manager.to_sql
54
+ quietly { assert_equal expected_sql, manager.to_sql }
55
55
  end
56
56
 
57
57
  it 'should use fully qualified table name in update statement' do
58
58
  table = Arel::Table.new(:table)
59
- manager = Arel::UpdateManager.new(Arel::Table.engine)
59
+ manager = Arel::UpdateManager.new
60
60
  manager.table(table).where(table[:id].eq(42))
61
61
  manager.set([[table[:name], "Bob"]])
62
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
63
+ quietly { assert_equal expected_sql, manager.to_sql }
64
64
  end
65
65
 
66
66
  it 'should use fully qualified table name in delete statement' do
67
67
  table = Arel::Table.new(:table)
68
- manager = Arel::DeleteManager.new(Arel::Table.engine)
68
+ manager = Arel::DeleteManager.new
69
69
  manager.from(table).where(table[:id].eq(42))
70
70
  expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
71
- assert_equal expected_sql, manager.to_sql
71
+ quietly { assert_equal expected_sql, manager.to_sql }
72
72
  end
73
73
 
74
74
  end
@@ -1,6 +1,7 @@
1
1
  require 'support/paths_sqlserver'
2
2
  require 'bundler/setup'
3
3
  Bundler.require :default, :development
4
+ require 'pry'
4
5
  require 'support/minitest_sqlserver'
5
6
  require 'cases/helper'
6
7
  require 'support/load_schema_sqlserver'
@@ -15,7 +16,9 @@ module ActiveRecord
15
16
  SQLServer = ActiveRecord::ConnectionAdapters::SQLServer
16
17
 
17
18
  include ARTest::SQLServer::CoerceableTest,
18
- ARTest::SQLServer::ConnectionReflection
19
+ ARTest::SQLServer::ConnectionReflection,
20
+ ARTest::SQLServer::SqlCounterSqlserver,
21
+ ActiveSupport::Testing::Stream
19
22
 
20
23
  let(:logger) { ActiveRecord::Base.logger }
21
24
 
@@ -34,20 +37,6 @@ module ActiveRecord
34
37
  klass.use_output_inserted = true
35
38
  end
36
39
 
37
- def silence_stream(stream)
38
- old_stream = stream.dup
39
- stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
40
- stream.sync = true
41
- yield
42
- ensure
43
- stream.reopen(old_stream)
44
- old_stream.close
45
- end
46
-
47
- def quietly
48
- silence_stream(STDOUT) { silence_stream(STDERR) { yield } }
49
- end
50
-
51
40
  end
52
41
  end
53
42
 
@@ -73,7 +73,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase
73
73
  end
74
74
 
75
75
  it 'copes with eager loading un-locked paginated' do
76
- eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY/
76
+ eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/
77
77
  loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/
78
78
  assert_sql(eager_ids_sql, loader_sql) do
79
79
  people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a
@@ -2,7 +2,7 @@ require 'cases/helper_sqlserver'
2
2
 
3
3
  class SQLServerRakeTest < ActiveRecord::TestCase
4
4
 
5
- self.use_transactional_fixtures = false
5
+ self.use_transactional_tests = false
6
6
 
7
7
  cattr_accessor :azure_skip
8
8
  self.azure_skip = connection_sqlserver_azure?
@@ -42,24 +42,24 @@ class SQLServerRakeCreateTest < SQLServerRakeTest
42
42
  self.azure_skip = false
43
43
 
44
44
  it 'establishes connection to database after create ' do
45
- db_tasks.create configuration
45
+ quietly { db_tasks.create configuration }
46
46
  connection.current_database.must_equal(new_database)
47
47
  end
48
48
 
49
49
  it 'creates database with default collation' do
50
- db_tasks.create configuration
50
+ quietly { db_tasks.create configuration }
51
51
  connection.collation.must_equal 'SQL_Latin1_General_CP1_CI_AS'
52
52
  end
53
53
 
54
54
  it 'creates database with given collation' do
55
- db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS')
55
+ quietly { db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS') }
56
56
  connection.collation.must_equal 'Latin1_General_CI_AS'
57
57
  end
58
58
 
59
59
  it 'prints error message when database exists' do
60
- db_tasks.create configuration
60
+ quietly { db_tasks.create configuration }
61
61
  message = capture(:stderr) { db_tasks.create configuration }
62
- message.must_match %r{activerecord_unittest_tasks already exists}
62
+ message.must_match %r{activerecord_unittest_tasks.*already exists}
63
63
  end
64
64
 
65
65
  end
@@ -69,8 +69,10 @@ class SQLServerRakeDropTest < SQLServerRakeTest
69
69
  self.azure_skip = false
70
70
 
71
71
  it 'drops database and uses master' do
72
- db_tasks.create configuration
73
- db_tasks.drop configuration
72
+ quietly do
73
+ db_tasks.create configuration
74
+ db_tasks.drop configuration
75
+ end
74
76
  connection.current_database.must_equal 'master'
75
77
  end
76
78
 
@@ -84,7 +86,7 @@ end
84
86
  class SQLServerRakePurgeTest < SQLServerRakeTest
85
87
 
86
88
  before do
87
- db_tasks.create(configuration)
89
+ quietly { db_tasks.create(configuration) }
88
90
  connection.create_table :users, force: true do |t|
89
91
  t.string :name, :email
90
92
  t.timestamps null: false
@@ -94,7 +96,7 @@ class SQLServerRakePurgeTest < SQLServerRakeTest
94
96
  it 'clears active connections, drops database, and recreates with established connection' do
95
97
  connection.current_database.must_equal(new_database)
96
98
  connection.tables.must_include 'users'
97
- db_tasks.purge(configuration)
99
+ quietly { db_tasks.purge(configuration) }
98
100
  connection.current_database.must_equal(new_database)
99
101
  connection.tables.wont_include 'users'
100
102
  end
@@ -103,7 +105,9 @@ end
103
105
 
104
106
  class SQLServerRakeCharsetTest < SQLServerRakeTest
105
107
 
106
- before { db_tasks.create(configuration) }
108
+ before do
109
+ quietly { db_tasks.create(configuration) }
110
+ end
107
111
 
108
112
  it 'retrieves charset' do
109
113
  db_tasks.charset(configuration).must_equal 'iso_1'
@@ -113,7 +117,9 @@ end
113
117
 
114
118
  class SQLServerRakeCollationTest < SQLServerRakeTest
115
119
 
116
- before { db_tasks.create(configuration) }
120
+ before do
121
+ quietly { db_tasks.create(configuration) }
122
+ end
117
123
 
118
124
  it 'retrieves collation' do
119
125
  db_tasks.collation(configuration).must_equal 'SQL_Latin1_General_CP1_CI_AS'
@@ -127,7 +133,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest
127
133
  let(:filedata) { File.read(filename) }
128
134
 
129
135
  before do
130
- db_tasks.create(configuration)
136
+ quietly { db_tasks.create(configuration) }
131
137
  connection.create_table :users, force: true do |t|
132
138
  t.string :name, :email
133
139
  t.text :background1
@@ -156,7 +162,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest
156
162
  filedata.must_match %r{CREATE TABLE dbo\.users}
157
163
  db_tasks.purge(configuration)
158
164
  connection.tables.wont_include 'users'
159
- db_tasks.load_schema_for configuration, :sql, filename
165
+ db_tasks.load_schema configuration, :sql, filename
160
166
  connection.tables.must_include 'users'
161
167
  end
162
168
 
@@ -9,49 +9,48 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
9
9
 
10
10
  it 'sst_datatypes' do
11
11
  generate_schema_for_table 'sst_datatypes'
12
- # Exact Numerics
13
- assert_line :bigint, type: 'bigint', limit: '8', precision: nil, scale: nil, default: '42'
14
- assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42'
15
- assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42'
16
- assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42'
17
- assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: 'true'
18
- assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: '9', scale: '2', default: '12345.01'
19
- assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: '18', scale: '0', default: '191.0'
20
- assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: '36', scale: '2', default: '12345678901234567890.01'
21
- assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2'
22
- assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2'
12
+ assert_line :bigint, type: 'bigint', limit: nil, precision: nil, scale: nil, default: 42
13
+ assert_line :int, type: 'integer', limit: nil, precision: nil, scale: nil, default: 42
14
+ assert_line :smallint, type: 'integer', limit: 2, precision: nil, scale: nil, default: 42
15
+ assert_line :tinyint, type: 'integer', limit: 1, precision: nil, scale: nil, default: 42
16
+ assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: true
17
+ assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: 9, scale: 2, default: 12345.01
18
+ assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: 18, scale: 0, default: 191.0
19
+ assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01
20
+ assert_line :money, type: 'money', limit: nil, precision: 19, scale: 4, default: 4.2
21
+ assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: 4.2
23
22
  # Approximate Numerics
24
- assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001'
25
- assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]}
23
+ assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: 123.00000001
24
+ assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: 123.45
26
25
  # Date and Time
27
- assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "\"01-01-0001\""
28
- assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "\"01-01-1753 00:00:00.123\""
26
+ assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "01-01-0001"
27
+ assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123"
29
28
  if connection_dblib_73?
30
- assert_line :datetime2_7, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: "\"12-31-9999 23:59:59.9999999\""
31
- assert_line :datetime2_3, type: 'datetime2', limit: nil, precision: '3', scale: nil, default: nil
32
- assert_line :datetime2_1, type: 'datetime2', limit: nil, precision: '1', scale: nil, default: nil
29
+ assert_line :datetime2_7, type: 'datetime', limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999"
30
+ assert_line :datetime2_3, type: 'datetime', limit: nil, precision: 3, scale: nil, default: nil
31
+ assert_line :datetime2_1, type: 'datetime', limit: nil, precision: 1, scale: nil, default: nil
33
32
  end
34
- assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "\"01-01-1901 15:45:00\""
33
+ assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0"
35
34
  if connection_dblib_73?
36
- assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: "\"04:20:00.2883215\""
37
- assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil
35
+ assert_line :time_7, type: 'time', limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215"
36
+ assert_line :time_2, type: 'time', limit: nil, precision: 2, scale: nil, default: nil
38
37
  end
39
38
  # Character Strings
40
- assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\""
41
- assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\""
42
- assert_line :varchar_max, type: 'varchar_max', limit: '2147483647', precision: nil, scale: nil, default: "\"test varchar_max\""
43
- assert_line :text, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: "\"test text\""
39
+ assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil
40
+ assert_line :varchar_50, type: 'varchar', limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil
41
+ assert_line :varchar_max, type: 'varchar_max', limit: 2147483647, precision: nil, scale: nil, default: "test varchar_max", collation: nil
42
+ assert_line :text, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: "test text", collation: nil
44
43
  # Unicode Character Strings
45
- assert_line :nchar_10, type: 'nchar', limit: '10', precision: nil, scale: nil, default: "\"12345678åå\""
46
- assert_line :nvarchar_50, type: 'string', limit: '50', precision: nil, scale: nil, default: "\"test nvarchar_50 åå\""
47
- assert_line :nvarchar_max, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: "\"test nvarchar_max åå\""
48
- assert_line :ntext, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: "\"test ntext åå\""
44
+ assert_line :nchar_10, type: 'nchar', limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: nil
45
+ assert_line :nvarchar_50, type: 'string', limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil
46
+ assert_line :nvarchar_max, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil
47
+ assert_line :ntext, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: "test ntext åå", collation: nil
49
48
  # Binary Strings
50
- assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil
51
- assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil
52
- assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil
49
+ assert_line :binary_49, type: 'binary_basic', limit: 49, precision: nil, scale: nil, default: nil
50
+ assert_line :varbinary_49, type: 'varbinary', limit: 49, precision: nil, scale: nil, default: nil
51
+ assert_line :varbinary_max, type: 'binary', limit: 2147483647, precision: nil, scale: nil, default: nil
53
52
  # Other Data Types
54
- assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
53
+ assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: -> { "newid()" }
55
54
  assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
56
55
  end
57
56
 
@@ -71,18 +70,18 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
71
70
  columns['time_col'].sql_type.must_equal 'time(7)'
72
71
  columns['date_col'].sql_type.must_equal 'date'
73
72
  columns['binary_col'].sql_type.must_equal 'varbinary(max)'
74
- assert_line :integer_col, type: 'integer', limit: '4', precision: nil, scale: nil, default: nil
75
- assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil
73
+ assert_line :integer_col, type: 'integer', limit: nil, precision: nil, scale: nil, default: nil
74
+ assert_line :bigint_col, type: 'bigint', limit: nil, precision: nil, scale: nil, default: nil
76
75
  assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil
77
- assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil
76
+ assert_line :decimal_col, type: 'decimal', limit: nil, precision: 18, scale: 0, default: nil
78
77
  assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil
79
- assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil
80
- assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil
78
+ assert_line :string_col, type: 'string', limit: nil, precision: nil, scale: nil, default: nil
79
+ assert_line :text_col, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: nil
81
80
  assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
82
81
  assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
83
- assert_line :time_col, type: 'time', limit: nil, precision: '7', scale: nil, default: nil
82
+ assert_line :time_col, type: 'time', limit: nil, precision: 7, scale: nil, default: nil
84
83
  assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil
85
- assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil
84
+ assert_line :binary_col, type: 'binary', limit: 2147483647, precision: nil, scale: nil, default: nil
86
85
  # Our type methods.
87
86
  columns['real_col'].sql_type.must_equal 'real'
88
87
  columns['money_col'].sql_type.must_equal 'money'
@@ -99,16 +98,16 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
99
98
  columns['uuid_col'].sql_type.must_equal 'uniqueidentifier'
100
99
  columns['sstimestamp_col'].sql_type.must_equal 'timestamp'
101
100
  assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil
102
- assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil
103
- assert_line :datetime2_col, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: nil
104
- assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil
105
- assert_line :char_col, type: 'char', limit: '1', precision: nil, scale: nil, default: nil
106
- assert_line :varchar_col, type: 'varchar', limit: '8000', precision: nil, scale: nil, default: nil
107
- assert_line :text_basic_col, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: nil
108
- assert_line :nchar_col, type: 'nchar', limit: '1', precision: nil, scale: nil, default: nil
109
- assert_line :ntext_col, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: nil
110
- assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil
111
- assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil
101
+ assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil
102
+ assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil
103
+ assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil
104
+ assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil
105
+ assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil
106
+ assert_line :text_basic_col, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: nil
107
+ assert_line :nchar_col, type: 'nchar', limit: 1, precision: nil, scale: nil, default: nil
108
+ assert_line :ntext_col, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: nil
109
+ assert_line :binary_basic_col, type: 'binary_basic', limit: 1, precision: nil, scale: nil, default: nil
110
+ assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil
112
111
  assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
113
112
  assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
114
113
  end
@@ -126,7 +125,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
126
125
  it 'no id with model driven primary key' do
127
126
  output = generate_schema_for_table 'sst_no_pk_data'
128
127
  output.must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do}
129
- assert_line :name, type: 'string', limit: '4000'
128
+ assert_line :name, type: 'string', limit: nil, default: nil, collation: nil
130
129
  end
131
130
 
132
131
 
@@ -155,15 +154,19 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
155
154
  def assert_line(column_name, options={})
156
155
  line = line(column_name)
157
156
  assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}"
158
- [:type, :limit, :precision, :scale, :default].each do |key|
157
+ [:type, :limit, :precision, :scale, :collation, :default].each do |key|
159
158
  next unless options.key?(key)
160
159
  actual = key == :type ? line.send(:type_method) : line.send(key)
161
160
  expected = options[key]
162
161
  message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}"
163
162
  if expected.nil?
164
163
  actual.must_be_nil message
165
- elsif expected.is_a?(Regexp)
166
- actual.must_match expected, message
164
+ elsif expected.is_a?(Array)
165
+ actual.must_include expected, message
166
+ elsif expected.is_a?(Float)
167
+ actual.must_be_close_to expected, 0.001
168
+ elsif expected.is_a?(Proc)
169
+ actual.call.must_equal(expected.call)
167
170
  else
168
171
  actual.must_equal expected, message
169
172
  end
@@ -172,24 +175,52 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
172
175
 
173
176
  class SchemaLine
174
177
 
175
- attr_reader :line
178
+ LINE_PARSER = %r{t\.(\w+)\s+"(.*?)"[,\s+](.*)}
176
179
 
177
- def self.match(method_name, pattern)
178
- define_method(method_name) { line.match(pattern).try :[], 1 }
180
+ attr_reader :line,
181
+ :type_method,
182
+ :col_name,
183
+ :options
184
+
185
+ def self.option(method_name)
186
+ define_method(method_name) { options.present? ? options[method_name.to_sym] : nil }
179
187
  end
180
188
 
181
189
  def initialize(line)
182
190
  @line = line
191
+ @type_method, @col_name, @options = parse_line
183
192
  end
184
193
 
185
- match :type_method, %r{\A\s+t\.(.*?)\s}
186
- match :limit, %r{\slimit:\s(.*?)[,\s]}
187
- match :default, %r{\sdefault:\s(.*)\n}
188
- match :precision, %r{\sprecision:\s(.*?)[,\s]}
189
- match :scale, %r{\sscale:\s(.*?)[,\s]}
194
+ option :limit
195
+ option :precision
196
+ option :scale
197
+ option :default
198
+ option :collation
190
199
 
191
200
  def to_s
192
- line
201
+ line.squish
202
+ end
203
+
204
+ def inspect
205
+ "#<SchemaLine col_name=#{col_name.inspect}, options=#{options.inspect}>"
206
+ end
207
+
208
+ private
209
+
210
+ def parse_line
211
+ _all, type_method, col_name, options = @line.match(LINE_PARSER).to_a
212
+ options = parse_options(options)
213
+ [type_method, col_name, options]
214
+ end
215
+
216
+ def parse_options(opts)
217
+ if opts.present?
218
+ eval "{#{opts}}"
219
+ else
220
+ {}
221
+ end
222
+ rescue SyntaxError
223
+ {}
193
224
  end
194
225
 
195
226
  end