sequel 5.36.0 → 5.41.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +56 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/doc/cheat_sheet.rdoc +5 -5
  6. data/doc/code_order.rdoc +0 -12
  7. data/doc/fork_safety.rdoc +84 -0
  8. data/doc/opening_databases.rdoc +5 -1
  9. data/doc/postgresql.rdoc +1 -1
  10. data/doc/querying.rdoc +3 -3
  11. data/doc/release_notes/5.37.0.txt +30 -0
  12. data/doc/release_notes/5.38.0.txt +28 -0
  13. data/doc/release_notes/5.39.0.txt +19 -0
  14. data/doc/release_notes/5.40.0.txt +40 -0
  15. data/doc/release_notes/5.41.0.txt +25 -0
  16. data/doc/sql.rdoc +1 -1
  17. data/doc/transactions.rdoc +0 -8
  18. data/lib/sequel/adapters/jdbc.rb +15 -3
  19. data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
  20. data/lib/sequel/adapters/shared/mssql.rb +21 -1
  21. data/lib/sequel/adapters/shared/oracle.rb +1 -1
  22. data/lib/sequel/adapters/shared/postgres.rb +6 -4
  23. data/lib/sequel/adapters/shared/sqlite.rb +35 -1
  24. data/lib/sequel/core.rb +5 -6
  25. data/lib/sequel/database/connecting.rb +0 -1
  26. data/lib/sequel/database/misc.rb +14 -0
  27. data/lib/sequel/database/schema_generator.rb +6 -0
  28. data/lib/sequel/database/schema_methods.rb +16 -6
  29. data/lib/sequel/database/transactions.rb +1 -1
  30. data/lib/sequel/dataset/actions.rb +10 -6
  31. data/lib/sequel/dataset/features.rb +10 -0
  32. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  33. data/lib/sequel/dataset/sql.rb +32 -10
  34. data/lib/sequel/extensions/blank.rb +8 -0
  35. data/lib/sequel/extensions/date_arithmetic.rb +8 -9
  36. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  37. data/lib/sequel/extensions/inflector.rb +8 -0
  38. data/lib/sequel/extensions/migration.rb +9 -1
  39. data/lib/sequel/extensions/named_timezones.rb +5 -1
  40. data/lib/sequel/extensions/pg_array.rb +1 -0
  41. data/lib/sequel/extensions/pg_interval.rb +34 -8
  42. data/lib/sequel/extensions/pg_row.rb +1 -0
  43. data/lib/sequel/extensions/pg_row_ops.rb +24 -0
  44. data/lib/sequel/extensions/query.rb +2 -0
  45. data/lib/sequel/extensions/schema_dumper.rb +3 -3
  46. data/lib/sequel/model/associations.rb +28 -4
  47. data/lib/sequel/model/base.rb +21 -4
  48. data/lib/sequel/model/plugins.rb +5 -0
  49. data/lib/sequel/plugins/association_proxies.rb +2 -0
  50. data/lib/sequel/plugins/auto_validations.rb +15 -1
  51. data/lib/sequel/plugins/class_table_inheritance.rb +0 -5
  52. data/lib/sequel/plugins/composition.rb +5 -1
  53. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  54. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  55. data/lib/sequel/plugins/dirty.rb +44 -0
  56. data/lib/sequel/plugins/nested_attributes.rb +3 -1
  57. data/lib/sequel/plugins/pg_array_associations.rb +4 -0
  58. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
  59. data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
  60. data/lib/sequel/plugins/tree.rb +9 -4
  61. data/lib/sequel/plugins/validation_helpers.rb +6 -2
  62. data/lib/sequel/timezones.rb +8 -3
  63. data/lib/sequel/version.rb +1 -1
  64. metadata +33 -21
@@ -51,7 +51,19 @@ module Sequel
51
51
  # Raise a Sequel::AdapterNotFound if evaluating the class name raises a NameError.
52
52
  def self.load_driver(drv, gem=nil)
