activerecord-jdbcsqlserver-adapter 50.1.0 → 51.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +4 -5
  4. data/BACKERS.md +32 -0
  5. data/CHANGELOG.md +21 -87
  6. data/README.md +2 -3
  7. data/VERSION +1 -1
  8. data/activerecord-jdbcsqlserver-adapter.gemspec +3 -2
  9. data/appveyor.yml +1 -1
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +3 -1
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -1
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +55 -0
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +4 -2
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +5 -3
  15. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +41 -18
  16. data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +2 -12
  17. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -0
  18. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +21 -0
  19. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +72 -52
  20. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +15 -7
  21. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -4
  22. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -0
  23. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +3 -6
  24. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +27 -10
  25. data/lib/active_record/tasks/sqlserver_database_tasks.rb +2 -2
  26. data/lib/activerecord-jdbcsqlserver-adapter.rb +1 -1
  27. data/lib/arel/visitors/sqlserver.rb +16 -3
  28. data/test/bin/setup.sh +19 -0
  29. data/test/cases/adapter_test_sqlserver.rb +17 -20
  30. data/test/cases/coerced_tests.rb +117 -11
  31. data/test/cases/column_test_sqlserver.rb +1 -1
  32. data/test/cases/helper_sqlserver.rb +6 -1
  33. data/test/cases/pessimistic_locking_test_sqlserver.rb +28 -11
  34. data/test/cases/schema_dumper_test_sqlserver.rb +10 -10
  35. data/test/cases/specific_schema_test_sqlserver.rb +0 -6
  36. data/test/cases/trigger_test_sqlserver.rb +31 -0
  37. data/test/config.yml +2 -2
  38. data/test/models/sqlserver/trigger.rb +7 -0
  39. data/test/models/sqlserver/trigger_history.rb +3 -0
  40. data/test/schema/sqlserver_specific_schema.rb +39 -5
  41. data/test/support/sql_counter_sqlserver.rb +3 -2
  42. metadata +23 -16
  43. data/RAILS5-TODO.md +0 -5
  44. data/lib/jdbc_mssql_driver_loader.rb +0 -22
  45. data/test/models/sqlserver/dot_table_name.rb +0 -3
@@ -48,7 +48,7 @@ module ActiveRecord
48
48
  create true
49
49
  end
50
50
 
51
- def structure_dump(filename)
51
+ def structure_dump(filename, extra_flags)
52
52
  command = [
53
53
  "defncopy",
54
54
  "-S #{Shellwords.escape(configuration['host'])}",
@@ -71,7 +71,7 @@ module ActiveRecord
71
71
  File.open(filename, "w") { |file| file.puts dump }
72
72
  end
73
73
 
74
- def structure_load(filename)
74
+ def structure_load(filename, extra_flags)
75
75
  connection.execute File.read(filename)
76
76
  end
77
77
 
@@ -2,7 +2,7 @@
2
2
  require 'active_record/connection_adapters/sqlserver/core_ext/date_time'
3
3
 
4
4
  # Load the jar file for the jdbc driver
5
- require_relative './jdbc_mssql_driver_loader'
5
+ require 'jdbc/mssql'
6
6
 
7
7
  # Standadard arjdbc functionality
8
8
  require 'arjdbc/abstract/connection_management'
@@ -95,17 +95,29 @@ module Arel
95
95
  collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector
96
96
  end
97
97
  if o.right.any?
98
- collector << " " if o.left
98
+ collector << SPACE if o.left
99
99
  collector = inject_join o.right, collector, ' '
100
100
  end
101
101
  collector
102
102
  end
103
103
 
104
+ def visit_Arel_Nodes_InnerJoin o, collector
105
+ collector << "INNER JOIN "
106
+ collector = visit o.left, collector
107
+ collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
108
+ if o.right
109
+ collector << SPACE
110
+ visit(o.right, collector)
111
+ else
112
+ collector
113
+ end
114
+ end
115
+
104
116
  def visit_Arel_Nodes_OuterJoin o, collector
105
117
  collector << "LEFT OUTER JOIN "
106
118
  collector = visit o.left, collector
107
119
  collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
108
- collector << " "
120
+ collector << SPACE
109
121
  visit o.right, collector
110
122
  end
111
123
 
@@ -190,7 +202,8 @@ module Arel
190
202
 
191
203
  def primary_Key_From_Table t
192
204
  return unless t
193
- column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.try(:second).try(:name)
205
+ column_name = @connection.schema_cache.primary_keys(t.name) ||
206
+ @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
194
207
  column_name ? t[column_name] : nil
195
208
  end
196
209
 
data/test/bin/setup.sh ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ tag=2017-GA
7
+
8
+ docker pull metaskills/mssql-server-linux-rails:$tag
9
+
10
+ container=$(docker ps -a -q --filter ancestor=metaskills/mssql-server-linux-rails:$tag)
11
+ if [[ -z $container ]]; then
12
+ docker run -p 1433:1433 -d metaskills/mssql-server-linux-rails:$tag && sleep 10
13
+ exit
14
+ fi
15
+
16
+ container=$(docker ps -q --filter ancestor=metaskills/mssql-server-linux-rails:$tag)
17
+ if [[ -z $container ]]; then
18
+ docker start $container && sleep 10
19
+ fi
@@ -13,10 +13,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
13
13
  let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" }
14
14
  let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" }
15
15
 
16
- it 'has basic and non-senstive information in the adpaters inspect method' do
16
+ it 'has basic and non-sensitive information in the adapters inspect method' do
17
17
  string = connection.inspect
18
18
  string.must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter}
