sequel 5.7.1 → 5.8.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +53 -1
  3. data/doc/association_basics.rdoc +2 -2
  4. data/doc/migration.rdoc +11 -10
  5. data/doc/postgresql.rdoc +71 -0
  6. data/doc/release_notes/5.8.0.txt +170 -0
  7. data/lib/sequel/adapters/jdbc.rb +6 -1
  8. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -3
  9. data/lib/sequel/adapters/mysql2.rb +2 -1
  10. data/lib/sequel/adapters/postgres.rb +32 -10
  11. data/lib/sequel/adapters/shared/mssql.rb +11 -11
  12. data/lib/sequel/adapters/shared/mysql.rb +51 -6
  13. data/lib/sequel/adapters/shared/oracle.rb +12 -2
  14. data/lib/sequel/adapters/shared/postgres.rb +97 -30
  15. data/lib/sequel/adapters/shared/sqlanywhere.rb +2 -2
  16. data/lib/sequel/adapters/shared/sqlite.rb +6 -1
  17. data/lib/sequel/dataset/features.rb +5 -0
  18. data/lib/sequel/dataset/query.rb +48 -19
  19. data/lib/sequel/exceptions.rb +7 -0
  20. data/lib/sequel/extensions/connection_expiration.rb +8 -3
  21. data/lib/sequel/extensions/pg_enum.rb +28 -5
  22. data/lib/sequel/plugins/association_proxies.rb +16 -4
  23. data/lib/sequel/plugins/error_splitter.rb +16 -11
  24. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +260 -0
  25. data/lib/sequel/plugins/subclasses.rb +1 -1
  26. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  27. data/lib/sequel/version.rb +2 -2
  28. data/spec/adapters/mysql_spec.rb +0 -1
  29. data/spec/adapters/postgres_spec.rb +169 -4
  30. data/spec/adapters/sqlite_spec.rb +13 -0
  31. data/spec/core/dataset_spec.rb +21 -0
  32. data/spec/extensions/association_proxies_spec.rb +21 -7
  33. data/spec/extensions/connection_expiration_spec.rb +13 -1
  34. data/spec/extensions/pg_auto_constraint_validations_spec.rb +165 -0
  35. data/spec/extensions/pg_enum_spec.rb +26 -22
  36. data/spec/extensions/tactical_eager_loading_spec.rb +11 -0
  37. data/spec/integration/dataset_test.rb +30 -6
  38. data/spec/integration/plugin_test.rb +2 -2
  39. metadata +6 -2
@@ -342,6 +342,7 @@ module Sequel
342
342
  /conflicted with the CHECK constraint/ => CheckConstraintViolation,
343
343
  /column does not allow nulls/ => NotNullConstraintViolation,
344
344
  /was deadlocked on lock resources with another process and has been chosen as the deadlock victim/ => SerializationFailure,
345
+ /Lock request time out period exceeded\./ => DatabaseLockTimeout,
345
346
  }.freeze
346
347
  def database_error_regexps
347
348
  DATABASE_ERROR_REGEXPS
@@ -712,6 +713,11 @@ module Sequel
712
713
  false
713
714
  end
714
715
 
716
+ # MSSQL supports NOWAIT.
717
+ def supports_nowait?
718
+ true
719
+ end
720
+
715
721
  # MSSQL 2012+ supports offsets in correlated subqueries.
716
722
  def supports_offsets_in_correlated_subqueries?
717
723
  is_2012_or_later?
@@ -958,6 +964,7 @@ module Sequel
958
964
  def select_lock_sql(sql)
959
965
  lock = @opts[:lock]
960
966
  skip_locked = @opts[:skip_locked]
967
+ nowait = @opts[:nowait]
961
968
  for_update = lock == :update
962
969
  dirty = lock == :dirty
963
970
  lock_hint = for_update || dirty
@@ -966,19 +973,12 @@ module Sequel
966
973
  sql << " WITH ("
967
974
 
968
975
  if lock_hint
969
- sql << if for_update
970
- 'UPDLOCK'
971
- else
972
- 'NOLOCK'
973
- end
974
- end
975
-
976
- if lock_hint && skip_locked
977
- sql << ', '
976
+ sql << (for_update ? 'UPDLOCK' : 'NOLOCK')
978
977
  end
979
978
 
980
- if skip_locked
981
- sql << "READPAST"
979
+ if skip_locked || nowait
980
+ sql << ', ' if lock_hint
981
+ sql << (skip_locked ? "READPAST" : "NOWAIT")
982
982
  end
