activerecord-sqlserver-adapter 8.0.10 → 8.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/.devcontainer/Dockerfile +1 -1
- data/.github/workflows/ci.yml +34 -3
- data/CHANGELOG.md +14 -68
- data/Dockerfile.ci +1 -1
- data/Gemfile +7 -9
- data/Guardfile +2 -2
- data/README.md +33 -13
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +15 -16
- data/compose.ci.yaml +8 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +1 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +118 -83
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +7 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +24 -12
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +17 -8
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +162 -156
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -12
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +118 -66
- data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -9
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +5 -5
- data/lib/arel/visitors/sqlserver.rb +55 -26
- data/test/cases/active_schema_test_sqlserver.rb +45 -23
- data/test/cases/adapter_test_sqlserver.rb +72 -59
- data/test/cases/coerced_tests.rb +365 -161
- data/test/cases/column_test_sqlserver.rb +328 -316
- data/test/cases/connection_test_sqlserver.rb +15 -11
- data/test/cases/enum_test_sqlserver.rb +8 -9
- data/test/cases/execute_procedure_test_sqlserver.rb +1 -1
- data/test/cases/fetch_test_sqlserver.rb +1 -1
- data/test/cases/helper_sqlserver.rb +7 -3
- data/test/cases/index_test_sqlserver.rb +8 -6
- data/test/cases/insert_all_test_sqlserver.rb +3 -28
- data/test/cases/json_test_sqlserver.rb +8 -8
- data/test/cases/lateral_test_sqlserver.rb +2 -2
- data/test/cases/migration_test_sqlserver.rb +12 -12
- data/test/cases/pessimistic_locking_test_sqlserver.rb +6 -6
- data/test/cases/primary_keys_test_sqlserver.rb +4 -4
- data/test/cases/rake_test_sqlserver.rb +15 -7
- data/test/cases/schema_dumper_test_sqlserver.rb +109 -113
- data/test/cases/schema_test_sqlserver.rb +7 -7
- data/test/cases/transaction_test_sqlserver.rb +6 -8
- data/test/cases/trigger_test_sqlserver.rb +1 -1
- data/test/cases/utils_test_sqlserver.rb +3 -3
- data/test/cases/view_test_sqlserver.rb +12 -8
- data/test/cases/virtual_column_test_sqlserver.rb +113 -0
- data/test/migrations/create_clients_and_change_column_collation.rb +2 -2
- data/test/models/sqlserver/edge_schema.rb +2 -2
- data/test/schema/sqlserver_specific_schema.rb +49 -37
- data/test/support/coerceable_test_sqlserver.rb +10 -10
- data/test/support/connection_reflection.rb +0 -5
- data/test/support/core_ext/backtrace_cleaner.rb +36 -0
- data/test/support/query_assertions.rb +6 -6
- data/test/support/rake_helpers.rb +6 -10
- metadata +12 -107
|
@@ -16,7 +16,9 @@ module Arel
|
|
|
16
16
|
BIND_BLOCK = proc { |i| "@#{i - 1}" }
|
|
17
17
|
private_constant :BIND_BLOCK
|
|
18
18
|
|
|
19
|
-
def bind_block
|
|
19
|
+
def bind_block
|
|
20
|
+
BIND_BLOCK
|
|
21
|
+
end
|
|
20
22
|
|
|
21
23
|
def visit_Arel_Nodes_Bin(o, collector)
|
|
22
24
|
visit o.expr, collector
|
|
@@ -29,10 +31,49 @@ module Arel
|
|
|
29
31
|
visit o.right, collector
|
|
30
32
|
end
|
|
31
33
|
|
|
34
|
+
# Same as SQLite and PostgreSQL.
|
|
32
35
|
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
collector.retryable = false
|
|
37
|
+
o = prepare_update_statement(o)
|
|
38
|
+
|
|
39
|
+
collector << "UPDATE "
|
|
40
|
+
|
|
41
|
+
# UPDATE with JOIN is in the form of:
|
|
42
|
+
#
|
|
43
|
+
# UPDATE t1
|
|
44
|
+
# SET ..
|
|
45
|
+
# FROM t1 JOIN t2 ON t2.join_id = t1.join_id ..
|
|
46
|
+
# WHERE ..
|
|
47
|
+
if has_join_sources?(o)
|
|
48
|
+
collector = visit o.relation.left, collector
|
|
49
|
+
collect_nodes_for o.values, collector, " SET "
|
|
50
|
+
collector << " FROM "
|
|
51
|
+
collector = inject_join o.relation.right, collector, " "
|
|
52
|
+
else
|
|
53
|
+
collector = visit o.relation, collector
|
|
54
|
+
collect_nodes_for o.values, collector, " SET "
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
|
58
|
+
collect_nodes_for o.orders, collector, " ORDER BY "
|
|
59
|
+
maybe_visit o.limit, collector
|
|
60
|
+
maybe_visit o.comment, collector
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Similar to PostgreSQL and SQLite.
|
|
64
|
+
def prepare_update_statement(o)
|
|
65
|
+
if o.key && has_join_sources?(o) && !has_group_by_and_having?(o) && !has_limit_or_offset_or_orders?(o)
|
|
66
|
+
# Join clauses cannot reference the target table, so alias the
|
|
67
|
+
# updated table, place the entire relation in the FROM clause, and
|
|
68
|
+
# add a self-join (which requires the primary key)
|
|
69
|
+
stmt = o.clone
|
|
70
|
+
|
|
71
|
+
stmt.relation, stmt.wheres = o.relation.clone, o.wheres.clone
|
|
72
|
+
stmt.relation.right = [stmt.relation.left, *stmt.relation.right]
|
|
73
|
+
# Don't need to use alias
|
|
74
|
+
stmt
|
|
35
75
|
else
|
|
76
|
+
# If using subquery, we need to add limit
|
|
36
77
|
o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil?
|
|
37
78
|
|
|
38
79
|
super
|
|
@@ -61,19 +102,8 @@ module Arel
|
|
|
61
102
|
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
|
62
103
|
end
|
|
63
104
|
|
|
64
|
-
def update_statement_using_join(o, collector)
|
|
65
|
-
collector.retryable = false
|
|
66
|
-
|
|
67
|
-
collector << "UPDATE "
|
|
68
|
-
visit o.relation.left, collector
|
|
69
|
-
collect_nodes_for o.values, collector, " SET "
|
|
70
|
-
collector << " FROM "
|
|
71
|
-
visit o.relation, collector
|
|
72
|
-
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
|
73
|
-
end
|
|
74
|
-
|
|
75
105
|
def visit_Arel_Nodes_Lock(o, collector)
|
|
76
|
-
o.expr = Arel.sql("WITH(UPDLOCK)") if o.expr.to_s
|
|
106
|
+
o.expr = Arel.sql("WITH(UPDLOCK)") if /FOR UPDATE/.match?(o.expr.to_s)
|
|
77
107
|
collector << " "
|
|
78
108
|
visit o.expr, collector
|
|
79
109
|
end
|
|
@@ -87,12 +117,11 @@ module Arel
|
|
|
87
117
|
def visit_Arel_Nodes_Limit(o, collector)
|
|
88
118
|
if node_value(o) == 0
|
|
89
119
|
collector << FETCH0
|
|
90
|
-
collector << ROWS_ONLY
|
|
91
120
|
else
|
|
92
121
|
collector << FETCH
|
|
93
122
|
visit o.expr, collector
|
|
94
|
-
collector << ROWS_ONLY
|
|
95
123
|
end
|
|
124
|
+
collector << ROWS_ONLY
|
|
96
125
|
end
|
|
97
126
|
|
|
98
127
|
def visit_Arel_Nodes_Grouping(o, collector)
|
|
@@ -105,10 +134,10 @@ module Arel
|
|
|
105
134
|
|
|
106
135
|
visit o.left, collector
|
|
107
136
|
|
|
108
|
-
if o.type == :in
|
|
109
|
-
|
|
137
|
+
collector << if o.type == :in
|
|
138
|
+
" IN ("
|
|
110
139
|
else
|
|
111
|
-
|
|
140
|
+
" NOT IN ("
|
|
112
141
|
end
|
|
113
142
|
|
|
114
143
|
values = o.casted_values
|
|
@@ -170,14 +199,14 @@ module Arel
|
|
|
170
199
|
quote_table_name(o.name)
|
|
171
200
|
end
|
|
172
201
|
end
|
|
173
|
-
rescue
|
|
202
|
+
rescue
|
|
174
203
|
quote_table_name(o.name)
|
|
175
204
|
end
|
|
176
205
|
|
|
177
|
-
if o.table_alias
|
|
178
|
-
|
|
206
|
+
collector << if o.table_alias
|
|
207
|
+
"#{table_name} #{quote_table_name o.table_alias}"
|
|
179
208
|
else
|
|
180
|
-
|
|
209
|
+
table_name
|
|
181
210
|
end
|
|
182
211
|
end
|
|
183
212
|
|
|
@@ -297,7 +326,7 @@ module Arel
|
|
|
297
326
|
end
|
|
298
327
|
|
|
299
328
|
def select_statement_lock?
|
|
300
|
-
@select_statement
|
|
329
|
+
@select_statement&.lock
|
|
301
330
|
end
|
|
302
331
|
|
|
303
332
|
def make_Fetch_Possible_And_Deterministic(o)
|
|
@@ -330,7 +359,7 @@ module Arel
|
|
|
330
359
|
elsif Arel::Nodes::SqlLiteral === core.from
|
|
331
360
|
Arel::Table.new(core.from)
|
|
332
361
|
elsif Arel::Nodes::JoinSource === core.source
|
|
333
|
-
Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left.left
|
|
362
|
+
(Arel::Nodes::SqlLiteral === core.source.left) ? Arel::Table.new(core.source.left, @engine) : core.source.left.left
|
|
334
363
|
end
|
|
335
364
|
end
|
|
336
365
|
|
|
@@ -12,48 +12,50 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
after do
|
|
15
|
-
connection.drop_table :schema_test_table
|
|
15
|
+
connection.drop_table :schema_test_table
|
|
16
|
+
rescue
|
|
17
|
+
nil
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
it
|
|
19
|
-
assert_queries_match(
|
|
20
|
+
it "default index" do
|
|
21
|
+
assert_queries_match("CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do
|
|
20
22
|
connection.add_index :schema_test_table, "foo"
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
|
|
24
|
-
it
|
|
25
|
-
assert_queries_match(
|
|
26
|
+
it "unique index" do
|
|
27
|
+
assert_queries_match("CREATE UNIQUE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do
|
|
26
28
|
connection.add_index :schema_test_table, "foo", unique: true
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
it
|
|
32
|
+
it "where condition on index" do
|
|
31
33
|
assert_queries_match("CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo]) WHERE state = 'active'") do
|
|
32
34
|
connection.add_index :schema_test_table, "foo", where: "state = 'active'"
|
|
33
35
|
end
|
|
34
36
|
end
|
|
35
37
|
|
|
36
|
-
it
|
|
38
|
+
it "if index does not exist" do
|
|
37
39
|
assert_queries_match("IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = 'index_schema_test_table_on_foo') " \
|
|
38
40
|
"CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do
|
|
39
41
|
connection.add_index :schema_test_table, "foo", if_not_exists: true
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
|
|
43
|
-
it
|
|
44
|
-
assert_queries_match(
|
|
45
|
+
it "clustered index" do
|
|
46
|
+
assert_queries_match("CREATE CLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do
|
|
45
47
|
connection.add_index :schema_test_table, "foo", type: :clustered
|
|
46
48
|
end
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
it
|
|
50
|
-
assert_queries_match(
|
|
51
|
+
it "nonclustered index" do
|
|
52
|
+
assert_queries_match("CREATE NONCLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do
|
|
51
53
|
connection.add_index :schema_test_table, "foo", type: :nonclustered
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
|
|
56
|
-
describe
|
|
58
|
+
describe "collation" do
|
|
57
59
|
it "create column with NOT NULL and COLLATE" do
|
|
58
60
|
assert_nothing_raised do
|
|
59
61
|
connection.create_table :not_null_with_collation_table, force: true, id: false do |t|
|
|
@@ -61,12 +63,16 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
65
|
ensure
|
|
64
|
-
|
|
66
|
+
begin
|
|
67
|
+
connection.drop_table :not_null_with_collation_table
|
|
68
|
+
rescue
|
|
69
|
+
nil
|
|
70
|
+
end
|
|
65
71
|
end
|
|
66
72
|
end
|
|
67
73
|
|
|
68
|
-
describe
|
|
69
|
-
it
|
|
74
|
+
describe "datetimeoffset precision" do
|
|
75
|
+
it "valid precisions are correct" do
|
|
70
76
|
assert_nothing_raised do
|
|
71
77
|
connection.create_table :datetimeoffset_precisions do |t|
|
|
72
78
|
t.datetimeoffset :precision_default
|
|
@@ -81,22 +87,30 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
81
87
|
assert_equal columns.find { |column| column.name == "precision_5" }.precision, 5
|
|
82
88
|
assert_equal columns.find { |column| column.name == "precision_7" }.precision, 7
|
|
83
89
|
ensure
|
|
84
|
-
|
|
90
|
+
begin
|
|
91
|
+
connection.drop_table :datetimeoffset_precisions
|
|
92
|
+
rescue
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
85
95
|
end
|
|
86
96
|
|
|
87
|
-
it
|
|
97
|
+
it "invalid precision raises exception" do
|
|
88
98
|
assert_raise(ActiveRecord::ActiveRecordError) do
|
|
89
99
|
connection.create_table :datetimeoffset_precisions do |t|
|
|
90
100
|
t.datetimeoffset :precision_8, precision: 8
|
|
91
101
|
end
|
|
92
102
|
end
|
|
93
103
|
ensure
|
|
94
|
-
|
|
104
|
+
begin
|
|
105
|
+
connection.drop_table :datetimeoffset_precisions
|
|
106
|
+
rescue
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
95
109
|
end
|
|
96
110
|
end
|
|
97
111
|
|
|
98
|
-
describe
|
|
99
|
-
it
|
|
112
|
+
describe "time precision" do
|
|
113
|
+
it "valid precisions are correct" do
|
|
100
114
|
assert_nothing_raised do
|
|
101
115
|
connection.create_table :time_precisions do |t|
|
|
102
116
|
t.time :precision_default
|
|
@@ -111,17 +125,25 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
111
125
|
assert_equal columns.find { |column| column.name == "precision_5" }.precision, 5
|
|
112
126
|
assert_equal columns.find { |column| column.name == "precision_7" }.precision, 7
|
|
113
127
|
ensure
|
|
114
|
-
|
|
128
|
+
begin
|
|
129
|
+
connection.drop_table :time_precisions
|
|
130
|
+
rescue
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
115
133
|
end
|
|
116
134
|
|
|
117
|
-
it
|
|
135
|
+
it "invalid precision raises exception" do
|
|
118
136
|
assert_raise(ActiveRecord::ActiveRecordError) do
|
|
119
137
|
connection.create_table :time_precisions do |t|
|
|
120
138
|
t.time :precision_8, precision: 8
|
|
121
139
|
end
|
|
122
140
|
end
|
|
123
141
|
ensure
|
|
124
|
-
|
|
142
|
+
begin
|
|
143
|
+
connection.drop_table :time_precisions
|
|
144
|
+
rescue
|
|
145
|
+
nil
|
|
146
|
+
end
|
|
125
147
|
end
|
|
126
148
|
end
|
|
127
149
|
end
|
|
@@ -14,10 +14,10 @@ require "models/discount"
|
|
|
14
14
|
class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
15
15
|
fixtures :tasks
|
|
16
16
|
|
|
17
|
-
let(:arunit_connection)
|
|
17
|
+
let(:arunit_connection) { Topic.lease_connection }
|
|
18
18
|
let(:arunit2_connection) { College.lease_connection }
|
|
19
|
-
let(:arunit_database)
|
|
20
|
-
let(:arunit2_database)
|
|
19
|
+
let(:arunit_database) { arunit_connection.pool.db_config.database }
|
|
20
|
+
let(:arunit2_database) { arunit2_connection.pool.db_config.database }
|
|
21
21
|
|
|
22
22
|
let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" }
|
|
23
23
|
let(:basic_merge_sql) { "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [id], [name], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([id], [name]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name]" }
|
|
@@ -50,20 +50,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
it "table exists works if table name prefixed by schema and owner" do
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Topic.table_name = "topics"
|
|
66
|
-
end
|
|
53
|
+
assert_equal "topics", Topic.table_name
|
|
54
|
+
assert Topic.table_exists?
|
|
55
|
+
|
|
56
|
+
# Test when owner included in table name.
|
|
57
|
+
Topic.table_name = "dbo.topics"
|
|
58
|
+
assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists."
|
|
59
|
+
|
|
60
|
+
# Test when database and owner included in table name.
|
|
61
|
+
Topic.table_name = "#{arunit_database}.dbo.topics"
|
|
62
|
+
assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists."
|
|
63
|
+
ensure
|
|
64
|
+
Topic.table_name = "topics"
|
|
67
65
|
end
|
|
68
66
|
|
|
69
67
|
it "test table existence across database schemas" do
|
|
@@ -77,18 +75,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
77
75
|
assert_not_equal arunit_database, arunit2_database
|
|
78
76
|
|
|
79
77
|
# Assert that the Topics table exists when using the Topics connection.
|
|
80
|
-
assert arunit_connection.table_exists?(
|
|
81
|
-
assert arunit_connection.table_exists?(
|
|
82
|
-
assert arunit_connection.table_exists?("#{arunit_database}.dbo.topics"),
|
|
78
|
+
assert arunit_connection.table_exists?("topics"), "Topics table exists using table name"
|
|
79
|
+
assert arunit_connection.table_exists?("dbo.topics"), "Topics table exists using owner and table name"
|
|
80
|
+
assert arunit_connection.table_exists?("#{arunit_database}.dbo.topics"), "Topics table exists using database, owner and table name"
|
|
83
81
|
|
|
84
82
|
# Assert that the Colleges table exists when using the Colleges connection.
|
|
85
|
-
assert arunit2_connection.table_exists?(
|
|
86
|
-
assert arunit2_connection.table_exists?(
|
|
87
|
-
assert arunit2_connection.table_exists?("#{arunit2_database}.dbo.colleges"),
|
|
83
|
+
assert arunit2_connection.table_exists?("colleges"), "College table exists using table name"
|
|
84
|
+
assert arunit2_connection.table_exists?("dbo.colleges"), "College table exists using owner and table name"
|
|
85
|
+
assert arunit2_connection.table_exists?("#{arunit2_database}.dbo.colleges"), "College table exists using database, owner and table name"
|
|
88
86
|
|
|
89
87
|
# Assert that the tables exist when using each others connection.
|
|
90
|
-
assert arunit_connection.table_exists?("#{arunit2_database}.dbo.colleges"),
|
|
91
|
-
assert arunit2_connection.table_exists?("#{arunit_database}.dbo.topics"),
|
|
88
|
+
assert arunit_connection.table_exists?("#{arunit2_database}.dbo.colleges"), "Colleges table exists using Topics connection"
|
|
89
|
+
assert arunit2_connection.table_exists?("#{arunit_database}.dbo.topics"), "Topics table exists using Colleges connection"
|
|
92
90
|
end
|
|
93
91
|
|
|
94
92
|
it "return true to insert sql query for inserts only" do
|
|
@@ -118,20 +116,20 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
118
116
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
|
119
117
|
configuration = db_config.configuration_hash.merge(database: "nonexistent_activerecord_unittest")
|
|
120
118
|
assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(configuration),
|
|
121
|
-
|
|
119
|
+
"expected database #{configuration[:database]} to not exist"
|
|
122
120
|
end
|
|
123
121
|
|
|
124
122
|
it "test database exists returns true when the database exists" do
|
|
125
123
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
|
126
124
|
assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(db_config.configuration_hash),
|
|
127
|
-
|
|
125
|
+
"expected database #{db_config.database} to exist"
|
|
128
126
|
end
|
|
129
127
|
|
|
130
128
|
it "test primary key violation" do
|
|
131
|
-
Post.create!(id: 0, title:
|
|
129
|
+
Post.create!(id: 0, title: "Setup", body: "Create post with primary key of zero")
|
|
132
130
|
|
|
133
131
|
assert_raise ActiveRecord::RecordNotUnique do
|
|
134
|
-
Post.create!(id: 0, title:
|
|
132
|
+
Post.create!(id: 0, title: "Test", body: "Try to create another post with primary key of zero")
|
|
135
133
|
end
|
|
136
134
|
end
|
|
137
135
|
|
|
@@ -141,12 +139,20 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
141
139
|
end
|
|
142
140
|
|
|
143
141
|
after do
|
|
144
|
-
|
|
142
|
+
begin
|
|
143
|
+
connection.execute("SET LANGUAGE #{@default_language}")
|
|
144
|
+
rescue
|
|
145
|
+
nil
|
|
146
|
+
end
|
|
145
147
|
connection.send :initialize_dateformatter
|
|
146
148
|
end
|
|
147
149
|
|
|
148
150
|
it "memos users dateformat" do
|
|
149
|
-
|
|
151
|
+
begin
|
|
152
|
+
connection.execute("SET LANGUAGE us_english")
|
|
153
|
+
rescue
|
|
154
|
+
nil
|
|
155
|
+
end
|
|
150
156
|
dateformat = connection.instance_variable_get(:@database_dateformat)
|
|
151
157
|
assert_equal "mdy", dateformat
|
|
152
158
|
end
|
|
@@ -206,7 +212,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
206
212
|
connection.lowercase_schema_reflection = true
|
|
207
213
|
|
|
208
214
|
assert_nothing_raised do
|
|
209
|
-
post = Post.create!(title:
|
|
215
|
+
post = Post.create!(title: "Setup", body: "Record to be deleted")
|
|
210
216
|
post.destroy!
|
|
211
217
|
end
|
|
212
218
|
end
|
|
@@ -237,12 +243,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
237
243
|
end
|
|
238
244
|
|
|
239
245
|
it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do
|
|
240
|
-
assert_equal "[funny_jokes]",
|
|
241
|
-
assert_equal "[funny_jokes]",
|
|
242
|
-
assert_equal "[funny_jokes]",
|
|
243
|
-
assert_equal "[funny_jokes]",
|
|
244
|
-
assert_equal "[funny_jokes]",
|
|
245
|
-
assert_equal "[funny_jokes]",
|
|
246
|
+
assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql)
|
|
247
|
+
assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted)
|
|
248
|
+
assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered)
|
|
249
|
+
assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_sp)
|
|
250
|
+
assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted_sp)
|
|
251
|
+
assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered_sp)
|
|
246
252
|
|
|
247
253
|
assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql)
|
|
248
254
|
assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql_unquoted)
|
|
@@ -326,7 +332,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
326
332
|
|
|
327
333
|
describe "disabling referential integrity" do
|
|
328
334
|
before do
|
|
329
|
-
connection.disable_referential_integrity {
|
|
335
|
+
connection.disable_referential_integrity {
|
|
336
|
+
SSTestHasPk.delete_all
|
|
337
|
+
SSTestHasFk.delete_all
|
|
338
|
+
}
|
|
330
339
|
@parent = SSTestHasPk.create!
|
|
331
340
|
@member = SSTestHasFk.create!(fk_id: @parent.id)
|
|
332
341
|
end
|
|
@@ -467,8 +476,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
467
476
|
assert_equal columns.size, SSTestCustomersView.columns.size
|
|
468
477
|
columns.each do |colname|
|
|
469
478
|
assert_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Column,
|
|
470
|
-
|
|
471
|
-
|
|
479
|
+
SSTestCustomersView.columns_hash[colname],
|
|
480
|
+
"Column name #{colname.inspect} was not found in these columns #{SSTestCustomersView.columns.map(&:name).inspect}"
|
|
472
481
|
end
|
|
473
482
|
end
|
|
474
483
|
|
|
@@ -494,8 +503,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
494
503
|
assert_equal columns.size, SSTestStringDefaultsView.columns.size
|
|
495
504
|
columns.each do |colname|
|
|
496
505
|
assert_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Column,
|
|
497
|
-
|
|
498
|
-
|
|
506
|
+
SSTestStringDefaultsView.columns_hash[colname],
|
|
507
|
+
"Column name #{colname.inspect} was not found in these columns #{SSTestStringDefaultsView.columns.map(&:name).inspect}"
|
|
499
508
|
end
|
|
500
509
|
end
|
|
501
510
|
|
|
@@ -507,7 +516,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
507
516
|
|
|
508
517
|
it "find default values" do
|
|
509
518
|
assert_equal "null", SSTestStringDefaultsView.new.pretend_null,
|
|
510
|
-
|
|
519
|
+
SSTestStringDefaultsView.columns_hash["pretend_null"].inspect
|
|
511
520
|
end
|
|
512
521
|
|
|
513
522
|
it "respond true to data_source_exists?" do
|
|
@@ -517,12 +526,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
517
526
|
# That have more than 4000 chars for their definition
|
|
518
527
|
|
|
519
528
|
it "cope with null returned for the definition" do
|
|
520
|
-
assert_nothing_raised
|
|
529
|
+
assert_nothing_raised { SSTestStringDefaultsBigView.columns }
|
|
521
530
|
end
|
|
522
531
|
|
|
523
532
|
it "using alternate view definition still be able to find real default" do
|
|
524
533
|
assert_equal "null", SSTestStringDefaultsBigView.new.pretend_null,
|
|
525
|
-
|
|
534
|
+
SSTestStringDefaultsBigView.columns_hash["pretend_null"].inspect
|
|
526
535
|
end
|
|
527
536
|
end
|
|
528
537
|
|
|
@@ -597,30 +606,30 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
597
606
|
end
|
|
598
607
|
end
|
|
599
608
|
|
|
600
|
-
describe
|
|
609
|
+
describe "table is in non-dbo schema" do
|
|
601
610
|
it "records can be created successfully" do
|
|
602
611
|
assert_difference("Alien.count", 1) do
|
|
603
|
-
Alien.create!(name:
|
|
612
|
+
Alien.create!(name: "Trisolarans")
|
|
604
613
|
end
|
|
605
614
|
end
|
|
606
615
|
|
|
607
|
-
it
|
|
616
|
+
it "records can be inserted using SQL" do
|
|
608
617
|
assert_difference("Alien.count", 2) do
|
|
609
618
|
Alien.lease_connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')")
|
|
610
619
|
end
|
|
611
620
|
end
|
|
612
621
|
end
|
|
613
622
|
|
|
614
|
-
describe
|
|
615
|
-
it
|
|
623
|
+
describe "table names contains spaces" do
|
|
624
|
+
it "records can be created successfully" do
|
|
616
625
|
assert_difference("TableWithSpaces.count", 1) do
|
|
617
|
-
TableWithSpaces.create!(name:
|
|
626
|
+
TableWithSpaces.create!(name: "Bob")
|
|
618
627
|
end
|
|
619
628
|
end
|
|
620
629
|
end
|
|
621
630
|
|
|
622
631
|
describe "exec_insert" do
|
|
623
|
-
it
|
|
632
|
+
it "values clause should be case-insensitive" do
|
|
624
633
|
assert_difference("Post.count", 4) do
|
|
625
634
|
first_insert = connection.exec_insert("INSERT INTO [posts] ([id],[title],[body]) VALUES(100, 'Title', 'Body'), (102, 'Title', 'Body')")
|
|
626
635
|
second_insert = connection.exec_insert("INSERT INTO [posts] ([id],[title],[body]) values(113, 'Body', 'Body'), (114, 'Body', 'Body')")
|
|
@@ -636,33 +645,37 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
|
636
645
|
@conn = ActiveRecord::Base.lease_connection
|
|
637
646
|
end
|
|
638
647
|
|
|
639
|
-
it
|
|
648
|
+
it "raises an error when the foreign key is mismatched" do
|
|
640
649
|
error = assert_raises(ActiveRecord::MismatchedForeignKey) do
|
|
641
650
|
@conn.add_reference :engines, :old_car
|
|
642
651
|
@conn.add_foreign_key :engines, :old_cars
|
|
643
652
|
end
|
|
644
653
|
|
|
645
654
|
assert_match(
|
|
646
|
-
%r
|
|
655
|
+
%r{Column 'old_cars\.id' is not the same data type as referencing column 'engines\.old_car_id' in foreign key '.*'},
|
|
647
656
|
error.message
|
|
648
657
|
)
|
|
649
658
|
assert_not_nil error.cause
|
|
650
659
|
assert_equal @conn.pool, error.connection_pool
|
|
651
660
|
ensure
|
|
652
|
-
|
|
661
|
+
begin
|
|
662
|
+
@conn.execute("ALTER TABLE engines DROP COLUMN old_car_id")
|
|
663
|
+
rescue
|
|
664
|
+
nil
|
|
665
|
+
end
|
|
653
666
|
end
|
|
654
667
|
end
|
|
655
668
|
|
|
656
669
|
describe "placeholder conditions" do
|
|
657
|
-
it
|
|
670
|
+
it "using time placeholder" do
|
|
658
671
|
assert_equal Task.where("starting < ?", Time.now).count, 1
|
|
659
672
|
end
|
|
660
673
|
|
|
661
|
-
it
|
|
674
|
+
it "using date placeholder" do
|
|
662
675
|
assert_equal Task.where("starting < ?", Date.today).count, 1
|
|
663
676
|
end
|
|
664
677
|
|
|
665
|
-
it
|
|
678
|
+
it "using date-time placeholder" do
|
|
666
679
|
assert_equal Task.where("starting < ?", DateTime.current).count, 1
|
|
667
680
|
end
|
|
668
681
|
end
|