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.
- checksums.yaml +4 -4
- data/lib/sequel/adapters/amalgalite.rb +16 -1
- data/lib/sequel/adapters/postgres.rb +1 -0
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +39 -5
- data/lib/sequel/adapters/sqlite.rb +15 -1
- data/lib/sequel/ast_transformer.rb +2 -0
- data/lib/sequel/core.rb +15 -1
- data/lib/sequel/database/misc.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/dataset/actions.rb +65 -10
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/dataset/sql.rb +10 -2
- data/lib/sequel/extensions/constraint_validations.rb +3 -3
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -12
- data/lib/sequel/extensions/pg_auto_parameterize.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +2 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
- data/lib/sequel/extensions/pg_json_ops.rb +12 -5
- data/lib/sequel/extensions/pg_range.rb +11 -0
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/set_literalizer.rb +20 -39
- data/lib/sequel/extensions/split_array_nil.rb +12 -2
- data/lib/sequel/extensions/to_dot.rb +5 -0
- data/lib/sequel/model/associations.rb +7 -7
- data/lib/sequel/model/base.rb +31 -3
- data/lib/sequel/plugins/deprecated_associations.rb +151 -0
- data/lib/sequel/plugins/insert_returning_select.rb +10 -1
- data/lib/sequel/plugins/pg_array_associations.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +5 -5
- data/lib/sequel/plugins/split_values.rb +10 -0
- data/lib/sequel/plugins/static_cache.rb +13 -0
- data/lib/sequel/plugins/subset_static_cache.rb +15 -0
- data/lib/sequel/plugins/table_select.rb +7 -0
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e5d22a32d8c5f27e15e00203d0c2ce2a2fb3f5352d7b7c25c82389f4947882c
|
|
4
|
+
data.tar.gz: 81909dc070cc8d48def396ae76e953a1f31dc478b45d3a5fa8b69646bc7df094
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
|
@@ -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 = "\"#{
|
|
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 =
|
|
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)
|
|
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
|
-
|
|
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
|
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.
|
data/lib/sequel/database/misc.rb
CHANGED
|
@@ -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
|
|
17
|
-
single_record single_record!
|
|
18
|
-
|
|
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.
|
|
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
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -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?
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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($)
|