983
983
 
984
984
  sql << ')'
@@ -58,6 +58,7 @@ module Sequel
58
58
  where(:TABLE_NAME=>im.call(table), :TABLE_SCHEMA=>Sequel.function(:DATABASE)).
59
59
  exclude(:CONSTRAINT_NAME=>'PRIMARY').
60
60
  exclude(:REFERENCED_TABLE_NAME=>nil).
61
+ order(:CONSTRAINT_NAME, :POSITION_IN_UNIQUE_CONSTRAINT).
61
62
  select(Sequel[:CONSTRAINT_NAME].as(:name), Sequel[:COLUMN_NAME].as(:column), Sequel[:REFERENCED_TABLE_NAME].as(:table), Sequel[:REFERENCED_COLUMN_NAME].as(:key))
62
63
 
63
64
  h = {}
@@ -407,6 +408,7 @@ module Sequel
407
408
  /cannot be null/ => NotNullConstraintViolation,
408
409
  /Deadlock found when trying to get lock; try restarting transaction/ => SerializationFailure,
409
410
  /CONSTRAINT .+ failed for/ => CheckConstraintViolation,
411
+ /\AStatement aborted because lock\(s\) could not be acquired immediately and NOWAIT is set\./ => DatabaseLockTimeout,
410
412
  }.freeze
411
413
  def database_error_regexps
412
414
  DATABASE_ERROR_REGEXPS
@@ -561,10 +563,10 @@ module Sequel
561
563
  MATCH_AGAINST = ["MATCH ".freeze, " AGAINST (".freeze, ")".freeze].freeze
562
564
  MATCH_AGAINST_BOOLEAN = ["MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE)".freeze].freeze
563
565
 
564
- Dataset.def_sql_method(self, :delete, %w'delete from where order limit')
566
+ Dataset.def_sql_method(self, :delete, %w'with delete from where order limit')
565
567
  Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
566
568
  Dataset.def_sql_method(self, :select, %w'with select distinct calc_found_rows columns from join where group having compounds order limit lock')
567
- Dataset.def_sql_method(self, :update, %w'update ignore table set where order limit')
569
+ Dataset.def_sql_method(self, :update, %w'with update ignore table set where order limit')
568
570
 
569
571
  include Sequel::Dataset::Replace
570
572
  include UnmodifiedIdentifiers::DatasetMethods
@@ -579,6 +581,12 @@ module Sequel
579
581
  super
580
582
  end
581
583
  when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
584
+ if !db.mariadb? && db.server_version >= 80000 && [:~, :'!~'].include?(op)
585
+ func = Sequel.function(:REGEXP_LIKE, args[0], args[1], 'c')
586
+ func = ~func if op == :'!~'
587
+ return literal_append(sql, func)
588
+ end
589
+
582
590
  sql << '('
583
591
  literal_append(sql, args[0])
584
592
  sql << ' '
@@ -651,7 +659,7 @@ module Sequel
651
659
  # Load the PrettyTable class, needed for explain output
652
660
  Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
653
661
 
