activerecord-sqlserver-adapter 6.0.0.rc2 → 6.1.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +26 -0
  3. data/CHANGELOG.md +20 -41
  4. data/README.md +32 -3
  5. data/RUNNING_UNIT_TESTS.md +1 -1
  6. data/VERSION +1 -1
  7. data/activerecord-sqlserver-adapter.gemspec +1 -1
  8. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +0 -9
  9. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +7 -2
  10. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
  11. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +28 -16
  12. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +7 -7
  13. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -1
  14. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -3
  15. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +31 -8
  16. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +27 -7
  17. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +0 -1
  18. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +2 -2
  19. data/lib/active_record/connection_adapters/sqlserver/type.rb +1 -0
  20. data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
  21. data/lib/active_record/connection_adapters/sqlserver/utils.rb +1 -1
  22. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +100 -68
  23. data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -0
  24. data/lib/active_record/sqlserver_base.rb +9 -15
  25. data/lib/active_record/tasks/sqlserver_database_tasks.rb +17 -14
  26. data/lib/arel/visitors/sqlserver.rb +111 -39
  27. data/test/cases/adapter_test_sqlserver.rb +48 -14
  28. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  29. data/test/cases/coerced_tests.rb +598 -78
  30. data/test/cases/column_test_sqlserver.rb +5 -2
  31. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  32. data/test/cases/execute_procedure_test_sqlserver.rb +9 -0
  33. data/test/cases/in_clause_test_sqlserver.rb +27 -0
  34. data/test/cases/lateral_test_sqlserver.rb +35 -0
  35. data/test/cases/migration_test_sqlserver.rb +51 -0
  36. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  37. data/test/cases/order_test_sqlserver.rb +7 -0
  38. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  39. data/test/cases/rake_test_sqlserver.rb +3 -2
  40. data/test/cases/schema_dumper_test_sqlserver.rb +20 -3
  41. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  42. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  43. data/test/schema/sqlserver_specific_schema.rb +17 -0
  44. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  45. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
  46. data/test/support/sql_counter_sqlserver.rb +14 -12
  47. metadata +32 -13
  48. data/.travis.yml +0 -23
@@ -157,13 +157,16 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
157
157
  _(col.default).must_equal BigDecimal("191")
158
158
  _(obj.numeric_18_0).must_equal BigDecimal("191")
159
159
  _(col.default_function).must_be_nil
160
+
160
161
  type = connection.lookup_cast_type_from_column(col)
161
- _(type).must_be_instance_of Type::Decimal
162
+ _(type).must_be_instance_of Type::DecimalWithoutScale
162
163
  _(type.limit).must_be_nil
163
164
  _(type.precision).must_equal 18
164
- _(type.scale).must_equal 0
165
+ _(type.scale).must_be_nil
166
+
165
167
  obj.numeric_18_0 = "192.1"
166
168
  _(obj.numeric_18_0).must_equal BigDecimal("192")
169
+
167
170
  obj.save!
168
171
  _(obj.reload.numeric_18_0).must_equal BigDecimal("192")
169
172
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+
5
+ class TestDisconnectedAdapter < ActiveRecord::TestCase
6
+ self.use_transactional_tests = false
7
+
8
+ def setup
9
+ @connection = ActiveRecord::Base.connection
10
+ end
11
+
12
+ teardown do
13
+ return if in_memory_db?
14
+ db_config = ActiveRecord::Base.connection_db_config
15
+ ActiveRecord::Base.establish_connection(db_config)
16
+ end
17
+
18
+ test "can't execute procedures while disconnected" do
19
+ @connection.execute_procedure :sp_tables, "sst_datatypes"
20
+ @connection.disconnect!
21
+ assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
22
+ @connection.execute_procedure :sp_tables, "sst_datatypes"
23
+ end
24
+ end
25
+
26
+ test "can't execute query while disconnected" do
27
+ sql = "SELECT count(*) from products WHERE id IN(@0, @1)"
28
+ binds = [
29
+ ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new),
30
+ ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new)
31
+ ]
32
+
33
+ @connection.exec_query sql, "TEST", binds
34
+ @connection.disconnect!
35
+ assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
36
+ @connection.exec_query sql, "TEST", binds
37
+ end
38
+ end
39
+ end
@@ -41,4 +41,13 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase
41
41
  date_base = connection.select_value("select GETUTCDATE()")
