activerecord-sqlserver-adapter 6.0.1 → 6.1.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +26 -0
- data/CHANGELOG.md +29 -46
- data/README.md +32 -3
- data/RUNNING_UNIT_TESTS.md +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -10
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +9 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +28 -16
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +8 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +31 -9
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +36 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +0 -1
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -1
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +100 -69
- data/lib/active_record/connection_adapters/sqlserver_column.rb +75 -19
- data/lib/active_record/sqlserver_base.rb +9 -15
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +17 -14
- data/lib/arel/visitors/sqlserver.rb +125 -40
- data/test/cases/adapter_test_sqlserver.rb +50 -16
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/coerced_tests.rb +611 -78
- data/test/cases/column_test_sqlserver.rb +9 -2
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +9 -0
- data/test/cases/fetch_test_sqlserver.rb +18 -0
- data/test/cases/in_clause_test_sqlserver.rb +27 -0
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +51 -0
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +7 -0
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +38 -2
- data/test/cases/schema_dumper_test_sqlserver.rb +14 -3
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/models/sqlserver/composite_pk.rb +9 -0
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/schema/sqlserver_specific_schema.rb +25 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
- data/test/support/sql_counter_sqlserver.rb +14 -12
- metadata +29 -9
- data/.travis.yml +0 -23
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -28
@@ -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::
|
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).
|
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
|
@@ -296,6 +299,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
296
299
|
_(obj.date).must_equal Date.civil(0001, 4, 1)
|
297
300
|
obj.reload
|
298
301
|
_(obj.date).must_equal Date.civil(0001, 4, 1)
|
302
|
+
# Can filter by date range
|
303
|
+
_(obj).must_equal obj.class.where(date: obj.date..Date::Infinity.new).first
|
299
304
|
# Can keep and return assigned date.
|
300
305
|
assert_obj_set_and_save :date, Date.civil(1972, 04, 14)
|
301
306
|
# Can accept and cast time objects.
|
@@ -330,6 +335,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
|
|
330
335
|
obj.reload
|
331
336
|
_(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>"
|
332
337
|
_(obj).must_equal obj.class.where(datetime: time).first
|
338
|
+
# Can filter by datetime range
|
339
|
+
_(obj).must_equal obj.class.where(datetime: time..DateTime::Infinity.new).first
|
333
340
|
# Will cast to true DB value on attribute write, save and return again.
|
334
341
|
time = Time.utc 2010, 04, 01, 12, 34, 56, 234567
|
335
342
|
time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000
|
@@ -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
|
@@ -49,3 +49,21 @@ class FetchTestSqlserver < ActiveRecord::TestCase
|
|
49
49
|
@books = (1..10).map { |i| Book.create! name: "Name-#{i}" }
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
class DeterministicFetchWithCompositePkTestSQLServer < ActiveRecord::TestCase
|
54
|
+
it "orders by the identity column if table has one" do
|
55
|
+
SSCompositePkWithIdentity.delete_all
|
56
|
+
SSCompositePkWithIdentity.create(pk_col_two: 2)
|
57
|
+
SSCompositePkWithIdentity.create(pk_col_two: 1)
|
58
|
+
|
59
|
+
_(SSCompositePkWithIdentity.take(1).map(&:pk_col_two)).must_equal [2]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "orders by the first column if table has no identity column" do
|
63
|
+
SSCompositePkWithoutIdentity.delete_all
|
64
|
+
SSCompositePkWithoutIdentity.create(pk_col_one: 2, pk_col_two: 2)
|
65
|
+
SSCompositePkWithoutIdentity.create(pk_col_one: 1, pk_col_two: 1)
|
66
|
+
|
67
|
+
_(SSCompositePkWithoutIdentity.take(1).map(&:pk_col_two)).must_equal [1]
|
68
|
+
end
|
69
|
+
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.
|
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,42 @@ 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
|
155
|
+
db_tasks.load_schema db_config, :sql, filename
|
155
156
|
_(connection.tables).must_include "users"
|
156
157
|
end
|
157
158
|
end
|
159
|
+
|
160
|
+
class SQLServerRakeSchemaCacheDumpLoadTest < SQLServerRakeTest
|
161
|
+
let(:filename) { File.join ARTest::SQLServer.test_root_sqlserver, "schema_cache.yml" }
|
162
|
+
let(:filedata) { File.read(filename) }
|
163
|
+
|
164
|
+
before do
|
165
|
+
quietly { db_tasks.create(configuration) }
|
166
|
+
|
167
|
+
connection.create_table :users, force: true do |t|
|
168
|
+
t.string :name, null: false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
after do
|
173
|
+
FileUtils.rm_rf(filename)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "dumps schema cache with SQL Server metadata" do
|
177
|
+
quietly { db_tasks.dump_schema_cache connection, filename }
|
178
|
+
|
179
|
+
schema_cache = YAML.load(File.read(filename))
|
180
|
+
|
181
|
+
col_id, col_name = connection.schema_cache.columns("users")
|
182
|
+
|
183
|
+
assert col_id.is_identity
|
184
|
+
assert col_id.is_primary
|
185
|
+
assert_equal col_id.ordinal_position, 1
|
186
|
+
assert_equal col_id.table_name, "users"
|
187
|
+
|
188
|
+
assert_not col_name.is_identity
|
189
|
+
assert_not col_name.is_primary
|
190
|
+
assert_equal col_name.ordinal_position, 2
|
191
|
+
assert_equal col_name.table_name, "users"
|
192
|
+
end
|
193
|
+
end
|