sequel 5.45.0 → 5.77.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +434 -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 +27 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +28 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- 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/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +23 -13
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +3 -3
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +63 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +24 -22
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +56 -51
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +89 -45
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +42 -0
- data/lib/sequel/adapters/shared/mssql.rb +91 -10
- data/lib/sequel/adapters/shared/mysql.rb +78 -3
- data/lib/sequel/adapters/shared/oracle.rb +86 -7
- data/lib/sequel/adapters/shared/postgres.rb +576 -171
- data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
- data/lib/sequel/adapters/shared/sqlite.rb +92 -8
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +99 -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 +57 -31
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +70 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +11 -6
- data/lib/sequel/database/schema_methods.rb +23 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +111 -15
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +170 -41
- data/lib/sequel/dataset/sql.rb +190 -71
- 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 +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +14 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- 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 +36 -8
- 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 +11 -10
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +1 -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 +57 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +33 -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 +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
- 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 +11 -11
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +125 -2
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +13 -26
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- 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/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +286 -92
- data/lib/sequel/model/base.rb +53 -33
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +74 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +29 -8
- data/lib/sequel/plugins/composition.rb +3 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- 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 +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +8 -3
- 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 +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_array_associations.rb +46 -34
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -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 +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- 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 +41 -11
- 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 +109 -19
@@ -28,22 +28,22 @@
|
|
28
28
|
# connections on every checkout without setting up coarse
|
29
29
|
# connection checkouts will hurt performance, in some cases
|
30
30
|
# significantly. Note that setting up coarse connection
|
31
|
-
# checkouts reduces the concurrency level
|
31
|
+
# checkouts reduces the concurrency level achievable. For
|
32
32
|
# example, in a web application, using Database#synchronize
|
33
33
|
# in a rack middleware will limit the number of concurrent
|
34
34
|
# web requests to the number to connections in the database
|
35
35
|
# connection pool.
|
36
36
|
#
|
37
|
-
# Note that this extension
|
38
|
-
# and
|
39
|
-
#
|
40
|
-
# not affected. As the only reason to use the single threaded
|
37
|
+
# Note that this extension does not work with the single
|
38
|
+
# threaded and sharded single threaded connection pools.
|
39
|
+
# As the only reason to use the single threaded
|
41
40
|
# pools is for speed, and this extension makes the connection
|
42
41
|
# pool slower, there's not much point in modifying this
|
43
42
|
# extension to work with the single threaded pools. The
|
44
|
-
# threaded pools work fine even in single threaded
|
45
|
-
# you are currently using a single threaded pool
|
46
|
-
# use this extension, switch to using
|
43
|
+
# non-single threaded pools work fine even in single threaded
|
44
|
+
# code, so if you are currently using a single threaded pool
|
45
|
+
# and want to use this extension, switch to using another
|
46
|
+
# pool.
|
47
47
|
#
|
48
48
|
# Related module: Sequel::ConnectionValidator
|
49
49
|
|
@@ -61,6 +61,11 @@ module Sequel
|
|
61
61
|
|
62
62
|
# Initialize the data structures used by this extension.
|
63
63
|
def self.extended(pool)
|
64
|
+
case pool.pool_type
|
65
|
+
when :single, :sharded_single
|
66
|
+
raise Error, "cannot load connection_validator extension if using single or sharded_single connection pool"
|
67
|
+
end
|
68
|
+
|
64
69
|
pool.instance_exec do
|
65
70
|
sync do
|
66
71
|
@connection_timestamps ||= {}
|
@@ -103,8 +108,9 @@ module Sequel
|
|
103
108
|
Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout &&
|
104
109
|
!db.valid_connection?(conn)
|
105
110
|
|
106
|
-
|
107
|
-
|
111
|
+
case pool_type
|
112
|
+
when :sharded_threaded, :sharded_timed_queue
|
113
|
+
sync{@allocated[a.last].delete(Sequel.current)}
|
108
114
|
else
|
109
115
|
sync{@allocated.delete(Sequel.current)}
|
110
116
|
end
|
@@ -120,4 +126,3 @@ module Sequel
|
|
120
126
|
|
121
127
|
Database.register_extension(:connection_validator){|db| db.pool.extend(ConnectionValidator)}
|
122
128
|
end
|
123
|
-
|
@@ -126,7 +126,7 @@
|
|
126
126
|
# be emulated by dropping the table and recreating it with the constraints.
|
127
127
|
# If you want to use this plugin on SQLite with an alter_table block,
|
128
128
|
# you should drop all constraint validation metadata using
|
129
|
-
# <tt>drop_constraint_validations_for(:
|
129
|
+
# <tt>drop_constraint_validations_for(table: 'table')</tt>, and then
|
130
130
|
# readd all constraints you want to use inside the alter table block,
|
131
131
|
# making no other changes inside the alter_table block.
|
132
132
|
#
|
@@ -15,6 +15,12 @@ raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VE
|
|
15
15
|
# :nocov:
|
16
16
|
|
17
17
|
module Sequel::CoreRefinements
|
18
|
+
# :nocov:
|
19
|
+
include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
|
20
|
+
# :nocov:
|
21
|
+
INCLUDE_METH = include_meth
|
22
|
+
private_constant :INCLUDE_METH
|
23
|
+
|
18
24
|
refine Array do
|
19
25
|
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, not matching all of the
|
20
26
|
# conditions.
|
@@ -161,8 +167,8 @@ module Sequel::CoreRefinements
|
|
161
167
|
end
|
162
168
|
|
163
169
|
refine String do
|
164
|
-
|
165
|
-
|
170
|
+
send include_meth, Sequel::SQL::AliasMethods
|
171
|
+
send include_meth, Sequel::SQL::CastMethods
|
166
172
|
|
167
173
|
# Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
|
168
174
|
# literalization, e.g.:
|
@@ -189,15 +195,34 @@ module Sequel::CoreRefinements
|
|
189
195
|
end
|
190
196
|
|
191
197
|
refine Symbol do
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
198
|
+
send include_meth, Sequel::SQL::AliasMethods
|
199
|
+
send include_meth, Sequel::SQL::CastMethods
|
200
|
+
send include_meth, Sequel::SQL::OrderMethods
|
201
|
+
send include_meth, Sequel::SQL::BooleanMethods
|
202
|
+
send include_meth, Sequel::SQL::NumericMethods
|
203
|
+
|
204
|
+
# :nocov:
|
205
|
+
remove_method :* if RUBY_VERSION >= '3.1'
|
206
|
+
# :nocov:
|
207
|
+
|
208
|
+
send include_meth, Sequel::SQL::QualifyingMethods
|
209
|
+
send include_meth, Sequel::SQL::StringMethods
|
210
|
+
send include_meth, Sequel::SQL::SubscriptMethods
|
211
|
+
send include_meth, Sequel::SQL::ComplexExpressionMethods
|
212
|
+
|
213
|
+
# :nocov:
|
214
|
+
if RUBY_VERSION >= '3.1'
|
215
|
+
remove_method :*
|
216
|
+
def *(ce=(arg=false;nil))
|
217
|
+
if arg == false
|
218
|
+
Sequel::SQL::ColumnAll.new(self)
|
219
|
+
else
|
220
|
+
Sequel::SQL::NumericExpression.new(:*, self, ce)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
# :nocov:
|
201
226
|
|
202
227
|
# Returns receiver wrapped in an <tt>Sequel::SQL::Identifier</tt>.
|
203
228
|
#
|
@@ -25,13 +25,17 @@
|
|
25
25
|
# By default, values are casted to the generic timestamp type for the
|
26
26
|
# database. You can override the cast type using the :cast option:
|
27
27
|
#
|
28
|
-
# 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)
|
29
29
|
#
|
30
30
|
# These expressions can be used in your datasets, or anywhere else that
|
31
31
|
# Sequel expressions are allowed:
|
32
32
|
#
|
33
33
|
# DB[:table].select(add.as(:d)).where(sub > Sequel::CURRENT_TIMESTAMP)
|
34
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
|
+
#
|
35
39
|
# Related module: Sequel::SQL::DateAdd
|
36
40
|
|
37
41
|
#
|
@@ -54,7 +58,16 @@ module Sequel
|
|
54
58
|
interval = interval.parts
|
55
59
|
end
|
56
60
|
parts = {}
|
57
|
-
interval.each
|
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
|
58
71
|
DateAdd.new(expr, parts, opts)
|
59
72
|
end
|
60
73
|
end
|
@@ -68,6 +81,7 @@ module Sequel
|
|
68
81
|
module DatasetMethods
|
69
82
|
DURATION_UNITS = [:years, :months, :days, :hours, :minutes, :seconds].freeze
|
70
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
|
71
85
|
MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
|
72
86
|
MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
|
73
87
|
H2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s[0...-1].freeze}).freeze
|
@@ -87,14 +101,28 @@ module Sequel
|
|
87
101
|
|
88
102
|
cast = case db_type = db.database_type
|
89
103
|
when :postgres
|
90
|
-
|
91
|
-
|
92
|
-
|
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?
|
93
120
|
end
|
94
|
-
|
95
|
-
|
121
|
+
|
122
|
+
if interval
|
123
|
+
return complex_expression_sql_append(sql, :+, [casted, interval])
|
96
124
|
else
|
97
|
-
return
|
125
|
+
return literal_append(sql, casted)
|
98
126
|
end
|
99
127
|
when :sqlite
|
100
128
|
args = [expr]
|
@@ -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
|
@@ -14,12 +14,12 @@
|
|
14
14
|
#
|
15
15
|
# ds = DB[:items].extension(:duplicate_columns_handler)
|
16
16
|
#
|
17
|
-
#
|
18
|
-
# or any object that responds to :call.
|
17
|
+
# If the Database option :on_duplicate_columns is set, it configures how this
|
18
|
+
# extension works. The value should be # or any object that responds to :call.
|
19
19
|
#
|
20
|
-
# on_duplicate_columns: :raise
|
21
|
-
# on_duplicate_columns: :warn
|
22
|
-
# on_duplicate_columns: :ignore
|
20
|
+
# on_duplicate_columns: :raise # or 'raise'
|
21
|
+
# on_duplicate_columns: :warn # or 'warn'
|
22
|
+
# on_duplicate_columns: :ignore # or anything unrecognized
|
23
23
|
# on_duplicate_columns: lambda{|columns| arbitrary_condition? ? :raise : :warn}
|
24
24
|
#
|
25
25
|
# You may also configure duplicate columns handling for a specific dataset:
|
@@ -30,9 +30,10 @@
|
|
30
30
|
# ds.on_duplicate_columns{|columns| arbitrary_condition? ? :raise : :warn}
|
31
31
|
# ds.on_duplicate_columns(lambda{|columns| arbitrary_condition? ? :raise : :warn})
|
32
32
|
#
|
33
|
-
# If :raise is specified, a Sequel::DuplicateColumnError is raised.
|
34
|
-
# If :warn is specified, you will receive a warning via +warn+.
|
33
|
+
# If :raise or 'raise' is specified, a Sequel::DuplicateColumnError is raised.
|
34
|
+
# If :warn or 'warn' is specified, you will receive a warning via +warn+.
|
35
35
|
# If a callable is specified, it will be called.
|
36
|
+
# For other values, duplicate columns are ignored (Sequel's default behavior)
|
36
37
|
# If no on_duplicate_columns is specified, the default is :warn.
|
37
38
|
#
|
38
39
|
# Related module: Sequel::DuplicateColumnsHandler
|
@@ -44,7 +45,7 @@ module Sequel
|
|
44
45
|
# :nocov:
|
45
46
|
|
46
47
|
# 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
|
48
|
+
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
49
|
raise Error, "Cannot provide both an argument and a block to on_duplicate_columns" if handler && block
|
49
50
|
clone(:on_duplicate_columns=>handler||block)
|
50
51
|
end
|
@@ -64,9 +65,9 @@ module Sequel
|
|
64
65
|
message = "#{caller(*CALLER_ARGS).first}: One or more duplicate columns present in #{cols.inspect}"
|
65
66
|
|
66
67
|
case duplicate_columns_handler_type(cols)
|
67
|
-
when :raise
|
68
|
+
when :raise, 'raise'
|
68
69
|
raise DuplicateColumnError, message
|
69
|
-
when :warn
|
70
|
+
when :warn, 'warn'
|
70
71
|
warn message
|
71
72
|
end
|
72
73
|
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
|
|
@@ -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
|
@@ -159,6 +159,19 @@ module Sequel
|
|
159
159
|
migration.up = block
|
160
160
|
migration.down = MigrationReverser.new.reverse(&block)
|
161
161
|
end
|
162
|
+
|
163
|
+
# Creates a revert migration. This is the same as creating
|
164
|
+
# the same block with +down+, but it also calls the block and attempts
|
165
|
+
# to create a +up+ block that will reverse the changes made by
|
166
|
+
# the block. This is designed to revert the changes in the
|
167
|
+
# provided block.
|
168
|
+
#
|
169
|
+
# There are no guarantees that this will work perfectly
|
170
|
+
# in all cases, but it works for some simple cases.
|
171
|
+
def revert(&block)
|
172
|
+
migration.down = block
|
173
|
+
migration.up = MigrationReverser.new.reverse(&block)
|
174
|
+
end
|
162
175
|
end
|
163
176
|
|
164
177
|
# Handles the reversing of reversible migrations. Basically records
|
@@ -270,6 +283,10 @@ module Sequel
|
|
270
283
|
def rename_column(name, new_name)
|
271
284
|
@actions << [:rename_column, new_name, name]
|
272
285
|
end
|
286
|
+
|
287
|
+
def set_column_allow_null(name, allow_null=true)
|
288
|
+
@actions << [:set_column_allow_null, name, !allow_null]
|
289
|
+
end
|
273
290
|
end
|
274
291
|
|
275
292
|
# The preferred method for writing Sequel migrations, using a DSL:
|
@@ -377,7 +394,7 @@ module Sequel
|
|
377
394
|
# Raise a NotCurrentError unless the migrator is current, takes the same
|
378
395
|
# arguments as #run.
|
379
396
|
def self.check_current(*args)
|
380
|
-
raise(NotCurrentError, '
|
397
|
+
raise(NotCurrentError, 'current migration version does not match latest available version') unless is_current?(*args)
|
381
398
|
end
|
382
399
|
|
383
400
|
# Return whether the migrator is current (i.e. it does not need to make
|
@@ -388,6 +405,9 @@ module Sequel
|
|
388
405
|
|
389
406
|
# Migrates the supplied database using the migration files in the specified directory. Options:
|
390
407
|
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
408
|
+
# It is very risky to use this option, since it can result in
|
409
|
+
# the database schema version number not matching the expected
|
410
|
+
# database schema.
|
391
411
|
# :column :: The column in the :table argument storing the migration version (default: :version).
|
392
412
|
# :current :: The current version of the database. If not given, it is retrieved from the database
|
393
413
|
# using the :table and :column options.
|
@@ -475,11 +495,7 @@ module Sequel
|
|
475
495
|
@use_transactions
|
476
496
|
end
|
477
497
|
|
478
|
-
|
479
|
-
db.transaction(&block)
|
480
|
-
else
|
481
|
-
yield
|
482
|
-
end
|
498
|
+
db.transaction(:skip_transaction=>use_trans == false, &block)
|
483
499
|
end
|
484
500
|
|
485
501
|
# Load the migration file, raising an exception if the file does not define
|
@@ -542,7 +558,7 @@ module Sequel
|
|
542
558
|
|
543
559
|
@direction = current < target ? :up : :down
|
544
560
|
|
545
|
-
if @direction == :down && @current >= @files.length
|
561
|
+
if @direction == :down && @current >= @files.length && !@allow_missing_migration_files
|
546
562
|
raise Migrator::Error, "Missing migration version(s) needed to migrate down to target version (current: #{current}, target: #{target})"
|
547
563
|
end
|
548
564
|
|
@@ -677,6 +693,13 @@ module Sequel
|
|
677
693
|
@migration_tuples = get_migration_tuples
|
678
694
|
end
|
679
695
|
|
696
|
+
# Apply the migration in the given file path. See Migrator.run for the
|
697
|
+
# available options. Additionally, this method supports the :direction
|
698
|
+
# option for whether to run the migration up (default) or down.
|
699
|
+
def self.run_single(db, path, opts=OPTS)
|
700
|
+
new(db, File.dirname(path), opts).run_single(path, opts[:direction] || :up)
|
701
|
+
end
|
702
|
+
|
680
703
|
# The timestamp migrator is current if there are no migrations to apply
|
681
704
|
# in either direction.
|
682
705
|
def is_current?
|
@@ -686,20 +709,39 @@ module Sequel
|
|
686
709
|
# Apply all migration tuples on the database
|
687
710
|
def run
|
688
711
|
migration_tuples.each do |m, f, direction|
|
689
|
-
|
690
|
-
db.log_info("Begin applying migration #{f}, direction: #{direction}")
|
691
|
-
checked_transaction(m) do
|
692
|
-
m.apply(db, direction)
|
693
|
-
fi = f.downcase
|
694
|
-
direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
|
695
|
-
end
|
696
|
-
db.log_info("Finished applying migration #{f}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
|
712
|
+
apply_migration(m, f, direction)
|
697
713
|
end
|
698
714
|
nil
|
699
715
|
end
|
700
716
|
|
717
|
+
# Apply single migration tuple at the given path with the given direction
|
718
|
+
# on the database.
|
719
|
+
def run_single(path, direction)
|
720
|
+
migration = load_migration_file(path)
|
721
|
+
file_name = File.basename(path)
|
722
|
+
already_applied = applied_migrations.include?(file_name.downcase)
|
723
|
+
|
724
|
+
return if direction == :up ? already_applied : !already_applied
|
725
|
+
|
726
|
+
apply_migration(migration, file_name, direction)
|
727
|
+
nil
|
728
|
+
end
|
729
|
+
|
701
730
|
private
|
702
731
|
|
732
|
+
# Apply a single migration with the given filename in the given direction.
|
733
|
+
def apply_migration(migration, file_name, direction)
|
734
|
+
fi = file_name.downcase
|
735
|
+
t = Time.now
|
736
|
+
|
737
|
+
db.log_info("Begin applying migration #{file_name}, direction: #{direction}")
|
738
|
+
checked_transaction(migration) do
|
739
|
+
migration.apply(db, direction)
|
740
|
+
direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
|
741
|
+
end
|
742
|
+
db.log_info("Finished applying migration #{file_name}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
|
743
|
+
end
|
744
|
+
|
703
745
|
# Convert the schema_info table to the new schema_migrations table format,
|
704
746
|
# using the version of the schema_info table and the current migration files.
|
705
747
|
def convert_from_schema_info
|