sequel 5.96.0 → 5.99.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/adapters/amalgalite.rb +16 -1
  3. data/lib/sequel/adapters/postgres.rb +1 -0
  4. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  5. data/lib/sequel/adapters/shared/mysql.rb +1 -1
  6. data/lib/sequel/adapters/shared/postgres.rb +39 -5
  7. data/lib/sequel/adapters/sqlite.rb +15 -1
  8. data/lib/sequel/ast_transformer.rb +2 -0
  9. data/lib/sequel/core.rb +15 -1
  10. data/lib/sequel/database/misc.rb +1 -1
  11. data/lib/sequel/database/schema_generator.rb +1 -0
  12. data/lib/sequel/dataset/actions.rb +65 -10
  13. data/lib/sequel/dataset/query.rb +1 -1
  14. data/lib/sequel/dataset/sql.rb +10 -2
  15. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  16. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  17. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  18. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  19. data/lib/sequel/extensions/pg_auto_parameterize.rb +2 -2
  20. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  21. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +2 -1
  22. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  23. data/lib/sequel/extensions/pg_json_ops.rb +12 -5
  24. data/lib/sequel/extensions/pg_range.rb +11 -0
  25. data/lib/sequel/extensions/pg_row.rb +2 -2
  26. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  27. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  28. data/lib/sequel/extensions/to_dot.rb +5 -0
  29. data/lib/sequel/model/associations.rb +7 -7
  30. data/lib/sequel/model/base.rb +31 -3
  31. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  32. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  33. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  34. data/lib/sequel/plugins/rcte_tree.rb +5 -5
  35. data/lib/sequel/plugins/split_values.rb +10 -0
  36. data/lib/sequel/plugins/static_cache.rb +13 -0
  37. data/lib/sequel/plugins/subset_static_cache.rb +15 -0
  38. data/lib/sequel/plugins/table_select.rb +7 -0
  39. data/lib/sequel/sql.rb +1 -1
  40. data/lib/sequel/version.rb +1 -1
  41. metadata +3 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52123540e6fde4ed7ade970b36fc60898bae7463c0e22f63c4205748365cd82b
4
- data.tar.gz: e3374571d1e4fc490d3e2dc847679a2d8893b196c152d26f760b723605951ddd
3
+ metadata.gz: 7e5d22a32d8c5f27e15e00203d0c2ce2a2fb3f5352d7b7c25c82389f4947882c
4
+ data.tar.gz: 81909dc070cc8d48def396ae76e953a1f31dc478b45d3a5fa8b69646bc7df094
5
5
  SHA512:
6
- metadata.gz: a4a48515dcb90df9ca6dde4b657f6d1ccce0724b0bd6d09c2a36aed3f37d338fe468135f177fb7cd5f985c79a5e06a91d1459731dc2fdc21601083863d9efd1a
7
- data.tar.gz: 3c18417f6991a190781cf0098aa81dae3077f52e5fed75162352ffe10fc2f065b5b9ffad1db3bdded7ecc8afed7ea698fc1047570a98dc8a7a87470e7cc2290e
6
+ metadata.gz: c24ae48ece66511a09b2d8a5ad8b5b82e7486a4446bcb66108140b8afe449cd9c9e7645a29ca0bbe0c994d5c060a52f1ab4c41434dc068175751b1d6bd57b5dc
7
+ data.tar.gz: e8acd2e5cb4272bd47aed87e3077018dfc5ba9673856b871c8222eec6b2de1be0843840969e046efc459b52902753e9e0f0e35b95dcdcf31914d98b5f487d78a
@@ -62,11 +62,26 @@ module Sequel
62
62
 
63
63
  # Mimic the file:// uri, by having 2 preceding slashes specify a relative
64
64
  # path, and 3 preceding slashes specify an absolute path.
65
+ # Also support no preceding slashes to specify a relative path.
65
66
  def self.uri_to_options(uri) # :nodoc:
