sequel 5.39.0 → 5.72.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/CHANGELOG +408 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +13 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +26 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +28 -16
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +90 -9
- data/lib/sequel/adapters/shared/mysql.rb +47 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -1
- data/lib/sequel/adapters/shared/postgres.rb +496 -178
- data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
- data/lib/sequel/adapters/shared/sqlite.rb +116 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +55 -31
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +46 -53
- data/lib/sequel/database/schema_methods.rb +18 -2
- data/lib/sequel/dataset/actions.rb +108 -14
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +171 -44
- data/lib/sequel/dataset/sql.rb +182 -47
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +439 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +11 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +32 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -3
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +11 -24
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +21 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +345 -101
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +10 -6
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +46 -12
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +132 -38
@@ -8,9 +8,10 @@
|
|
8
8
|
# DB.extension :date_arithmetic
|
9
9
|
#
|
10
10
|
# Then you can use the Sequel.date_add and Sequel.date_sub methods
|
11
|
-
# to return Sequel expressions
|
11
|
+
# to return Sequel expressions (this example shows the only supported
|
12
|
+
# keys for the second argument):
|
12
13
|
#
|
13
|
-
# add = Sequel.date_add(:date_column, years: 1, months: 2, days:
|
14
|
+
# add = Sequel.date_add(:date_column, years: 1, months: 2, weeks: 2, days: 1)
|
14
15
|
# sub = Sequel.date_sub(:date_column, hours: 1, minutes: 2, seconds: 3)
|
15
16
|
#
|
16
17
|
# In addition to specifying the interval as a hash, there is also
|
@@ -24,13 +25,17 @@
|
|
24
25
|
# By default, values are casted to the generic timestamp type for the
|
25
26
|
# database. You can override the cast type using the :cast option:
|
26
27
|
#
|
27
|
-
# add = Sequel.date_add(:date_column, {years: 1, months: 2, days: 3}, :
|
28
|
+
# add = Sequel.date_add(:date_column, {years: 1, months: 2, days: 3}, cast: :timestamptz)
|
28
29
|
#
|
29
30
|
# These expressions can be used in your datasets, or anywhere else that
|
30
31
|
# Sequel expressions are allowed:
|
31
32
|
#
|
32
33
|
# DB[:table].select(add.as(:d)).where(sub > Sequel::CURRENT_TIMESTAMP)
|
33
34
|
#
|
35
|
+
# On most databases, the values you provide for years/months/days/etc. must
|
36
|
+
# be numeric values and not arbitrary SQL expressions. However, on PostgreSQL
|
37
|
+
# 9.4+, use of arbitrary SQL expressions is supported.
|
38
|
+
#
|
34
39
|
# Related module: Sequel::SQL::DateAdd
|
35
40
|
|
36
41
|
#
|
@@ -49,14 +54,21 @@ module Sequel
|
|
49
54
|
# Options:
|
50
55
|
# :cast :: Cast to the specified type instead of the default if casting
|
51
56
|
def date_sub(expr, interval, opts=OPTS)
|
52
|
-
|
53
|
-
|
54
|
-
interval.each{|k,v| h[k] = -v unless v.nil?}
|
55
|
-
h
|
56
|
-
else
|
57
|
-
-interval
|
57
|
+
if defined?(ActiveSupport::Duration) && interval.is_a?(ActiveSupport::Duration)
|
58
|
+
interval = interval.parts
|
58
59
|
end
|
59
|
-
|
60
|
+
parts = {}
|
61
|
+
interval.each do |k,v|
|
62
|
+
case v
|
63
|
+
when nil
|
64
|
+
# ignore
|
65
|
+
when Numeric
|
66
|
+
parts[k] = -v
|
67
|
+
else
|
68
|
+
parts[k] = Sequel::SQL::NumericExpression.new(:*, v, -1)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
DateAdd.new(expr, parts, opts)
|
60
72
|
end
|
61
73
|
end
|
62
74
|
|
@@ -69,6 +81,7 @@ module Sequel
|
|
69
81
|
module DatasetMethods
|
70
82
|
DURATION_UNITS = [:years, :months, :days, :hours, :minutes, :seconds].freeze
|
71
83
|
DEF_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s.freeze}).freeze
|
84
|
+
POSTGRES_DURATION_UNITS = DURATION_UNITS.zip([:years, :months, :days, :hours, :mins, :secs].map{|s| s.to_s.freeze}).freeze
|
72
85
|
MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
|
73
86
|
MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
|
74
87
|
H2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s[0...-1].freeze}).freeze
|
@@ -88,14 +101,28 @@ module Sequel
|
|
88
101
|
|
89
102
|
cast = case db_type = db.database_type
|
90
103
|
when :postgres
|
91
|
-
|
92
|
-
|
93
|
-
|
104
|
+
casted = Sequel.cast(expr, cast_type)
|
105
|
+
|
106
|
+
if db.server_version >= 90400
|
107
|
+
placeholder = []
|
108
|
+
vals = []
|
109
|
+
each_valid_interval_unit(h, POSTGRES_DURATION_UNITS) do |value, sql_unit|
|
110
|
+
placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
|
111
|
+
vals << value
|
112
|
+
end
|
113
|
+
interval = Sequel.function(:make_interval, Sequel.lit(placeholder, *vals)) unless vals.empty?
|
114
|
+
else
|
115
|
+
parts = String.new
|
116
|
+
each_valid_interval_unit(h, DEF_DURATION_UNITS) do |value, sql_unit|
|
117
|
+
parts << "#{value} #{sql_unit} "
|
118
|
+
end
|
119
|
+
interval = Sequel.cast(parts, :interval) unless parts.empty?
|
94
120
|
end
|
95
|
-
|
96
|
-
|
121
|
+
|
122
|
+
if interval
|
123
|
+
return complex_expression_sql_append(sql, :+, [casted, interval])
|
97
124
|
else
|
98
|
-
return
|
125
|
+
return literal_append(sql, casted)
|
99
126
|
end
|
100
127
|
when :sqlite
|
101
128
|
args = [expr]
|
@@ -113,12 +140,12 @@ module Sequel
|
|
113
140
|
end
|
114
141
|
when :mssql, :h2, :access, :sqlanywhere
|
115
142
|
units = case db_type
|
116
|
-
when :mssql, :sqlanywhere
|
117
|
-
MSSQL_DURATION_UNITS
|
118
143
|
when :h2
|
119
144
|
H2_DURATION_UNITS
|
120
145
|
when :access
|
121
146
|
ACCESS_DURATION_UNITS
|
147
|
+
else
|
148
|
+
MSSQL_DURATION_UNITS
|
122
149
|
end
|
123
150
|
each_valid_interval_unit(h, units) do |value, sql_unit|
|
124
151
|
expr = Sequel.function(:DATEADD, sql_unit, value, expr)
|
@@ -186,22 +213,35 @@ module Sequel
|
|
186
213
|
# ActiveSupport::Duration :: Converted to a hash using the interval's parts.
|
187
214
|
def initialize(expr, interval, opts=OPTS)
|
188
215
|
@expr = expr
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
216
|
+
|
217
|
+
h = Hash.new(0)
|
218
|
+
interval = interval.parts unless interval.is_a?(Hash)
|
219
|
+
interval.each do |unit, value|
|
220
|
+
# skip nil values
|
221
|
+
next unless value
|
222
|
+
|
223
|
+
# Convert weeks to days, as ActiveSupport::Duration can use weeks,
|
224
|
+
# but the database-specific literalizers only support days.
|
225
|
+
if unit == :weeks
|
226
|
+
unit = :days
|
227
|
+
value *= 7
|
228
|
+
end
|
229
|
+
|
230
|
+
unless DatasetMethods::DURATION_UNITS.include?(unit)
|
231
|
+
raise Sequel::Error, "Invalid key used in DateAdd interval hash: #{unit.inspect}"
|
232
|
+
end
|
233
|
+
|
234
|
+
# Attempt to prevent SQL injection by users who pass untrusted strings
|
235
|
+
# as interval values. It doesn't make sense to support literal strings,
|
236
|
+
# due to the numeric adding below.
|
237
|
+
if value.is_a?(String)
|
238
|
+
raise Sequel::InvalidValue, "cannot provide String value as interval part: #{value.inspect}"
|
196
239
|
end
|
197
|
-
|
198
|
-
|
199
|
-
h = Hash.new(0)
|
200
|
-
interval.parts.each{|unit, value| h[unit] += value}
|
201
|
-
Hash[h]
|
240
|
+
|
241
|
+
h[unit] += value
|
202
242
|
end
|
203
243
|
|
204
|
-
@interval.freeze
|
244
|
+
@interval = Hash[h].freeze
|
205
245
|
@cast_type = opts[:cast] if opts[:cast]
|
206
246
|
freeze
|
207
247
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The date_parse_input_handler extension allows for configuring how input
|
4
|
+
# to date parsing methods should be handled. By default, the
|
5
|
+
# extension does not change behavior. However, you can use the
|
6
|
+
# +Sequel.date_parse_input_handler+ method to support custom handling
|
7
|
+
# of input strings to the date parsing methods. For example, if you want
|
8
|
+
# to implement a length check to prevent denial of service vulnerabilities
|
9
|
+
# in older versions of Ruby, you can do:
|
10
|
+
#
|
11
|
+
# Sequel.extension :date_parse_input_handler
|
12
|
+
# Sequel.date_parse_input_handler do |string|
|
13
|
+
# raise Sequel::InvalidValue, "string length (200) exceeds the limit 128" if string.bytesize > 128
|
14
|
+
# string
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# You can also use +Sequel.date_parse_input_handler+ to modify the string
|
18
|
+
# that will be passed to the parsing methods. For example, you could
|
19
|
+
# truncate it:
|
20
|
+
#
|
21
|
+
# Sequel.date_parse_input_handler do |string|
|
22
|
+
# string.b[0, 128]
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Be aware that modern versions of Ruby will raise an exception if
|
26
|
+
# date parsing input exceeds 128 bytes.
|
27
|
+
|
28
|
+
module Sequel
|
29
|
+
module DateParseInputHandler
|
30
|
+
def date_parse_input_handler(&block)
|
31
|
+
singleton_class.class_eval do
|
32
|
+
define_method(:handle_date_parse_input, &block)
|
33
|
+
private :handle_date_parse_input
|
34
|
+
alias handle_date_parse_input handle_date_parse_input
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Call date parse input handler with input string.
|
39
|
+
def string_to_date(string)
|
40
|
+
super(handle_date_parse_input(string))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Call date parse input handler with input string.
|
44
|
+
def string_to_datetime(string)
|
45
|
+
super(handle_date_parse_input(string))
|
46
|
+
end
|
47
|
+
|
48
|
+
# Call date parse input handler with input string.
|
49
|
+
def string_to_time(string)
|
50
|
+
super(handle_date_parse_input(string))
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Call date parse input handler with input string.
|
56
|
+
def _date_parse(string)
|
57
|
+
super(handle_date_parse_input(string))
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return string as-is by default, so by default behavior does not change.
|
61
|
+
def handle_date_parse_input(string)
|
62
|
+
string
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
extend DateParseInputHandler
|
67
|
+
end
|
@@ -19,7 +19,11 @@ module Sequel::DateTimeParseToTime
|
|
19
19
|
# Use DateTime.parse.to_time to do the conversion if the input a string and is assumed to
|
20
20
|
# be in UTC and there is no offset information in the string.
|
21
21
|
def convert_input_timestamp(v, input_timezone)
|
22
|
-
if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !
|
22
|
+
if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !_date_parse(v).has_key?(:offset)
|
23
|
+
# :nocov:
|
24
|
+
# Whether this is fully branch covered depends on the order in which the specs are run.
|
25
|
+
v = handle_date_parse_input(v) if respond_to?(:handle_date_parse_input, true)
|
26
|
+
# :nocov:
|
23
27
|
t = DateTime.parse(v).to_time
|
24
28
|
case application_timezone
|
25
29
|
when nil, :local
|
@@ -44,7 +44,7 @@ module Sequel
|
|
44
44
|
# :nocov:
|
45
45
|
|
46
46
|
# Customize handling of duplicate columns for this dataset.
|
47
|
-
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless
|
47
|
+
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless defined?(yield); nil), &block)
|
48
48
|
raise Error, "Cannot provide both an argument and a block to on_duplicate_columns" if handler && block
|
49
49
|
clone(:on_duplicate_columns=>handler||block)
|
50
50
|
end
|
@@ -56,7 +56,11 @@ module Sequel
|
|
56
56
|
|
57
57
|
# Dump the index cache to the filename given in Marshal format.
|
58
58
|
def dump_index_cache(file)
|
59
|
-
|
59
|
+
indexes = {}
|
60
|
+
@indexes.sort.each do |k, v|
|
61
|
+
indexes[k] = v
|
62
|
+
end
|
63
|
+
File.open(file, 'wb'){|f| f.write(Marshal.dump(indexes))}
|
60
64
|
nil
|
61
65
|
end
|
62
66
|
|
@@ -102,9 +102,17 @@ class String
|
|
102
102
|
# Yield the Inflections module if a block is given, and return
|
103
103
|
# the Inflections module.
|
104
104
|
def self.inflections
|
105
|
-
yield Inflections if
|
105
|
+
yield Inflections if defined?(yield)
|
106
106
|
Inflections
|
107
107
|
end
|
108
|
+
|
109
|
+
%w'classify constantize dasherize demodulize foreign_key humanize pluralize singularize tableize underscore'.each do |m|
|
110
|
+
# :nocov:
|
111
|
+
if method_defined?(m)
|
112
|
+
alias_method(m, m)
|
113
|
+
end
|
114
|
+
# :nocov:
|
115
|
+
end
|
108
116
|
|
109
117
|
# By default, camelize converts the string to UpperCamelCase. If the argument to camelize
|
110
118
|
# is set to :lower then camelize produces lowerCamelCase.
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The is_distinct_from extension adds the ability to use the
|
4
|
+
# SQL standard IS DISTINCT FROM operator, which is similar to the
|
5
|
+
# not equals operator, except that NULL values are considered
|
6
|
+
# equal. PostgreSQL, SQLite 3.39+, and H2 currently support this operator. On
|
7
|
+
# other databases, support is emulated.
|
8
|
+
#
|
9
|
+
# First, you need to load the extension into the database:
|
10
|
+
#
|
11
|
+
# DB.extension :is_distinct_from
|
12
|
+
#
|
13
|
+
# Then you can use the Sequel.is_distinct_from to create the expression
|
14
|
+
# objects:
|
15
|
+
#
|
16
|
+
# expr = Sequel.is_distinct_from(:column_a, :column_b)
|
17
|
+
# # (column_a IS DISTINCT FROM column_b)
|
18
|
+
#
|
19
|
+
# You can also use the +is_distinct_from+ method on most Sequel expressions:
|
20
|
+
#
|
21
|
+
# expr = Sequel[:column_a].is_distinct_from(:column_b)
|
22
|
+
# # (column_a IS DISTINCT FROM column_b)
|
23
|
+
#
|
24
|
+
# These expressions can be used in your datasets, or anywhere else that
|
25
|
+
# Sequel expressions are allowed:
|
26
|
+
#
|
27
|
+
# DB[:table].where(expr)
|
28
|
+
#
|
29
|
+
# Related module: Sequel::SQL::IsDistinctFrom
|
30
|
+
|
31
|
+
#
|
32
|
+
module Sequel
|
33
|
+
module SQL
|
34
|
+
module Builders
|
35
|
+
# Return a IsDistinctFrom expression object, using the IS DISTINCT FROM operator
|
36
|
+
# with the given left hand side and right hand side.
|
37
|
+
def is_distinct_from(lhs, rhs)
|
38
|
+
BooleanExpression.new(:NOOP, IsDistinctFrom.new(lhs, rhs))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Represents an SQL expression using the IS DISTINCT FROM operator.
|
43
|
+
class IsDistinctFrom < GenericExpression
|
44
|
+
# These methods are added to expressions, allowing them to return IS DISTINCT
|
45
|
+
# FROM expressions based on the receiving expression.
|
46
|
+
module Methods
|
47
|
+
# Return a IsDistinctFrom expression, using the IS DISTINCT FROM operator,
|
48
|
+
# with the receiver as the left hand side and the argument as the right hand side.
|
49
|
+
def is_distinct_from(rhs)
|
50
|
+
BooleanExpression.new(:NOOP, IsDistinctFrom.new(self, rhs))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# These methods are added to datasets using the is_distinct_from extension
|
55
|
+
# extension, for the purposes of correctly literalizing IsDistinctFrom
|
56
|
+
# expressions for the appropriate database type.
|
57
|
+
module DatasetMethods
|
58
|
+
# Append the SQL fragment for the IS DISTINCT FROM expression to the SQL query.
|
59
|
+
def is_distinct_from_sql_append(sql, idf)
|
60
|
+
lhs = idf.lhs
|
61
|
+
rhs = idf.rhs
|
62
|
+
|
63
|
+
if supports_is_distinct_from?
|
64
|
+
sql << "("
|
65
|
+
literal_append(sql, lhs)
|
66
|
+
sql << " IS DISTINCT FROM "
|
67
|
+
literal_append(sql, rhs)
|
68
|
+
sql << ")"
|
69
|
+
elsif db.database_type == :derby && (lhs == nil || rhs == nil)
|
70
|
+
if lhs == nil && rhs == nil
|
71
|
+
sql << literal_false
|
72
|
+
elsif lhs == nil
|
73
|
+
literal_append(sql, ~Sequel.expr(rhs=>nil))
|
74
|
+
else
|
75
|
+
literal_append(sql, ~Sequel.expr(lhs=>nil))
|
76
|
+
end
|
77
|
+
else
|
78
|
+
literal_append(sql, Sequel.case({(Sequel.expr(lhs=>rhs) | [[lhs, nil], [rhs, nil]]) => 0}, 1) => 1)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Whether the database supports IS DISTINCT FROM.
|
85
|
+
def supports_is_distinct_from?
|
86
|
+
if defined?(super)
|
87
|
+
return super
|
88
|
+
end
|
89
|
+
|
90
|
+
case db.database_type
|
91
|
+
when :postgres, :h2
|
92
|
+
true
|
93
|
+
when :sqlite
|
94
|
+
db.sqlite_version >= 33900
|
95
|
+
else
|
96
|
+
false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# The left hand side of the IS DISTINCT FROM expression.
|
102
|
+
attr_reader :lhs
|
103
|
+
|
104
|
+
# The right hand side of the IS DISTINCT FROM expression.
|
105
|
+
attr_reader :rhs
|
106
|
+
|
107
|
+
def initialize(lhs, rhs)
|
108
|
+
@lhs = lhs
|
109
|
+
@rhs = rhs
|
110
|
+
end
|
111
|
+
|
112
|
+
to_s_method :is_distinct_from_sql
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class SQL::GenericExpression
|
117
|
+
include SQL::IsDistinctFrom::Methods
|
118
|
+
end
|
119
|
+
|
120
|
+
class LiteralString
|
121
|
+
include SQL::IsDistinctFrom::Methods
|
122
|
+
end
|
123
|
+
|
124
|
+
Dataset.register_extension(:is_distinct_from, SQL::IsDistinctFrom::DatasetMethods)
|
125
|
+
end
|
126
|
+
|
127
|
+
# :nocov:
|
128
|
+
if Sequel.core_extensions?
|
129
|
+
class Symbol
|
130
|
+
include Sequel::SQL::IsDistinctFrom::Methods
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
if defined?(Sequel::CoreRefinements)
|
135
|
+
module Sequel::CoreRefinements
|
136
|
+
refine Symbol do
|
137
|
+
send INCLUDE_METH, Sequel::SQL::IsDistinctFrom::Methods
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
# :nocov:
|
@@ -8,6 +8,9 @@
|
|
8
8
|
# :decimal :: use 0.0 for unsupported strings
|
9
9
|
# :string :: silently allow hash and array conversion to string
|
10
10
|
#
|
11
|
+
# This also removes bytesize checks for string inputs for float, integer
|
12
|
+
# and decimal conversions.
|
13
|
+
#
|
11
14
|
# To load the extension into the database:
|
12
15
|
#
|
13
16
|
# DB.extension :looser_typecasting
|
@@ -68,7 +68,9 @@ module Sequel
|
|
68
68
|
# Allow calling private methods for backwards compatibility
|
69
69
|
@db.send(method_sym, *args, &block)
|
70
70
|
end
|
71
|
+
# :nocov:
|
71
72
|
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
73
|
+
# :nocov:
|
72
74
|
|
73
75
|
# This object responds to all methods the database responds to.
|
74
76
|
def respond_to_missing?(meth, include_private)
|
@@ -268,6 +270,10 @@ module Sequel
|
|
268
270
|
def rename_column(name, new_name)
|
269
271
|
@actions << [:rename_column, new_name, name]
|
270
272
|
end
|
273
|
+
|
274
|
+
def set_column_allow_null(name, allow_null=true)
|
275
|
+
@actions << [:set_column_allow_null, name, !allow_null]
|
276
|
+
end
|
271
277
|
end
|
272
278
|
|
273
279
|
# The preferred method for writing Sequel migrations, using a DSL:
|
@@ -375,7 +381,7 @@ module Sequel
|
|
375
381
|
# Raise a NotCurrentError unless the migrator is current, takes the same
|
376
382
|
# arguments as #run.
|
377
383
|
def self.check_current(*args)
|
378
|
-
raise(NotCurrentError, '
|
384
|
+
raise(NotCurrentError, 'current migration version does not match latest available version') unless is_current?(*args)
|
379
385
|
end
|
380
386
|
|
381
387
|
# Return whether the migrator is current (i.e. it does not need to make
|
@@ -386,6 +392,9 @@ module Sequel
|
|
386
392
|
|
387
393
|
# Migrates the supplied database using the migration files in the specified directory. Options:
|
388
394
|
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
395
|
+
# It is very risky to use this option, since it can result in
|
396
|
+
# the database schema version number not matching the expected
|
397
|
+
# database schema.
|
389
398
|
# :column :: The column in the :table argument storing the migration version (default: :version).
|
390
399
|
# :current :: The current version of the database. If not given, it is retrieved from the database
|
391
400
|
# using the :table and :column options.
|
@@ -540,7 +549,7 @@ module Sequel
|
|
540
549
|
|
541
550
|
@direction = current < target ? :up : :down
|
542
551
|
|
543
|
-
if @direction == :down && @current >= @files.length
|
552
|
+
if @direction == :down && @current >= @files.length && !@allow_missing_migration_files
|
544
553
|
raise Migrator::Error, "Missing migration version(s) needed to migrate down to target version (current: #{current}, target: #{target})"
|
545
554
|
end
|
546
555
|
|
@@ -68,6 +68,10 @@ module Sequel
|
|
68
68
|
private
|
69
69
|
|
70
70
|
if RUBY_VERSION >= '2.6'
|
71
|
+
# Whether Time.at with :nsec and :in is broken. True on JRuby < 9.3.9.0.
|
72
|
+
BROKEN_TIME_AT_WITH_NSEC = defined?(JRUBY_VERSION) && (JRUBY_VERSION < '9.3' || (JRUBY_VERSION < '9.4' && JRUBY_VERSION.split('.')[2].to_i < 9))
|
73
|
+
private_constant :BROKEN_TIME_AT_WITH_NSEC
|
74
|
+
|
71
75
|
# Convert the given input Time (which must be in UTC) to the given input timezone,
|
72
76
|
# which should be a TZInfo::Timezone instance.
|
73
77
|
def convert_input_time_other(v, input_timezone)
|
@@ -76,35 +80,49 @@ module Sequel
|
|
76
80
|
raise unless disamb = tzinfo_disambiguator_for(v)
|
77
81
|
period = input_timezone.period_for_local(v, &disamb)
|
78
82
|
offset = period.utc_total_offset
|
79
|
-
|
83
|
+
# :nocov:
|
84
|
+
if BROKEN_TIME_AT_WITH_NSEC
|
85
|
+
Time.at(v.to_i - offset, :in => input_timezone) + v.nsec/1000000000.0
|
86
|
+
# :nocov:
|
87
|
+
else
|
88
|
+
Time.at(v.to_i - offset, v.nsec, :nsec, :in => input_timezone)
|
89
|
+
end
|
80
90
|
end
|
81
91
|
|
82
92
|
# Convert the given input Time to the given output timezone,
|
83
93
|
# which should be a TZInfo::Timezone instance.
|
84
94
|
def convert_output_time_other(v, output_timezone)
|
85
|
-
|
95
|
+
# :nocov:
|
96
|
+
if BROKEN_TIME_AT_WITH_NSEC
|
97
|
+
Time.at(v.to_i, :in => output_timezone) + v.nsec/1000000000.0
|
98
|
+
# :nocov:
|
99
|
+
else
|
100
|
+
Time.at(v.to_i, v.nsec, :nsec, :in => output_timezone)
|
101
|
+
end
|
86
102
|
end
|
87
|
-
else
|
88
103
|
# :nodoc:
|
89
104
|
# :nocov:
|
105
|
+
else
|
90
106
|
def convert_input_time_other(v, input_timezone)
|
91
107
|
local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
92
|
-
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
108
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + v.nsec/1000000000.0
|
93
109
|
end
|
94
110
|
|
95
111
|
if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
|
96
112
|
def convert_output_time_other(v, output_timezone)
|
97
113
|
v = output_timezone.utc_to_local(v.getutc)
|
98
114
|
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
99
|
-
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + local_offset
|
115
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + v.nsec/1000000000.0 + local_offset
|
100
116
|
end
|
101
117
|
else
|
102
118
|
def convert_output_time_other(v, output_timezone)
|
103
119
|
v = output_timezone.utc_to_local(v.getutc)
|
104
120
|
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
105
|
-
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
121
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + v.nsec/1000000000.0
|
106
122
|
end
|
107
123
|
end
|
124
|
+
# :nodoc:
|
125
|
+
# :nocov:
|
108
126
|
end
|
109
127
|
|
110
128
|
# Handle both TZInfo 1 and TZInfo 2
|
@@ -142,6 +160,8 @@ module Sequel
|
|
142
160
|
# Convert timezone offset from UTC to the offset for the output_timezone
|
143
161
|
(v - local_offset).new_offset(local_offset)
|
144
162
|
end
|
163
|
+
# :nodoc:
|
164
|
+
# :nocov:
|
145
165
|
end
|
146
166
|
|
147
167
|
# Returns TZInfo::Timezone instance if given a String.
|
@@ -54,7 +54,7 @@ module Sequel
|
|
54
54
|
# an enumerator if no block is given.
|
55
55
|
def each_page(page_size)
|
56
56
|
raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
|
57
|
-
return to_enum(:each_page, page_size) unless
|
57
|
+
return to_enum(:each_page, page_size) unless defined?(yield)
|
58
58
|
record_count = count
|
59
59
|
total_pages = (record_count / page_size.to_f).ceil
|
60
60
|
(1..total_pages).each{|page_no| yield paginate(page_no, page_size, record_count)}
|
@@ -213,6 +213,7 @@ module Sequel
|
|
213
213
|
scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, type)}"
|
214
214
|
define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)}
|
215
215
|
private meth
|
216
|
+
alias_method(meth, meth)
|
216
217
|
end
|
217
218
|
|
218
219
|
@schema_type_classes[:"#{type}_array"] = PGArray
|
@@ -227,16 +228,35 @@ module Sequel
|
|
227
228
|
when Array
|
228
229
|
"{#{a.map{|i| bound_variable_array(i)}.join(',')}}"
|
229
230
|
when Sequel::SQL::Blob
|
230
|
-
|
231
|
+
bound_variable_array_string(literal(a)[BLOB_RANGE].gsub("''", "'"))
|
231
232
|
when Sequel::LiteralString
|
232
233
|
a
|
233
234
|
when String
|
234
|
-
|
235
|
+
bound_variable_array_string(a)
|
236
|
+
when Float
|
237
|
+
if a.infinite?
|
238
|
+
a > 0 ? '"Infinity"' : '"-Infinity"'
|
239
|
+
elsif a.nan?
|
240
|
+
'"NaN"'
|
241
|
+
else
|
242
|
+
literal(a)
|
243
|
+
end
|
235
244
|
else
|
236
|
-
|
245
|
+
if (s = bound_variable_arg(a, nil)).is_a?(String)
|
246
|
+
bound_variable_array_string(s)
|
247
|
+
else
|
248
|
+
literal(a)
|
249
|
+
end
|
237
250
|
end
|
238
251
|
end
|
239
252
|
|
253
|
+
# Escape strings used as array members in bound variables. Most complex
|
254
|
+
# will create a regular string with bound_variable_arg, and then use this
|
255
|
+
# escaping to format it as an array member.
|
256
|
+
def bound_variable_array_string(s)
|
257
|
+
"\"#{s.gsub(/("|\\)/, '\\\\\1')}\""
|
258
|
+
end
|
259
|
+
|
240
260
|
# Look into both the current database's array schema types and the global
|
241
261
|
# array schema types to get the type symbol for the given database type
|
242
262
|
# string.
|
@@ -245,7 +265,7 @@ module Sequel
|
|
245
265
|
end
|
246
266
|
|
247
267
|
# Make the column type detection handle registered array types.
|
248
|
-
def
|
268
|
+
def schema_array_type(db_type)
|
249
269
|
if (db_type =~ /\A([^(]+)(?:\([^(]+\))?\[\]\z/io) && (type = pg_array_schema_type($1))
|
250
270
|
type
|
251
271
|
else
|
@@ -456,6 +476,14 @@ module Sequel
|
|
456
476
|
end
|
457
477
|
end
|
458
478
|
|
479
|
+
# Allow automatic parameterization of the receiver if all elements can be
|
480
|
+
# can be automatically parameterized.
|
481
|
+
def sequel_auto_param_type(ds)
|
482
|
+
if array_type && all?{|x| nil == x || ds.send(:auto_param_type, x)}
|
483
|
+
"::#{array_type}[]"
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
459
487
|
private
|
460
488
|
|
461
489
|
# Recursive method that handles multi-dimensional
|
@@ -108,7 +108,7 @@ module Sequel
|
|
108
108
|
|
109
109
|
# Call the ANY function:
|
110
110
|
#
|
111
|
-
# array_op.
|
111
|
+
# array_op.any # ANY(array)
|
112
112
|
#
|
113
113
|
# Usually used like:
|
114
114
|
#
|
@@ -329,7 +329,7 @@ end
|
|
329
329
|
if defined?(Sequel::CoreRefinements)
|
330
330
|
module Sequel::CoreRefinements
|
331
331
|
refine Symbol do
|
332
|
-
|
332
|
+
send INCLUDE_METH, Sequel::Postgres::ArrayOpMethods
|
333
333
|
end
|
334
334
|
end
|
335
335
|
end
|