sequel 5.31.0 → 5.32.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|