66
- { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
67
+ database = if uri.host.nil?
68
+ case uri.path
69
+ when '/'
70
+ nil
71
+ when nil
72
+ uri.opaque
73
+ else
74
+ uri.path
75
+ end
76
+ else
77
+ "#{uri.host}#{uri.path}"
78
+ end
79
+
80
+ { :database => database }
67
81
  end
68
82
  private_class_method :uri_to_options
69
83
 
84
+
70
85
  # Connect to the database. Since SQLite is a file based database,
71
86
  # the only options available are :database (to specify the database
72
87
  # name), and :timeout, to specify how long to wait for the database to
@@ -516,6 +516,7 @@ module Sequel
516
516
  end
517
517
  ensure
518
518
  conn.execute("UNLISTEN *")
519
+ true while conn.wait_for_notify(0)
519
520
  end
520
521
  end
521
522
  end
@@ -679,7 +679,7 @@ module Sequel
679
679
 
680
680
  # MSSQL uses the CONTAINS keyword for full text search
681
681
  def full_text_search(cols, terms, opts = OPTS)
682
- terms = "\"#{terms.join('" OR "')}\"" if terms.is_a?(Array)
682
+ terms = "\"#{Sequel.array_or_set_join(terms, '" OR "')}\"" if terms.is_a?(Array) || terms.is_a?(Set)
683
683
  where(Sequel.lit("CONTAINS (?, ?)", cols, terms))
684
684
  end
685
685
 
@@ -797,7 +797,7 @@ module Sequel
797
797
 
798
798
  # MySQL specific full text search syntax.
799
799
  def full_text_sql(cols, terms, opts = OPTS)
800
- terms = terms.join(' ') if terms.is_a?(Array)
800
+ terms = Sequel.array_or_set_join(terms, ' ') if terms.is_a?(Array) || terms.is_a?(Set)
801
801
  SQL::PlaceholderLiteralString.new((opts[:boolean] ? MATCH_AGAINST_BOOLEAN : MATCH_AGAINST), [Array(cols), terms])
802
802
  end
803
803
 
@@ -571,6 +571,7 @@ module Sequel
571
571
  # :if_exists :: Don't raise an error if the schema doesn't exist.
572
572
  def drop_schema(name, opts=OPTS)
573
573
  self << drop_schema_sql(name, opts)
574
+ remove_all_cached_schemas
574
575
  end
575
576
 
576
577
  # Drops a trigger from the database. Arguments:
@@ -668,12 +669,26 @@ module Sequel
668
669
  _set_constraints(' IMMEDIATE', opts)
669
670
  end
670
671
 
671
- # Use the pg_* system tables to determine indexes on a table
672
+ # Use the pg_* system tables to determine indexes on a table. Options:
673
+ #
674
+ # :include_partial :: Set to true to include partial indexes
675
+ # :invalid :: Set to true or :only to only return invalid indexes.
676
+ # Set to :include to also return both valid and invalid indexes.
677
+ # When not set or other value given, does not return invalid indexes.
672
678
  def indexes(table, opts=OPTS)
673
679
  m = output_identifier_meth
674
680
  cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
675
681
  cond[:indpred] = nil unless opts[:include_partial]
676
682
 
683
+ case opts[:invalid]
684
+ when true, :only
685
+ cond[:indisvalid] = false
686
+ when :include
687
+ # nothing
688
+ else
689
+ cond[:indisvalid] = true
690
+ end
691
+
677
692
  indexes = {}
678
693
  _indexes_ds.where_each(cond) do |r|
679
694
  i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
@@ -726,6 +741,14 @@ module Sequel
726
741
  Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
727
742
  end
728
743
 
744
+ # Rename a schema in the database. Arguments:
745
+ # name :: Current name of the schema
746
+ # opts :: New name for the schema
747
+ def rename_schema(name, new_name)
748
+ self << rename_schema_sql(name, new_name)
749
+ remove_all_cached_schemas
750
+ end
751
+
729
752
  # Refresh the materialized view with the given name.
730
753
  #
731
754
  # DB.refresh_view(:items_view)
@@ -1040,8 +1063,7 @@ module Sequel
1040
1063
  where{{
1041
1064
  indc[:relkind]=>%w'i I',
1042
1065
  ind[:indisprimary]=>false,
1043
- :indexprs=>nil,
1044
- :indisvalid=>true}}.
1066
+ :indexprs=>nil}}.
1045
1067
  order(*order).