19
- string.must_match %r{version\: \d\d\.\d}
19
+ string.must_match %r{version\: \d+\.\d}
20
20
  string.must_match %r{mode: (dblib|jdbc)}
21
21
  string.must_match %r{azure: (true|false)}
22
22
  string.wont_match %r{host}
@@ -37,10 +37,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
37
37
  assert_equal 'SQLServer', connection.adapter_name
38
38
  end
39
39
 
40
- it 'supports migrations' do
41
- assert connection.supports_migrations?
42
- end
43
-
44
40
  it 'support DDL in transactions' do
45
41
  assert connection.supports_ddl_transactions?
46
42
  end
@@ -145,12 +141,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
145
141
  end
146
142
 
147
143
  it 'return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column' do
148
- assert_equal '[funny_jokes]', connection.send(:query_requires_identity_insert?,@identity_insert_sql)
149
- assert_equal '[funny_jokes]', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted)
150
- assert_equal '[funny_jokes]', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered)
151
- assert_equal '[funny_jokes]', connection.send(:query_requires_identity_insert?,@identity_insert_sql_sp)
152
- assert_equal '[funny_jokes]', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted_sp)
153
- assert_equal '[funny_jokes]', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered_sp)
144
+ assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql)
145
+ assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted)
146
+ assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered)
147
+ assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_sp)
148
+ assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted_sp)
149
+ assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered_sp)
154
150
  end
155
151
 
156
152
  it 'return false to #query_requires_identity_insert? for normal SQL' do
@@ -266,23 +262,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
266
262
  end
267
263
 
268
264
  it 'create integers when limit is 4' do
269
- assert_equal 'integer', connection.type_to_sql(:integer, 4)
265
+ assert_equal 'integer', connection.type_to_sql(:integer, limit: 4)
270
266
  end
271
267
 
272
268
  it 'create integers when limit is 3' do
273
- assert_equal 'integer', connection.type_to_sql(:integer, 3)
269
+ assert_equal 'integer', connection.type_to_sql(:integer, limit: 3)
274
270
  end
275
271
 
276
272
  it 'create smallints when limit is less than 3' do
277
- assert_equal 'smallint', connection.type_to_sql(:integer, 2)
278
- assert_equal 'smallint', connection.type_to_sql(:integer, 1)
273
+ assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2)
274
+ assert_equal 'smallint', connection.type_to_sql(:integer, limit: 1)
279
275
  end
280
276
 
281
277
  it 'create bigints when limit is greateer than 4' do