53
53
  load_gem(gem) if gem
54
- eval drv
54
+ if drv.is_a?(String)
55
+ eval drv
56
+ else
57
+ *try, last = drv
58
+ try.each do |try_drv|
59
+ begin
60
+ return eval(try_drv)
61
+ rescue NameError
62
+ end
63
+ end
64
+
65
+ eval last
66
+ end
55
67
  rescue NameError
56
68
  raise Sequel::AdapterNotFound, "#{drv} not loaded#{", try installing jdbc-#{gem.to_s.downcase} gem" if gem}"
57
69
  end
@@ -59,11 +71,11 @@ module Sequel
59
71
  class TypeConvertor
60
72
  CONVERTORS = convertors = {}
61
73
  %w'Boolean Float Double Int Long Short'.each do |meth|
62
- x = convertors[meth.to_sym] = Object.new
74
+ x = x = convertors[meth.to_sym] = Object.new
63
75
  class_eval("def x.call(r, i) v = r.get#{meth}(i); v unless r.wasNull end", __FILE__, __LINE__)
64
76
  end
65
77
  %w'Object Array String Time Date Timestamp BigDecimal Blob Bytes Clob'.each do |meth|
66
- x = convertors[meth.to_sym] = Object.new
78
+ x = x = convertors[meth.to_sym] = Object.new
67
79
  class_eval("def x.call(r, i) r.get#{meth}(i) end", __FILE__, __LINE__)
68
80
  end
69
81
  x = convertors[:RubyTime] = Object.new
@@ -1,15 +1,15 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('com.mysql.jdbc.Driver', :MySQL)
4
- require_relative '../shared/mysql'
5
-
6
3
  module Sequel
7
4
  module JDBC
5
+ driver = Sequel::JDBC.load_driver(%w'com.mysql.cj.jdbc.Driver com.mysql.jdbc.Driver', :MySQL)
6
+ require_relative '../shared/mysql'
7
+
8
8
  Sequel.synchronize do
9
9
  DATABASE_SETUP[:mysql] = proc do |db|
10
10
  db.extend(Sequel::JDBC::MySQL::DatabaseMethods)
11
11
  db.extend_datasets Sequel::MySQL::DatasetMethods
12
- com.mysql.jdbc.Driver
12
+ driver
13
13
  end
14
14
  end
15
15
 
@@ -244,6 +244,16 @@ module Sequel
244
244
 
245
245
  private
246
246
 
247
+ # Add CLUSTERED or NONCLUSTERED as needed
248
+ def add_clustered_sql_fragment(sql, opts)
249
+ clustered = opts[:clustered]
250
+ unless clustered.nil?
251
+ sql += " #{'NON' unless clustered}CLUSTERED"
252
+ end
253
+
254
+ sql
255
+ end
256
+
247
257
  # Add dropping of the default constraint to the list of SQL queries.
248
258
  # This is necessary before dropping the column or changing its type.
249
259
  def add_drop_default_constraint_sql(sqls, table, column)
@@ -284,7 +294,7 @@ module Sequel
284
294
  when :set_column_null
285
295
  sch = schema(table).find{|k,v| k.to_s == op[:name].to_s}.last
286
296
  type = sch[:db_type]
287
- if [:string, :decimal].include?(sch[:type]) && !["text", "ntext"].include?(type) && (size = (sch[:max_chars] || sch[:column_size]))
297
+ if [:string, :decimal, :blob].include?(sch[:type]) && !["text", "ntext"].include?(type) && (size = (sch[:max_chars] || sch[:column_size]))
288
298
  size = "MAX" if size == -1
289
299
  type += "(#{size}#{", #{sch[:scale]}" if sch[:scale] && sch[:scale].to_i > 0})"
290
300
  end
@@ -396,6 +406,11 @@ module Sequel
396
406
  super.with_quote_identifiers(true)
397
407
  end
398
408
 