42
42
  assert_equal date_base.change(usec: 0), date_proc.change(usec: 0)
43
43
  end
44
+
45
+ it 'test deprecation with transaction return when executing procedure' do
46
+ assert_deprecated do
47
+ ActiveRecord::Base.transaction do
48
+ connection.execute_procedure("my_getutcdate")
49
+ return
50
+ end
51
+ end
52
+ end
44
53
  end
@@ -33,4 +33,31 @@ class InClauseTestSQLServer < ActiveRecord::TestCase
33
33
  assert_includes posts.to_sql, "ORDER BY [authors].[name]"
34
34
  assert_equal 8, posts.length
35
35
  end
36
+
37
+ it "removes ordering from 'not' subqueries" do
38
+ authors_subquery = Author.where.not(name: ["Mary", "Bob"]).order(:name)
39
+ posts = Post.where(author: authors_subquery)
40
+
41
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
42
+ assert_not_includes posts.to_sql, "ORDER BY [authors].[name]"
43
+ assert_equal 5, posts.length
44
+ end
45
+
46
+ it "does not remove ordering from 'not' subquery that includes a limit" do
47
+ authors_subquery = Author.where.not(name: ["Ronan", "Mary", "Bob"]).order(:name).limit(2)
48
+ posts = Post.where(author: authors_subquery)
49
+
50
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
51
+ assert_includes posts.to_sql, "ORDER BY [authors].[name]"
52
+ assert_equal 5, posts.length
53
+ end
54
+
55
+ it "does not remove ordering from 'not' subquery that includes an offset" do
56
+ authors_subquery = Author.where.not(name: ["David", "Ronan", "Cian"]).order(:name).offset(1)
57
+ posts = Post.where(author: authors_subquery)
58
+
59
+ assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
60
+ assert_includes posts.to_sql, "ORDER BY [authors].[name]"
61
+ assert_equal 3, posts.length
62
+ end
36
63
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "models/post"
5
+ require "models/author"
6
+
7
+ class LateralTestSQLServer < ActiveRecord::TestCase
8
+ fixtures :posts, :authors
9
+
10
+ it 'uses OUTER APPLY for OUTER JOIN LATERAL' do
11
+ post = Arel::Table.new(:posts)
12
+ author = Arel::Table.new(:authors)
13
+ subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42))
14
+
15
+ one = Arel::Nodes::Quoted.new(1)
16
+ eq = Arel::Nodes::Equality.new(one, one)
17
+
18
+ sql = author.project(Arel.star).where(author[:name].matches("David")).outer_join(subselect.lateral.as("bar")).on(eq).to_sql
19
+ results = ActiveRecord::Base.connection.exec_query sql
20
+ assert_equal sql, "SELECT * FROM [authors] OUTER APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'"
21
+ assert_equal results.length, 1
22
+ end
23
+
24
+ it 'uses CROSS APPLY for INNER JOIN LATERAL' do
25
+ post = Arel::Table.new(:posts)
26
+ author = Arel::Table.new(:authors)
27
+ subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42))
28
+
29
+ sql = author.project(Arel.star).where(author[:name].matches("David")).join(subselect.lateral.as("bar")).to_sql
30
+ results = ActiveRecord::Base.connection.exec_query sql
31
+
32
+ assert_equal sql, "SELECT * FROM [authors] CROSS APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'"
33
+ assert_equal results.length, 0
34
+ end
35
+ end
@@ -63,5 +63,56 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
63
63
  it "change null and default" do
64
64
  assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil }
65
65
  end