654
- ds = db.send(:metadata_dataset).with_sql((opts[:extended] ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
662
+ ds = db.send(:metadata_dataset).with_sql(((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
655
663
  rows = ds.all
656
664
  Sequel::PrettyTable.string(rows, ds.columns)
657
665
  end
@@ -731,8 +739,16 @@ module Sequel
731
739
  sql << '`' << c.to_s.gsub('`', '``') << '`'
732
740
  end
733
741
 
742
+ # MariaDB 10.2+ and MySQL 8+ support CTEs
734
743
  def supports_cte?(type=:select)
735
- type == :select && db.mariadb? && db.server_version >= 100200
744
+ if db.mariadb?
745
+ type == :select && db.server_version >= 100200
746
+ else
747
+ case type
748
+ when :select, :update, :delete
749
+ db.server_version >= 80000
750
+ end
751
+ end
736
752
  end
737
753
 
738
754
  # MySQL does not support derived column lists
@@ -766,6 +782,11 @@ module Sequel
766
782
  true
767
783
  end
768
784
 
785
+ # MySQL 8+ supports NOWAIT.
786
+ def supports_nowait?
787
+ !db.mariadb? && db.server_version >= 80000
788
+ end
789
+
769
790
  # MySQL's DISTINCT ON emulation using GROUP BY does not respect the
770
791
  # query's ORDER BY clause.
771
792
  def supports_ordered_distinct_on?
@@ -777,14 +798,20 @@ module Sequel
777
798
  true
778
799
  end
779
800
 
801
+ # MySQL 8+ supports SKIP LOCKED.
802
+ def supports_skip_locked?
803
+ !db.mariadb? && db.server_version >= 80000
804
+ end
805
+
780
806
  # Check the database setting for whether fractional timestamps
781
807
  # are suppported.
782
808
  def supports_timestamp_usecs?
783
809
  db.supports_timestamp_usecs?
784
810
  end
785
811
 
812
+ # MariaDB 10.2+ and MySQL 8+ support window functions
786
813
  def supports_window_functions?
787
- db.mariadb? && db.server_version >= 100200
814
+ db.server_version >= (db.mariadb? ? 100200 : 80000)
788
815
  end
789
816
 
790
817
  # Sets up the update methods to use UPDATE IGNORE.
@@ -946,8 +973,26 @@ module Sequel
946
973
  end
947
974
 
948
975
  # Support FOR SHARE locking when using the :share lock style.
976
+ # Use SKIP LOCKED if skipping locked rows.
949
977
  def select_lock_sql(sql)
950
- @opts[:lock] == :share ? (sql << ' LOCK IN SHARE MODE') : super
978
+ lock = @opts[:lock]
979
+ if lock == :share
980
+ if !db.mariadb? && db.server_version >= 80000
981
+ sql << ' FOR SHARE'
982
+ else
983
+ sql << ' LOCK IN SHARE MODE'
984
+ end
985
+ else
986
+ super
987
+ end
988
+
989
+ if lock
990
+ if @opts[:skip_locked]
991
+ sql << " SKIP LOCKED"
992
+ elsif @opts[:nowait]
993
+ sql << " NOWAIT"
994
+ end
995
+ end
951
996
  end
952
997
 
953
998
  # MySQL specific SQL_CALC_FOUND_ROWS option
@@ -225,6 +225,7 @@ module Sequel
225
225
  /check constraint .+ violated/ => CheckConstraintViolation,
226
226
  /cannot insert NULL into|cannot update .+ to NULL/ => NotNullConstraintViolation,
227
227
  /can't serialize access for this transaction/ => SerializationFailure,
228
+ /resource busy and acquire with NOWAIT specified or timeout/ => DatabaseLockTimeout,
228
229
  }.freeze
229
230
  def database_error_regexps
230
231
  DATABASE_ERROR_REGEXPS
@@ -487,6 +488,11 @@ module Sequel
487
488
  false
488
489
  end
489
490
 
491
+ # Oracle supports NOWAIT.
492
+ def supports_nowait?
493
+ true
494
+ end
495
+
490
496
  # Oracle does not support offsets in correlated subqueries.
491
497
  def supports_offsets_in_correlated_subqueries?
492
498
  false
@@ -622,8 +628,12 @@ module Sequel
622
628
  def select_lock_sql(sql)
623
629
  super
624
630
 
625
- if @opts[:skip_locked]
626
- sql << " SKIP LOCKED"
631
+ if @opts[:lock]
632
+ if @opts[:skip_locked]
633
+ sql << " SKIP LOCKED"
634
+ elsif @opts[:nowait]
635
+ sql << " NOWAIT"
636
+ end
627
637
  end
628
638
  end
629
639
 
@@ -213,6 +213,32 @@ module Sequel
213
213
  run("COMMIT PREPARED #{literal(transaction_id)}", opts)
214
214
  end
215
215
 
216
+ # A hash of metadata for CHECK constraints on the table.
217
+ # Keys are CHECK constraint name symbols. Values are hashes with the following keys:
218
+ # :definition :: An SQL fragment for the definition of the constraint
219
+ # :columns :: An array of column symbols for the columns referenced in the constraint
220
+ def check_constraints(table)
221
+ m = output_identifier_meth
222
+
223
+ rows = metadata_dataset.
224
+ from{pg_constraint.as(:co)}.
225
+ join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
226
+ where(:conrelid=>regclass_oid(table), :contype=>'c').
227
+ select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
228
+
229
+ hash = {}
230
+ rows.each do |row|
231
+ constraint = m.call(row[:constraint])
232
+ if entry = hash[constraint]
233
+ entry[:columns] << m.call(row[:column])
234
+ else
235
+ hash[constraint] = {:definition=>row[:definition], :columns=>[m.call(row[:column])]}
236
+ end
237
+ end
238
+
239
+ hash
240
+ end
241
+
216
242
  # Convert the first primary key column in the +table+ from being a serial column to being an identity column.
217
243
  # If the column is already an identity column, assume it was already converted and make no changes.
218
244
  #
@@ -396,67 +422,95 @@ module Sequel
396
422
 
397
423
  # Return full foreign key information using the pg system tables, including
398
424
  # :name, :on_delete, :on_update, and :deferrable entries in the hashes.
425
+ #
426
+ # Supports additional options:
427
+ # :reverse :: Instead of returning foreign keys in the current table, return
428
+ # foreign keys in other tables that reference the current table.
429
+ # :schema :: Set to true to have the :table value in the hashes be a qualified
430
+ # identifier. Set to false to use a separate :schema value with
431
+ # the related schema. Defaults to whether the given table argument
432
+ # is a qualified identifier.
399
433
  def foreign_key_list(table, opts=OPTS)
400
434
  m = output_identifier_meth
401
435
  schema, _ = opts.fetch(:schema, schema_and_table(table))
402
436
  oid = regclass_oid(table)
437
+ reverse = opts[:reverse]
438
+
439
+ if reverse
440
+ ctable = Sequel[:att2]
441
+ cclass = Sequel[:cl2]
442
+ rtable = Sequel[:att]
443
+ rclass = Sequel[:cl]
444
+ else
445
+ ctable = Sequel[:att]
446
+ cclass = Sequel[:cl]
447
+ rtable = Sequel[:att2]
448
+ rclass = Sequel[:cl2]
449
+ end
403
450
 
404
451
  if server_version >= 90500
405
- cpos = Sequel.expr{array_position(co[:conkey], att[:attnum])}
406
- rpos = Sequel.expr{array_position(co[:confkey], att2[:attnum])}
452
+ cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
453
+ rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
407
454
  else
408
455
  range = 0...32
409
- cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, att[:attnum])}
410
- rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, att2[:attnum])}
456
+ cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
457
+ rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
411
458
  end
