sequel 5.60.1 → 5.81.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 +280 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +27 -24
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/code_order.rdoc +5 -3
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +18 -7
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +7 -2
- 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/release_notes/5.78.0.txt +67 -0
- data/doc/release_notes/5.79.0.txt +28 -0
- data/doc/release_notes/5.80.0.txt +40 -0
- data/doc/release_notes/5.81.0.txt +31 -0
- data/doc/schema_modification.rdoc +3 -3
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +14 -14
- data/doc/testing.rdoc +16 -12
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +3 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/mysql.rb +19 -7
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/postgres.rb +10 -5
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +71 -9
- data/lib/sequel/adapters/shared/mysql.rb +82 -3
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +469 -160
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +39 -3
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/adapters/trilogy.rb +117 -0
- 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/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/database/connecting.rb +25 -1
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +65 -14
- data/lib/sequel/database/query.rb +72 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +12 -5
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/dataset_module.rb +1 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/graph.rb +1 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +119 -45
- data/lib/sequel/dataset/sql.rb +58 -36
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/exceptions.rb +5 -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 +21 -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/caller_logging.rb +4 -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/date_arithmetic.rb +1 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +65 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pg_array.rb +33 -4
- 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 +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +5 -0
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_interval.rb +10 -11
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_json_ops.rb +3 -3
- data/lib/sequel/extensions/pg_multirange.rb +6 -11
- data/lib/sequel/extensions/pg_range.rb +9 -14
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/provenance.rb +110 -0
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/server_block.rb +2 -1
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +38 -11
- data/lib/sequel/model/base.rb +32 -18
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +28 -7
- data/lib/sequel/plugins/composition.rb +2 -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/finder.rb +4 -2
- data/lib/sequel/plugins/input_transformer.rb +1 -1
- data/lib/sequel/plugins/list.rb +5 -2
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- 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_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- 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/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +1 -1
- data/lib/sequel/plugins/static_cache.rb +38 -0
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +14 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +29 -2
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/version.rb +2 -2
- metadata +79 -6
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The transaction_connection_validator extension automatically
|
|
4
|
+
# retries a transaction on a connection if an disconnect error
|
|
5
|
+
# is raised when sending the statement to begin a new
|
|
6
|
+
# transaction, as long as the user has not already checked out
|
|
7
|
+
# a connection. This is safe to do because no other queries
|
|
8
|
+
# have been issued on the connection, and no user-level code
|
|
9
|
+
# is run before retrying.
|
|
10
|
+
#
|
|
11
|
+
# This approach to connection validation can be significantly
|
|
12
|
+
# lower overhead than the connection_validator extension,
|
|
13
|
+
# though it does not handle all cases handled by the
|
|
14
|
+
# connection_validator extension. However, it performs the
|
|
15
|
+
# validation checks on every new transaction, so it will
|
|
16
|
+
# automatically handle disconnected connections in some cases
|
|
17
|
+
# where the connection_validator extension will not by default
|
|
18
|
+
# (as the connection_validator extension only checks
|
|
19
|
+
# connections if they have not been used in the last hour by
|
|
20
|
+
# default).
|
|
21
|
+
#
|
|
22
|
+
# Related module: Sequel::TransactionConnectionValidator
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
module Sequel
|
|
26
|
+
module TransactionConnectionValidator
|
|
27
|
+
class DisconnectRetry < DatabaseDisconnectError
|
|
28
|
+
# The connection that raised the disconnect error
|
|
29
|
+
attr_accessor :connection
|
|
30
|
+
|
|
31
|
+
# The underlying disconnect error, in case it needs to be reraised.
|
|
32
|
+
attr_accessor :database_error
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Rescue disconnect errors raised when beginning a new transaction. If there
|
|
36
|
+
# is a disconnnect error, it should be safe to retry the transaction using a
|
|
37
|
+
# new connection, as we haven't yielded control to the user yet.
|
|
38
|
+
def transaction(opts=OPTS)
|
|
39
|
+
super
|
|
40
|
+
rescue DisconnectRetry => e
|
|
41
|
+
if synchronize(opts[:server]){|conn| conn.equal?(e.connection)}
|
|
42
|
+
# If retrying would use the same connection, that means the
|
|
43
|
+
# connection was not removed from the pool, which means the caller has
|
|
44
|
+
# already checked out the connection, and retrying will not be successful.
|
|
45
|
+
# In this case, we can only reraise the exception.
|
|
46
|
+
raise e.database_error
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
num_retries ||= 0
|
|
50
|
+
num_retries += 1
|
|
51
|
+
retry if num_retries < 5
|
|
52
|
+
|
|
53
|
+
raise e.database_error
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Reraise disconnect errors as DisconnectRetry so they can be retried.
|
|
59
|
+
def begin_new_transaction(conn, opts)
|
|
60
|
+
super
|
|
61
|
+
rescue Sequel::DatabaseDisconnectError, *database_error_classes => e
|
|
62
|
+
if e.is_a?(Sequel::DatabaseDisconnectError) || disconnect_error?(e, OPTS)
|
|
63
|
+
exception = DisconnectRetry.new(e.message)
|
|
64
|
+
exception.set_backtrace([])
|
|
65
|
+
exception.connection = conn
|
|
66
|
+
unless e.is_a?(Sequel::DatabaseError)
|
|
67
|
+
e = Sequel.convert_exception_class(e, database_error_class(e, OPTS))
|
|
68
|
+
end
|
|
69
|
+
exception.database_error = e
|
|
70
|
+
raise exception
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
raise
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
Database.register_extension(:transaction_connection_validator, TransactionConnectionValidator)
|
|
78
|
+
end
|
|
@@ -276,7 +276,7 @@ module Sequel
|
|
|
276
276
|
|
|
277
277
|
if eo[:no_results]
|
|
278
278
|
no_results = true
|
|
279
|
-
elsif eo[:eager_block] || eo[:loader] == false
|
|
279
|
+
elsif eo[:eager_block] || eo[:loader] == false || !use_placeholder_loader?
|
|
280
280
|
ds = eager_loading_dataset(eo)
|
|
281
281
|
|
|
282
282
|
strategy = ds.opts[:eager_limit_strategy] || strategy
|
|
@@ -299,13 +299,28 @@ module Sequel
|
|
|
299
299
|
strategy = :ruby if strategy == :correlated_subquery
|
|
300
300
|
strategy = nil if strategy == :ruby && assign_singular?
|
|
301
301
|
objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
|
|
302
|
+
|
|
303
|
+
if strategy == :window_function
|
|
304
|
+
delete_rn = ds.row_number_column
|
|
305
|
+
objects.each{|obj| obj.values.delete(delete_rn)}
|
|
306
|
+
end
|
|
302
307
|
elsif strategy == :union
|
|
303
308
|
objects = []
|
|
304
309
|
ds = associated_dataset
|
|
305
310
|
loader = union_eager_loader
|
|
306
311
|
joiner = " UNION ALL "
|
|
307
312
|
ids.each_slice(subqueries_per_union).each do |slice|
|
|
308
|
-
|
|
313
|
+
sql = loader.send(:sql_origin)
|
|
314
|
+
join = false
|
|
315
|
+
slice.each do |k|
|
|
316
|
+
if join
|
|
317
|
+
sql << joiner
|
|
318
|
+
else
|
|
319
|
+
join = true
|
|
320
|
+
end
|
|
321
|
+
loader.append_sql(sql, *k)
|
|
322
|
+
end
|
|
323
|
+
objects.concat(ds.with_sql(sql).to_a)
|
|
309
324
|
end
|
|
310
325
|
ds = ds.eager(cascade) if cascade
|
|
311
326
|
ds.send(:post_load, objects)
|
|
@@ -441,7 +456,7 @@ module Sequel
|
|
|
441
456
|
def placeholder_loader
|
|
442
457
|
if use_placeholder_loader?
|
|
443
458
|
cached_fetch(:placeholder_loader) do
|
|
444
|
-
|
|
459
|
+
associated_dataset.placeholder_literalizer_loader do |pl, ds|
|
|
445
460
|
ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
|
446
461
|
if self[:block]
|
|
447
462
|
ds = self[:block].call(ds)
|
|
@@ -743,8 +758,8 @@ module Sequel
|
|
|
743
758
|
# A placeholder literalizer used to speed up eager loading.
|
|
744
759
|
def placeholder_eager_loader
|
|
745
760
|
cached_fetch(:placeholder_eager_loader) do
|
|
746
|
-
|
|
747
|
-
apply_eager_limit_strategy(
|
|
761
|
+
eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
|
|
762
|
+
apply_eager_limit_strategy(ds.where(predicate_key=>pl.arg), eager_limit_strategy)
|
|
748
763
|
end
|
|
749
764
|
end
|
|
750
765
|
end
|
|
@@ -803,7 +818,7 @@ module Sequel
|
|
|
803
818
|
# loading a limited association.
|
|
804
819
|
def union_eager_loader
|
|
805
820
|
cached_fetch(:union_eager_loader) do
|
|
806
|
-
|
|
821
|
+
associated_dataset.placeholder_literalizer_loader do |pl, ds|
|
|
807
822
|
ds = self[:eager_block].call(ds) if self[:eager_block]
|
|
808
823
|
keys = predicate_keys
|
|
809
824
|
ds = ds.where(keys.map{pl.arg}.zip(keys))
|
|
@@ -817,7 +832,7 @@ module Sequel
|
|
|
817
832
|
|
|
818
833
|
# Whether the placeholder loader can be used to load the association.
|
|
819
834
|
def use_placeholder_loader?
|
|
820
|
-
self[:use_placeholder_loader]
|
|
835
|
+
self[:use_placeholder_loader] && _associated_dataset.supports_placeholder_literalizer?
|
|
821
836
|
end
|
|
822
837
|
end
|
|
823
838
|
|
|
@@ -1717,6 +1732,8 @@ module Sequel
|
|
|
1717
1732
|
# :graph_select :: A column or array of columns to select from the associated table
|
|
1718
1733
|
# when eagerly loading the association via +eager_graph+. Defaults to all
|
|
1719
1734
|
# columns in the associated table.
|
|
1735
|
+
# :graph_use_association_block :: Makes eager_graph consider the association block. Without this, eager_graph
|
|
1736
|
+
# ignores the bock and only use the :graph_* options.
|
|
1720
1737
|
# :instance_specific :: Marks the association as instance specific. Should be used if the association block
|
|
1721
1738
|
# uses instance specific state, or transient state (accessing current date/time, etc.).
|
|
1722
1739
|
# :limit :: Limit the number of records to the provided value. Use
|
|
@@ -2456,6 +2473,9 @@ module Sequel
|
|
|
2456
2473
|
# Return dataset to graph into given the association reflection, applying the :callback option if set.
|
|
2457
2474
|
def eager_graph_dataset(opts, eager_options)
|
|
2458
2475
|
ds = opts.associated_class.dataset
|
|
2476
|
+
if opts[:graph_use_association_block] && (b = opts[:block])
|
|
2477
|
+
ds = b.call(ds)
|
|
2478
|
+
end
|
|
2459
2479
|
if cb = eager_options[:callback]
|
|
2460
2480
|
ds = cb.call(ds)
|
|
2461
2481
|
end
|
|
@@ -3367,8 +3387,15 @@ module Sequel
|
|
|
3367
3387
|
local_opts = ds.opts[:eager_graph][:local]
|
|
3368
3388
|
limit_strategy = r.eager_graph_limit_strategy(local_opts[:limit_strategy])
|
|
3369
3389
|
|
|
3370
|
-
|
|
3371
|
-
|
|
3390
|
+
# SEQUEL6: remove and integrate the auto_restrict_eager_graph plugin
|
|
3391
|
+
if !r[:orig_opts].has_key?(:graph_conditions) && !r[:orig_opts].has_key?(:graph_only_conditions) && !r.has_key?(:graph_block) && !r[:allow_eager_graph]
|
|
3392
|
+
if r[:conditions] && !Sequel.condition_specifier?(r[:conditions])
|
|
3393
|
+
raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
|
|
3394
|
+
end
|
|
3395
|
+
|
|
3396
|
+
if r[:block] && !r[:graph_use_association_block]
|
|
3397
|
+
warn "eager_graph used for association when association given a block without graph options. The block is ignored in this case. This will result in an exception starting in Sequel 6. Model: #{r[:model]}, association: #{r[:name]}"
|
|
3398
|
+
end
|
|
3372
3399
|
end
|
|
3373
3400
|
|
|
3374
3401
|
ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>join_type || local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
|
|
@@ -3547,11 +3574,11 @@ module Sequel
|
|
|
3547
3574
|
end
|
|
3548
3575
|
|
|
3549
3576
|
# Eagerly load all specified associations.
|
|
3550
|
-
def eager_load(a, eager_assoc=@opts[:eager])
|
|
3577
|
+
def eager_load(a, eager_assoc=@opts[:eager], m=model)
|
|
3551
3578
|
return if a.empty?
|
|
3552
3579
|
|
|
3553
3580
|
# Reflections for all associations to eager load
|
|
3554
|
-
reflections = eager_assoc.keys.map{|assoc|
|
|
3581
|
+
reflections = eager_assoc.keys.map{|assoc| m.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
|
3555
3582
|
|
|
3556
3583
|
perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
|
|
3557
3584
|
|
data/lib/sequel/model/base.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Sequel
|
|
|
17
17
|
# natural_join, natural_left_join, natural_right_join, offset, order, order_append, order_by,
|
|
18
18
|
# order_more, order_prepend, paged_each, qualify, reverse, reverse_order, right_join,
|
|
19
19
|
# right_outer_join, select, select_all, select_append, select_group, select_hash,
|
|
20
|
-
# select_hash_groups, select_map, select_more, select_order_map, server,
|
|
20
|
+
# select_hash_groups, select_map, select_more, select_order_map, select_prepend, server,
|
|
21
21
|
# single_record, single_record!, single_value, single_value!, sum, to_hash, to_hash_groups,
|
|
22
22
|
# truncate, unfiltered, ungraphed, ungrouped, union, unlimited, unordered, where, where_all,
|
|
23
23
|
# where_each, where_single_value, with, with_recursive, with_sql
|
|
@@ -606,6 +606,7 @@ module Sequel
|
|
|
606
606
|
@db_schema = get_db_schema
|
|
607
607
|
end
|
|
608
608
|
|
|
609
|
+
@fast_pk_lookup_sql = @fast_instance_delete_sql = nil unless @dataset.supports_placeholder_literalizer?
|
|
609
610
|
reset_instance_dataset
|
|
610
611
|
self
|
|
611
612
|
end
|
|
@@ -1141,7 +1142,7 @@ module Sequel
|
|
|
1141
1142
|
#
|
|
1142
1143
|
# Artist[1] === Artist[1] # => true
|
|
1143
1144
|
# Artist.new === Artist.new # => false
|
|
1144
|
-
# Artist[1].set(:
|
|
1145
|
+
# Artist[1].set(name: 'Bob') === Artist[1] # => true
|
|
1145
1146
|
def ===(obj)
|
|
1146
1147
|
case pkv = pk
|
|
1147
1148
|
when nil
|
|
@@ -1160,7 +1161,7 @@ module Sequel
|
|
|
1160
1161
|
#
|
|
1161
1162
|
# Artist[1].pk_equal?(Artist[1]) # => true
|
|
1162
1163
|
# Artist.new.pk_equal?(Artist.new) # => false
|
|
1163
|
-
# Artist[1].set(:
|
|
1164
|
+
# Artist[1].set(name: 'Bob').pk_equal?(Artist[1]) # => true
|
|
1164
1165
|
alias pk_equal? ===
|
|
1165
1166
|
|
|
1166
1167
|
# class is defined in Object, but it is also a keyword,
|
|
@@ -1232,7 +1233,7 @@ module Sequel
|
|
|
1232
1233
|
#
|
|
1233
1234
|
# Artist[1] == Artist[1] # => true
|
|
1234
1235
|
# Artist.new == Artist.new # => true
|
|
1235
|
-
# Artist[1].set(:
|
|
1236
|
+
# Artist[1].set(name: 'Bob') == Artist[1] # => false
|
|
1236
1237
|
def eql?(obj)
|
|
1237
1238
|
(obj.class == model) && (obj.values == @values)
|
|
1238
1239
|
end
|
|
@@ -1243,18 +1244,21 @@ module Sequel
|
|
|
1243
1244
|
@errors ||= errors_class.new
|
|
1244
1245
|
end
|
|
1245
1246
|
|
|
1247
|
+
EXISTS_SELECT_ = SQL::AliasedExpression.new(1, :one)
|
|
1248
|
+
private_constant :EXISTS_SELECT_
|
|
1249
|
+
|
|
1246
1250
|
# Returns true when current instance exists, false otherwise.
|
|
1247
1251
|
# Generally an object that isn't new will exist unless it has
|
|
1248
1252
|
# been deleted. Uses a database query to check for existence,
|
|
1249
1253
|
# unless the model object is new, in which case this is always
|
|
1250
1254
|
# false.
|
|
1251
1255
|
#
|
|
1252
|
-
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
|
1256
|
+
# Artist[1].exists? # SELECT 1 AS one FROM artists WHERE (id = 1)
|
|
1253
1257
|
# # => true
|
|
1254
1258
|
# Artist.new.exists?
|
|
1255
1259
|
# # => false
|
|
1256
1260
|
def exists?
|
|
1257
|
-
new? ? false : !this.get(
|
|
1261
|
+
new? ? false : !this.get(EXISTS_SELECT_).nil?
|
|
1258
1262
|
end
|
|
1259
1263
|
|
|
1260
1264
|
# Ignore the model's setter method cache when this instances extends a module, as the
|
|
@@ -1334,13 +1338,13 @@ module Sequel
|
|
|
1334
1338
|
# a = Artist[1]
|
|
1335
1339
|
# Artist.db.transaction do
|
|
1336
1340
|
# a.lock!
|
|
1337
|
-
# a.update(:
|
|
1341
|
+
# a.update(name: 'A')
|
|
1338
1342
|
# end
|
|
1339
1343
|
#
|
|
1340
1344
|
# a = Artist[2]
|
|
1341
1345
|
# Artist.db.transaction do
|
|
1342
1346
|
# a.lock!('FOR NO KEY UPDATE')
|
|
1343
|
-
# a.update(:
|
|
1347
|
+
# a.update(name: 'B')
|
|
1344
1348
|
# end
|
|
1345
1349
|
def lock!(style=:update)
|
|
1346
1350
|
_refresh(this.lock_style(style)) unless new?
|
|
@@ -1944,8 +1948,10 @@ module Sequel
|
|
|
1944
1948
|
end
|
|
1945
1949
|
|
|
1946
1950
|
# If transactions should be used, wrap the yield in a transaction block.
|
|
1947
|
-
def checked_transaction(opts=OPTS)
|
|
1948
|
-
|
|
1951
|
+
def checked_transaction(opts=OPTS, &block)
|
|
1952
|
+
h = {:server=>this_server}.merge!(opts)
|
|
1953
|
+
h[:skip_transaction] = true unless use_transaction?(opts)
|
|
1954
|
+
db.transaction(h, &block)
|
|
1949
1955
|
end
|
|
1950
1956
|
|
|
1951
1957
|
# Change the value of the column to given value, recording the change.
|
|
@@ -2030,19 +2036,20 @@ module Sequel
|
|
|
2030
2036
|
meths = setter_methods(type)
|
|
2031
2037
|
strict = strict_param_setting
|
|
2032
2038
|
hash.each do |k,v|
|
|
2039
|
+
k = k.to_s
|
|
2033
2040
|
m = "#{k}="
|
|
2034
2041
|
if meths.include?(m)
|
|
2035
2042
|
set_column_value(m, v)
|
|
2036
2043
|
elsif strict
|
|
2037
2044
|
# Avoid using respond_to? or creating symbols from user input
|
|
2038
2045
|
if public_methods.map(&:to_s).include?(m)
|
|
2039
|
-
if Array(model.primary_key).map(&:to_s).member?(k
|
|
2040
|
-
raise MassAssignmentRestriction
|
|
2046
|
+
if Array(model.primary_key).map(&:to_s).member?(k) && model.restrict_primary_key?
|
|
2047
|
+
raise MassAssignmentRestriction.create("#{k} is a restricted primary key", self, k)
|
|
2041
2048
|
else
|
|
2042
|
-
raise MassAssignmentRestriction
|
|
2049
|
+
raise MassAssignmentRestriction.create("#{k} is a restricted column", self, k)
|
|
2043
2050
|
end
|
|
2044
2051
|
else
|
|
2045
|
-
raise MassAssignmentRestriction
|
|
2052
|
+
raise MassAssignmentRestriction.create("method #{m} doesn't exist", self, k)
|
|
2046
2053
|
end
|
|
2047
2054
|
end
|
|
2048
2055
|
end
|
|
@@ -2146,8 +2153,9 @@ module Sequel
|
|
|
2146
2153
|
# # DELETE FROM artists WHERE (id = 2)
|
|
2147
2154
|
# # ...
|
|
2148
2155
|
def destroy
|
|
2149
|
-
|
|
2150
|
-
|
|
2156
|
+
@db.transaction(:server=>opts[:server], :skip_transaction=>model.use_transactions == false) do
|
|
2157
|
+
all(&:destroy).length
|
|
2158
|
+
end
|
|
2151
2159
|
end
|
|
2152
2160
|
|
|
2153
2161
|
# If there is no order already defined on this dataset, order it by
|
|
@@ -2227,11 +2235,17 @@ module Sequel
|
|
|
2227
2235
|
|
|
2228
2236
|
private
|
|
2229
2237
|
|
|
2238
|
+
# Return the dataset ordered by the model's primary key. This should not
|
|
2239
|
+
# be used if the model does not have a primary key.
|
|
2240
|
+
def _force_primary_key_order
|
|
2241
|
+
cached_dataset(:_pk_order_ds){order(*model.primary_key)}
|
|
2242
|
+
end
|
|
2243
|
+
|
|
2230
2244
|
# If the dataset is not already ordered, and the model has a primary key,
|
|
2231
2245
|
# return a clone ordered by the primary key.
|
|
2232
2246
|
def _primary_key_order
|
|
2233
|
-
if @opts[:order].nil? && model &&
|
|
2234
|
-
|
|
2247
|
+
if @opts[:order].nil? && model && model.primary_key
|
|
2248
|
+
_force_primary_key_order
|
|
2235
2249
|
end
|
|
2236
2250
|
end
|
|
2237
2251
|
|
|
@@ -8,6 +8,9 @@ module Sequel
|
|
|
8
8
|
# automatically creates class methods for public dataset
|
|
9
9
|
# methods.
|
|
10
10
|
class DatasetModule < Dataset::DatasetModule
|
|
11
|
+
# The model class related to this dataset module.
|
|
12
|
+
attr_reader :model
|
|
13
|
+
|
|
11
14
|
# Store the model related to this dataset module.
|
|
12
15
|
def initialize(model)
|
|
13
16
|
@model = model
|
|
@@ -24,11 +24,23 @@ module Sequel
|
|
|
24
24
|
UndefinedAssociation = Class.new(Error)
|
|
25
25
|
).name
|
|
26
26
|
|
|
27
|
-
(
|
|
28
27
|
# Raised when a mass assignment method is called in strict mode with either a restricted column
|
|
29
28
|
# or a column without a setter method.
|
|
30
|
-
MassAssignmentRestriction
|
|
31
|
-
|
|
29
|
+
class MassAssignmentRestriction < Error
|
|
30
|
+
# The Sequel::Model object related to this exception.
|
|
31
|
+
attr_reader :model
|
|
32
|
+
|
|
33
|
+
# The column related to this exception, as a string.
|
|
34
|
+
attr_reader :column
|
|
35
|
+
|
|
36
|
+
# Create an instance of this class with the model and column set.
|
|
37
|
+
def self.create(msg, model, column)
|
|
38
|
+
e = new("#{msg} for class #{model.class.inspect}")
|
|
39
|
+
e.instance_variable_set(:@model, model)
|
|
40
|
+
e.instance_variable_set(:@column, column)
|
|
41
|
+
e
|
|
42
|
+
end
|
|
43
|
+
end
|
|
32
44
|
|
|
33
45
|
# Exception class raised when +raise_on_save_failure+ is set and validation fails
|
|
34
46
|
class ValidationFailed < Error
|
|
@@ -9,14 +9,16 @@ module Sequel
|
|
|
9
9
|
# 2. not_null validations on NOT NULL columns (optionally, presence validations)
|
|
10
10
|
# 3. unique validations on columns or sets of columns with unique indexes
|
|
11
11
|
# 4. max length validations on string columns
|
|
12
|
+
# 5. no null byte validations on string columns
|
|
13
|
+
# 6. minimum and maximum values on columns
|
|
12
14
|
#
|
|
13
|
-
# To determine the columns to use for the type/not_null/max_length validations,
|
|
15
|
+
# To determine the columns to use for the type/not_null/max_length/no_null_byte/max_value/min_value validations,
|
|
14
16
|
# the plugin looks at the database schema for the model's table. To determine
|
|
15
17
|
# the unique validations, Sequel looks at the indexes on the table. In order
|
|
16
18
|
# for this plugin to be fully functional, the underlying database adapter needs
|
|
17
19
|
# to support both schema and index parsing. Additionally, unique validations are
|
|
18
20
|
# only added for models that select from a simple table, they are not added for models
|
|
19
|
-
# that select from a subquery
|
|
21
|
+
# that select from a subquery.
|
|
20
22
|
#
|
|
21
23
|
# This plugin uses the validation_helpers plugin underneath to implement the
|
|
22
24
|
# validations. It does not allow for any per-column validation message
|
|
@@ -50,7 +52,7 @@ module Sequel
|
|
|
50
52
|
#
|
|
51
53
|
# Model.plugin :auto_validations, unique_opts: {only_if_modified: true}
|
|
52
54
|
#
|
|
53
|
-
# This works for unique_opts, max_length_opts, schema_types_opts,
|
|
55
|
+
# This works for unique_opts, max_length_opts, schema_types_opts, max_value_opts, min_value_opts, no_null_byte_opts,
|
|
54
56
|
# explicit_not_null_opts, and not_null_opts.
|
|
55
57
|
#
|
|
56
58
|
# If you only want auto_validations to add validations to columns that do not already
|
|
@@ -72,6 +74,19 @@ module Sequel
|
|
|
72
74
|
SCHEMA_TYPES_OPTIONS = NOT_NULL_OPTIONS
|
|
73
75
|
UNIQUE_OPTIONS = NOT_NULL_OPTIONS
|
|
74
76
|
NO_NULL_BYTE_OPTIONS = MAX_LENGTH_OPTIONS
|
|
77
|
+
MAX_VALUE_OPTIONS = {:from=>:values, :allow_nil=>true, :skip_invalid=>true}.freeze
|
|
78
|
+
MIN_VALUE_OPTIONS = MAX_VALUE_OPTIONS
|
|
79
|
+
AUTO_VALIDATE_OPTIONS = {
|
|
80
|
+
:no_null_byte=>NO_NULL_BYTE_OPTIONS,
|
|
81
|
+
:not_null=>NOT_NULL_OPTIONS,
|
|
82
|
+
:explicit_not_null=>EXPLICIT_NOT_NULL_OPTIONS,
|
|
83
|
+
:max_length=>MAX_LENGTH_OPTIONS,
|
|
84
|
+
:max_value=>MAX_VALUE_OPTIONS,
|
|
85
|
+
:min_value=>MIN_VALUE_OPTIONS,
|
|
86
|
+
:schema_types=>SCHEMA_TYPES_OPTIONS,
|
|
87
|
+
:unique=>UNIQUE_OPTIONS
|
|
88
|
+
}.freeze
|
|
89
|
+
|
|
75
90
|
EMPTY_ARRAY = [].freeze
|
|
76
91
|
|
|
77
92
|
def self.apply(model, opts=OPTS)
|
|
@@ -82,17 +97,11 @@ module Sequel
|
|
|
82
97
|
@auto_validate_not_null_columns = []
|
|
83
98
|
@auto_validate_explicit_not_null_columns = []
|
|
84
99
|
@auto_validate_max_length_columns = []
|
|
100
|
+
@auto_validate_max_value_columns = []
|
|
101
|
+
@auto_validate_min_value_columns = []
|
|
85
102
|
@auto_validate_unique_columns = []
|
|
86
103
|
@auto_validate_types = true
|
|
87
|
-
|
|
88
|
-
@auto_validate_options = {
|
|
89
|
-
:no_null_byte=>NO_NULL_BYTE_OPTIONS,
|
|
90
|
-
:not_null=>NOT_NULL_OPTIONS,
|
|
91
|
-
:explicit_not_null=>EXPLICIT_NOT_NULL_OPTIONS,
|
|
92
|
-
:max_length=>MAX_LENGTH_OPTIONS,
|
|
93
|
-
:schema_types=>SCHEMA_TYPES_OPTIONS,
|
|
94
|
-
:unique=>UNIQUE_OPTIONS
|
|
95
|
-
}.freeze
|
|
104
|
+
@auto_validate_options = AUTO_VALIDATE_OPTIONS
|
|
96
105
|
end
|
|
97
106
|
end
|
|
98
107
|
|
|
@@ -105,7 +114,7 @@ module Sequel
|
|
|
105
114
|
end
|
|
106
115
|
|
|
107
116
|
h = @auto_validate_options.dup
|
|
108
|
-
[:not_null, :explicit_not_null, :max_length, :no_null_byte, :schema_types, :unique].each do |type|
|
|
117
|
+
[:not_null, :explicit_not_null, :max_length, :max_value, :min_value, :no_null_byte, :schema_types, :unique].each do |type|
|
|
109
118
|
if type_opts = opts[:"#{type}_opts"]
|
|
110
119
|
h[type] = h[type].merge(type_opts).freeze
|
|
111
120
|
end
|
|
@@ -135,6 +144,14 @@ module Sequel
|
|
|
135
144
|
# pairs, with the first entry being the column name and second entry being the maximum length.
|
|
136
145
|
attr_reader :auto_validate_max_length_columns
|
|
137
146
|
|
|
147
|
+
# The columns with automatch max value validations, as an array of
|
|
148
|
+
# pairs, with the first entry being the column name and second entry being the maximum value.
|
|
149
|
+
attr_reader :auto_validate_max_value_columns
|
|
150
|
+
|
|
151
|
+
# The columns with automatch min value validations, as an array of
|
|
152
|
+
# pairs, with the first entry being the column name and second entry being the minimum value.
|
|
153
|
+
attr_reader :auto_validate_min_value_columns
|
|
154
|
+
|
|
138
155
|
# The columns or sets of columns with automatic unique validations
|
|
139
156
|
attr_reader :auto_validate_unique_columns
|
|
140
157
|
|
|
@@ -148,6 +165,8 @@ module Sequel
|
|
|
148
165
|
:@auto_validate_not_null_columns=>:dup,
|
|
149
166
|
:@auto_validate_explicit_not_null_columns=>:dup,
|
|
150
167
|
:@auto_validate_max_length_columns=>:dup,
|
|
168
|
+
:@auto_validate_max_value_columns=>:dup,
|
|
169
|
+
:@auto_validate_min_value_columns=>:dup,
|
|
151
170
|
:@auto_validate_unique_columns=>:dup,
|
|
152
171
|
:@auto_validate_options => :dup)
|
|
153
172
|
Plugins.after_set_dataset(self, :setup_auto_validations)
|
|
@@ -168,18 +187,23 @@ module Sequel
|
|
|
168
187
|
@auto_validate_not_null_columns.freeze
|
|
169
188
|
@auto_validate_explicit_not_null_columns.freeze
|
|
170
189
|
@auto_validate_max_length_columns.freeze
|
|
190
|
+
@auto_validate_max_value_columns.freeze
|
|
191
|
+
@auto_validate_min_value_columns.freeze
|
|
171
192
|
@auto_validate_unique_columns.freeze
|
|
172
193
|
|
|
173
194
|
super
|
|
174
195
|
end
|
|
175
196
|
|
|
176
197
|
# Skip automatic validations for the given validation type
|
|
177
|
-
# (:not_null, :types, :unique, :max_length, :
|
|
198
|
+
# (:not_null, :no_null_byte, :types, :unique, :max_length, :max_value, :min_value).
|
|
178
199
|
# If :all is given as the type, skip all auto validations.
|
|
200
|
+
#
|
|
201
|
+
# Skipping types validation automatically skips max_value and min_value validations,
|
|
202
|
+
# since those validations require valid types.
|
|
179
203
|
def skip_auto_validations(type)
|
|
180
204
|
case type
|
|
181
205
|
when :all
|
|
182
|
-
[:not_null, :no_null_byte, :types, :unique, :max_length].each{|v| skip_auto_validations(v)}
|
|
206
|
+
[:not_null, :no_null_byte, :types, :unique, :max_length, :max_value, :min_value].each{|v| skip_auto_validations(v)}
|
|
183
207
|
when :not_null
|
|
184
208
|
auto_validate_not_null_columns.clear
|
|
185
209
|
auto_validate_explicit_not_null_columns.clear
|
|
@@ -199,6 +223,8 @@ module Sequel
|
|
|
199
223
|
explicit_not_null_cols += Array(primary_key)
|
|
200
224
|
@auto_validate_explicit_not_null_columns = explicit_not_null_cols.uniq
|
|
201
225
|
@auto_validate_max_length_columns = db_schema.select{|col, sch| sch[:type] == :string && sch[:max_length].is_a?(Integer)}.map{|col, sch| [col, sch[:max_length]]}
|
|
226
|
+
@auto_validate_max_value_columns = db_schema.select{|col, sch| sch[:max_value]}.map{|col, sch| [col, sch[:max_value]]}
|
|
227
|
+
@auto_validate_min_value_columns = db_schema.select{|col, sch| sch[:min_value]}.map{|col, sch| [col, sch[:min_value]]}
|
|
202
228
|
@auto_validate_no_null_byte_columns = db_schema.select{|_, sch| sch[:type] == :string}.map{|col, _| col}
|
|
203
229
|
table = dataset.first_source_table
|
|
204
230
|
@auto_validate_unique_columns = if db.supports_index_parsing? && [Symbol, SQL::QualifiedIdentifier, SQL::Identifier, String].any?{|c| table.is_a?(c)}
|
|
@@ -248,6 +274,18 @@ module Sequel
|
|
|
248
274
|
|
|
249
275
|
unless skip.include?(:types) || !model.auto_validate_types?
|
|
250
276
|
validates_schema_types(keys, opts[:schema_types])
|
|
277
|
+
|
|
278
|
+
unless skip.include?(:max_value) || ((max_value_columns = model.auto_validate_max_value_columns).empty?)
|
|
279
|
+
max_value_columns.each do |col, max|
|
|
280
|
+
validates_max_value(max, col, opts[:max_value])
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
unless skip.include?(:min_value) || ((min_value_columns = model.auto_validate_min_value_columns).empty?)
|
|
285
|
+
min_value_columns.each do |col, min|
|
|
286
|
+
validates_min_value(min, col, opts[:min_value])
|
|
287
|
+
end
|
|
288
|
+
end
|
|
251
289
|
end
|
|
252
290
|
|
|
253
291
|
unless skip.include?(:unique)
|
|
@@ -104,7 +104,7 @@ module Sequel
|
|
|
104
104
|
# should reference the subquery alias (and qualified identifiers should not be needed
|
|
105
105
|
# unless joining to another table):
|
|
106
106
|
#
|
|
107
|
-
# a = Executive.where(:
|
|
107
|
+
# a = Executive.where(id: 1).first # works
|
|
108
108
|
# a = Executive.where{{employees[:id]=>1}}.first # works
|
|
109
109
|
# a = Executive.where{{executives[:id]=>1}}.first # doesn't work
|
|
110
110
|
#
|
|
@@ -115,7 +115,7 @@ module Sequel
|
|
|
115
115
|
#
|
|
116
116
|
# pks = Executive.where{num_staff < 10}.select_map(:id)
|
|
117
117
|
# Executive.cti_tables.reverse_each do |table|
|
|
118
|
-
# DB.from(table).where(:
|
|
118
|
+
# DB.from(table).where(id: pks).delete
|
|
119
119
|
# end
|
|
120
120
|
#
|
|
121
121
|
# = Usage
|
|
@@ -31,7 +31,6 @@ rescue RuntimeError, OpenSSL::Cipher::CipherError
|
|
|
31
31
|
# :nocov:
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
require 'base64'
|
|
35
34
|
require 'securerandom'
|
|
36
35
|
|
|
37
36
|
module Sequel
|
|
@@ -326,7 +325,7 @@ module Sequel
|
|
|
326
325
|
# DB.alter_table(:ce_test) do
|
|
327
326
|
# c = Sequel[:encrypted_column_name]
|
|
328
327
|
# add_constraint(:enc_base64) do
|
|
329
|
-
# octet_length(decode(regexp_replace(regexp_replace(c, '_', '/', 'g'), '-', '+', 'g'), 'base64')) >= 65
|
|
328
|
+
# octet_length(decode(regexp_replace(regexp_replace(c, '_', '/', 'g'), '-', '+', 'g'), 'base64')) >= 65
|
|
330
329
|
# end
|
|
331
330
|
# end
|
|
332
331
|
#
|
|
@@ -375,7 +374,7 @@ module Sequel
|
|
|
375
374
|
# Decrypt using any supported format and any available key.
|
|
376
375
|
def decrypt(data)
|
|
377
376
|
begin
|
|
378
|
-
data =
|
|
377
|
+
data = urlsafe_decode64(data)
|
|
379
378
|
rescue ArgumentError
|
|
380
379
|
raise Error, "Unable to decode encrypted column: invalid base64"
|
|
381
380
|
end
|
|
@@ -448,7 +447,7 @@ module Sequel
|
|
|
448
447
|
# The prefix string of columns for the given search type and the first configured encryption key.
|
|
449
448
|
# Used to find values that do not use this prefix in order to perform reencryption.
|
|
450
449
|
def current_key_prefix(search_type)
|
|
451
|
-
|
|
450
|
+
urlsafe_encode64("#{search_type.chr}\0#{@key_id.chr}")
|
|
452
451
|
end
|
|
453
452
|
|
|
454
453
|
# The prefix values to search for the given data (an array of strings), assuming the column uses
|
|
@@ -472,11 +471,33 @@ module Sequel
|
|
|
472
471
|
|
|
473
472
|
private
|
|
474
473
|
|
|
474
|
+
if RUBY_VERSION >= '2.4'
|
|
475
|
+
def decode64(str)
|
|
476
|
+
str.unpack1("m0")
|
|
477
|
+
end
|
|
478
|
+
# :nocov:
|
|
479
|
+
else
|
|
480
|
+
def decode64(str)
|
|
481
|
+
str.unpack("m0")[0]
|
|
482
|
+
end
|
|
483
|
+
# :nocov:
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def urlsafe_encode64(bin)
|
|
487
|
+
str = [bin].pack("m0")
|
|
488
|
+
str.tr!("+/", "-_")
|
|
489
|
+
str
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def urlsafe_decode64(str)
|
|
493
|
+
decode64(str.tr("-_", "+/"))
|
|
494
|
+
end
|
|
495
|
+
|
|
475
496
|
# An array of strings, one for each configured encryption key, to find encypted values matching
|
|
476
497
|
# the given data and search format.
|
|
477
498
|
def _search_prefixes(data, search_type)
|
|
478
499
|
@key_map.map do |key_id, (key, _)|
|
|
479
|
-
|
|
500
|
+
urlsafe_encode64(_search_prefix(data, search_type, key_id, key))
|
|
480
501
|
end
|
|
481
502
|
end
|
|
482
503
|
|
|
@@ -509,7 +530,7 @@ module Sequel
|
|
|
509
530
|
cipher_text << cipher.update(data) if data_size > 0
|
|
510
531
|
cipher_text << cipher.final
|
|
511
532
|
|
|
512
|
-
|
|
533
|
+
urlsafe_encode64("#{prefix}#{random_data}#{cipher_iv}#{cipher.auth_tag}#{cipher_text}")
|
|
513
534
|
end
|
|
514
535
|
end
|
|
515
536
|
|
|
@@ -565,7 +586,7 @@ module Sequel
|
|
|
565
586
|
end
|
|
566
587
|
end
|
|
567
588
|
|
|
568
|
-
def self.apply(model, opts=OPTS)
|
|
589
|
+
def self.apply(model, opts=OPTS, &_)
|
|
569
590
|
model.plugin :serialization
|
|
570
591
|
end
|
|
571
592
|
|
|
@@ -39,8 +39,8 @@ module Sequel
|
|
|
39
39
|
# also be implemented as:
|
|
40
40
|
#
|
|
41
41
|
# Album.composition :date,
|
|
42
|
-
# :
|
|
43
|
-
# :
|
|
42
|
+
# composer: proc{Date.new(year, month, day) if year || month || day},
|
|
43
|
+
# decomposer: (proc do
|
|
44
44
|
# if d = compositions[:date]
|
|
45
45
|
# self.year = d.year
|
|
46
46
|
# self.month = d.month
|