activerecord-sqlserver-adapter 5.1.6 → 5.2.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +16 -19
  4. data/CHANGELOG.md +6 -46
  5. data/Dockerfile +14 -0
  6. data/Gemfile +1 -2
  7. data/README.md +4 -4
  8. data/VERSION +1 -1
  9. data/activerecord-sqlserver-adapter.gemspec +1 -1
  10. data/circle.yml +8 -6
  11. data/docker-compose.ci.yml +11 -0
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +11 -18
  13. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +13 -2
  14. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +45 -9
  15. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +2 -1
  16. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +2 -2
  17. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +33 -14
  18. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +3 -4
  19. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +1 -1
  20. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +7 -0
  21. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +14 -12
  22. data/lib/active_record/tasks/sqlserver_database_tasks.rb +3 -1
  23. data/lib/arel/visitors/sqlserver.rb +1 -1
  24. data/lib/arel_sqlserver.rb +0 -1
  25. data/test/bin/install-freetds.sh +1 -1
  26. data/test/cases/adapter_test_sqlserver.rb +10 -2
  27. data/test/cases/coerced_tests.rb +262 -13
  28. data/test/cases/column_test_sqlserver.rb +6 -0
  29. data/test/cases/helper_sqlserver.rb +2 -1
  30. data/test/cases/migration_test_sqlserver.rb +3 -1
  31. data/test/cases/order_test_sqlserver.rb +19 -19
  32. data/test/schema/sqlserver_specific_schema.rb +9 -1
  33. data/test/support/core_ext/query_cache.rb +29 -0
  34. metadata +11 -9
  35. data/test/bin/.keep +0 -0
@@ -26,13 +26,13 @@ module ActiveRecord
26
26
 
27
27
  end
28
28
 
29
- Transaction.send :include, SQLServerTransaction
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, run_commit_callbacks: false)
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 :include, SQLServerRealTransaction
62
-
61
+ RealTransaction.send :prepend, SQLServerRealTransaction
63
62
  end
64
63
  end
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module SQLServer
4
4
  module Type
5
- class Json < ActiveRecord::Type::Internal::AbstractJson
5
+ class Json < ActiveRecord::Type::Json
6
6
 
7
7
  end
8
8
  end
@@ -4,6 +4,13 @@ module ActiveRecord
4
4
  module Type
5
5
  class String < ActiveRecord::Type::String
6
6
 
7
+ def changed_in_place?(raw_old_value, new_value)
8
+ if raw_old_value.is_a?(Data)
9
+ raw_old_value.value != new_value
10
+ else
11
+ super
12
+ end
13
+ end
7
14
 
8
15
  end
9
16
  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
- @sqlserver_azure ||= !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i)
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 @version_year if defined?(@version_year)
439
- @version_year = begin
440
- vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
441
- return 2016 if vstring =~ /vNext/
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
- "-S #{Shellwords.escape(configuration['host'])}",
56
+ server_arg,
55
57
  "-D #{Shellwords.escape(configuration['database'])}",
56
58
  "-U #{Shellwords.escape(configuration['username'])}",
57
59
  "-P #{Shellwords.escape(configuration['password'])}",
@@ -14,7 +14,7 @@ module Arel
14
14
  # SQLServer ToSql/Visitor (Overides)
15
15
 
16
16
  def visit_Arel_Nodes_BindParam o, collector
17
- collector.add_bind(o) { |i| "@#{i-1}" }
17
+ collector.add_bind(o.value) { |i| "@#{i-1}" }
18
18
  end
19
19
 
20
20
  def visit_Arel_Nodes_Bin o, collector
@@ -1,3 +1,2 @@
1
1
  require 'arel'
2
- require 'arel/visitors/bind_visitor'
3
2
  require 'arel/visitors/sqlserver'
@@ -5,7 +5,7 @@ set -e
5
5
 
6
6
  FREETDS_VERSION=1.00.21
7
7
 
8
- wget ftp://ftp.freetds.org/pub/freetds/stable/freetds-$FREETDS_VERSION.tar.gz
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 less than 3' do
276
+ it 'create smallints when limit is 2' do
272
277
  assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2)
273
- assert_equal 'smallint', connection.type_to_sql(:integer, limit: 1)
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
@@ -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! :test_migration_sets_internal_metadata_even_when_fully_migrated
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! :test_cache_does_not_wrap_string_results_in_arrays
646
- def test_cache_does_not_wrap_string_results_in_arrays_coerced
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