409
+ # Handle clustered and nonclustered primary keys
410
+ def primary_key_constraint_sql_fragment(opts)
411
+ add_clustered_sql_fragment(super, opts)
412
+ end
413
+
399
414
  # Use sp_rename to rename the table
400
415
  def rename_table_sql(name, new_name)
401
416
  "sp_rename #{literal(quote_schema_table(name))}, #{quote_identifier(schema_and_table(new_name).pop)}"
@@ -492,6 +507,11 @@ module Sequel
492
507
  :'varbinary(max)'
493
508
  end
494
509
 
510
+ # Handle clustered and nonclustered unique constraints
511
+ def unique_constraint_sql_fragment(opts)
512
+ add_clustered_sql_fragment(super, opts)
513
+ end
514
+
495
515
  # MSSQL supports views with check option, but not local.
496
516
  def view_with_check_option_support
497
517
  true
@@ -184,7 +184,7 @@ module Sequel
184
184
 
185
185
  def create_table_from_generator(name, generator, options)
186
186
  drop_statement, create_statements = create_table_sql_list(name, generator, options)
187
- (execute_ddl(drop_statement) rescue nil) if drop_statement
187
+ swallow_database_error{execute_ddl(drop_statement)} if drop_statement
188
188
  create_statements.each{|sql| execute_ddl(sql)}
189
189
  end
190
190
 
@@ -781,7 +781,7 @@ module Sequel
781
781
  return @server_version if @server_version
782
782
  ds = dataset
783
783
  ds = ds.server(server) if server
784
- @server_version ||= ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value rescue 0
784
+ @server_version = swallow_database_error{ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value} || 0
785
785
  end
786
786
 
787
787
  # PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
@@ -846,7 +846,7 @@ module Sequel
846
846
  # :schema :: The schema to search
847
847
  # :server :: The server to use
848
848
  def tables(opts=OPTS, &block)
849
- pg_class_relname('r', opts, &block)
849
+ pg_class_relname(['r', 'p'], opts, &block)
850
850
  end
851
851
 
852
852
  # Check whether the given type name string/symbol (e.g. :hstore) is supported by
@@ -1500,9 +1500,11 @@ module Sequel
1500
1500
  # disallowed or there is a size specified, use the varchar type.
1501
1501
  # Otherwise use the text type.
1502
1502
  def type_literal_generic_string(column)
1503
- if column[:fixed]
1503
+ if column[:text]
1504
+ :text
1505
+ elsif column[:fixed]
1504
1506
  "char(#{column[:size]||255})"
1505
- elsif column[:text] == false or column[:size]
1507
+ elsif column[:text] == false || column[:size]
1506
1508
  "varchar(#{column[:size]||255})"
1507
1509
  else
1508
1510
  :text
@@ -561,7 +561,7 @@ module Sequel
561
561
  Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
562
562
  Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
563
563
  Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'with values compounds'], ['else', %w'with select distinct columns from join where group having window compounds order limit lock']])
564
- Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
564
+ Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 33300', %w'with update table set from where'], ['elsif db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
565
565
 
566
566
  def cast_sql_append(sql, expr, type)
567
567
  if type == Time or type == DateTime
@@ -753,6 +753,11 @@ module Sequel
753
753
  false
754
754
  end
755
755
 
756
+ # SQLite does not support deleting from a joined dataset
757
+ def supports_deleting_joins?
758
+ false
759
+ end
760
+
756
761
  # SQLite does not support INTERSECT ALL or EXCEPT ALL
757
762
  def supports_intersect_except_all?
758
763
  false
@@ -763,6 +768,11 @@ module Sequel
763
768
  false
764
769
  end
765
770
 
771
+ # SQLite 3.33.0 supports modifying joined datasets
772
+ def supports_modifying_joins?
773
+ db.sqlite_version >= 33300
774
+ end
775
+
766
776
  # SQLite does not support multiple columns for the IN/NOT IN operators
767
777
  def supports_multiple_column_in?
768
778
  false
