activerecord-jdbcsqlserver-adapter 51.1.0 → 52.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +22 -39
- data/{Dockerfile → Dockerfile.ci} +0 -0
- data/Gemfile +1 -3
- data/README.md +5 -8
- data/VERSION +1 -1
- data/activerecord-jdbcsqlserver-adapter.gemspec +2 -3
- data/docker-compose.ci.yml +7 -5
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +25 -29
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +14 -18
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +43 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +13 -2
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +53 -10
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +5 -13
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +2 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +43 -27
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +7 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +20 -14
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +3 -1
- data/lib/activerecord-jdbcsqlserver-adapter.rb +3 -0
- data/lib/arel/visitors/sqlserver.rb +1 -1
- data/lib/arel_sqlserver.rb +0 -1
- data/test/bin/install-freetds.sh +18 -0
- data/test/cases/adapter_test_sqlserver.rb +29 -21
- data/test/cases/change_column_null_test_sqlserver.rb +42 -0
- data/test/cases/coerced_tests.rb +304 -30
- data/test/cases/column_test_sqlserver.rb +496 -462
- data/test/cases/connection_test_sqlserver.rb +2 -2
- data/test/cases/fetch_test_sqlserver.rb +5 -5
- data/test/cases/helper_sqlserver.rb +6 -0
- data/test/cases/json_test_sqlserver.rb +6 -6
- data/test/cases/migration_test_sqlserver.rb +13 -3
- data/test/cases/order_test_sqlserver.rb +19 -19
- data/test/cases/pessimistic_locking_test_sqlserver.rb +9 -9
- data/test/cases/rake_test_sqlserver.rb +20 -20
- data/test/cases/schema_dumper_test_sqlserver.rb +34 -33
- data/test/cases/schema_test_sqlserver.rb +2 -2
- data/test/cases/showplan_test_sqlserver.rb +25 -10
- data/test/cases/specific_schema_test_sqlserver.rb +11 -11
- data/test/cases/transaction_test_sqlserver.rb +9 -9
- data/test/cases/trigger_test_sqlserver.rb +8 -8
- data/test/cases/utils_test_sqlserver.rb +36 -36
- data/test/cases/uuid_test_sqlserver.rb +8 -8
- data/test/migrations/create_clients_and_change_column_null.rb +23 -0
- data/test/schema/datatypes/2012.sql +1 -0
- data/test/schema/sqlserver_specific_schema.rb +9 -1
- data/test/support/core_ext/query_cache.rb +29 -0
- metadata +19 -10
- data/BACKERS.md +0 -32
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
require 'migrations/create_clients_and_change_column_null'
|
3
|
+
|
4
|
+
class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase
|
5
|
+
before do
|
6
|
+
@old_verbose = ActiveRecord::Migration.verbose
|
7
|
+
ActiveRecord::Migration.verbose = false
|
8
|
+
CreateClientsAndChangeColumnNull.new.up
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
CreateClientsAndChangeColumnNull.new.down
|
13
|
+
ActiveRecord::Migration.verbose = @old_verbose
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_column(table, name)
|
17
|
+
table.find { |column| column.name == name }
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:clients_table) { connection.columns('clients') }
|
21
|
+
let(:name_column) { find_column(clients_table, 'name') }
|
22
|
+
let(:code_column) { find_column(clients_table, 'code') }
|
23
|
+
let(:value_column) { find_column(clients_table, 'value') }
|
24
|
+
|
25
|
+
describe '#change_column_null' do
|
26
|
+
it 'does not change the column limit' do
|
27
|
+
_(name_column.limit).must_equal 15
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'does not change the column default' do
|
31
|
+
_(code_column.default).must_equal 'n/a'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not change the column precision' do
|
35
|
+
_(value_column.precision).must_equal 32
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not change the column scale' do
|
39
|
+
_(value_column.scale).must_equal 8
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/test/cases/coerced_tests.rb
CHANGED
@@ -33,6 +33,7 @@ module ActiveRecord
|
|
33
33
|
class AdapterTest < ActiveRecord::TestCase
|
34
34
|
# I really dont think we can support legacy binds.
|
35
35
|
coerce_tests! :test_select_all_with_legacy_binds
|
36
|
+
coerce_tests! :test_insert_update_delete_with_legacy_binds
|
36
37
|
|
37
38
|
# As far as I can tell, SQL Server does not support null bytes in strings.
|
38
39
|
coerce_tests! :test_update_prepared_statement
|
@@ -72,7 +73,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
|
72
73
|
end
|
73
74
|
|
74
75
|
|
75
|
-
|
76
|
+
class NumericDataTest < ActiveRecord::TestCase
|
77
|
+
# We do not have do the DecimalWithoutScale type.
|
78
|
+
coerce_tests! :test_numeric_fields
|
79
|
+
coerce_tests! :test_numeric_fields_with_scale
|
80
|
+
end
|
76
81
|
|
77
82
|
class BasicsTest < ActiveRecord::TestCase
|
78
83
|
coerce_tests! :test_column_names_are_escaped
|
@@ -81,10 +86,6 @@ class BasicsTest < ActiveRecord::TestCase
|
|
81
86
|
assert_equal '[t]]]', conn.quote_column_name('t]')
|
82
87
|
end
|
83
88
|
|
84
|
-
# We do not have do the DecimalWithoutScale type.
|
85
|
-
coerce_tests! :test_numeric_fields
|
86
|
-
coerce_tests! :test_numeric_fields_with_scale
|
87
|
-
|
88
89
|
# Just like PostgreSQLAdapter does.
|
89
90
|
coerce_tests! :test_respect_internal_encoding
|
90
91
|
|
@@ -114,13 +115,14 @@ class BasicsTest < ActiveRecord::TestCase
|
|
114
115
|
end
|
115
116
|
end
|
116
117
|
|
118
|
+
# Need to escape `quoted_id` once it contains brackets
|
117
119
|
coerce_tests! %r{column names are quoted when using #from clause and model has ignored columns}
|
118
|
-
|
120
|
+
test "column names are quoted when using #from clause and model has ignored columns coerced" do
|
119
121
|
refute_empty Developer.ignored_columns
|
120
122
|
query = Developer.from("developers").to_sql
|
121
123
|
quoted_id = "#{Developer.quoted_table_name}.#{Developer.quoted_primary_key}"
|
122
124
|
|
123
|
-
assert_match(/SELECT #{Regexp.
|
125
|
+
assert_match(/SELECT #{Regexp.escape(quoted_id)}.* FROM developers/, query)
|
124
126
|
end
|
125
127
|
end
|
126
128
|
|
@@ -142,16 +144,53 @@ end
|
|
142
144
|
|
143
145
|
|
144
146
|
|
147
|
+
# module ActiveRecord
|
148
|
+
# class BindParameterTest < ActiveRecord::TestCase
|
149
|
+
# # Same as original coerced test except log is found using `EXEC sp_executesql` wrapper.
|
150
|
+
# # coerce_tests! :test_binds_are_logged
|
151
|
+
# # def test_binds_are_logged_coerced
|
152
|
+
# # sub = Arel::Nodes::BindParam.new(1)
|
153
|
+
# # binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)]
|
154
|
+
# # sql = "select * from topics where id = #{sub.to_sql}"
|
155
|
+
# #
|
156
|
+
# # @connection.exec_query(sql, "SQL", binds)
|
157
|
+
# #
|
158
|
+
# # logged_sql = "EXEC sp_executesql N'#{sql}', N'#{sub.to_sql} int', #{sub.to_sql} = 1"
|
159
|
+
# # message = @subscriber.calls.find { |args| args[4][:sql] == logged_sql }
|
160
|
+
# #
|
161
|
+
# # assert_equal binds, message[4][:binds]
|
162
|
+
# # end
|
163
|
+
# #
|
164
|
+
# # # SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`.
|
165
|
+
# # coerce_tests! :test_statement_cache
|
166
|
+
# # coerce_tests! :test_statement_cache_with_query_cache
|
167
|
+
# # coerce_tests! :test_statement_cache_with_find_by
|
168
|
+
# # coerce_tests! :test_statement_cache_with_in_clause
|
169
|
+
# # coerce_tests! :test_statement_cache_with_sql_string_literal
|
170
|
+
# end
|
171
|
+
# end
|
172
|
+
|
173
|
+
|
145
174
|
module ActiveRecord
|
146
|
-
class
|
147
|
-
#
|
148
|
-
coerce_tests! :
|
175
|
+
class InstrumentationTest < ActiveRecord::TestCase
|
176
|
+
# This fails randomly due to schema cache being lost?
|
177
|
+
coerce_tests! :test_payload_name_on_load
|
178
|
+
def test_payload_name_on_load_coerced
|
179
|
+
Book.create(name: "test book")
|
180
|
+
Book.first
|
181
|
+
subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
182
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
183
|
+
if event.payload[:sql].match "SELECT"
|
184
|
+
assert_equal "Book Load", event.payload[:name]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
Book.first
|
188
|
+
ensure
|
189
|
+
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
|
190
|
+
end
|
149
191
|
end
|
150
192
|
end
|
151
193
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
194
|
class CalculationsTest < ActiveRecord::TestCase
|
156
195
|
# This fails randomly due to schema cache being lost?
|
157
196
|
coerce_tests! :test_offset_is_kept
|
@@ -173,28 +212,36 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
173
212
|
def test_limit_is_kept_coerced
|
174
213
|
queries = capture_sql_ss { Account.limit(1).count }
|
175
214
|
assert_equal 1, queries.length
|
176
|
-
queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1}
|
215
|
+
_(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1}
|
177
216
|
end unless defined? JRUBY_VERSION
|
178
217
|
|
179
218
|
def test_limit_is_kept_coerced
|
180
219
|
queries = capture_sql_ss { Account.limit(1).count }
|
181
220
|
assert_equal 1, queries.length
|
182
|
-
queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT \? ROWS ONLY}
|
221
|
+
_(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT \? ROWS ONLY}
|
183
222
|
end if defined? JRUBY_VERSION
|
184
223
|
|
185
224
|
coerce_tests! :test_limit_with_offset_is_kept
|
186
225
|
def test_limit_with_offset_is_kept_coerced
|
187
226
|
queries = capture_sql_ss { Account.limit(1).offset(1).count }
|
188
227
|
assert_equal 1, queries.length
|
189
|
-
queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1}
|
228
|
+
_(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1}
|
190
229
|
end unless defined? JRUBY_VERSION
|
191
230
|
|
192
231
|
def test_limit_with_offset_is_kept_coerced
|
193
232
|
queries = capture_sql_ss { Account.limit(1).offset(1).count }
|
194
233
|
assert_equal 1, queries.length
|
195
|
-
queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET \? ROWS FETCH NEXT \? ROWS ONLY}
|
234
|
+
_(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET \? ROWS FETCH NEXT \? ROWS ONLY}
|
196
235
|
end if defined? JRUBY_VERSION
|
197
236
|
|
237
|
+
# SQL Server needs an alias for the calculated column
|
238
|
+
coerce_tests! :test_distinct_count_all_with_custom_select_and_order
|
239
|
+
def test_distinct_count_all_with_custom_select_and_order_coerced
|
240
|
+
accounts = Account.distinct.select("credit_limit % 10 AS the_limit").order(Arel.sql("credit_limit % 10"))
|
241
|
+
assert_queries(1) { assert_equal 3, accounts.count(:all) }
|
242
|
+
assert_queries(1) { assert_equal 3, accounts.load.size }
|
243
|
+
end
|
244
|
+
|
198
245
|
# Leave it up to users to format selects/functions so HAVING works correctly.
|
199
246
|
coerce_tests! :test_having_with_strong_parameters
|
200
247
|
end
|
@@ -208,6 +255,7 @@ module ActiveRecord
|
|
208
255
|
coerce_tests! :test_create_table_with_bigint,
|
209
256
|
:test_create_table_with_defaults
|
210
257
|
end
|
258
|
+
|
211
259
|
class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase
|
212
260
|
# In SQL Server you have to delete the tables yourself in the right order.
|
213
261
|
coerce_tests! :test_create_table_with_force_cascade_drops_dependent_objects
|
@@ -249,7 +297,7 @@ module ActiveRecord
|
|
249
297
|
def test_add_column_without_limit_coerced
|
250
298
|
add_column :test_models, :description, :string, limit: nil
|
251
299
|
TestModel.reset_column_information
|
252
|
-
TestModel.columns_hash["description"].limit.must_equal 4000
|
300
|
+
_(TestModel.columns_hash["description"].limit).must_equal 4000
|
253
301
|
end
|
254
302
|
end
|
255
303
|
end
|
@@ -335,7 +383,7 @@ class MigrationTest < ActiveRecord::TestCase
|
|
335
383
|
end
|
336
384
|
|
337
385
|
# For some reason our tests set Rails.@_env which breaks test env switching.
|
338
|
-
coerce_tests! :
|
386
|
+
coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists
|
339
387
|
coerce_tests! :test_internal_metadata_stores_environment
|
340
388
|
end
|
341
389
|
|
@@ -361,6 +409,7 @@ module ActiveRecord
|
|
361
409
|
# a value of 'default_env' will still show tests failing. Just ignoring all
|
362
410
|
# of them since we have no monkey in this circus.
|
363
411
|
MergeAndResolveDefaultUrlConfigTest.coerce_all_tests! if defined?(MergeAndResolveDefaultUrlConfigTest)
|
412
|
+
ConnectionHandlerTest.coerce_all_tests! if defined?(ConnectionHandlerTest)
|
364
413
|
end
|
365
414
|
end
|
366
415
|
|
@@ -549,7 +598,7 @@ class InheritanceTest < ActiveRecord::TestCase
|
|
549
598
|
coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
|
550
599
|
def test_eager_load_belongs_to_primary_key_quoting_coerced
|
551
600
|
con = Account.connection
|
552
|
-
assert_sql(/\[companies\]\.\[id\] =
|
601
|
+
assert_sql(/\[companies\]\.\[id\] = \?/) do
|
553
602
|
Account.all.merge!(:includes => :firm).find(1)
|
554
603
|
end
|
555
604
|
end
|
@@ -602,6 +651,7 @@ end
|
|
602
651
|
|
603
652
|
|
604
653
|
|
654
|
+
require 'models/parrot'
|
605
655
|
require 'models/topic'
|
606
656
|
class PersistenceTest < ActiveRecord::TestCase
|
607
657
|
# We can not UPDATE identity columns.
|
@@ -611,14 +661,14 @@ class PersistenceTest < ActiveRecord::TestCase
|
|
611
661
|
coerce_tests! :test_update_all_doesnt_ignore_order
|
612
662
|
def test_update_all_doesnt_ignore_order_coerced
|
613
663
|
david, mary = authors(:david), authors(:mary)
|
614
|
-
david.id.must_equal 1
|
615
|
-
mary.id.must_equal 2
|
616
|
-
david.name.wont_equal mary.name
|
664
|
+
_(david.id).must_equal 1
|
665
|
+
_(mary.id).must_equal 2
|
666
|
+
_(david.name).wont_equal mary.name
|
617
667
|
assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
|
618
668
|
Author.where('[id] > 1').order(:id).update_all(name: 'Test')
|
619
669
|
end
|
620
|
-
david.reload.name.must_equal 'David'
|
621
|
-
mary.reload.name.must_equal 'Test'
|
670
|
+
_(david.reload.name).must_equal 'David'
|
671
|
+
_(mary.reload.name).must_equal 'Test'
|
622
672
|
end
|
623
673
|
|
624
674
|
# We can not UPDATE identity columns.
|
@@ -644,6 +694,19 @@ class PersistenceTest < ActiveRecord::TestCase
|
|
644
694
|
# assert_nothing_raised { topic.reload }
|
645
695
|
# assert_equal topic.title, Topic.find(1234).title
|
646
696
|
end
|
697
|
+
|
698
|
+
coerce_tests! :test_delete_new_record
|
699
|
+
def test_delete_new_record_coerced
|
700
|
+
client = Client.new(name: "37signals")
|
701
|
+
client.delete
|
702
|
+
assert_predicate client, :frozen?
|
703
|
+
|
704
|
+
assert_not client.save
|
705
|
+
assert_raise(ActiveRecord::RecordNotSaved) { client.save! }
|
706
|
+
|
707
|
+
assert_predicate client, :frozen?
|
708
|
+
assert_raise(FrozenError) { client.name = "something else" } # For some reason we get a FrozenError instead of a RuntimeError here
|
709
|
+
end
|
647
710
|
end
|
648
711
|
|
649
712
|
|
@@ -678,8 +741,8 @@ end
|
|
678
741
|
|
679
742
|
require 'models/task'
|
680
743
|
class QueryCacheTest < ActiveRecord::TestCase
|
681
|
-
coerce_tests! :
|
682
|
-
def
|
744
|
+
coerce_tests! :test_cache_does_not_wrap_results_in_arrays
|
745
|
+
def test_cache_does_not_wrap_results_in_arrays_coerced
|
683
746
|
Task.cache do
|
684
747
|
assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
685
748
|
end
|
@@ -694,16 +757,16 @@ class RelationTest < ActiveRecord::TestCase
|
|
694
757
|
# Use LEN vs LENGTH function.
|
695
758
|
coerce_tests! :test_reverse_order_with_function
|
696
759
|
def test_reverse_order_with_function_coerced
|
697
|
-
topics = Topic.order("LEN(title)").reverse_order
|
760
|
+
topics = Topic.order(Arel.sql("LEN(title)")).reverse_order
|
698
761
|
assert_equal topics(:second).title, topics.first.title
|
699
762
|
end
|
700
763
|
|
701
764
|
# Use LEN vs LENGTH function.
|
702
765
|
coerce_tests! :test_reverse_order_with_function_other_predicates
|
703
766
|
def test_reverse_order_with_function_other_predicates_coerced
|
704
|
-
topics = Topic.order("author_name, LEN(title), id").reverse_order
|
767
|
+
topics = Topic.order(Arel.sql("author_name, LEN(title), id")).reverse_order
|
705
768
|
assert_equal topics(:second).title, topics.first.title
|
706
|
-
topics = Topic.order("LEN(author_name), id, LEN(title)").reverse_order
|
769
|
+
topics = Topic.order(Arel.sql("LEN(author_name), id, LEN(title)")).reverse_order
|
707
770
|
assert_equal topics(:fifth).title, topics.first.title
|
708
771
|
end
|
709
772
|
|
@@ -733,6 +796,22 @@ class RelationTest < ActiveRecord::TestCase
|
|
733
796
|
# so we are skipping all together.
|
734
797
|
coerce_tests! :test_empty_complex_chained_relations
|
735
798
|
|
799
|
+
# Can't apply offset withour ORDER
|
800
|
+
coerce_tests! %r{using a custom table affects the wheres}
|
801
|
+
test 'using a custom table affects the wheres coerced' do
|
802
|
+
post = posts(:welcome)
|
803
|
+
|
804
|
+
assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take
|
805
|
+
end
|
806
|
+
|
807
|
+
# Can't apply offset withour ORDER
|
808
|
+
coerce_tests! %r{using a custom table with joins affects the joins}
|
809
|
+
test 'using a custom table with joins affects the joins coerced' do
|
810
|
+
post = posts(:welcome)
|
811
|
+
|
812
|
+
assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
|
813
|
+
end
|
814
|
+
|
736
815
|
# Use LEN() vs length() function.
|
737
816
|
coerce_tests! :test_reverse_arel_assoc_order_with_function
|
738
817
|
def test_reverse_arel_assoc_order_with_function_coerced
|
@@ -741,6 +820,24 @@ class RelationTest < ActiveRecord::TestCase
|
|
741
820
|
end
|
742
821
|
end
|
743
822
|
|
823
|
+
class ActiveRecord::RelationTest < ActiveRecord::TestCase
|
824
|
+
coerce_tests! :test_relation_merging_with_merged_symbol_joins_is_aliased
|
825
|
+
def test_relation_merging_with_merged_symbol_joins_is_aliased__coerced
|
826
|
+
categorizations_with_authors = Categorization.joins(:author)
|
827
|
+
queries = capture_sql { Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(categorizations_with_authors).to_a }
|
828
|
+
|
829
|
+
nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size }
|
830
|
+
assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query"
|
831
|
+
|
832
|
+
# using `\W` as the column separator
|
833
|
+
query_matches = queries.any? do |sql|
|
834
|
+
%r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql)
|
835
|
+
end
|
836
|
+
|
837
|
+
assert query_matches, "Should be aliasing the child INNER JOINs in query"
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
744
841
|
|
745
842
|
|
746
843
|
|
@@ -893,10 +990,55 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
|
|
893
990
|
end
|
894
991
|
end
|
895
992
|
end
|
993
|
+
|
994
|
+
unless defined? JRUBY_VERSION
|
995
|
+
# datetime is rounded to increments of .000, .003, or .007 seconds
|
996
|
+
coerce_tests! :test_datetime_precision_is_truncated_on_assignment
|
997
|
+
def test_datetime_precision_is_truncated_on_assignment_coerced
|
998
|
+
@connection.create_table(:foos, force: true)
|
999
|
+
@connection.add_column :foos, :created_at, :datetime, precision: 0
|
1000
|
+
@connection.add_column :foos, :updated_at, :datetime, precision: 6
|
1001
|
+
|
1002
|
+
time = ::Time.now.change(nsec: 123456789)
|
1003
|
+
foo = Foo.new(created_at: time, updated_at: time)
|
1004
|
+
|
1005
|
+
assert_equal 0, foo.created_at.nsec
|
1006
|
+
assert_equal 123457000, foo.updated_at.nsec
|
1007
|
+
|
1008
|
+
foo.save!
|
1009
|
+
foo.reload
|
1010
|
+
|
1011
|
+
assert_equal 0, foo.created_at.nsec
|
1012
|
+
assert_equal 123457000, foo.updated_at.nsec
|
1013
|
+
end
|
1014
|
+
end
|
896
1015
|
end
|
897
1016
|
|
898
1017
|
|
899
1018
|
|
1019
|
+
class TimePrecisionTest < ActiveRecord::TestCase
|
1020
|
+
# datetime is rounded to increments of .000, .003, or .007 seconds
|
1021
|
+
coerce_tests! :test_time_precision_is_truncated_on_assignment
|
1022
|
+
def test_time_precision_is_truncated_on_assignment_coerced
|
1023
|
+
@connection.create_table(:foos, force: true)
|
1024
|
+
@connection.add_column :foos, :start, :time, precision: 0
|
1025
|
+
@connection.add_column :foos, :finish, :time, precision: 6
|
1026
|
+
|
1027
|
+
time = ::Time.now.change(nsec: 123456789)
|
1028
|
+
foo = Foo.new(start: time, finish: time)
|
1029
|
+
|
1030
|
+
assert_equal 0, foo.start.nsec
|
1031
|
+
assert_equal 123457000, foo.finish.nsec
|
1032
|
+
|
1033
|
+
foo.save!
|
1034
|
+
foo.reload
|
1035
|
+
|
1036
|
+
assert_equal 0, foo.start.nsec
|
1037
|
+
assert_equal 123457000, foo.finish.nsec
|
1038
|
+
end
|
1039
|
+
end unless defined? JRUBY_VERSION
|
1040
|
+
|
1041
|
+
|
900
1042
|
|
901
1043
|
class DefaultNumbersTest < ActiveRecord::TestCase
|
902
1044
|
# We do better with native types and do not return strings for everything.
|
@@ -949,3 +1091,135 @@ module ActiveRecord
|
|
949
1091
|
end
|
950
1092
|
end
|
951
1093
|
|
1094
|
+
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
1095
|
+
coerce_tests! %r{always allows Arel}
|
1096
|
+
test 'order: always allows Arel' do
|
1097
|
+
ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) }
|
1098
|
+
ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) }
|
1099
|
+
|
1100
|
+
assert_equal ids_depr, ids_disabled
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
test "pluck: always allows Arel" do
|
1104
|
+
values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
|
1105
|
+
values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
|
1106
|
+
|
1107
|
+
assert_equal values_depr, values_disabled
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
|
1111
|
+
coerce_tests! %r{order: disallows invalid Array arguments}
|
1112
|
+
test "order: disallows invalid Array arguments" do
|
1113
|
+
with_unsafe_raw_sql_disabled do
|
1114
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1115
|
+
Post.order(["author_id", "len(title)"]).pluck(:id)
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
coerce_tests! %r{order: allows valid Array arguments}
|
1121
|
+
test "order: allows valid Array arguments" do
|
1122
|
+
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
1123
|
+
|
1124
|
+
ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
|
1125
|
+
ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
|
1126
|
+
|
1127
|
+
assert_equal ids_expected, ids_depr
|
1128
|
+
assert_equal ids_expected, ids_disabled
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
coerce_tests! %r{order: logs deprecation warning for unrecognized column}
|
1132
|
+
test "order: logs deprecation warning for unrecognized column" do
|
1133
|
+
with_unsafe_raw_sql_deprecated do
|
1134
|
+
assert_deprecated(/Dangerous query method/) do
|
1135
|
+
Post.order("len(title)")
|
1136
|
+
end
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
coerce_tests! %r{pluck: disallows invalid column name}
|
1141
|
+
test "pluck: disallows invalid column name" do
|
1142
|
+
with_unsafe_raw_sql_disabled do
|
1143
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1144
|
+
Post.pluck("len(title)")
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
coerce_tests! %r{pluck: disallows invalid column name amongst valid names}
|
1150
|
+
test "pluck: disallows invalid column name amongst valid names" do
|
1151
|
+
with_unsafe_raw_sql_disabled do
|
1152
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1153
|
+
Post.pluck(:title, "len(title)")
|
1154
|
+
end
|
1155
|
+
end
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
coerce_tests! %r{pluck: disallows invalid column names with includes}
|
1159
|
+
test "pluck: disallows invalid column names with includes" do
|
1160
|
+
with_unsafe_raw_sql_disabled do
|
1161
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1162
|
+
Post.includes(:comments).pluck(:title, "len(title)")
|
1163
|
+
end
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
coerce_tests! %r{pluck: logs deprecation warning}
|
1168
|
+
test "pluck: logs deprecation warning" do
|
1169
|
+
with_unsafe_raw_sql_deprecated do
|
1170
|
+
assert_deprecated(/Dangerous query method/) do
|
1171
|
+
Post.includes(:comments).pluck(:title, "len(title)")
|
1172
|
+
end
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
|
1178
|
+
class ReservedWordTest < ActiveRecord::TestCase
|
1179
|
+
coerce_tests! :test_change_columns
|
1180
|
+
def test_change_columns_coerced
|
1181
|
+
assert_nothing_raised { @connection.change_column_default(:group, :order, "whatever") }
|
1182
|
+
assert_nothing_raised { @connection.change_column("group", "order", :text) }
|
1183
|
+
assert_nothing_raised { @connection.change_column_null("group", "order", true) }
|
1184
|
+
assert_nothing_raised { @connection.rename_column(:group, :order, :values) }
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
|
1189
|
+
|
1190
|
+
class OptimisticLockingTest < ActiveRecord::TestCase
|
1191
|
+
# We do not allow updating identities, but we can test using a non-identity key
|
1192
|
+
coerce_tests! :test_update_with_dirty_primary_key
|
1193
|
+
def test_update_with_dirty_primary_key_coerced
|
1194
|
+
assert_raises(ActiveRecord::RecordNotUnique) do
|
1195
|
+
record = StringKeyObject.find('record1')
|
1196
|
+
record.id = 'record2'
|
1197
|
+
record.save!
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
record = StringKeyObject.find('record1')
|
1201
|
+
record.id = 'record42'
|
1202
|
+
record.save!
|
1203
|
+
|
1204
|
+
assert StringKeyObject.find('record42')
|
1205
|
+
assert_raises(ActiveRecord::RecordNotFound) do
|
1206
|
+
StringKeyObject.find('record1')
|
1207
|
+
end
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
|
1212
|
+
|
1213
|
+
class RelationMergingTest < ActiveRecord::TestCase
|
1214
|
+
coerce_tests! :test_merging_with_order_with_binds
|
1215
|
+
def test_merging_with_order_with_binds_coerced
|
1216
|
+
relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
|
1217
|
+
assert_equal ["title LIKE N'%suffix'"], relation.order_values
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
|
1222
|
+
class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
1223
|
+
# Temporarily coerce this test due to https://github.com/rails/rails/issues/34945
|
1224
|
+
coerce_tests! :test_eager_loading_too_may_ids
|
1225
|
+
end
|