sequel 5.31.0 → 5.32.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 +16 -0
- data/doc/advanced_associations.rdoc +4 -4
- data/doc/association_basics.rdoc +3 -3
- data/doc/code_order.rdoc +12 -2
- data/doc/model_dataset_method_design.rdoc +1 -1
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/testing.rdoc +1 -0
- data/lib/sequel/adapters/shared/access.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +5 -5
- data/lib/sequel/adapters/shared/mysql.rb +9 -9
- data/lib/sequel/adapters/shared/oracle.rb +16 -16
- data/lib/sequel/adapters/shared/postgres.rb +18 -8
- data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
- data/lib/sequel/adapters/shared/sqlite.rb +1 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +2 -2
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +318 -314
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +2 -2
- data/lib/sequel/extensions/connection_validator.rb +2 -2
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/index_caching.rb +9 -7
- data/lib/sequel/extensions/integer64.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +13 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +2 -0
- data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/server_block.rb +3 -3
- data/lib/sequel/model/base.rb +48 -47
- data/lib/sequel/model/plugins.rb +1 -0
- data/lib/sequel/plugins/association_lazy_eager_option.rb +2 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/boolean_subsets.rb +4 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +26 -26
- data/lib/sequel/plugins/dirty.rb +13 -13
- data/lib/sequel/plugins/json_serializer.rb +3 -7
- data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
- data/lib/sequel/plugins/subclasses.rb +2 -0
- data/lib/sequel/timezones.rb +6 -4
- data/lib/sequel/version.rb +1 -1
- metadata +6 -2
@@ -325,7 +325,7 @@ module Sequel
|
|
325
325
|
:integer
|
326
326
|
when /\Adate\z/io
|
327
327
|
:date
|
328
|
-
when /\A((small)?datetime|timestamp( with(out)? time zone)?)
|
328
|
+
when /\A((small)?datetime|timestamp(\(\d\))?( with(out)? time zone)?)\z/io
|
329
329
|
:datetime
|
330
330
|
when /\Atime( with(out)? time zone)?\z/io
|
331
331
|
:time
|
@@ -80,9 +80,9 @@ module Sequel
|
|
80
80
|
Sequel.elapsed_seconds_since(cet[0]) > cet[1]
|
81
81
|
|
82
82
|
if pool_type == :sharded_threaded
|
83
|
-
sync{allocated(a.last).delete(
|
83
|
+
sync{allocated(a.last).delete(Sequel.current)}
|
84
84
|
else
|
85
|
-
sync{@allocated.delete(
|
85
|
+
sync{@allocated.delete(Sequel.current)}
|
86
86
|
end
|
87
87
|
|
88
88
|
disconnect_connection(conn)
|
@@ -104,9 +104,9 @@ module Sequel
|
|
104
104
|
!db.valid_connection?(conn)
|
105
105
|
|
106
106
|
if pool_type == :sharded_threaded
|
107
|
-
sync{allocated(a.last).delete(
|
107
|
+
sync{allocated(a.last).delete(Sequel.current)}
|
108
108
|
else
|
109
|
-
sync{@allocated.delete(
|
109
|
+
sync{@allocated.delete(Sequel.current)}
|
110
110
|
end
|
111
111
|
|
112
112
|
disconnect_connection(conn)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The fiber_concurrency extension changes the default concurrency
|
4
|
+
# primitive in Sequel to be Fiber.current instead of Thread.current.
|
5
|
+
# This is the value used in various hash keys to implement safe
|
6
|
+
# concurrency (thread-safe concurrency by default, fiber-safe
|
7
|
+
# concurrency with this extension. It can be enabled via:
|
8
|
+
#
|
9
|
+
# Sequel.extension :fiber_concurrency
|
10
|
+
#
|
11
|
+
# Related module: Sequel::FiberConcurrency
|
12
|
+
|
13
|
+
require 'fiber'
|
14
|
+
|
15
|
+
module Sequel
|
16
|
+
module FiberConcurrency
|
17
|
+
# Make the current concurrency primitive be Fiber.current.
|
18
|
+
def current
|
19
|
+
Fiber.current
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
extend FiberConcurrency
|
24
|
+
end
|
@@ -54,13 +54,6 @@ module Sequel
|
|
54
54
|
db.instance_variable_set(:@indexes, {})
|
55
55
|
end
|
56
56
|
|
57
|
-
# Remove the index cache for the given schema name
|
58
|
-
def remove_cached_schema(table)
|
59
|
-
k = quote_schema_table(table)
|
60
|
-
Sequel.synchronize{@indexes.delete(k)}
|
61
|
-
super
|
62
|
-
end
|
63
|
-
|
64
57
|
# Dump the index cache to the filename given in Marshal format.
|
65
58
|
def dump_index_cache(file)
|
66
59
|
File.open(file, 'wb'){|f| f.write(Marshal.dump(@indexes))}
|
@@ -101,6 +94,15 @@ module Sequel
|
|
101
94
|
Sequel.synchronize{@indexes[quoted_name] = result}
|
102
95
|
result
|
103
96
|
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Remove the index cache for the given schema name
|
101
|
+
def remove_cached_schema(table)
|
102
|
+
k = quote_schema_table(table)
|
103
|
+
Sequel.synchronize{@indexes.delete(k)}
|
104
|
+
super
|
105
|
+
end
|
104
106
|
end
|
105
107
|
|
106
108
|
Database.register_extension(:index_caching, IndexCaching)
|
@@ -40,12 +40,20 @@ module Sequel
|
|
40
40
|
def self.extended(db)
|
41
41
|
db.instance_exec do
|
42
42
|
extend_datasets(InetDatasetMethods)
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
|
44
|
+
if !defined?(SEQUEL_PG_VERSION_INTEGER) || SEQUEL_PG_VERSION_INTEGER >= 11300
|
45
|
+
# sequel_pg 1.13.0+ will use inet/cidr conversion procs, but doing so is
|
46
|
+
# slower, so don't add the conversion procs if using sequel_pg 1.13.0+.
|
47
|
+
meth = IPAddr.method(:new)
|
48
|
+
add_conversion_proc(869, meth)
|
49
|
+
add_conversion_proc(650, meth)
|
50
|
+
if respond_to?(:register_array_type)
|
51
|
+
register_array_type('inet', :oid=>1041, :scalar_oid=>869)
|
52
|
+
register_array_type('cidr', :oid=>651, :scalar_oid=>650)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
46
56
|
if respond_to?(:register_array_type)
|
47
|
-
register_array_type('inet', :oid=>1041, :scalar_oid=>869)
|
48
|
-
register_array_type('cidr', :oid=>651, :scalar_oid=>650)
|
49
57
|
register_array_type('macaddr', :oid=>1040, :scalar_oid=>829)
|
50
58
|
end
|
51
59
|
@schema_type_classes[:ipaddr] = IPAddr
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The run_transaction_hooks extension allows for running after_commit or
|
4
|
+
# after_rollback extensions before commit or rollback. It then removes
|
5
|
+
# the hook after running it, so it will not be run twice.
|
6
|
+
#
|
7
|
+
# This extension should only be used in transactional tests where the
|
8
|
+
# transaction always rolls back, to test the behavior of the after_commit
|
9
|
+
# and after_rollback hooks. Any other usage is probably a bad idea.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# DB.extension :run_transaction_hooks
|
14
|
+
# x = 1
|
15
|
+
# DB.transaction(rollback: :always) do
|
16
|
+
# DB.after_rollback{x = 3}
|
17
|
+
# DB.after_commit{x = 2}
|
18
|
+
#
|
19
|
+
# x # => 1
|
20
|
+
# DB.run_after_rollback_hooks
|
21
|
+
# x # => 3
|
22
|
+
# DB.run_after_commit_hooks
|
23
|
+
# x # => 2
|
24
|
+
# end
|
25
|
+
# x # => 2
|
26
|
+
|
27
|
+
#
|
28
|
+
class Sequel::Database
|
29
|
+
module RunTransactionHooks
|
30
|
+
# Run all savepoint and transaction after_commit hooks for the current transaction,
|
31
|
+
# and remove the hooks after running them.
|
32
|
+
# Options:
|
33
|
+
# :server :: The server/shard to use.
|
34
|
+
def run_after_commit_hooks(opts=OPTS)
|
35
|
+
_run_transaction_hooks(:after_commit, opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Run all savepoint and transaction after_rollback hooks for the current transaction,
|
39
|
+
# and remove the hooks after running them.
|
40
|
+
# Options:
|
41
|
+
# :server :: The server/shard to use.
|
42
|
+
def run_after_rollback_hooks(opts=OPTS)
|
43
|
+
_run_transaction_hooks(:after_rollback, opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def _run_transaction_hooks(type, opts)
|
49
|
+
synchronize(opts[:server]) do |conn|
|
50
|
+
unless h = _trans(conn)
|
51
|
+
raise Error, "Cannot call run_#{type}_hooks outside of a transaction"
|
52
|
+
end
|
53
|
+
|
54
|
+
if hooks = h[type]
|
55
|
+
hooks.each(&:call)
|
56
|
+
hooks.clear
|
57
|
+
end
|
58
|
+
|
59
|
+
if (savepoints = h[:savepoints])
|
60
|
+
savepoints.each do |savepoint|
|
61
|
+
if hooks = savepoint[type]
|
62
|
+
hooks.each(&:call)
|
63
|
+
hooks.clear
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
register_extension(:run_transaction_hooks, RunTransactionHooks)
|
72
|
+
end
|
@@ -143,13 +143,13 @@ module Sequel
|
|
143
143
|
|
144
144
|
# Make the given server the new default server for the current thread.
|
145
145
|
def set_default_server(default_server, read_only_server=default_server)
|
146
|
-
sync{(@default_servers[
|
146
|
+
sync{(@default_servers[Sequel.current] ||= [])} << [default_server, read_only_server]
|
147
147
|
end
|
148
148
|
|
149
149
|
# Remove the current default server for the current thread, restoring the
|
150
150
|
# previous default server.
|
151
151
|
def clear_default_server
|
152
|
-
t =
|
152
|
+
t = Sequel.current
|
153
153
|
a = sync{@default_servers[t]}
|
154
154
|
a.pop
|
155
155
|
sync{@default_servers.delete(t)} if a.empty?
|
@@ -157,7 +157,7 @@ module Sequel
|
|
157
157
|
|
158
158
|
# Use the server given to with_server for the given thread, if appropriate.
|
159
159
|
def pick_server(server)
|
160
|
-
a = sync{@default_servers[
|
160
|
+
a = sync{@default_servers[Sequel.current]}
|
161
161
|
if !a || a.empty?
|
162
162
|
super
|
163
163
|
else
|
data/lib/sequel/model/base.rb
CHANGED
@@ -460,47 +460,6 @@ module Sequel
|
|
460
460
|
super
|
461
461
|
end
|
462
462
|
|
463
|
-
# If possible, set the dataset for the model subclass as soon as it
|
464
|
-
# is created. Also, make sure the inherited class instance variables
|
465
|
-
# are copied into the subclass.
|
466
|
-
#
|
467
|
-
# Sequel queries the database to get schema information as soon as
|
468
|
-
# a model class is created:
|
469
|
-
#
|
470
|
-
# class Artist < Sequel::Model # Causes schema query
|
471
|
-
# end
|
472
|
-
def inherited(subclass)
|
473
|
-
super
|
474
|
-
ivs = subclass.instance_variables
|
475
|
-
inherited_instance_variables.each do |iv, dup|
|
476
|
-
next if ivs.include?(iv)
|
477
|
-
if (sup_class_value = instance_variable_get(iv)) && dup
|
478
|
-
sup_class_value = case dup
|
479
|
-
when :dup
|
480
|
-
sup_class_value.dup
|
481
|
-
when :hash_dup
|
482
|
-
h = {}
|
483
|
-
sup_class_value.each{|k,v| h[k] = v.dup}
|
484
|
-
h
|
485
|
-
when Proc
|
486
|
-
dup.call(sup_class_value)
|
487
|
-
else
|
488
|
-
raise Error, "bad inherited instance variable type: #{dup.inspect}"
|
489
|
-
end
|
490
|
-
end
|
491
|
-
subclass.instance_variable_set(iv, sup_class_value)
|
492
|
-
end
|
493
|
-
|
494
|
-
unless ivs.include?(:@dataset)
|
495
|
-
if @dataset && self != Model
|
496
|
-
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
497
|
-
elsif (n = subclass.name) && !n.to_s.empty?
|
498
|
-
db
|
499
|
-
subclass.set_dataset(subclass.implicit_table_name)
|
500
|
-
end
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
463
|
# Returns the implicit table name for the model class, which is the demodulized,
|
505
464
|
# underscored, pluralized name of the class.
|
506
465
|
#
|
@@ -515,12 +474,6 @@ module Sequel
|
|
515
474
|
call(values)
|
516
475
|
end
|
517
476
|
|
518
|
-
# Clear the setter_methods cache when a setter method is added.
|
519
|
-
def method_added(meth)
|
520
|
-
clear_setter_methods_cache if meth.to_s.end_with?('=')
|
521
|
-
super
|
522
|
-
end
|
523
|
-
|
524
477
|
# Mark the model as not having a primary key. Not having a primary key
|
525
478
|
# can cause issues, among which is that you won't be able to update records.
|
526
479
|
#
|
@@ -854,6 +807,47 @@ module Sequel
|
|
854
807
|
meths
|
855
808
|
end
|
856
809
|
|
810
|
+
# If possible, set the dataset for the model subclass as soon as it
|
811
|
+
# is created. Also, make sure the inherited class instance variables
|
812
|
+
# are copied into the subclass.
|
813
|
+
#
|
814
|
+
# Sequel queries the database to get schema information as soon as
|
815
|
+
# a model class is created:
|
816
|
+
#
|
817
|
+
# class Artist < Sequel::Model # Causes schema query
|
818
|
+
# end
|
819
|
+
def inherited(subclass)
|
820
|
+
super
|
821
|
+
ivs = subclass.instance_variables
|
822
|
+
inherited_instance_variables.each do |iv, dup|
|
823
|
+
next if ivs.include?(iv)
|
824
|
+
if (sup_class_value = instance_variable_get(iv)) && dup
|
825
|
+
sup_class_value = case dup
|
826
|
+
when :dup
|
827
|
+
sup_class_value.dup
|
828
|
+
when :hash_dup
|
829
|
+
h = {}
|
830
|
+
sup_class_value.each{|k,v| h[k] = v.dup}
|
831
|
+
h
|
832
|
+
when Proc
|
833
|
+
dup.call(sup_class_value)
|
834
|
+
else
|
835
|
+
raise Error, "bad inherited instance variable type: #{dup.inspect}"
|
836
|
+
end
|
837
|
+
end
|
838
|
+
subclass.instance_variable_set(iv, sup_class_value)
|
839
|
+
end
|
840
|
+
|
841
|
+
unless ivs.include?(:@dataset)
|
842
|
+
if @dataset && self != Model
|
843
|
+
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
844
|
+
elsif (n = subclass.name) && !n.to_s.empty?
|
845
|
+
db
|
846
|
+
subclass.set_dataset(subclass.implicit_table_name)
|
847
|
+
end
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
857
851
|
# A hash of instance variables to automatically set up in subclasses.
|
858
852
|
# Keys are instance variable symbols, values should be:
|
859
853
|
# nil :: Assign directly from superclass to subclass (frozen objects)
|
@@ -908,6 +902,12 @@ module Sequel
|
|
908
902
|
opts[:class_name] ||= '::' + ((name || '').split("::")[0..-2] + [camelize(default)]).join('::')
|
909
903
|
end
|
910
904
|
|
905
|
+
# Clear the setter_methods cache when a setter method is added.
|
906
|
+
def method_added(meth)
|
907
|
+
clear_setter_methods_cache if meth.to_s.end_with?('=')
|
908
|
+
super
|
909
|
+
end
|
910
|
+
|
911
911
|
# Module that the class includes that holds methods the class adds for column accessors and
|
912
912
|
# associations so that the methods can be overridden with +super+.
|
913
913
|
def overridable_methods_module
|
@@ -1718,6 +1718,7 @@ module Sequel
|
|
1718
1718
|
|
1719
1719
|
# The values hash to use when inserting a new record.
|
1720
1720
|
alias _insert_values values
|
1721
|
+
private :_insert_values
|
1721
1722
|
|
1722
1723
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
1723
1724
|
# is used for reading newly inserted values from the database
|
data/lib/sequel/model/plugins.rb
CHANGED
@@ -36,6 +36,8 @@ module Sequel
|
|
36
36
|
# Album.plugin :association_lazy_eager_option
|
37
37
|
module AssociationLazyEagerOption
|
38
38
|
module InstanceMethods
|
39
|
+
private
|
40
|
+
|
39
41
|
# Return a dataset for the association after applying any dynamic callback.
|
40
42
|
def _associated_dataset(opts, dynamic_opts)
|
41
43
|
ds = super
|
@@ -39,6 +39,8 @@ module Sequel
|
|
39
39
|
# Album.plugin :association_multi_add_remove
|
40
40
|
module AssociationMultiAddRemove
|
41
41
|
module ClassMethods
|
42
|
+
private
|
43
|
+
|
42
44
|
# Define the methods use to add/remove/set multiple associated objects
|
43
45
|
# in a single method call.
|
44
46
|
def def_association_instance_methods(opts)
|
@@ -109,6 +109,8 @@ module Sequel
|
|
109
109
|
|
110
110
|
Plugins.inherited_instance_variables(self, :@association_proxy_to_dataset=>nil)
|
111
111
|
|
112
|
+
private
|
113
|
+
|
112
114
|
# Changes the association method to return a proxy instead of the associated objects
|
113
115
|
# directly.
|
114
116
|
def def_association_method(opts)
|
@@ -31,7 +31,10 @@ module Sequel
|
|
31
31
|
# Create boolean subset methods for each boolean column.
|
32
32
|
def self.configure(model, &block)
|
33
33
|
model.instance_exec do
|
34
|
-
|
34
|
+
if block
|
35
|
+
define_singleton_method(:boolean_subset_args, &block)
|
36
|
+
singleton_class.send(:private, :boolean_subset_args)
|
37
|
+
end
|
35
38
|
create_boolean_subsets if @dataset
|
36
39
|
end
|
37
40
|
end
|
@@ -278,6 +278,27 @@ module Sequel
|
|
278
278
|
|
279
279
|
Plugins.inherited_instance_variables(self, :@cti_models=>nil, :@cti_tables=>nil, :@cti_table_columns=>nil, :@cti_instance_dataset=>nil, :@cti_table_map=>nil, :@cti_alias=>nil, :@cti_ignore_subclass_columns=>nil, :@cti_qualify_tables=>nil)
|
280
280
|
|
281
|
+
# The table name for the current model class's main table.
|
282
|
+
def table_name
|
283
|
+
if cti_tables && cti_tables.length > 1
|
284
|
+
@cti_alias
|
285
|
+
else
|
286
|
+
super
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# The name of the most recently joined table.
|
291
|
+
def cti_table_name
|
292
|
+
cti_tables ? cti_tables.last : dataset.first_source_alias
|
293
|
+
end
|
294
|
+
|
295
|
+
# The model class for the given key value.
|
296
|
+
def sti_class_from_key(key)
|
297
|
+
sti_class(sti_model_map[key])
|
298
|
+
end
|
299
|
+
|
300
|
+
private
|
301
|
+
|
281
302
|
def inherited(subclass)
|
282
303
|
ds = sti_dataset
|
283
304
|
|
@@ -340,27 +361,6 @@ module Sequel
|
|
340
361
|
end
|
341
362
|
end
|
342
363
|
|
343
|
-
# The table name for the current model class's main table.
|
344
|
-
def table_name
|
345
|
-
if cti_tables && cti_tables.length > 1
|
346
|
-
@cti_alias
|
347
|
-
else
|
348
|
-
super
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
# The name of the most recently joined table.
|
353
|
-
def cti_table_name
|
354
|
-
cti_tables ? cti_tables.last : dataset.first_source_alias
|
355
|
-
end
|
356
|
-
|
357
|
-
# The model class for the given key value.
|
358
|
-
def sti_class_from_key(key)
|
359
|
-
sti_class(sti_model_map[key])
|
360
|
-
end
|
361
|
-
|
362
|
-
private
|
363
|
-
|
364
364
|
# If using a subquery for class table inheritance, also use a subquery
|
365
365
|
# when setting subclass dataset.
|
366
366
|
def sti_subclass_dataset(key)
|
@@ -383,11 +383,6 @@ module Sequel
|
|
383
383
|
self
|
384
384
|
end
|
385
385
|
|
386
|
-
# Don't allow use of prepared statements.
|
387
|
-
def use_prepared_statements_for?(type)
|
388
|
-
false
|
389
|
-
end
|
390
|
-
|
391
386
|
# Set the sti_key column based on the sti_key_map.
|
392
387
|
def before_validation
|
393
388
|
if new? && (set = self[model.sti_key])
|
@@ -438,6 +433,11 @@ module Sequel
|
|
438
433
|
end
|
439
434
|
end
|
440
435
|
end
|
436
|
+
|
437
|
+
# Don't allow use of prepared statements.
|
438
|
+
def use_prepared_statements_for?(type)
|
439
|
+
false
|
440
|
+
end
|
441
441
|
end
|
442
442
|
end
|
443
443
|
end
|