activerecord-sqlserver-adapter_new 4.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/CHANGELOG.md +212 -0
  4. data/CODE_OF_CONDUCT.md +31 -0
  5. data/Gemfile +61 -0
  6. data/Guardfile +29 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +201 -0
  9. data/RUNNING_UNIT_TESTS.md +121 -0
  10. data/Rakefile +48 -0
  11. data/VERSION +1 -0
  12. data/activerecord-sqlserver-adapter_new.gemspec +20 -0
  13. data/appveyor.yml +39 -0
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
  15. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
  16. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +40 -0
  17. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +34 -0
  19. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  20. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +386 -0
  21. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +68 -0
  22. data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
  23. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +114 -0
  25. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +52 -0
  26. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +473 -0
  27. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
  28. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
  29. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
  30. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +76 -0
  31. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +57 -0
  32. data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
  33. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
  34. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
  35. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +12 -0
  36. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +38 -0
  37. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +21 -0
  38. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +41 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  40. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +31 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +12 -0
  42. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +15 -0
  43. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +12 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +15 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +22 -0
  49. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
  50. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
  51. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +40 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +76 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
  54. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
  55. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
  56. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
  57. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
  59. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
  60. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
  61. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
  62. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
  63. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
  64. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
  65. data/lib/active_record/connection_adapters/sqlserver/utils.rb +136 -0
  66. data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
  67. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +405 -0
  68. data/lib/active_record/connection_adapters/sqlserver_column.rb +53 -0
  69. data/lib/active_record/sqlserver_base.rb +20 -0
  70. data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
  71. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  72. data/lib/arel/visitors/sqlserver.rb +214 -0
  73. data/lib/arel_sqlserver.rb +3 -0
  74. data/test/appveyor/dbsetup.ps1 +27 -0
  75. data/test/appveyor/dbsetup.sql +11 -0
  76. data/test/cases/adapter_test_sqlserver.rb +444 -0
  77. data/test/cases/coerced_tests.rb +713 -0
  78. data/test/cases/column_test_sqlserver.rb +780 -0
  79. data/test/cases/connection_test_sqlserver.rb +142 -0
  80. data/test/cases/execute_procedure_test_sqlserver.rb +44 -0
  81. data/test/cases/fetch_test_sqlserver.rb +57 -0
  82. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  83. data/test/cases/helper_sqlserver.rb +54 -0
  84. data/test/cases/migration_test_sqlserver.rb +61 -0
  85. data/test/cases/order_test_sqlserver.rb +147 -0
  86. data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
  87. data/test/cases/rake_test_sqlserver.rb +163 -0
  88. data/test/cases/schema_dumper_test_sqlserver.rb +198 -0
  89. data/test/cases/schema_test_sqlserver.rb +54 -0
  90. data/test/cases/scratchpad_test_sqlserver.rb +9 -0
  91. data/test/cases/showplan_test_sqlserver.rb +65 -0
  92. data/test/cases/specific_schema_test_sqlserver.rb +167 -0
  93. data/test/cases/transaction_test_sqlserver.rb +66 -0
  94. data/test/cases/utils_test_sqlserver.rb +129 -0
  95. data/test/cases/uuid_test_sqlserver.rb +48 -0
  96. data/test/config.yml +41 -0
  97. data/test/debug.rb +14 -0
  98. data/test/fixtures/1px.gif +0 -0
  99. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  100. data/test/models/sqlserver/booking.rb +3 -0
  101. data/test/models/sqlserver/customers_view.rb +3 -0
  102. data/test/models/sqlserver/datatype.rb +3 -0
  103. data/test/models/sqlserver/datatype_migration.rb +3 -0
  104. data/test/models/sqlserver/dollar_table_name.rb +3 -0
  105. data/test/models/sqlserver/dot_table_name.rb +3 -0
  106. data/test/models/sqlserver/edge_schema.rb +13 -0
  107. data/test/models/sqlserver/fk_has_fk.rb +3 -0
  108. data/test/models/sqlserver/fk_has_pk.rb +3 -0
  109. data/test/models/sqlserver/natural_pk_data.rb +4 -0
  110. data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
  111. data/test/models/sqlserver/no_pk_data.rb +3 -0
  112. data/test/models/sqlserver/object_default.rb +3 -0
  113. data/test/models/sqlserver/quoted_table.rb +7 -0
  114. data/test/models/sqlserver/quoted_view_1.rb +3 -0
  115. data/test/models/sqlserver/quoted_view_2.rb +3 -0
  116. data/test/models/sqlserver/string_default.rb +3 -0
  117. data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
  118. data/test/models/sqlserver/string_defaults_view.rb +3 -0
  119. data/test/models/sqlserver/tinyint_pk.rb +3 -0
  120. data/test/models/sqlserver/upper.rb +3 -0
  121. data/test/models/sqlserver/uppered.rb +3 -0
  122. data/test/models/sqlserver/uuid.rb +3 -0
  123. data/test/schema/datatypes/2012.sql +55 -0
  124. data/test/schema/sqlserver_specific_schema.rb +207 -0
  125. data/test/support/coerceable_test_sqlserver.rb +45 -0
  126. data/test/support/connection_reflection.rb +37 -0
  127. data/test/support/load_schema_sqlserver.rb +29 -0
  128. data/test/support/minitest_sqlserver.rb +1 -0
  129. data/test/support/paths_sqlserver.rb +50 -0
  130. data/test/support/rake_helpers.rb +41 -0
  131. data/test/support/sql_counter_sqlserver.rb +32 -0
  132. metadata +253 -0