282
- assert_equal 'bigint', connection.type_to_sql(:integer, 5)
283
- assert_equal 'bigint', connection.type_to_sql(:integer, 6)
284
- assert_equal 'bigint', connection.type_to_sql(:integer, 7)
285
- assert_equal 'bigint', connection.type_to_sql(:integer, 8)
278
+ assert_equal 'bigint', connection.type_to_sql(:integer, limit: 5)
279
+ assert_equal 'bigint', connection.type_to_sql(:integer, limit: 6)
280
+ assert_equal 'bigint', connection.type_to_sql(:integer, limit: 7)
281
+ assert_equal 'bigint', connection.type_to_sql(:integer, limit: 8)
286
282
  end
287
283
 
288
284
  it 'create floats when no limit supplied' do
@@ -427,3 +423,4 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
427
423
  end
428
424
 
429
425
  end
426
+
@@ -31,6 +31,9 @@ end
31
31
  require 'models/event'
32
32
  module ActiveRecord
33
33
  class AdapterTest < ActiveRecord::TestCase
34
+ # I really dont think we can support legacy binds.
35
+ coerce_tests! :test_select_all_with_legacy_binds
36
+
34
37
  # As far as I can tell, SQL Server does not support null bytes in strings.
35
38
  coerce_tests! :test_update_prepared_statement
36
39
  coerce_tests! :test_log_invalid_encoding if defined? JRUBY_VERSION # JRuby just happily converts the encoding
@@ -53,14 +56,14 @@ end
53
56
 
54
57
  require 'models/topic'
55
58
  class AttributeMethodsTest < ActiveRecord::TestCase
56
- coerce_tests! :test_typecast_attribute_from_select_to_false
59
+ coerce_tests! %r{typecast attribute from select to false}
57
60
  def test_typecast_attribute_from_select_to_false_coerced
58
61
  Topic.create(:title => 'Budget')
59
62
  topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 2, 1, 0) as is_test").first
60
63
  assert !topic.is_test?
61
64
  end
62
65
 
63
- coerce_tests! :test_typecast_attribute_from_select_to_true
66
+ coerce_tests! %r{typecast attribute from select to true}
64
67
  def test_typecast_attribute_from_select_to_true_coerced
65
68
  Topic.create(:title => 'Budget')
66
69
  topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first
@@ -88,6 +91,37 @@ class BasicsTest < ActiveRecord::TestCase
88
91
  # Caused in Rails v4.2.5 by adding `firm_id` column in this http://git.io/vBfMs
89
92
  # commit. Trust Rails has this covered.
90
93
  coerce_tests! :test_find_keeps_multiple_group_values