1046
1068
  select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
1047
1069
 
@@ -1748,7 +1770,7 @@ module Sequel
1748
1770
  index_type = :gist
1749
1771
  end
1750
1772
 
1751
- "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
1773
+ "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON#{' ONLY' if index[:only]} #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
1752
1774
  end
1753
1775
 
1754
1776
  # Setup datastructures shared by all postgres adapters.
@@ -1806,6 +1828,18 @@ module Sequel
1806
1828
  super
1807
1829
  end
1808
1830
 
1831
+ # Clear all cached schema information
1832
+ def remove_all_cached_schemas
1833
+ @primary_keys.clear
1834
+ @primary_key_sequences.clear
1835
+ @schemas.clear
1836
+ end
1837
+
1838
+ # SQL for renaming a schema.
1839
+ def rename_schema_sql(name, new_name)
1840
+ "ALTER SCHEMA #{quote_identifier(name)} RENAME TO #{quote_identifier(new_name)}"
1841
+ end
1842
+
1809
1843
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
1810
1844
  # a rename table operation, so specifying a new schema in new_name will not have an effect.
1811
1845
  def rename_table_sql(name, new_name)
@@ -2139,7 +2173,7 @@ module Sequel
2139
2173
  end
2140
2174
 
2141
2175
  unless opts[:tsquery]
2142
- phrase_terms = terms.is_a?(Array) ? terms.join(' | ') : terms
2176
+ phrase_terms = terms.is_a?(Array) || terms.is_a?(Set) ? Sequel.array_or_set_join(terms, ' | ') : terms
2143
2177
 
2144
2178
  query_func = case to_tsquery = opts[:to_tsquery]
2145
2179
  when :phrase, :plain
@@ -89,8 +89,22 @@ module Sequel
89
89
 
90
90
  # Mimic the file:// uri, by having 2 preceding slashes specify a relative
91
91
  # path, and 3 preceding slashes specify an absolute path.
92
+ # Also support no preceding slashes to specify a relative path.
92
93
  def self.uri_to_options(uri) # :nodoc:
93
- { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
94
+ database = if uri.host.nil?
95
+ case uri.path
96
+ when '/'
97
+ nil
98
+ when nil
99
+ uri.opaque
100
+ else
101
+ uri.path
102
+ end
103
+ else
104
+ "#{uri.host}#{uri.path}"
105
+ end
106
+
107
+ { :database => database }
94
108
  end
95
109
 
96
110
  private_class_method :uri_to_options
@@ -26,6 +26,8 @@ module Sequel
26
26
  h = {}
27
27
  o.each{|k, val| h[v(k)] = v(val)}
28
28
  h
29
+ when Set
30
+ Set.new(o.map{|x| v(x)})
29
31
  when SQL::NumericExpression
30
32
  if o.op == :extract
31
33
  o.class.new(o.op, o.args[0], v(o.args[1]))
data/lib/sequel/core.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- %w'bigdecimal date thread time uri'.each{|f| require f}
3
+ %w'bigdecimal date set thread time uri'.each{|f| require f}
4
4
 
5
5
  # Top level module for Sequel
6
6
  #
@@ -164,6 +164,20 @@ module Sequel
164
164
  JSON::ParserError
165
165
  end
166
166
 
167
+ if RUBY_VERSION >= '3'
168
+ # Join the array or set.
169
+ def array_or_set_join(obj, arg)
170
+ obj.join(arg)
171
+ end
172
+ # :nocov:
173
+ else
174
+ def array_or_set_join(obj, arg)
175
+ obj = obj.to_a if obj.is_a?(Set)
176
+ obj.join(arg)
177
+ end
178
+ end
179
+ # :nocov:
180
+
167
181
  if RUBY_VERSION >= '3.3'
168
182
  # Create a new module using the block, and set the temporary name
169
183
  # on it using the given a containing module and name.
@@ -635,7 +635,7 @@ module Sequel
635
635
  # Typecast the value to a String
636
636
  def typecast_value_string(value)
637
637
  case value
638
- when Hash, Array
638
+ when Hash, Array, Set
639
639
  raise Sequel::InvalidValue, "invalid value for String: #{value.inspect}"
640
640
  else
641
641
  value.to_s
@@ -299,6 +299,7 @@ module Sequel
299
299
  # :opclass :: Set an opclass to use for all columns (per-column opclasses require
300
300
  # custom SQL).
301
301
  # :tablespace :: Specify tablespace for index.
302
+ # :only :: Create index only for given table, not for any child tables (PostgreSQL 11+)
302
303
  #
303
304
  # Microsoft SQL Server specific options:
304
305
  #
@@ -11,11 +11,12 @@ module Sequel
11
11
 
12
12
  # Action methods defined by Sequel that execute code on the database.
13
13
  ACTION_METHODS = (<<-METHS).split.map(&:to_sym).freeze
14
- << [] all as_hash avg count columns columns! delete each
14
+ << [] all as_hash as_set avg count columns columns! delete each
15
15
  empty? fetch_rows first first! get import insert last
16
- map max min multi_insert paged_each select_hash select_hash_groups select_map select_order_map
17
- single_record single_record! single_value single_value! sum to_hash to_hash_groups truncate update
18
- where_all where_each where_single_value
16
+ map max min multi_insert paged_each select_hash select_hash_groups
17
+ select_map select_order_map select_set single_record single_record!
18
+ single_value single_value! sum to_hash to_hash_groups
19
+ truncate update where_all where_each where_single_value
19
20
  METHS
20
21
 
21
22
  # The clone options to use when retrieving columns for a dataset.
@@ -51,6 +52,26 @@ module Sequel
51
52
  _all(block){|a| each{|r| a << r}}
52
53
  end
53
54
 
55
+ # Returns sets for column values for each record in the dataset.
56
+ #
57
+ # DB[:table].as_set(:id) # SELECT * FROM table
58
+ # # => Set[1, 2, 3, ...]
59
+ #
60
+ # You can also provide an array of column names, in which case the elements
61
+ # of the returned set are arrays (not sets):
62
+ #
63
+ # DB[:table].as_set([:id, :name]) # SELECT * FROM table
64
+ # # => Set[[1, 'A'], [2, 'B'], [3, 'C'], ...]
65
+ def as_set(column)
66
+ return naked.as_set(column) if row_proc
67
+
68
+ if column.is_a?(Array)
69
+ to_set{|r| r.values_at(*column)}
70
+ else
71
+ to_set{|r| r[column]}
72
+ end
73
+ end
74
+
54
75
  # Returns the average value for the given column/expression.
55
76
  # Uses a virtual row block if no argument is given.
56
77
  #
@@ -727,10 +748,7 @@ module Sequel
727
748
  end
728
749
 
729
750
  # Selects the column given (either as an argument or as a block), and
730
- # returns an array of all values of that column in the dataset. If you
731
- # give a block argument that returns an array with multiple entries,
732
- # the contents of the resulting array are undefined. Raises an Error
733
- # if called with both an argument and a block.
751
+ # returns an array of all values of that column in the dataset.
734
752
  #
735
753
  # DB[:table].select_map(:id) # SELECT id FROM table
736
754
  # # => [3, 5, 8, 1, ...]
@@ -768,6 +786,34 @@ module Sequel
768
786
  _select_map(column, true, &block)
769
787
  end
770
788
 
789
+ # Selects the column given (either as an argument or as a block), and
790
+ # returns a set of all values of that column in the dataset.
791
+ #
792
+ # DB[:table].select_set(:id) # SELECT id FROM table
793
+ # # => Set[3, 5, 8, 1, ...]
794
+ #
795
+ # DB[:table].select_set{id * 2} # SELECT (id * 2) FROM table
796
+ # # => Set[6, 10, 16, 2, ...]
797
+ #
798
+ # You can also provide an array of column names, which returns a set
799
+ # with array elements (not set elements):
800
+ #
801
+ # DB[:table].select_map([:id, :name]) # SELECT id, name FROM table
802
+ # # => Set[[1, 'A'], [2, 'B'], [3, 'C'], ...]
803
+ #
804
+ # If you provide an array of expressions, you must be sure that each entry
805
+ # in the array has an alias that Sequel can determine.
806
+ def select_set(column=nil, &block)
807
+ ds = ungraphed.naked
808
+ columns = Array(column)
809
+ virtual_row_columns(columns, block)
810
+ if column.is_a?(Array) || (columns.length > 1)
811
+ ds.select(*columns)._select_set_multiple(hash_key_symbols(columns))
812
+ else
813
+ ds.select(auto_alias_expression(columns.first))._select_set_single
814
+ end
815
+ end
816
+
771
817
  # Limits the dataset to one record, and returns the first record in the dataset,
772
818
  # or nil if the dataset has no records. Users should probably use +first+ instead of
773
819
  # this method. Example:
@@ -1092,6 +1138,17 @@ module Sequel
1092
1138
  map{|r| r[k||=r.keys.first]}
1093
1139
  end
1094
1140
 
1141
+ # Return a set of arrays of values given by the symbols in ret_cols.
1142
+ def _select_set_multiple(ret_cols)
1143
+ to_set{|r| r.values_at(*ret_cols)}
1144
+ end
1145
+
1146
+ # Returns a set of the first value in each row.
1147
+ def _select_set_single
1148
+ k = nil
1149
+ to_set{|r| r[k||=r.keys.first]}
1150
+ end
1151
+
1095
1152
  # A dataset for returning single values from the current dataset.
1096
1153
  def single_value_ds
1097
1154
  clone(:limit=>1).ungraphed.naked
@@ -1256,8 +1313,6 @@ module Sequel
1256
1313
  # receiver's current order. This yields the row and the array of order expressions
1257
1314
  # to the block, which should return an array of values to use.
1258
1315
  def ignore_values_preceding(row)
1259
- @opts[:order].map{|v| v.is_a?(SQL::OrderedExpression) ? v.expression : v}
1260
-
1261
1316
  order_exprs = @opts[:order].map do |v|
1262
1317
  if v.is_a?(SQL::OrderedExpression)
1263
1318
  descending = v.descending
@@ -1496,7 +1496,7 @@ module Sequel
1496
1496
  end
1497
1497
  when LiteralString
1498
1498
  LiteralString.new("(#{expr})")
1499
- when Numeric, SQL::NumericExpression, SQL::StringExpression, Proc, String
1499
+ when Numeric, SQL::NumericExpression, SQL::StringExpression, Proc, String, Set
1500
1500
  raise Error, "Invalid filter expression: #{expr.inspect}"
1501
1501
  when TrueClass, FalseClass
1502
1502
  if supports_where_true?
@@ -85,6 +85,8 @@ module Sequel
85
85
  literal_date_append(sql, v)
86
86
  when Dataset
87
87
  literal_dataset_append(sql, v)
88
+ when Set
89
+ literal_set_append(sql, v)
88
90
  else
89
91
  literal_other_append(sql, v)
90
92
  end
@@ -375,9 +377,9 @@ module Sequel
375
377
  cols = args[0]
376
378
  vals = args[1]
377
379
  col_array = true if cols.is_a?(Array)
378
- if vals.is_a?(Array)
380
+ if vals.is_a?(Array) || vals.is_a?(Set)
379
381
  val_array = true
380
- empty_val_array = vals == []
382
+ empty_val_array = vals.empty?
381
383
  end
382
384
  if empty_val_array
383
385
  literal_append(sql, empty_array_value(op, cols))
@@ -1448,6 +1450,12 @@ module Sequel
1448
1450
  end
1449
1451
  end
1450
1452
 
1453
+ # Append a literalization of the set to SQL string.
1454
+ # Treats as an expression as an SQL value list.
1455
+ def literal_set_append(sql, v)
1456
+ array_sql_append(sql, v)
1457
+ end
1458
+
1451
1459
  # SQL fragment for Sequel::SQLTime, containing just the time part
1452
1460
  def literal_sqltime(v)
1453
1461
  v.strftime(default_time_format)
@@ -436,7 +436,7 @@ module Sequel
436
436
  else
437
437
  raise Error, "validates includes with a range only supports integers currently, cannot handle: #{arg.inspect}"
438
438
  end
439
- elsif arg.is_a?(Array)
439
+ elsif arg.is_a?(Array) || arg.is_a?(Set)
440
440
  if arg.all?{|x| x.is_a?(Integer)}
441
441
  validation_type = :includes_int_array
442
442
  elsif arg.all?{|x| x.is_a?(String)}
@@ -444,9 +444,9 @@ module Sequel
444
444
  else
445
445
  raise Error, "validates includes with an array only supports strings and integers currently, cannot handle: #{arg.inspect}"
446
446
  end
447
- arg = arg.join(',')
447
+ arg = Sequel.array_or_set_join(arg, ',')
448
448
  else
449
- raise Error, "validates includes only supports arrays and ranges currently, cannot handle: #{arg.inspect}"
449
+ raise Error, "validates includes only supports arrays, sets, and ranges currently, cannot handle: #{arg.inspect}"
450
450
  end
451
451
  when :like, :ilike
452
452
  generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.public_send(validation_type, c, arg)})