@@ -825,6 +835,13 @@ module Sequel
825
835
  end
826
836
  end
827
837
 
838
+ # Raise an InvalidOperation exception if insert is not allowed for this dataset.
839
+ def check_insert_allowed!
840
+ raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
841
+ raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset?
842
+ end
843
+ alias check_delete_allowed! check_insert_allowed!
844
+
828
845
  # SQLite supports a maximum of 500 rows in a VALUES clause.
829
846
  def default_import_slice
830
847
  500
@@ -944,6 +961,23 @@ module Sequel
944
961
  def _truncate_sql(table)
945
962
  "DELETE FROM #{table}"
946
963
  end
964
+
965
+ # Use FROM to specify additional tables in an update query
966
+ def update_from_sql(sql)
967
+ if(from = @opts[:from][1..-1]).empty?
968
+ raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
969
+ else
970
+ sql << ' FROM '
971
+ source_list_append(sql, from)
972
+ select_join_sql(sql)
973
+ end
974
+ end
975
+
976
+ # Only include the primary table in the main update clause
977
+ def update_table_sql(sql)
978
+ sql << ' '
979
+ source_list_append(sql, @opts[:from][0..0])
980
+ end
947
981
  end
948
982
  end
949
983
  end
data/lib/sequel/core.rb CHANGED
@@ -52,13 +52,12 @@ module Sequel
52
52
  #
53
53
  # Sequel.datetime_class = DateTime
54
54
  #
55
- # Note that +Time+ and +DateTime+ objects
56
- # have a different API, and in cases where they implement the same methods,
57
- # they often implement them differently (e.g. + using seconds on +Time+ and
58
- # days on +DateTime+).
55
+ # Note that +Time+ and +DateTime+ objects have a different API, and in
56
+ # cases where they implement the same methods, they often implement them
57
+ # differently (e.g. + using seconds on +Time+ and days on +DateTime+).
59
58
  attr_accessor :datetime_class
60
59
 
61
- # Set whether Sequel is being used in single threaded mode. by default,
60
+ # Set whether Sequel is being used in single threaded mode. By default,
62
61
  # Sequel uses a thread-safe connection pool, which isn't as fast as the
63
62
  # single threaded connection pool, and also has some additional thread
64
63
  # safety checks. If your program will only have one thread,
@@ -67,7 +66,7 @@ module Sequel
67
66
  # Sequel.single_threaded = true
68
67
  attr_accessor :single_threaded
69
68
 
70
- # Alias of original require method, as Sequel.require is does a relative
69
+ # Alias of original require method, as Sequel.require does a relative
71
70
  # require for backwards compatibility.
72
71
  alias orig_require require
73
72
  private :orig_require
@@ -55,7 +55,6 @@ module Sequel
55
55
 
56
56
  begin
57
57
  db = c.new(opts)
58
- db.test_connection if db.send(:typecast_value_boolean, opts.fetch(:test, true))
59
58
  if block_given?
60
59
  return yield(db)
61
60
  end
@@ -166,6 +166,7 @@ module Sequel
166
166
  end
167
167
 
168
168
  initialize_load_extensions(:extensions)
169
+ test_connection if typecast_value_boolean(@opts.fetch(:test, true)) && respond_to?(:connect, true)
169
170
  rescue
170
171
  Sequel.synchronize{::Sequel::DATABASES.delete(self)} if keep_reference
171
172
  raise
@@ -446,6 +447,19 @@ module Sequel
446
447
  end
447
448
  end
448
449
 
450
+ # Swallow database errors, unless they are connect/disconnect errors.
451
+ def swallow_database_error
452
+ yield
453
+ rescue Sequel::DatabaseDisconnectError, DatabaseConnectionError
454
+ # Always raise disconnect errors
455
+ raise
456
+ rescue Sequel::DatabaseError
457
+ # Don't raise other database errors.
458
+ nil
459
+ # else
460
+ # Don't rescue other exceptions, they will be raised normally.
461
+ end
462
+
449
463
  # Typecast the value to an SQL::Blob