94
+
95
+ def test_update_date_time_attributes
96
+ Time.use_zone("Eastern Time (US & Canada)") do
97
+ topic = Topic.find(1)
98
+ time = Time.zone.parse("2017-07-17 10:56")
99
+ topic.update_attributes!(written_on: time)
100
+ assert_equal(time, topic.written_on)
101
+ end
102
+ end
103
+
104
+ def test_update_date_time_attributes_with_default_timezone_local
105
+ with_env_tz 'America/New_York' do
106
+ with_timezone_config default: :local do
107
+ Time.use_zone("Eastern Time (US & Canada)") do
108
+ topic = Topic.find(1)
109
+ time = Time.zone.parse("2017-07-17 10:56")
110
+ topic.update_attributes!(written_on: time)
111
+ assert_equal(time, topic.written_on)
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ coerce_tests! %r{column names are quoted when using #from clause and model has ignored columns}
118
+ def test_column_names_are_quoted_when_using_from_clause_and_model_has_ignored_columns_coerced
119
+ refute_empty Developer.ignored_columns
120
+ query = Developer.from("developers").to_sql
121
+ quoted_id = "#{Developer.quoted_table_name}.#{Developer.quoted_primary_key}"
122
+
123
+ assert_match(/SELECT #{Regexp.quote(quoted_id)}.* FROM developers/, query)
124
+ end
91
125
  end
92
126
 
93
127
 
@@ -119,6 +153,15 @@ end
119
153
 
120
154
 
121
155
  class CalculationsTest < ActiveRecord::TestCase
156
+ # This fails randomly due to schema cache being lost?
157
+ coerce_tests! :test_offset_is_kept
158
+ def test_offset_is_kept_coerced
159
+ Account.first
160
+ queries = assert_sql { Account.offset(1).count }
161
+ assert_equal 1, queries.length
162
+ assert_match(/OFFSET/, queries.first)
163
+ end
164
+
122
165
  # Are decimal, not integer.
123
166
  coerce_tests! :test_should_return_decimal_average_of_integer_field
124
167
  def test_should_return_decimal_average_of_integer_field_coerced
@@ -174,6 +217,29 @@ end
174
217
 
175
218
 
176
219
 
220
+ module ActiveRecord
221
+ module ConnectionAdapters
222
+ class QuoteARBaseTest < ActiveRecord::TestCase
223
+
224
+ # Use our date format.
225
+ coerce_tests! :test_quote_ar_object
226
+ def test_quote_ar_object_coerced
227
+ value = DatetimePrimaryKey.new(id: @time)
228
+ assert_equal "'02-14-2017 12:34:56.789'", @connection.quote(value)
229
+ end
230
+
231
+ # Use our date format.
232
+ coerce_tests! :test_type_cast_ar_object
233
+ def test_type_cast_ar_object_coerced
234
+ value = DatetimePrimaryKey.new(id: @time)
235
+ assert_equal "02-14-2017 12:34:56.789", @connection.type_cast(value)
236
+ end
237
+
238
+ end
239
+ end
240
+ end
241
+
242
+
177
243
 
178
244
  module ActiveRecord
179
245
  class Migration
@@ -302,6 +368,10 @@ end
302
368
 
303
369
 
304
370
  module ActiveRecord
371
+ class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase
372
+ # Skip this test with /tmp/my_schema_cache.yml path on Windows.
373
+ coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
374
+ end
305
375
  class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
306
376
  # We extend `local_database?` so that common VM IPs can be used.
307
377
  coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
@@ -395,12 +465,6 @@ class FinderTest < ActiveRecord::TestCase
395
465
  end
396
466
  end if defined? JRUBY_VERSION
397
467
 
398
- coerce_tests! :test_string_sanitation
399
- def test_string_sanitation_coerced
400
- assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
401
- assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table")
402
- end
403
-
404
468
  coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
405
469
  def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
406
470
  assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
@@ -603,6 +667,15 @@ end
603
667
 
604
668
 
605
669
 
670
+ class PrimaryKeysTest < ActiveRecord::TestCase
671
+ # Gonna trust Rails core for this. We end up with 2 querys vs 3 asserted
672
+ # but as far as I can tell, this is only one for us anyway.
673
+ coerce_tests! :test_create_without_primary_key_no_extra_query
674
+ end
675
+
676
+
677
+
678
+
606
679
  require 'models/task'
607
680
  class QueryCacheTest < ActiveRecord::TestCase
608
681
  coerce_tests! :test_cache_does_not_wrap_string_results_in_arrays
@@ -646,6 +719,26 @@ class RelationTest < ActiveRecord::TestCase
646
719
  # Leave it up to users to format selects/functions so HAVING works correctly.
647
720
  coerce_tests! :test_multiple_where_and_having_clauses
648
721
  coerce_tests! :test_having_with_binds_for_both_where_and_having
722
+
723
+ # Find any limit via our expression.
724
+ coerce_tests! %r{relations don't load all records in #inspect}
725
+ def test_relations_dont_load_all_records_in_inspect_coerced
726
+ assert_sql(/NEXT \? ROWS ONLY/) do
727
+ Post.all.inspect
728
+ end
729
+ end
730
+
731
+ # I wanted to add `.order("author_id")` scope to avoid error: Column "posts.id" is invalid in the ORDER BY
732
+ # However, this pull request on Rails core drops order on exists relation. https://github.com/rails/rails/pull/28699
733
+ # so we are skipping all together.
734
+ coerce_tests! :test_empty_complex_chained_relations
735
+
736
+ # Use LEN() vs length() function.
737
+ coerce_tests! :test_reverse_arel_assoc_order_with_function
738
+ def test_reverse_arel_assoc_order_with_function_coerced
739
+ topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order
740
+ assert_equal topics(:second).title, topics.first.title
741
+ end
649
742
  end
650
743
 
651
744
 
@@ -677,9 +770,6 @@ class SchemaDumperTest < ActiveRecord::TestCase
677
770
  assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38}, output
678
771
  end
679
772
 
680
- # This accidently returns the wrong number because of our tables too.
681
- coerce_tests! :test_types_line_up
682
-
683
773
  # This is a poorly written test and really does not catch the bottom'ness it is meant too. Ours throw it off.
684
774
  coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues
685
775
 
@@ -843,3 +933,19 @@ module ActiveRecord
843
933
  #coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed
844
934
  end
845
935
  end
936
+
937
+
938
+
939
+
940
+ module ActiveRecord
941
+ module ConnectionAdapters
942
+ class SchemaCacheTest < ActiveRecord::TestCase
943
+ private
944
+ # We need to give the full path for this to work.
945
+ def schema_dump_path
946
+ File.join ARTest::SQLServer.root_activerecord, 'test/assets/schema_dump_5_1.yml'
947
+ end
948
+ end
949
+ end
950
+ end
951
+
@@ -36,7 +36,7 @@ class ColumnTestSQLServer < ActiveRecord::TestCase
36
36
  it 'bigint(8)' do
37
37
  col = column('bigint')
38
38
  col.sql_type.must_equal 'bigint(8)'
39
- col.type.must_equal :bigint
39
+ col.type.must_equal :integer
40
40
  col.null.must_equal true
41
41
  col.default.must_equal 42
42
42
  obj.bigint.must_equal 42
@@ -9,7 +9,7 @@ require 'support/load_schema_sqlserver'
9
9
  require 'support/coerceable_test_sqlserver'
10
10
  require 'support/sql_counter_sqlserver'
11
11
  require 'support/connection_reflection'
12
- require 'mocha/minitest'
12
+ require 'mocha/mini_test'
13
13
 
14
14
  module ActiveRecord
15
15
  class TestCase < ActiveSupport::TestCase
@@ -23,9 +23,14 @@ module ActiveRecord
23
23
 
24
24
  let(:logger) { ActiveRecord::Base.logger }
25
25
 
26
+ setup :ensure_clean_rails_env
26
27
 
27
28
  private
28
29
 
30
+ def ensure_clean_rails_env
31
+ Rails.instance_variable_set(:@_env, nil) if defined?(::Rails)
32
+ end
33
+
29
34
  def host_windows?
30
35
  RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
31
36
  end
@@ -46,23 +46,40 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase
46
46
  end
47
47
  end
48
48
 
49
- it 'reload with lock when #lock! called' do
50
- assert_nothing_raised do
51
- Person.transaction do
52
- person = Person.find 1
53
- old, person.first_name = person.first_name, 'fooman'
54
- person.lock!
55
- assert_equal old, person.first_name
56
- end
57
- end
58
- end
59
-
60
49
  it 'can add a custom lock directive' do
61
50
  assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do
62
51
  Person.lock('WITH(HOLDLOCK, ROWLOCK)').load
63
52
  end
64
53
  end
65
54
 
55
+ describe 'joining tables' do
56
+
57
+ it 'joined tables use updlock by default' do
58
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) INNER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do
59
+ Person.lock(true).joins(:readers).load
60
+ end
61
+ end
62
+
63
+ it 'joined tables can use custom lock directive' do
64
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) INNER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do
65
+ Person.lock('WITH(NOLOCK)').joins(:readers).load
66
+ end
67
+ end
68
+
69
+ it 'left joined tables use updlock by default' do
70
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do
71
+ Person.lock(true).left_joins(:readers).load
72
+ end
73
+ end
74
+
75
+ it 'left joined tables can use custom lock directive' do
76
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) LEFT OUTER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do
77
+ Person.lock('WITH(NOLOCK)').left_joins(:readers).load
78
+ end
79
+ end
80
+
81
+ end
82
+
66
83
  end
67
84
 
68
85
  describe 'For paginated finds' do