@@ -9,6 +9,13 @@
9
9
  # DB[:test].exclude(name: [])
10
10
  # # SELECT * FROM test WHERE (name = name)
11
11
  #
12
+ # This works for sets in addition to arrays:
13
+ #
14
+ # DB[:test].where(name: Set[])
15
+ # # SELECT * FROM test WHERE (name != name)
16
+ # DB[:test].exclude(name: Set[])
17
+ # # SELECT * FROM test WHERE (name = name)
18
+ #
12
19
  # The default Sequel behavior is to ignore NULLs, as the above
13
20
  # query is not generally optimized well by databases.
14
21
  #
@@ -34,6 +34,8 @@ module Sequel
34
34
  "#{obj.class}.new(#{obj.to_a.inspect})"
35
35
  when Array
36
36
  "[#{obj.map{|o| eval_inspect(o)}.join(', ')}]"
37
+ when Set
38
+ "Set[#{obj.map{|o| eval_inspect(o)}.join(', ')}]"
37
39
  when Hash
38
40
  "{#{obj.map{|k, v| "#{eval_inspect(k)} => #{eval_inspect(v)}"}.join(', ')}}"
39
41
  when Time
@@ -37,18 +37,11 @@ module Sequel
37
37
  value.to_s
38
38
  end
39
39
 
40
- if RUBY_VERSION >= '2.4'
41
- def _typecast_value_string_to_decimal(value)
42
- BigDecimal(value)
43
- rescue
44
- BigDecimal('0.0')
45
- end
46
- else
47
- # :nocov:
48
- def _typecast_value_string_to_decimal(value)
49
- BigDecimal(value)
50
- end
51
- # :nocov:
40
+ # Typecast invalid BigDecimal to 0.0.
41
+ def _typecast_value_string_to_decimal(value)
42
+ BigDecimal(value)
43
+ rescue
44
+ BigDecimal('0.0')
52
45
  end
53
46
  end
54
47
 
@@ -408,9 +408,9 @@ module Sequel
408
408
  end
409
409
  end
410
410
 
411
- # Whether the given argument is an array of integers or NULL values, recursively.
411
+ # Whether the given argument is an array or set of integers or NULL values, recursively.
412
412
  def _integer_array?(v)
413
- Array === v && v.all?{|x| nil == x || Integer === x}
413
+ (Array === v || Set === v) && v.all?{|x| nil == x || Integer === x}
414
414
  end
415
415
 
416
416
  # Create the bound variable string that will be used for the IN (int, ...) to = ANY($)