412
459
 
413
460
  ds = metadata_dataset.
414
461
  from{pg_constraint.as(:co)}.
415
- join(Sequel[:pg_class].as(:cl), :oid=>:conrelid).
416
- join(Sequel[:pg_attribute].as(:att), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
417
- join(Sequel[:pg_class].as(:cl2), :oid=>Sequel[:co][:confrelid]).
418
- join(Sequel[:pg_attribute].as(:att2), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
462
+ join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
463
+ join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
464
+ join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
465
+ join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
466
+ join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
419
467
  order{[co[:conname], cpos]}.
420
468
  where{{
421
469
  cl[:relkind]=>'r',
422
470
  co[:contype]=>'f',
423
471
  cl[:oid]=>oid,
424
472
  cpos=>rpos
425
- }}.
473
+ }}.
426
474
  select{[
427
475
  co[:conname].as(:name),
428
- att[:attname].as(:column),
476
+ ctable[:attname].as(:column),
429
477
  co[:confupdtype].as(:on_update),
430
478
  co[:confdeltype].as(:on_delete),
431
479
  cl2[:relname].as(:table),
432
- att2[:attname].as(:refcolumn),
433
- SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable)
480
+ rtable[:attname].as(:refcolumn),
481
+ SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
482
+ nsp[:nspname].as(:schema)
434
483
  ]}
435
484
 
436
- # If a schema is given, we only search in that schema, and the returned :table
437
- # entry is schema qualified as well.
438
- if schema
439
- ds = ds.join(Sequel[:pg_namespace].as(:nsp2), :oid=>Sequel[:cl2][:relnamespace]).
440
- select_append{nsp2[:nspname].as(:schema)}
441
- end
442
-
443
485
  h = {}
444
486
  fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
445
487
 
446
488
  ds.each do |row|
447
- if r = h[row[:name]]
489
+ if reverse
490
+ key = [row[:schema], row[:table], row[:name]]
491
+ else
492
+ key = row[:name]
493
+ end
494
+
495
+ if r = h[key]
448
496
  r[:columns] << m.call(row[:column])
449
497
  r[:key] << m.call(row[:refcolumn])
450
498
  else