@@ -0,0 +1,90 @@
1
+ require 'cases/helper_sqlserver'
2
+ require 'models/person'
3
+ require 'models/reader'
4
+
5
+ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase
6
+
7
+ fixtures :people, :readers
8
+
9
+ before do
10
+ Person.columns
11
+ Reader.columns
12
+ end
13
+
14
+ it 'uses with updlock by default' do
15
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)| do
16
+ Person.lock(true).to_a.must_equal Person.all.to_a
17
+ end
18
+ end
19
+
20
+ describe 'For simple finds with default lock option' do
21
+
22
+ it 'lock with simple find' do
23
+ assert_nothing_raised do
24
+ Person.transaction do
25
+ Person.lock(true).find(1).must_equal Person.find(1)
26
+ end
27
+ end
28
+ end
29
+
30
+ it 'lock with scoped find' do
31
+ assert_nothing_raised do
32
+ Person.transaction do
33
+ Person.lock(true).scoping do
34
+ Person.find(1).must_equal Person.find(1)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ it 'lock with eager find' do
41
+ assert_nothing_raised do
42
+ Person.transaction do
43
+ person = Person.lock(true).includes(:readers).find(1)
44
+ person.must_equal Person.find(1)
45
+ end
46
+ end
47
+ end
48
+
49
+ it 'reload with lock when #lock! called' do
50
+ assert_nothing_raised do
51
+ Person.transaction do
52
+ person = Person.find 1
53
+ old, person.first_name = person.first_name, 'fooman'
54
+ person.lock!
55
+ assert_equal old, person.first_name
56
+ end
57
+ end
58
+ end
59
+
60
+ it 'can add a custom lock directive' do
61
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do
62
+ Person.lock('WITH(HOLDLOCK, ROWLOCK)').load
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ describe 'For paginated finds' do
69
+
70
+ before do
71
+ Person.delete_all
72
+ 20.times { |n| Person.create!(first_name: "Thing_#{n}") }
73
+ end
74
+
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/
77
+ loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/
78
+ assert_sql(eager_ids_sql, loader_sql) do
79
+ people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a
80
+ people[0].first_name.must_equal 'Thing_10'
81
+ people[1].first_name.must_equal 'Thing_11'
82
+ people[2].first_name.must_equal 'Thing_12'
83
+ people[3].first_name.must_equal 'Thing_13'
84
+ people[4].first_name.must_equal 'Thing_14'
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,163 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class SQLServerRakeTest < ActiveRecord::TestCase
4
+
5
+ self.use_transactional_fixtures = false
6
+
7
+ cattr_accessor :azure_skip
8
+ self.azure_skip = connection_sqlserver_azure?
9
+
10
+ let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks }
11
+ let(:new_database) { 'activerecord_unittest_tasks' }
12
+ let(:default_configuration) { ARTest.connection_config['arunit'] }
13
+ let(:configuration) { default_configuration.merge('database' => new_database) }
14
+
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!
22
+ connection.disconnect!
23
+ end
24
+
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
36
+ end
37
+
38
+ end
39
+
40
+ class SQLServerRakeCreateTest < SQLServerRakeTest
41
+
42
+ self.azure_skip = false
43
+
44
+ it 'establishes connection to database after create ' do
45
+ db_tasks.create configuration
46
+ connection.current_database.must_equal(new_database)
47
+ end
48
+
49
+ it 'creates database with default collation' do
50
+ db_tasks.create configuration
51
+ connection.collation.must_equal 'SQL_Latin1_General_CP1_CI_AS'
52
+ end
53
+
54
+ it 'creates database with given collation' do
55
+ db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS')
56
+ connection.collation.must_equal 'Latin1_General_CI_AS'
57
+ end
58
+
59
+ it 'prints error message when database exists' do
60
+ db_tasks.create configuration
61
+ message = capture(:stderr) { db_tasks.create configuration }
62
+ message.must_match %r{activerecord_unittest_tasks already exists}
63
+ end
64
+
65
+ end
66
+
67
+ class SQLServerRakeDropTest < SQLServerRakeTest
68
+
69
+ self.azure_skip = false
70
+
71
+ it 'drops database and uses master' do
72
+ db_tasks.create configuration
73
+ db_tasks.drop configuration
74
+ connection.current_database.must_equal 'master'
75
+ end
76
+
77
+ it 'prints error message when database does not exist' do
78
+ message = capture(:stderr) { db_tasks.drop configuration.merge('database' => 'doesnotexist') }
79
+ message.must_match %r{'doesnotexist' does not exist}
80
+ end
81
+
82
+ end
83
+
84
+ class SQLServerRakePurgeTest < SQLServerRakeTest
85
+
86
+ before do
87
+ db_tasks.create(configuration)
88
+ connection.create_table :users, force: true do |t|
89
+ t.string :name, :email
90
+ t.timestamps null: false
91
+ end
92
+ end
93
+
94
+ it 'clears active connections, drops database, and recreates with established connection' do
95
+ connection.current_database.must_equal(new_database)
96
+ connection.tables.must_include 'users'
97
+ db_tasks.purge(configuration)
98
+ connection.current_database.must_equal(new_database)
99
+ connection.tables.wont_include 'users'
100
+ end
101
+
102
+ end
103
+
104
+ class SQLServerRakeCharsetTest < SQLServerRakeTest
105
+
106
+ before { db_tasks.create(configuration) }
107
+
108
+ it 'retrieves charset' do
109
+ db_tasks.charset(configuration).must_equal 'iso_1'
110
+ end
111
+
112
+ end
113
+
114
+ class SQLServerRakeCollationTest < SQLServerRakeTest
115
+
116
+ before { db_tasks.create(configuration) }
117
+
118
+ it 'retrieves collation' do
119
+ db_tasks.collation(configuration).must_equal 'SQL_Latin1_General_CP1_CI_AS'
120
+ end
121
+
122
+ end
123
+
124
+ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest
125
+
126
+ let(:filename) { File.join ARTest::SQLServer.migrations_root, 'structure.sql' }
127
+ let(:filedata) { File.read(filename) }
128
+
129
+ before do
130
+ db_tasks.create(configuration)
131
+ connection.create_table :users, force: true do |t|
132
+ t.string :name, :email
133
+ t.text :background1
134
+ t.text_basic :background2
135
+ t.timestamps null: false
136
+ end
137
+ end
138
+
139
+ after do
140
+ FileUtils.rm_rf(filename)
141
+ end
142
+
143
+ it 'dumps structure and accounts for defncopy oddities' do
144
+ skip 'debug defncopy on windows later' if host_windows?
145
+ quietly { db_tasks.structure_dump configuration, filename }
146
+ filedata.wont_match %r{\AUSE.*\z}
147
+ filedata.wont_match %r{\AGO.*\z}
148
+ filedata.must_match %r{email\s+nvarchar\(4000\)}
149
+ filedata.must_match %r{background1\s+nvarchar\(max\)}
150
+ filedata.must_match %r{background2\s+text\s+}
151
+ end
152
+
153
+ it 'can load dumped structure' do
154
+ skip 'debug defncopy on windows later' if host_windows?
155
+ quietly { db_tasks.structure_dump configuration, filename }
156
+ filedata.must_match %r{CREATE TABLE dbo\.users}
157
+ db_tasks.purge(configuration)
158
+ connection.tables.wont_include 'users'
159
+ db_tasks.load_schema_for configuration, :sql, filename
160
+ connection.tables.must_include 'users'
161
+ end
162
+
163
+ end
@@ -0,0 +1,198 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
4
+
5
+ before { all_tables }
6
+
7
+ let(:all_tables) { ActiveRecord::Base.connection.tables }
8
+ let(:schema) { @generated_schema }
9
+
10
+ it 'sst_datatypes' do
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'
23
+ # 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]}
26
+ # 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\""
29
+ 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
33
+ end
34
+ assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "\"01-01-1901 15:45:00\""
35
+ 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
38
+ end
39
+ # 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\""
44
+ # 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 åå\""
49
+ # 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
53
+ # Other Data Types
54
+ assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
55
+ assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
56
+ end
57
+
58
+ it 'sst_datatypes_migration' do
59
+ columns = SSTestDatatypeMigration.columns_hash
60
+ generate_schema_for_table 'sst_datatypes_migration'
61
+ # Simple Rails conventions
62
+ columns['integer_col'].sql_type.must_equal 'int(4)'
63
+ columns['bigint_col'].sql_type.must_equal 'bigint(8)'
64
+ columns['boolean_col'].sql_type.must_equal 'bit'
65
+ columns['decimal_col'].sql_type.must_equal 'decimal(18,0)'
66
+ columns['float_col'].sql_type.must_equal 'float'
67
+ columns['string_col'].sql_type.must_equal 'nvarchar(4000)'
68
+ columns['text_col'].sql_type.must_equal 'nvarchar(max)'
69
+ columns['datetime_col'].sql_type.must_equal 'datetime'
70
+ columns['timestamp_col'].sql_type.must_equal 'datetime'
71
+ columns['time_col'].sql_type.must_equal 'time(7)'
72
+ columns['date_col'].sql_type.must_equal 'date'
73
+ 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
76
+ 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
78
+ 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
81
+ assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
82
+ 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
84
+ 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
86
+ # Our type methods.
87
+ columns['real_col'].sql_type.must_equal 'real'
88
+ columns['money_col'].sql_type.must_equal 'money'
89
+ columns['datetime2_col'].sql_type.must_equal 'datetime2(7)'
90
+ columns['datetimeoffset'].sql_type.must_equal 'datetimeoffset(7)'
91
+ columns['smallmoney_col'].sql_type.must_equal 'smallmoney'
92
+ columns['char_col'].sql_type.must_equal 'char(1)'
93
+ columns['varchar_col'].sql_type.must_equal 'varchar(8000)'
94
+ columns['text_basic_col'].sql_type.must_equal 'text'
95
+ columns['nchar_col'].sql_type.must_equal 'nchar(1)'
96
+ columns['ntext_col'].sql_type.must_equal 'ntext'
97
+ columns['binary_basic_col'].sql_type.must_equal 'binary(1)'
98
+ columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)'
99
+ columns['uuid_col'].sql_type.must_equal 'uniqueidentifier'
100
+ columns['sstimestamp_col'].sql_type.must_equal 'timestamp'
101
+ 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
112
+ assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
113
+ assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
114
+ end
115
+
116
+ # Special Cases
117
+
118
+ it 'honor nonstandard primary keys' do
119
+ generate_schema_for_table('movies') do |output|
120
+ match = output.match(%r{create_table "movies"(.*)do})
121
+ assert_not_nil(match, "nonstandardpk table not found")
122
+ assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved"
123
+ end
124
+ end
125
+
126
+ it 'no id with model driven primary key' do
127
+ output = generate_schema_for_table 'sst_no_pk_data'
128
+ output.must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do}
129
+ assert_line :name, type: 'string', limit: '4000'
130
+ end
131
+
132
+
133
+ private
134
+
135
+ def generate_schema_for_table(*table_names)
136
+ require 'stringio'
137
+ stream = StringIO.new
138
+ ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names
139
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
140
+ @generated_schema = stream.string
141
+ yield @generated_schema if block_given?
142
+ @schema_lines = Hash.new
143
+ type_matcher = /\A\s+t\.\w+\s+"(.*?)"[,\n]/
144
+ @generated_schema.each_line do |line|
145
+ next unless line =~ type_matcher
146
+ @schema_lines[Regexp.last_match[1]] = SchemaLine.new(line)
147
+ end
148
+ @generated_schema
149
+ end
150
+
151
+ def line(column_name)
152
+ @schema_lines[column_name.to_s]
153
+ end
154
+
155
+ def assert_line(column_name, options={})
156
+ line = line(column_name)
157
+ 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|
159
+ next unless options.key?(key)
160
+ actual = key == :type ? line.send(:type_method) : line.send(key)
161
+ expected = options[key]
162
+ message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}"
163
+ if expected.nil?
164
+ actual.must_be_nil message
165
+ elsif expected.is_a?(Regexp)
166
+ actual.must_match expected, message
167
+ else
168
+ actual.must_equal expected, message
169
+ end
170
+ end
171
+ end
172
+
173
+ class SchemaLine
174
+
175
+ attr_reader :line
176
+
177
+ def self.match(method_name, pattern)
178
+ define_method(method_name) { line.match(pattern).try :[], 1 }
179
+ end
180
+
181
+ def initialize(line)
182
+ @line = line
183
+ end
184
+
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]}
190
+
191
+ def to_s
192
+ line
193
+ end
194
+
195
+ end
196
+
197
+ end
198
+