450
464
  def typecast_value_blob(value)
451
465
  value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
@@ -143,8 +143,14 @@ module Sequel
143
143
  # :identity :: Create an identity column.
144
144
  #
145
145
  # MySQL specific options:
146
+ #
146
147
  # :generated_type :: Set the type of column when using :generated_always_as,
147
148
  # should be :virtual or :stored to force a type.
149
+ #
150
+ # Microsoft SQL Server specific options:
151
+ #
152
+ # :clustered :: When using :primary_key or :unique, marks the primary key or unique
153
+ # constraint as CLUSTERED (if true), or NONCLUSTERED (if false).
148
154
  def column(name, type, opts = OPTS)
149
155
  columns << {:name => name, :type => type}.merge!(opts)
150
156
  if index_opts = opts[:index]
@@ -240,7 +240,7 @@ module Sequel
240
240
  if supports_create_or_replace_view?
241
241
  options = options.merge(:replace=>true)
242
242
  else
243
- drop_view(name) rescue nil
243
+ swallow_database_error{drop_view(name)}
244
244
  end
245
245
 
246
246
  create_view(name, source, options)
@@ -580,14 +580,14 @@ module Sequel
580
580
  sql << ' NULL'
581
581
  end
582
582
  end
583
-
583
+
584
584
  # Add primary key SQL fragment to column creation SQL.
585
585
  def column_definition_primary_key_sql(sql, column)
586
586
  if column[:primary_key]
587
587
  if name = column[:primary_key_constraint_name]
588
588
  sql << " CONSTRAINT #{quote_identifier(name)}"
589
589
  end
590
- sql << ' PRIMARY KEY'
590
+ sql << " " << primary_key_constraint_sql_fragment(column)
591
591
  constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
592
592
  end
593
593
  end
@@ -608,7 +608,7 @@ module Sequel
608
608
  if name = column[:unique_constraint_name]
609
609
  sql << " CONSTRAINT #{quote_identifier(name)}"
610
610
  end
611
- sql << ' UNIQUE'
611
+ sql << ' ' << unique_constraint_sql_fragment(column)
612
612
  constraint_deferrable_sql_append(sql, column[:unique_deferrable])
613
613
  end
614
614
  end
@@ -656,11 +656,11 @@ module Sequel
656
656
  check = "(#{check})" unless check[0..0] == '(' && check[-1..-1] == ')'
657
657
  sql << "CHECK #{check}"
658
658
  when :primary_key
659
- sql << "PRIMARY KEY #{literal(constraint[:columns])}"
659
+ sql << "#{primary_key_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
660
660
  when :foreign_key
661
661
  sql << column_references_table_constraint_sql(constraint.merge(:deferrable=>nil))
662
662
  when :unique
663
- sql << "UNIQUE #{literal(constraint[:columns])}"
663
+ sql << "#{unique_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
664
664
  else
665
665
  raise Error, "Invalid constraint type #{constraint[:type]}, should be :check, :primary_key, :foreign_key, or :unique"
666
666
  end
@@ -892,6 +892,11 @@ module Sequel
892
892
  on_delete_clause(action)
893
893
  end
894
894
 
895
+ # Add fragment for primary key specification, separated for easier overridding.
896
+ def primary_key_constraint_sql_fragment(_)
897
+ 'PRIMARY KEY'
898
+ end
899
+
895
900
  # Proxy the quote_schema_table method to the dataset
896
901
  def quote_schema_table(table)
897
902
  schema_utility_dataset.quote_schema_table(table)
@@ -1047,6 +1052,11 @@ module Sequel
1047
1052
  "#{type}#{literal(Array(elements)) if elements}#{' UNSIGNED' if column[:unsigned]}"
1048
1053
  end
1049
1054
 
1055
+ # Add fragment for unique specification, separated for easier overridding.
1056
+ def unique_constraint_sql_fragment(_)
1057
+ 'UNIQUE'
1058
+ end
1059
+
1050
1060
  # Whether clob should be used for String text: true columns.
