activerecord-sqlserver-adapter 5.1.6 → 5.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +16 -19
- data/CHANGELOG.md +6 -46
- data/Dockerfile +14 -0
- data/Gemfile +1 -2
- data/README.md +4 -4
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/circle.yml +8 -6
- data/docker-compose.ci.yml +11 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +11 -18
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +13 -2
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +45 -9
- 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 +33 -14
- 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_adapter.rb +14 -12
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +3 -1
- data/lib/arel/visitors/sqlserver.rb +1 -1
- data/lib/arel_sqlserver.rb +0 -1
- data/test/bin/install-freetds.sh +1 -1
- data/test/cases/adapter_test_sqlserver.rb +10 -2
- data/test/cases/coerced_tests.rb +262 -13
- data/test/cases/column_test_sqlserver.rb +6 -0
- data/test/cases/helper_sqlserver.rb +2 -1
- data/test/cases/migration_test_sqlserver.rb +3 -1
- data/test/cases/order_test_sqlserver.rb +19 -19
- data/test/schema/sqlserver_specific_schema.rb +9 -1
- data/test/support/core_ext/query_cache.rb +29 -0
- metadata +11 -9
- data/test/bin/.keep +0 -0
@@ -26,13 +26,13 @@ module ActiveRecord
|
|
26
26
|
|
27
27
|
end
|
28
28
|
|
29
|
-
Transaction.send :
|
29
|
+
Transaction.send :prepend, SQLServerTransaction
|
30
30
|
|
31
31
|
module SQLServerRealTransaction
|
32
32
|
|
33
33
|
attr_reader :starting_isolation_level
|
34
34
|
|
35
|
-
def initialize(connection, options,
|
35
|
+
def initialize(connection, options, *args)
|
36
36
|
@connection = connection
|
37
37
|
@starting_isolation_level = current_isolation_level if options[:isolation]
|
38
38
|
super
|
@@ -58,7 +58,6 @@ module ActiveRecord
|
|
58
58
|
|
59
59
|
end
|
60
60
|
|
61
|
-
RealTransaction.send :
|
62
|
-
|
61
|
+
RealTransaction.send :prepend, SQLServerRealTransaction
|
63
62
|
end
|
64
63
|
end
|
@@ -34,7 +34,6 @@ module ActiveRecord
|
|
34
34
|
SQLServer::Quoting,
|
35
35
|
SQLServer::DatabaseStatements,
|
36
36
|
SQLServer::Showplan,
|
37
|
-
SQLServer::SchemaDumper,
|
38
37
|
SQLServer::SchemaStatements,
|
39
38
|
SQLServer::DatabaseLimits,
|
40
39
|
SQLServer::DatabaseTasks
|
@@ -136,6 +135,10 @@ module ActiveRecord
|
|
136
135
|
false
|
137
136
|
end
|
138
137
|
|
138
|
+
def supports_savepoints?
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
139
142
|
def supports_in_memory_oltp?
|
140
143
|
@version_year >= 2014
|
141
144
|
end
|
@@ -189,7 +192,7 @@ module ActiveRecord
|
|
189
192
|
|
190
193
|
def tables_with_referential_integrity
|
191
194
|
schemas_and_tables = select_rows <<-SQL.strip_heredoc
|
192
|
-
SELECT s.name, o.name
|
195
|
+
SELECT DISTINCT s.name, o.name
|
193
196
|
FROM sys.foreign_keys i
|
194
197
|
INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
|
195
198
|
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
@@ -212,7 +215,7 @@ module ActiveRecord
|
|
212
215
|
end
|
213
216
|
|
214
217
|
def sqlserver_azure?
|
215
|
-
|
218
|
+
!!(sqlserver_version =~ /Azure/i)
|
216
219
|
end
|
217
220
|
|
218
221
|
def database_prefix_remote_server?
|
@@ -253,7 +256,7 @@ module ActiveRecord
|
|
253
256
|
|
254
257
|
# === Abstract Adapter (Misc Support) =========================== #
|
255
258
|
|
256
|
-
def initialize_type_map(m)
|
259
|
+
def initialize_type_map(m = type_map)
|
257
260
|
m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
|
258
261
|
# Exact Numerics
|
259
262
|
register_class_with_limit m, 'bigint(8)', SQLServer::Type::BigInteger
|
@@ -435,16 +438,15 @@ module ActiveRecord
|
|
435
438
|
end
|
436
439
|
|
437
440
|
def version_year
|
438
|
-
return
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
/SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i
|
443
|
-
rescue Exception => e
|
444
|
-
2016
|
445
|
-
end
|
441
|
+
return 2016 if sqlserver_version =~ /vNext/
|
442
|
+
/SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i
|
443
|
+
rescue StandardError => e
|
444
|
+
2016
|
446
445
|
end
|
447
446
|
|
447
|
+
def sqlserver_version
|
448
|
+
@sqlserver_version ||= _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
|
449
|
+
end
|
448
450
|
end
|
449
451
|
end
|
450
452
|
end
|
@@ -49,9 +49,11 @@ module ActiveRecord
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def structure_dump(filename, extra_flags)
|
52
|
+
server_arg = "-S #{Shellwords.escape(configuration['host'])}"
|
53
|
+
server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration['port']
|
52
54
|
command = [
|
53
55
|
"defncopy-ttds",
|
54
|
-
|
56
|
+
server_arg,
|
55
57
|
"-D #{Shellwords.escape(configuration['database'])}",
|
56
58
|
"-U #{Shellwords.escape(configuration['username'])}",
|
57
59
|
"-P #{Shellwords.escape(configuration['password'])}",
|
data/lib/arel_sqlserver.rb
CHANGED
data/test/bin/install-freetds.sh
CHANGED
@@ -5,7 +5,7 @@ set -e
|
|
5
5
|
|
6
6
|
FREETDS_VERSION=1.00.21
|
7
7
|
|
8
|
-
wget
|
8
|
+
wget http://www.freetds.org/files/stable/freetds-$FREETDS_VERSION.tar.gz
|
9
9
|
tar -xzf freetds-$FREETDS_VERSION.tar.gz
|
10
10
|
cd freetds-$FREETDS_VERSION
|
11
11
|
./configure --prefix=/opt/local \
|
@@ -231,6 +231,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
231
231
|
end
|
232
232
|
end
|
233
233
|
|
234
|
+
it 'not disable referential integrity for the same table twice' do
|
235
|
+
tables = SSTestHasPk.connection.tables_with_referential_integrity
|
236
|
+
assert_equal tables.size, tables.uniq.size
|
237
|
+
end
|
238
|
+
|
234
239
|
end
|
235
240
|
|
236
241
|
describe 'database statements' do
|
@@ -268,9 +273,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
268
273
|
assert_equal 'integer', connection.type_to_sql(:integer, limit: 3)
|
269
274
|
end
|
270
275
|
|
271
|
-
it 'create smallints when limit is
|
276
|
+
it 'create smallints when limit is 2' do
|
272
277
|
assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2)
|
273
|
-
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'create tinyints when limit is 1' do
|
281
|
+
assert_equal 'tinyint', connection.type_to_sql(:integer, limit: 1)
|
274
282
|
end
|
275
283
|
|
276
284
|
it 'create bigints when limit is greateer than 4' do
|
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
|
@@ -71,7 +72,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
|
71
72
|
end
|
72
73
|
|
73
74
|
|
74
|
-
|
75
|
+
class NumericDataTest < ActiveRecord::TestCase
|
76
|
+
# We do not have do the DecimalWithoutScale type.
|
77
|
+
coerce_tests! :test_numeric_fields
|
78
|
+
coerce_tests! :test_numeric_fields_with_scale
|
79
|
+
end
|
75
80
|
|
76
81
|
class BasicsTest < ActiveRecord::TestCase
|
77
82
|
coerce_tests! :test_column_names_are_escaped
|
@@ -80,10 +85,6 @@ class BasicsTest < ActiveRecord::TestCase
|
|
80
85
|
assert_equal '[t]]]', conn.quote_column_name('t]')
|
81
86
|
end
|
82
87
|
|
83
|
-
# We do not have do the DecimalWithoutScale type.
|
84
|
-
coerce_tests! :test_numeric_fields
|
85
|
-
coerce_tests! :test_numeric_fields_with_scale
|
86
|
-
|
87
88
|
# Just like PostgreSQLAdapter does.
|
88
89
|
coerce_tests! :test_respect_internal_encoding
|
89
90
|
|
@@ -112,6 +113,16 @@ class BasicsTest < ActiveRecord::TestCase
|
|
112
113
|
end
|
113
114
|
end
|
114
115
|
end
|
116
|
+
|
117
|
+
# Need to escape `quoted_id` once it contains brackets
|
118
|
+
coerce_tests! %r{column names are quoted when using #from clause and model has ignored columns}
|
119
|
+
test "column names are quoted when using #from clause and model has ignored columns coerced" do
|
120
|
+
refute_empty Developer.ignored_columns
|
121
|
+
query = Developer.from("developers").to_sql
|
122
|
+
quoted_id = "#{Developer.quoted_table_name}.#{Developer.quoted_primary_key}"
|
123
|
+
|
124
|
+
assert_match(/SELECT #{Regexp.escape(quoted_id)}.* FROM developers/, query)
|
125
|
+
end
|
115
126
|
end
|
116
127
|
|
117
128
|
|
@@ -140,7 +151,25 @@ module ActiveRecord
|
|
140
151
|
end
|
141
152
|
|
142
153
|
|
143
|
-
|
154
|
+
module ActiveRecord
|
155
|
+
class InstrumentationTest < ActiveRecord::TestCase
|
156
|
+
# This fails randomly due to schema cache being lost?
|
157
|
+
coerce_tests! :test_payload_name_on_load
|
158
|
+
def test_payload_name_on_load_coerced
|
159
|
+
Book.create(name: "test book")
|
160
|
+
Book.first
|
161
|
+
subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
162
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
163
|
+
if event.payload[:sql].match "SELECT"
|
164
|
+
assert_equal "Book Load", event.payload[:name]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
Book.first
|
168
|
+
ensure
|
169
|
+
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
144
173
|
|
145
174
|
class CalculationsTest < ActiveRecord::TestCase
|
146
175
|
# This fails randomly due to schema cache being lost?
|
@@ -173,6 +202,14 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
173
202
|
queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1}
|
174
203
|
end
|
175
204
|
|
205
|
+
# SQL Server needs an alias for the calculated column
|
206
|
+
coerce_tests! :test_distinct_count_all_with_custom_select_and_order
|
207
|
+
def test_distinct_count_all_with_custom_select_and_order_coerced
|
208
|
+
accounts = Account.distinct.select("credit_limit % 10 AS the_limit").order(Arel.sql("credit_limit % 10"))
|
209
|
+
assert_queries(1) { assert_equal 3, accounts.count(:all) }
|
210
|
+
assert_queries(1) { assert_equal 3, accounts.load.size }
|
211
|
+
end
|
212
|
+
|
176
213
|
# Leave it up to users to format selects/functions so HAVING works correctly.
|
177
214
|
coerce_tests! :test_having_with_strong_parameters
|
178
215
|
end
|
@@ -186,6 +223,7 @@ module ActiveRecord
|
|
186
223
|
coerce_tests! :test_create_table_with_bigint,
|
187
224
|
:test_create_table_with_defaults
|
188
225
|
end
|
226
|
+
|
189
227
|
class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase
|
190
228
|
# In SQL Server you have to delete the tables yourself in the right order.
|
191
229
|
coerce_tests! :test_create_table_with_force_cascade_drops_dependent_objects
|
@@ -313,7 +351,7 @@ class MigrationTest < ActiveRecord::TestCase
|
|
313
351
|
end
|
314
352
|
|
315
353
|
# For some reason our tests set Rails.@_env which breaks test env switching.
|
316
|
-
coerce_tests! :
|
354
|
+
coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists
|
317
355
|
coerce_tests! :test_internal_metadata_stores_environment
|
318
356
|
end
|
319
357
|
|
@@ -339,6 +377,7 @@ module ActiveRecord
|
|
339
377
|
# a value of 'default_env' will still show tests failing. Just ignoring all
|
340
378
|
# of them since we have no monkey in this circus.
|
341
379
|
MergeAndResolveDefaultUrlConfigTest.coerce_all_tests! if defined?(MergeAndResolveDefaultUrlConfigTest)
|
380
|
+
ConnectionHandlerTest.coerce_all_tests! if defined?(ConnectionHandlerTest)
|
342
381
|
end
|
343
382
|
end
|
344
383
|
|
@@ -513,7 +552,7 @@ class InheritanceTest < ActiveRecord::TestCase
|
|
513
552
|
coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
|
514
553
|
def test_eager_load_belongs_to_primary_key_quoting_coerced
|
515
554
|
con = Account.connection
|
516
|
-
assert_sql(/\[companies\]\.\[id\] = 1/) do
|
555
|
+
assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do
|
517
556
|
Account.all.merge!(:includes => :firm).find(1)
|
518
557
|
end
|
519
558
|
end
|
@@ -566,6 +605,7 @@ end
|
|
566
605
|
|
567
606
|
|
568
607
|
|
608
|
+
require 'models/parrot'
|
569
609
|
require 'models/topic'
|
570
610
|
class PersistenceTest < ActiveRecord::TestCase
|
571
611
|
# We can not UPDATE identity columns.
|
@@ -642,8 +682,8 @@ end
|
|
642
682
|
|
643
683
|
require 'models/task'
|
644
684
|
class QueryCacheTest < ActiveRecord::TestCase
|
645
|
-
coerce_tests! :
|
646
|
-
def
|
685
|
+
coerce_tests! :test_cache_does_not_wrap_results_in_arrays
|
686
|
+
def test_cache_does_not_wrap_results_in_arrays_coerced
|
647
687
|
Task.cache do
|
648
688
|
assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
649
689
|
end
|
@@ -658,16 +698,16 @@ class RelationTest < ActiveRecord::TestCase
|
|
658
698
|
# Use LEN vs LENGTH function.
|
659
699
|
coerce_tests! :test_reverse_order_with_function
|
660
700
|
def test_reverse_order_with_function_coerced
|
661
|
-
topics = Topic.order("LEN(title)").reverse_order
|
701
|
+
topics = Topic.order(Arel.sql("LEN(title)")).reverse_order
|
662
702
|
assert_equal topics(:second).title, topics.first.title
|
663
703
|
end
|
664
704
|
|
665
705
|
# Use LEN vs LENGTH function.
|
666
706
|
coerce_tests! :test_reverse_order_with_function_other_predicates
|
667
707
|
def test_reverse_order_with_function_other_predicates_coerced
|
668
|
-
topics = Topic.order("author_name, LEN(title), id").reverse_order
|
708
|
+
topics = Topic.order(Arel.sql("author_name, LEN(title), id")).reverse_order
|
669
709
|
assert_equal topics(:second).title, topics.first.title
|
670
|
-
topics = Topic.order("LEN(author_name), id, LEN(title)").reverse_order
|
710
|
+
topics = Topic.order(Arel.sql("LEN(author_name), id, LEN(title)")).reverse_order
|
671
711
|
assert_equal topics(:fifth).title, topics.first.title
|
672
712
|
end
|
673
713
|
|
@@ -697,6 +737,22 @@ class RelationTest < ActiveRecord::TestCase
|
|
697
737
|
# so we are skipping all together.
|
698
738
|
coerce_tests! :test_empty_complex_chained_relations
|
699
739
|
|
740
|
+
# Can't apply offset withour ORDER
|
741
|
+
coerce_tests! %r{using a custom table affects the wheres}
|
742
|
+
test 'using a custom table affects the wheres coerced' do
|
743
|
+
post = posts(:welcome)
|
744
|
+
|
745
|
+
assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take
|
746
|
+
end
|
747
|
+
|
748
|
+
# Can't apply offset withour ORDER
|
749
|
+
coerce_tests! %r{using a custom table with joins affects the joins}
|
750
|
+
test 'using a custom table with joins affects the joins coerced' do
|
751
|
+
post = posts(:welcome)
|
752
|
+
|
753
|
+
assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
|
754
|
+
end
|
755
|
+
|
700
756
|
# Use LEN() vs length() function.
|
701
757
|
coerce_tests! :test_reverse_arel_assoc_order_with_function
|
702
758
|
def test_reverse_arel_assoc_order_with_function_coerced
|
@@ -705,6 +761,24 @@ class RelationTest < ActiveRecord::TestCase
|
|
705
761
|
end
|
706
762
|
end
|
707
763
|
|
764
|
+
class ActiveRecord::RelationTest < ActiveRecord::TestCase
|
765
|
+
coerce_tests! :test_relation_merging_with_merged_symbol_joins_is_aliased
|
766
|
+
def test_relation_merging_with_merged_symbol_joins_is_aliased__coerced
|
767
|
+
categorizations_with_authors = Categorization.joins(:author)
|
768
|
+
queries = capture_sql { Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(categorizations_with_authors).to_a }
|
769
|
+
|
770
|
+
nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size }
|
771
|
+
assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query"
|
772
|
+
|
773
|
+
# using `\W` as the column separator
|
774
|
+
query_matches = queries.any? do |sql|
|
775
|
+
%r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql)
|
776
|
+
end
|
777
|
+
|
778
|
+
assert query_matches, "Should be aliasing the child INNER JOINs in query"
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
708
782
|
|
709
783
|
|
710
784
|
|
@@ -857,10 +931,53 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
|
|
857
931
|
end
|
858
932
|
end
|
859
933
|
end
|
934
|
+
|
935
|
+
# datetime is rounded to increments of .000, .003, or .007 seconds
|
936
|
+
coerce_tests! :test_datetime_precision_is_truncated_on_assignment
|
937
|
+
def test_datetime_precision_is_truncated_on_assignment_coerced
|
938
|
+
@connection.create_table(:foos, force: true)
|
939
|
+
@connection.add_column :foos, :created_at, :datetime, precision: 0
|
940
|
+
@connection.add_column :foos, :updated_at, :datetime, precision: 6
|
941
|
+
|
942
|
+
time = ::Time.now.change(nsec: 123456789)
|
943
|
+
foo = Foo.new(created_at: time, updated_at: time)
|
944
|
+
|
945
|
+
assert_equal 0, foo.created_at.nsec
|
946
|
+
assert_equal 123457000, foo.updated_at.nsec
|
947
|
+
|
948
|
+
foo.save!
|
949
|
+
foo.reload
|
950
|
+
|
951
|
+
assert_equal 0, foo.created_at.nsec
|
952
|
+
assert_equal 123457000, foo.updated_at.nsec
|
953
|
+
end
|
860
954
|
end
|
861
955
|
|
862
956
|
|
863
957
|
|
958
|
+
class TimePrecisionTest < ActiveRecord::TestCase
|
959
|
+
# datetime is rounded to increments of .000, .003, or .007 seconds
|
960
|
+
coerce_tests! :test_time_precision_is_truncated_on_assignment
|
961
|
+
def test_time_precision_is_truncated_on_assignment_coerced
|
962
|
+
@connection.create_table(:foos, force: true)
|
963
|
+
@connection.add_column :foos, :start, :time, precision: 0
|
964
|
+
@connection.add_column :foos, :finish, :time, precision: 6
|
965
|
+
|
966
|
+
time = ::Time.now.change(nsec: 123456789)
|
967
|
+
foo = Foo.new(start: time, finish: time)
|
968
|
+
|
969
|
+
assert_equal 0, foo.start.nsec
|
970
|
+
assert_equal 123457000, foo.finish.nsec
|
971
|
+
|
972
|
+
foo.save!
|
973
|
+
foo.reload
|
974
|
+
|
975
|
+
assert_equal 0, foo.start.nsec
|
976
|
+
assert_equal 123457000, foo.finish.nsec
|
977
|
+
end
|
978
|
+
end
|
979
|
+
|
980
|
+
|
864
981
|
|
865
982
|
class DefaultNumbersTest < ActiveRecord::TestCase
|
866
983
|
# We do better with native types and do not return strings for everything.
|
@@ -913,3 +1030,135 @@ module ActiveRecord
|
|
913
1030
|
end
|
914
1031
|
end
|
915
1032
|
|
1033
|
+
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
1034
|
+
coerce_tests! %r{always allows Arel}
|
1035
|
+
test 'order: always allows Arel' do
|
1036
|
+
ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) }
|
1037
|
+
ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) }
|
1038
|
+
|
1039
|
+
assert_equal ids_depr, ids_disabled
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
test "pluck: always allows Arel" do
|
1043
|
+
values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
|
1044
|
+
values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
|
1045
|
+
|
1046
|
+
assert_equal values_depr, values_disabled
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
|
1050
|
+
coerce_tests! %r{order: disallows invalid Array arguments}
|
1051
|
+
test "order: disallows invalid Array arguments" do
|
1052
|
+
with_unsafe_raw_sql_disabled do
|
1053
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1054
|
+
Post.order(["author_id", "len(title)"]).pluck(:id)
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
coerce_tests! %r{order: allows valid Array arguments}
|
1060
|
+
test "order: allows valid Array arguments" do
|
1061
|
+
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
1062
|
+
|
1063
|
+
ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
|
1064
|
+
ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
|
1065
|
+
|
1066
|
+
assert_equal ids_expected, ids_depr
|
1067
|
+
assert_equal ids_expected, ids_disabled
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
coerce_tests! %r{order: logs deprecation warning for unrecognized column}
|
1071
|
+
test "order: logs deprecation warning for unrecognized column" do
|
1072
|
+
with_unsafe_raw_sql_deprecated do
|
1073
|
+
assert_deprecated(/Dangerous query method/) do
|
1074
|
+
Post.order("len(title)")
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
coerce_tests! %r{pluck: disallows invalid column name}
|
1080
|
+
test "pluck: disallows invalid column name" do
|
1081
|
+
with_unsafe_raw_sql_disabled do
|
1082
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1083
|
+
Post.pluck("len(title)")
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
coerce_tests! %r{pluck: disallows invalid column name amongst valid names}
|
1089
|
+
test "pluck: disallows invalid column name amongst valid names" do
|
1090
|
+
with_unsafe_raw_sql_disabled do
|
1091
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1092
|
+
Post.pluck(:title, "len(title)")
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
coerce_tests! %r{pluck: disallows invalid column names with includes}
|
1098
|
+
test "pluck: disallows invalid column names with includes" do
|
1099
|
+
with_unsafe_raw_sql_disabled do
|
1100
|
+
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
1101
|
+
Post.includes(:comments).pluck(:title, "len(title)")
|
1102
|
+
end
|
1103
|
+
end
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
coerce_tests! %r{pluck: logs deprecation warning}
|
1107
|
+
test "pluck: logs deprecation warning" do
|
1108
|
+
with_unsafe_raw_sql_deprecated do
|
1109
|
+
assert_deprecated(/Dangerous query method/) do
|
1110
|
+
Post.includes(:comments).pluck(:title, "len(title)")
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
end
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
|
1117
|
+
class ReservedWordTest < ActiveRecord::TestCase
|
1118
|
+
coerce_tests! :test_change_columns
|
1119
|
+
def test_change_columns_coerced
|
1120
|
+
assert_nothing_raised { @connection.change_column_default(:group, :order, "whatever") }
|
1121
|
+
assert_nothing_raised { @connection.change_column("group", "order", :text) }
|
1122
|
+
assert_nothing_raised { @connection.change_column_null("group", "order", true) }
|
1123
|
+
assert_nothing_raised { @connection.rename_column(:group, :order, :values) }
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
|
1128
|
+
|
1129
|
+
class OptimisticLockingTest < ActiveRecord::TestCase
|
1130
|
+
# We do not allow updating identities, but we can test using a non-identity key
|
1131
|
+
coerce_tests! :test_update_with_dirty_primary_key
|
1132
|
+
def test_update_with_dirty_primary_key_coerced
|
1133
|
+
assert_raises(ActiveRecord::RecordNotUnique) do
|
1134
|
+
record = StringKeyObject.find('record1')
|
1135
|
+
record.id = 'record2'
|
1136
|
+
record.save!
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
record = StringKeyObject.find('record1')
|
1140
|
+
record.id = 'record42'
|
1141
|
+
record.save!
|
1142
|
+
|
1143
|
+
assert StringKeyObject.find('record42')
|
1144
|
+
assert_raises(ActiveRecord::RecordNotFound) do
|
1145
|
+
StringKeyObject.find('record1')
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
|
1151
|
+
|
1152
|
+
class RelationMergingTest < ActiveRecord::TestCase
|
1153
|
+
coerce_tests! :test_merging_with_order_with_binds
|
1154
|
+
def test_merging_with_order_with_binds_coerced
|
1155
|
+
relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
|
1156
|
+
assert_equal ["title LIKE N'%suffix'"], relation.order_values
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
|
1161
|
+
class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
1162
|
+
# Temporarily coerce this test due to https://github.com/rails/rails/issues/34945
|
1163
|
+
coerce_tests! :test_eager_loading_too_may_ids
|
1164
|
+
end
|