66
+
67
+ it "change collation" do
68
+ assert_nothing_raised { connection.change_column :sst_string_collation, :string_with_collation, :varchar, collation: :SQL_Latin1_General_CP437_BIN }
69
+
70
+ SstStringCollation.reset_column_information
71
+ assert_equal "SQL_Latin1_General_CP437_BIN", SstStringCollation.columns_hash['string_with_collation'].collation
72
+ end
73
+ end
74
+
75
+ describe "#create_schema" do
76
+ it "creates a new schema" do
77
+ connection.create_schema("some schema")
78
+
79
+ schemas = connection.exec_query("select name from sys.schemas").to_a
80
+
81
+ assert_includes schemas, { "name" => "some schema" }
82
+ end
83
+
84
+ it "creates a new schema with an owner" do
85
+ connection.create_schema("some schema", :guest)
86
+
87
+ schemas = connection.exec_query("select name, principal_id from sys.schemas").to_a
88
+
89
+ assert_includes schemas, { "name" => "some schema", "principal_id" => 2 }
90
+ end
91
+ end
92
+
93
+ describe "#change_table_schema" do
94
+ before { connection.create_schema("foo") }
95
+
96
+ it "transfer the given table to the given schema" do
97
+ connection.change_table_schema("foo", "orders")
98
+
99
+ assert connection.data_source_exists?("foo.orders")
100
+ end
101
+ end
102
+
103
+ describe "#drop_schema" do
104
+ before { connection.create_schema("some schema") }
105
+
106
+ it "drops a schema" do
107
+ schemas = connection.exec_query("select name from sys.schemas").to_a
108
+
109
+ assert_includes schemas, { "name" => "some schema" }
110
+
111
+ connection.drop_schema("some schema")
112
+
113
+ schemas = connection.exec_query("select name from sys.schemas").to_a
114
+
115
+ refute_includes schemas, { "name" => "some schema" }
116
+ end
66
117
  end
67
118
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "models/company"
5
+
6
+ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase
7
+ fixtures :companies
8
+
9
+ it "apply optimizations" do
10
+ assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
11
+ companies = Company.optimizer_hints("HASH GROUP")
12
+ companies = companies.distinct.select("firm_id")
13
+ assert_includes companies.explain, "| Hash Match | Aggregate |"
14
+ end
15
+
16
+ assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(ORDER GROUP\)\z}) do
17
+ companies = Company.optimizer_hints("ORDER GROUP")
18
+ companies = companies.distinct.select("firm_id")
19
+ assert_includes companies.explain, "| Stream Aggregate | Aggregate |"
20
+ end
21
+ end
22
+
23
+ it "apply multiple optimizations" do
24
+ assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP, FAST 1\)\z}) do
25
+ companies = Company.optimizer_hints("HASH GROUP", "FAST 1")
26
+ companies = companies.distinct.select("firm_id")
27
+ assert_includes companies.explain, "| Hash Match | Flow Distinct |"
28
+ end
29
+ end
30
+
31
+ it "support subqueries" do
32
+ assert_sql(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do
33
+ companies = Company.optimizer_hints("MAXDOP 2")
34
+ companies = companies.select(:id).where(firm_id: [0, 1]).limit(3)
35
+ assert_equal 3, companies.count
36
+ end
37
+ end
38
+
39
+ it "sanitize values" do
40
+ assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
41
+ companies = Company.optimizer_hints("OPTION (HASH GROUP)")
42
+ companies = companies.distinct.select("firm_id")
43
+ companies.to_a
44
+ end
45
+
46
+ assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
47
+ companies = Company.optimizer_hints("OPTION(HASH GROUP)")
48
+ companies = companies.distinct.select("firm_id")
49
+ companies.to_a
50
+ end
51
+
52
+ assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(TABLE HINT \(\[companies\], INDEX\(1\)\)\)\z}) do
53
+ companies = Company.optimizer_hints("OPTION(TABLE HINT ([companies], INDEX(1)))")
54
+ companies = companies.distinct.select("firm_id")
55
+ companies.to_a
56
+ end
57
+
58
+ assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
59
+ companies = Company.optimizer_hints("Option(HASH GROUP)")
60
+ companies = companies.distinct.select("firm_id")
61
+ companies.to_a
62
+ end
63
+ end
64
+
65
+ it "skip optimization after unscope" do
66
+ assert_sql("SELECT DISTINCT [companies].[firm_id] FROM [companies]") do
67
+ companies = Company.optimizer_hints("HASH GROUP")
68
+ companies = companies.distinct.select("firm_id")
69
+ companies.unscope(:optimizer_hints).load
70
+ end
71
+ end
72
+ end
@@ -143,4 +143,11 @@ class OrderTestSQLServer < ActiveRecord::TestCase
143
143
  assert_equal post1, Post.order(Arel.sql(order)).first
144
144
  assert_equal post2, Post.order(Arel.sql(order)).second
145
145
  end
