activerecord-sqlserver-adapter 4.2.18 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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