1051
1061
  def uses_clob_for_text?
1052
1062
  false
@@ -82,7 +82,7 @@ module Sequel
82
82
  # :server :: The server/shard the transaction is being executed on.
83
83
  def rollback_on_exit(opts=OPTS)
84
84
  synchronize(opts[:server]) do |conn|
85
- raise Error, "Cannot call Sequel:: Database#rollback_on_exit! unless inside a transaction" unless h = _trans(conn)
85
+ raise Error, "Cannot call Sequel:: Database#rollback_on_exit unless inside a transaction" unless h = _trans(conn)
86
86
  rollback = !opts[:cancel]
87
87
 
88
88
  if supports_savepoints?
@@ -607,14 +607,16 @@ module Sequel
607
607
  # as_hash, it accepts an optional :hash parameter, into which entries will
608
608
  # be merged.
609
609
  #
610
- # DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
610
+ # DB[:table].select_hash(:id, :name)
611
+ # # SELECT id, name FROM table
611
612
  # # => {1=>'a', 2=>'b', ...}
612
613
  #
613
614
  # You can also provide an array of column names for either the key_column,
614
615
  # the value column, or both:
615
616
  #
616
- # DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
617
- # # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
617
+ # DB[:table].select_hash([:id, :foo], [:name, :bar])
618
+ # # SELECT id, foo, name, bar FROM table
619
+ # # => {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
618
620
  #
619
621
  # When using this method, you must be sure that each expression has an alias
620
622
  # that Sequel can determine.
@@ -626,14 +628,16 @@ module Sequel
626
628
  # Similar to to_hash_groups, but only selects the columns given. Like to_hash_groups,
627
629
  # it accepts an optional :hash parameter, into which entries will be merged.
628
630
  #
629
- # DB[:table].select_hash_groups(:name, :id) # SELECT id, name FROM table
631
+ # DB[:table].select_hash_groups(:name, :id)
632
+ # # SELECT id, name FROM table
630
633
  # # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
631
634
  #
632
635
  # You can also provide an array of column names for either the key_column,
633
636
  # the value column, or both:
634
637
  #
635
- # DB[:table].select_hash_groups([:first, :middle], [:last, :id]) # SELECT * FROM table
636
- # # {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
638
+ # DB[:table].select_hash_groups([:first, :middle], [:last, :id])
639
+ # # SELECT first, middle, last, id FROM table
640
+ # # => {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
637
641
  #
638
642
  # When using this method, you must be sure that each expression has an alias
639
643
  # that Sequel can determine.
@@ -51,6 +51,11 @@ module Sequel
51
51
  false
52
52
  end
53
53
 
54
+ # Whether deleting from joined datasets is supported, false by default.
55
+ def supports_deleting_joins?
56
+ supports_modifying_joins?
57
+ end
58
+
54
59
  # Whether the database supports derived column lists (e.g.
55
60
  # "table_expr AS table_alias(column_alias1, column_alias2, ...)"), true by
56
61
  # default.
@@ -178,6 +183,11 @@ module Sequel
178
183
  true
179
184
  end
180
185
 
186
+ # Whether updating joined datasets is supported, false by default.
187
+ def supports_updating_joins?
188
+ supports_modifying_joins?
189
+ end
190
+
181
191
  # Whether the dataset supports the WINDOW clause to define windows used by multiple
182
192
  # window functions, false by default.
183
193
  def supports_window_clause?
@@ -201,7 +201,9 @@ module Sequel
201
201
  when :insert_pk
202
202
  fetch_rows(prepared_sql){|r| return r.values.first}
203
203
  when Array
204
+ # :nocov:
204
205
  case prepared_type[0]
206
+ # :nocov:
205
207
  when :map, :as_hash, :to_hash, :to_hash_groups
206
208
  public_send(*prepared_type, &block)
207
209
  end