146
+
147
+ # Executing this kind of queries will raise "A column has been specified more than once in the order by list"
148
+ # This test shows that we don't do anything to prevent this
149
+ it "doesn't deduplicate semantically equal orders" do
150
+ sql = Post.order(:id).order("posts.id ASC").to_sql
151
+ assert_equal "SELECT [posts].* FROM [posts] ORDER BY [posts].[id] ASC, posts.id ASC", sql
152
+ end
146
153
  end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "support/schema_dumping_helper"
5
+
6
+ class PrimaryKeyUuidTypeTest < ActiveRecord::TestCase
7
+ include SchemaDumpingHelper
8
+
9
+ self.use_transactional_tests = false
10
+
11
+ class Barcode < ActiveRecord::Base
12
+ end
13
+
14
+ setup do
15
+ @connection = ActiveRecord::Base.connection
16
+ @connection.create_table(:barcodes, primary_key: "code", id: :uuid, force: true)
17
+ end
18
+
19
+ teardown do
20
+ @connection.drop_table(:barcodes, if_exists: true)
21
+ end
22
+
23
+ def test_any_type_primary_key
24
+ assert_equal "code", Barcode.primary_key
25
+
26
+ column = Barcode.column_for_attribute(Barcode.primary_key)
27
+ assert_not column.null
28
+ assert_equal :uuid, column.type
29
+ assert_not_predicate column, :is_identity?
30
+ assert_predicate column, :is_primary?
31
+ ensure
32
+ Barcode.reset_column_information
33
+ end
34
+
35
+ test "schema dump primary key includes default" do
36
+ schema = dump_table_schema "barcodes"
37
+ assert_match %r/create_table "barcodes", primary_key: "code", id: :uuid, default: -> { "newid\(\)" }/, schema
38
+ end
39
+ end
40
+
41
+ class PrimaryKeyIntegerTest < ActiveRecord::TestCase
42
+ include SchemaDumpingHelper
43
+
44
+ self.use_transactional_tests = false
45
+
46
+ class Barcode < ActiveRecord::Base
47
+ end
48
+
49
+ class Widget < ActiveRecord::Base
50
+ end
51
+
52
+ setup do
53
+ @connection = ActiveRecord::Base.connection
54
+ end
55
+
56
+ teardown do
57
+ @connection.drop_table :barcodes, if_exists: true
58
+ @connection.drop_table :widgets, if_exists: true
59
+ end
60
+
61
+ test "integer primary key without default" do
62
+ @connection.create_table(:widgets, id: :integer, force: true)
63
+ column = @connection.columns(:widgets).find { |c| c.name == "id" }
64
+ assert_predicate column, :is_primary?
65
+ assert_predicate column, :is_identity?
66
+ assert_equal :integer, column.type
67
+ assert_not_predicate column, :bigint?
68
+
69
+ schema = dump_table_schema "widgets"
70
+ assert_match %r/create_table "widgets", id: :integer, force: :cascade do/, schema
71
+ end
72
+
73
+ test "bigint primary key without default" do
74
+ @connection.create_table(:widgets, id: :bigint, force: true)
75
+ column = @connection.columns(:widgets).find { |c| c.name == "id" }
76
+ assert_predicate column, :is_primary?
77
+ assert_predicate column, :is_identity?
78
+ assert_equal :integer, column.type
79
+ assert_predicate column, :bigint?
80
+
81
+ schema = dump_table_schema "widgets"
82
+ assert_match %r/create_table "widgets", force: :cascade do/, schema
83
+ end
84
+
85
+ test "don't set identity to integer and bigint when there is a default" do
86
+ @connection.create_table(:barcodes, id: :integer, default: nil, force: true)
87
+ @connection.create_table(:widgets, id: :bigint, default: nil, force: true)
88
+
89
+ column = @connection.columns(:widgets).find { |c| c.name == "id" }
90
+ assert_predicate column, :is_primary?
91
+ assert_not_predicate column, :is_identity?
92
+
93
+ schema = dump_table_schema "widgets"
94
+ assert_match %r/create_table "widgets", id: :bigint, default: nil, force: :cascade do/, schema
95
+
96
+ column = @connection.columns(:barcodes).find { |c| c.name == "id" }
97
+ assert_predicate column, :is_primary?
98
+ assert_not_predicate column, :is_identity?
99
+
100
+ schema = dump_table_schema "barcodes"
101
+ assert_match %r/create_table "barcodes", id: :integer, default: nil, force: :cascade do/, schema
102
+ end
103
+ end
@@ -10,8 +10,9 @@ class SQLServerRakeTest < ActiveRecord::TestCase
10
10
 