451
- h[row[:name]] = {
499
+ entry = h[key] = {
452
500
  :name=>m.call(row[:name]),
453
501
  :columns=>[m.call(row[:column])],
454
502
  :key=>[m.call(row[:refcolumn])],
455
503
  :on_update=>fklod_map[row[:on_update]],
456
504
  :on_delete=>fklod_map[row[:on_delete]],
457
505
  :deferrable=>row[:deferrable],
458
- :table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
506
+ :table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
459
507
  }
508
+
509
+ unless schema
510
+ # If not combining schema information into the :table entry
511
+ # include it as a separate entry.
512
+ entry[:schema] = m.call(row[:schema])
513
+ end
460
514
  end
461
515
  end
462
516
 
@@ -494,12 +548,12 @@ module Sequel
494
548
  indc[:relkind]=>'i',
495
549
  ind[:indisprimary]=>false,
496
550
  :indexprs=>nil,
497
- :indpred=>nil,
498
551
  :indisvalid=>true,
499
552
  tab[:oid]=>oid}}.
500
553
  order(*order).
501
554
  select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
502
555
 
556
+ ds = ds.where(:indpred=>nil) unless opts[:include_partial]
503
557
  ds = ds.where(:indisready=>true) if server_version >= 80300
504
558
  ds = ds.where(:indislive=>true) if server_version >= 90300
505
559
 
@@ -809,12 +863,12 @@ module Sequel
809
863
 
810
864
  VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
811
865
  # The SQL queries to execute when starting a new connection.
812
- def connection_configuration_sqls
866
+ def connection_configuration_sqls(opts=@opts)
813
867
  sqls = []
814
868
 
815
- sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(@opts.fetch(:force_standard_strings, true))
869
+ sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(opts.fetch(:force_standard_strings, true))
816
870
 
817
- cmm = @opts.fetch(:client_min_messages, :warning)
871
+ cmm = opts.fetch(:client_min_messages, :warning)
818
872
  if cmm && !cmm.to_s.empty?
819
873
  cmm = cmm.to_s.upcase.strip
820
874
  unless VALID_CLIENT_MIN_MESSAGES.include?(cmm)
@@ -823,7 +877,7 @@ module Sequel
823
877
  sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
824
878
  end
825
879
 
826
- if search_path = @opts[:search_path]
880
+ if search_path = opts[:search_path]
827
881
  case search_path
828
882
  when String
829
883
  search_path = search_path.split(",").map(&:strip)
@@ -863,6 +917,8 @@ module Sequel
863
917
  ExclusionConstraintViolation
864
918
  elsif sqlstate == '40P01'
865
919
  SerializationFailure
920
+ elsif sqlstate == '55P03'
921
+ DatabaseLockTimeout
866
922
  else
867
923
  super
868
924
  end
@@ -878,6 +934,7 @@ module Sequel
878
934
  [/violates not-null constraint/, NotNullConstraintViolation],
879
935
  [/conflicting key value violates exclusion constraint/, ExclusionConstraintViolation],
880
936
  [/could not serialize access/, SerializationFailure],
937
+ [/could not obtain lock on row in relation/, DatabaseLockTimeout],
881
938
  ].freeze
882
939
  def database_error_regexps
883
940
  DATABASE_ERROR_REGEXPS
@@ -1577,6 +1634,11 @@ module Sequel
1577
1634
  true
1578
1635
  end
1579
1636
 
1637
+ # PostgreSQL supports NOWAIT.
1638
+ def supports_nowait?
1639
+ true
1640
+ end
1641
+
1580
1642
  # Returning is always supported.
1581
1643
  def supports_returning?(type)
1582
1644
  true
@@ -1796,14 +1858,19 @@ module Sequel
1796
1858
  # Support FOR SHARE locking when using the :share lock style.
1797
1859
  # Use SKIP LOCKED if skipping locked rows.
1798
1860
  def select_lock_sql(sql)
1799
- if @opts[:lock] == :share
1861
+ lock = @opts[:lock]
1862
+ if lock == :share
1800
1863
  sql << ' FOR SHARE'
1801
1864
  else
1802
1865
  super
1803
1866
  end
1804
1867
 
1805
- if @opts[:skip_locked]
1806
- sql << " SKIP LOCKED"
1868
+ if lock
1869
+ if @opts[:skip_locked]
1870
+ sql << " SKIP LOCKED"
1871
+ elsif @opts[:nowait]
1872
+ sql << " NOWAIT"
1873
+ end
1807
1874
  end
1808
1875
  end
1809
1876