11
11
  let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks }
12
12
  let(:new_database) { "activerecord_unittest_tasks" }
13
- let(:default_configuration) { ARTest.connection_config["arunit"] }
13
+ let(:default_configuration) { ARTest.test_configuration_hashes["arunit"] }
14
14
  let(:configuration) { default_configuration.merge("database" => new_database) }
15
+ let(:db_config) { ActiveRecord::Base.configurations.resolve(configuration) }
15
16
 
16
17
  before { skip "on azure" if azure_skip }
17
18
  before { disconnect! unless azure_skip }
@@ -151,7 +152,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest
151
152
  _(filedata).must_match %r{CREATE TABLE dbo\.users}
152
153
  db_tasks.purge(configuration)
153
154
  _(connection.tables).wont_include "users"
154
- db_tasks.load_schema configuration, :sql, filename
155
+ db_tasks.load_schema db_config, :sql, filename
155
156
  _(connection.tables).must_include "users"
156
157
  end
157
158
  end
@@ -16,7 +16,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
16
16
  assert_line :tinyint, type: "integer", limit: 1, precision: nil, scale: nil, default: 42
17
17
  assert_line :bit, type: "boolean", limit: nil, precision: nil, scale: nil, default: true
18
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
19
+ assert_line :numeric_18_0, type: "decimal", limit: nil, precision: 18, scale: nil, default: 191
20
20
  assert_line :numeric_36_2, type: "decimal", limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01
21
21
  assert_line :money, type: "money", limit: nil, precision: 19, scale: 4, default: 4.2
22
22
  assert_line :smallmoney, type: "smallmoney", limit: nil, precision: 10, scale: 4, default: 4.2
@@ -75,7 +75,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
75
75
  assert_line :integer_col, type: "integer", limit: nil, precision: nil, scale: nil, default: nil
76
76
  assert_line :bigint_col, type: "bigint", limit: nil, precision: nil, scale: nil, default: nil
77
77
  assert_line :boolean_col, type: "boolean", limit: nil, precision: nil, scale: nil, default: nil
78
- assert_line :decimal_col, type: "decimal", limit: nil, precision: 18, scale: 0, default: nil
78
+ assert_line :decimal_col, type: "decimal", limit: nil, precision: 18, scale: nil, default: nil
79
79
  assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil
80
80
  assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil
81
81
  assert_line :text_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil
@@ -119,6 +119,15 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
119
119
  assert_line :json_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil
120
120
  end
121
121
 
122
+ it "dump column collation" do
123
+ generate_schema_for_table('sst_string_collation')
124
+
125
+ assert_line :string_without_collation, type: "string", limit: nil, default: nil, collation: nil
126
+ assert_line :string_default_collation, type: "varchar", limit: nil, default: nil, collation: nil
127
+ assert_line :string_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS"
128
+ assert_line :varchar_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS"
129
+ end
130
+
122
131
  # Special Cases
123
132
 
124
133
  it "honor nonstandard primary keys" do
@@ -135,6 +144,12 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
135
144
  assert_line :name, type: "string", limit: nil, default: nil, collation: nil
136
145
  end
137
146
 
147
+ it "dumps field with unique key constraints only once" do
148
+ output = generate_schema_for_table "unique_key_dumped_table"
149
+
150
+ _(output.scan('t.integer "unique_field"').length).must_equal(1)
151
+ end
152
+
138
153
  private
139
154
 
140
155
  def generate_schema_for_table(*table_names)
@@ -160,13 +175,15 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
160
175
 
161
176
  def assert_line(column_name, options = {})
162
177
  line = line(column_name)
163
- assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}"
178
+ assert line, "Could not find line with column name: #{column_name.inspect} in schema:\n#{schema}"
179
+
164
180
  [:type, :limit, :precision, :scale, :collation, :default].each do |key|
165
181
  next unless options.key?(key)
166
182
 
167
183
  actual = key == :type ? line.send(:type_method) : line.send(key)
168
184
  expected = options[key]
169
185
  message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}"
186
+
170
187
  if expected.nil?
171
188
  _(actual).must_be_nil message
172
189
  elsif expected.is_a?(Array)