sequel 5.39.0 → 5.63.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 +308 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +57 -25
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +13 -13
- data/doc/association_basics.rdoc +89 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/migration.rdoc +12 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +18 -11
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +58 -7
- data/lib/sequel/adapters/shared/mysql.rb +40 -2
- data/lib/sequel/adapters/shared/oracle.rb +76 -0
- data/lib/sequel/adapters/shared/postgres.rb +418 -174
- data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/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 +5 -1
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +8 -8
- data/lib/sequel/connection_pool/timed_queue.rb +257 -0
- data/lib/sequel/connection_pool.rb +47 -30
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +26 -2
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +38 -1
- data/lib/sequel/database/schema_generator.rb +45 -52
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +107 -13
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +118 -16
- data/lib/sequel/dataset/sql.rb +177 -47
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +7 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +23 -3
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +10 -23
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +19 -13
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_dumper.rb +13 -2
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +325 -96
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +3 -1
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
- data/lib/sequel/plugins/prepared_statements.rb +10 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -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 +38 -11
- 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 +97 -43
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
# :nocov:
|
|
4
|
+
raise LoadError, "Sequel::TimedQueueConnectionPool is only available on Ruby 3.2+" unless RUBY_VERSION >= '3.2'
|
|
5
|
+
# :nocov:
|
|
6
|
+
|
|
7
|
+
# A connection pool allowing multi-threaded access to a pool of connections,
|
|
8
|
+
# using a timed queue (only available in Ruby 3.2+).
|
|
9
|
+
class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
|
|
10
|
+
# The maximum number of connections this pool will create.
|
|
11
|
+
attr_reader :max_size
|
|
12
|
+
|
|
13
|
+
# The following additional options are respected:
|
|
14
|
+
# :max_connections :: The maximum number of connections the connection pool
|
|
15
|
+
# will open (default 4)
|
|
16
|
+
# :pool_timeout :: The amount of seconds to wait to acquire a connection
|
|
17
|
+
# before raising a PoolTimeout (default 5)
|
|
18
|
+
def initialize(db, opts = OPTS)
|
|
19
|
+
super
|
|
20
|
+
@max_size = Integer(opts[:max_connections] || 4)
|
|
21
|
+
raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
|
|
22
|
+
@mutex = Mutex.new
|
|
23
|
+
# Size inside array so this still works while the pool is frozen.
|
|
24
|
+
@size = [0]
|
|
25
|
+
@allocated = {}
|
|
26
|
+
@allocated.compare_by_identity
|
|
27
|
+
@timeout = Float(opts[:pool_timeout] || 5)
|
|
28
|
+
@queue = Queue.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Yield all of the available connections, and the one currently allocated to
|
|
32
|
+
# this thread. This will not yield connections currently allocated to other
|
|
33
|
+
# threads, as it is not safe to operate on them.
|
|
34
|
+
def all_connections
|
|
35
|
+
hold do |conn|
|
|
36
|
+
yield conn
|
|
37
|
+
|
|
38
|
+
# Use a hash to record all connections already seen. As soon as we
|
|
39
|
+
# come across a connection we've already seen, we stop the loop.
|
|
40
|
+
conns = {}
|
|
41
|
+
conns.compare_by_identity
|
|
42
|
+
while true
|
|
43
|
+
conn = nil
|
|
44
|
+
begin
|
|
45
|
+
break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
|
|
46
|
+
conns[conn] = true
|
|
47
|
+
yield conn
|
|
48
|
+
ensure
|
|
49
|
+
@queue.push(conn) if conn
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Removes all connections currently in the pool's queue. This method has the effect of
|
|
56
|
+
# disconnecting from the database, assuming that no connections are currently
|
|
57
|
+
# being used.
|
|
58
|
+
#
|
|
59
|
+
# Once a connection is requested using #hold, the connection pool
|
|
60
|
+
# creates new connections to the database.
|
|
61
|
+
def disconnect(opts=OPTS)
|
|
62
|
+
while conn = @queue.pop(timeout: 0)
|
|
63
|
+
disconnect_connection(conn)
|
|
64
|
+
end
|
|
65
|
+
fill_queue
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Chooses the first available connection, or if none are
|
|
70
|
+
# available, creates a new connection. Passes the connection to the supplied
|
|
71
|
+
# block:
|
|
72
|
+
#
|
|
73
|
+
# pool.hold {|conn| conn.execute('DROP TABLE posts')}
|
|
74
|
+
#
|
|
75
|
+
# Pool#hold is re-entrant, meaning it can be called recursively in
|
|
76
|
+
# the same thread without blocking.
|
|
77
|
+
#
|
|
78
|
+
# If no connection is immediately available and the pool is already using the maximum
|
|
79
|
+
# number of connections, Pool#hold will block until a connection
|
|
80
|
+
# is available or the timeout expires. If the timeout expires before a
|
|
81
|
+
# connection can be acquired, a Sequel::PoolTimeout is raised.
|
|
82
|
+
def hold(server=nil)
|
|
83
|
+
t = Sequel.current
|
|
84
|
+
if conn = sync{@allocated[t]}
|
|
85
|
+
return yield(conn)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
begin
|
|
89
|
+
conn = acquire(t)
|
|
90
|
+
yield conn
|
|
91
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
|
92
|
+
if disconnect_error?(e)
|
|
93
|
+
oconn = conn
|
|
94
|
+
conn = nil
|
|
95
|
+
disconnect_connection(oconn) if oconn
|
|
96
|
+
sync{@allocated.delete(t)}
|
|
97
|
+
fill_queue
|
|
98
|
+
end
|
|
99
|
+
raise
|
|
100
|
+
ensure
|
|
101
|
+
release(t) if conn
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def pool_type
|
|
106
|
+
:timed_queue
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The total number of connections in the pool.
|
|
110
|
+
def size
|
|
111
|
+
sync{@size[0]}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
# Create a new connection, after the pool's current size has already
|
|
117
|
+
# been updated to account for the new connection. If there is an exception
|
|
118
|
+
# when creating the connection, decrement the current size.
|
|
119
|
+
#
|
|
120
|
+
# This should only be called after can_make_new?. If there is an exception
|
|
121
|
+
# between when can_make_new? is called and when preallocated_make_new
|
|
122
|
+
# is called, it has the effect of reducing the maximum size of the
|
|
123
|
+
# connection pool by 1, since the current size of the pool will show a
|
|
124
|
+
# higher number than the number of connections allocated or
|
|
125
|
+
# in the queue.
|
|
126
|
+
#
|
|
127
|
+
# Calling code should not have the mutex when calling this.
|
|
128
|
+
def preallocated_make_new
|
|
129
|
+
make_new(:default)
|
|
130
|
+
rescue Exception
|
|
131
|
+
sync{@size[0] -= 1}
|
|
132
|
+
raise
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Decrement the current size of the pool when disconnecting connections.
|
|
136
|
+
#
|
|
137
|
+
# Calling code should not have the mutex when calling this.
|
|
138
|
+
def disconnect_connection(conn)
|
|
139
|
+
sync{@size[0] -= 1}
|
|
140
|
+
super
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# If there are any threads waiting on the queue, try to create
|
|
144
|
+
# new connections in a separate thread if the pool is not yet at the
|
|
145
|
+
# maximum size.
|
|
146
|
+
#
|
|
147
|
+
# The reason for this method is to handle cases where acquire
|
|
148
|
+
# could not retrieve a connection immediately, and the pool
|
|
149
|
+
# was already at the maximum size. In that case, the acquire will
|
|
150
|
+
# wait on the queue until the timeout. This method is called
|
|
151
|
+
# after disconnecting to potentially add new connections to the
|
|
152
|
+
# pool, so the threads that are currently waiting for connections
|
|
153
|
+
# do not timeout after the pool is no longer full.
|
|
154
|
+
def fill_queue
|
|
155
|
+
if @queue.num_waiting > 0
|
|
156
|
+
Thread.new do
|
|
157
|
+
while @queue.num_waiting > 0 && (conn = try_make_new)
|
|
158
|
+
@queue.push(conn)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Whether the given size is less than the maximum size of the pool.
|
|
165
|
+
# In that case, the pool's current size is incremented. If this
|
|
166
|
+
# method returns true, space in the pool for the connection is
|
|
167
|
+
# preallocated, and preallocated_make_new should be called to
|
|
168
|
+
# create the connection.
|
|
169
|
+
#
|
|
170
|
+
# Calling code should have the mutex when calling this.
|
|
171
|
+
def can_make_new?(current_size)
|
|
172
|
+
if @max_size > current_size
|
|
173
|
+
@size[0] += 1
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Try to make a new connection if there is space in the pool.
|
|
178
|
+
# If the pool is already full, look for dead threads/fibers and
|
|
179
|
+
# disconnect the related connections.
|
|
180
|
+
#
|
|
181
|
+
# Calling code should not have the mutex when calling this.
|
|
182
|
+
def try_make_new
|
|
183
|
+
return preallocated_make_new if sync{can_make_new?(@size[0])}
|
|
184
|
+
|
|
185
|
+
to_disconnect = nil
|
|
186
|
+
do_make_new = false
|
|
187
|
+
|
|
188
|
+
sync do
|
|
189
|
+
current_size = @size[0]
|
|
190
|
+
@allocated.keys.each do |t|
|
|
191
|
+
unless t.alive?
|
|
192
|
+
(to_disconnect ||= []) << @allocated.delete(t)
|
|
193
|
+
current_size -= 1
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
do_make_new = true if can_make_new?(current_size)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
begin
|
|
201
|
+
preallocated_make_new if do_make_new
|
|
202
|
+
ensure
|
|
203
|
+
if to_disconnect
|
|
204
|
+
to_disconnect.each{|conn| disconnect_connection(conn)}
|
|
205
|
+
fill_queue
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Assigns a connection to the supplied thread, if one
|
|
211
|
+
# is available.
|
|
212
|
+
#
|
|
213
|
+
# This should return a connection is one is available within the timeout,
|
|
214
|
+
# or raise PoolTimeout if a connection could not be acquired within the timeout.
|
|
215
|
+
#
|
|
216
|
+
# Calling code should not have the mutex when calling this.
|
|
217
|
+
def acquire(thread)
|
|
218
|
+
if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
|
|
219
|
+
sync{@allocated[thread] = conn}
|
|
220
|
+
else
|
|
221
|
+
name = db.opts[:name]
|
|
222
|
+
raise ::Sequel::PoolTimeout, "timeout: #{@timeout}#{", database name: #{name}" if name}"
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Create the maximum number of connections immediately. This should not be called
|
|
227
|
+
# with a true argument unles no code is currently operating on the database.
|
|
228
|
+
#
|
|
229
|
+
# Calling code should not have the mutex when calling this.
|
|
230
|
+
def preconnect(concurrent = false)
|
|
231
|
+
if concurrent
|
|
232
|
+
if times = sync{@max_size > (size = @size[0]) ? @max_size - size : false}
|
|
233
|
+
times.times.map{Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
|
|
234
|
+
end
|
|
235
|
+
else
|
|
236
|
+
while conn = try_make_new
|
|
237
|
+
@queue.push(conn)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
nil
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Releases the connection assigned to the supplied thread back to the pool.
|
|
245
|
+
#
|
|
246
|
+
# Calling code should not have the mutex when calling this.
|
|
247
|
+
def release(thread)
|
|
248
|
+
@queue.push(sync{@allocated.delete(thread)})
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Yield to the block while inside the mutex.
|
|
252
|
+
#
|
|
253
|
+
# Calling code should not have the mutex when calling this.
|
|
254
|
+
def sync
|
|
255
|
+
@mutex.synchronize{yield}
|
|
256
|
+
end
|
|
257
|
+
end
|
|
@@ -30,8 +30,11 @@ class Sequel::ConnectionPool
|
|
|
30
30
|
:threaded => :ThreadedConnectionPool,
|
|
31
31
|
:single => :SingleConnectionPool,
|
|
32
32
|
:sharded_threaded => :ShardedThreadedConnectionPool,
|
|
33
|
-
:sharded_single => :ShardedSingleConnectionPool
|
|
34
|
-
|
|
33
|
+
:sharded_single => :ShardedSingleConnectionPool,
|
|
34
|
+
:timed_queue => :TimedQueueConnectionPool,
|
|
35
|
+
}
|
|
36
|
+
POOL_CLASS_MAP.to_a.each{|k, v| POOL_CLASS_MAP[k.to_s] = v}
|
|
37
|
+
POOL_CLASS_MAP.freeze
|
|
35
38
|
|
|
36
39
|
# Class methods used to return an appropriate pool subclass, separated
|
|
37
40
|
# into a module for easier overridding by extensions.
|
|
@@ -74,26 +77,35 @@ class Sequel::ConnectionPool
|
|
|
74
77
|
|
|
75
78
|
# The after_connect proc used for this pool. This is called with each new
|
|
76
79
|
# connection made, and is usually used to set custom per-connection settings.
|
|
77
|
-
|
|
80
|
+
# Deprecated.
|
|
81
|
+
attr_reader :after_connect # SEQUEL6: Remove
|
|
78
82
|
|
|
79
|
-
#
|
|
80
|
-
|
|
83
|
+
# Override the after_connect proc for the connection pool. Deprecated.
|
|
84
|
+
# Disables support for shard-specific :after_connect and :connect_sqls if used.
|
|
85
|
+
def after_connect=(v) # SEQUEL6: Remove
|
|
86
|
+
@use_old_connect_api = true
|
|
87
|
+
@after_connect = v
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# An array of sql strings to execute on each new connection. Deprecated.
|
|
91
|
+
attr_reader :connect_sqls # SEQUEL6: Remove
|
|
92
|
+
|
|
93
|
+
# Override the connect_sqls for the connection pool. Deprecated.
|
|
94
|
+
# Disables support for shard-specific :after_connect and :connect_sqls if used.
|
|
95
|
+
def connect_sqls=(v) # SEQUEL6: Remove
|
|
96
|
+
@use_old_connect_api = true
|
|
97
|
+
@connect_sqls = v
|
|
98
|
+
end
|
|
81
99
|
|
|
82
100
|
# The Sequel::Database object tied to this connection pool.
|
|
83
101
|
attr_accessor :db
|
|
84
102
|
|
|
85
|
-
# Instantiates a connection pool with the given options.
|
|
86
|
-
|
|
87
|
-
# connection is needed. The following options are respected for all connection
|
|
88
|
-
# pools:
|
|
89
|
-
# :after_connect :: A callable object called after each new connection is made, with the
|
|
90
|
-
# connection object (and server argument if the callable accepts 2 arguments),
|
|
91
|
-
# useful for customizations that you want to apply to all connections.
|
|
92
|
-
# :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
|
|
93
|
-
def initialize(db, opts=OPTS)
|
|
103
|
+
# Instantiates a connection pool with the given Database and options.
|
|
104
|
+
def initialize(db, opts=OPTS) # SEQUEL6: Remove second argument, always use db.opts
|
|
94
105
|
@db = db
|
|
95
|
-
@
|
|
96
|
-
@
|
|
106
|
+
@use_old_connect_api = false # SEQUEL6: Remove
|
|
107
|
+
@after_connect = opts[:after_connect] # SEQUEL6: Remove
|
|
108
|
+
@connect_sqls = opts[:connect_sqls] # SEQUEL6: Remove
|
|
97
109
|
@error_classes = db.send(:database_error_classes).dup.freeze
|
|
98
110
|
end
|
|
99
111
|
|
|
@@ -119,25 +131,30 @@ class Sequel::ConnectionPool
|
|
|
119
131
|
# and checking for connection errors.
|
|
120
132
|
def make_new(server)
|
|
121
133
|
begin
|
|
122
|
-
|
|
134
|
+
if @use_old_connect_api
|
|
135
|
+
# SEQUEL6: Remove block
|
|
136
|
+
conn = @db.connect(server)
|
|
123
137
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
if ac = @after_connect
|
|
139
|
+
if ac.arity == 2
|
|
140
|
+
ac.call(conn, server)
|
|
141
|
+
else
|
|
142
|
+
ac.call(conn)
|
|
143
|
+
end
|
|
129
144
|
end
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
|
|
146
|
+
if cs = @connect_sqls
|
|
147
|
+
cs.each do |sql|
|
|
148
|
+
db.send(:log_connection_execute, conn, sql)
|
|
149
|
+
end
|
|
135
150
|
end
|
|
151
|
+
|
|
152
|
+
conn
|
|
153
|
+
else
|
|
154
|
+
@db.new_connection(server)
|
|
136
155
|
end
|
|
137
156
|
rescue Exception=>exception
|
|
138
157
|
raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
|
|
139
|
-
end
|
|
140
|
-
raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
|
|
141
|
-
conn
|
|
158
|
+
end || raise(Sequel::DatabaseConnectionError, "Connection parameters not valid")
|
|
142
159
|
end
|
|
143
160
|
end
|
data/lib/sequel/core.rb
CHANGED
|
@@ -176,6 +176,17 @@ module Sequel
|
|
|
176
176
|
JSON.parse(json, :create_additions=>false)
|
|
177
177
|
end
|
|
178
178
|
|
|
179
|
+
# If a mutex is given, synchronize access using it. If nil is given, just
|
|
180
|
+
# yield to the block. This is designed for cases where a mutex may or may
|
|
181
|
+
# not be provided.
|
|
182
|
+
def synchronize_with(mutex)
|
|
183
|
+
if mutex
|
|
184
|
+
mutex.synchronize{yield}
|
|
185
|
+
else
|
|
186
|
+
yield
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
179
190
|
# Convert each item in the array to the correct type, handling multi-dimensional
|
|
180
191
|
# arrays. For each element in the array or subarrays, call the converter,
|
|
181
192
|
# unless the value is nil.
|
|
@@ -267,11 +278,9 @@ module Sequel
|
|
|
267
278
|
#
|
|
268
279
|
# Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
|
|
269
280
|
def string_to_date(string)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
raise convert_exception_class(e, InvalidValue)
|
|
274
|
-
end
|
|
281
|
+
Date.parse(string, Sequel.convert_two_digit_years)
|
|
282
|
+
rescue => e
|
|
283
|
+
raise convert_exception_class(e, InvalidValue)
|
|
275
284
|
end
|
|
276
285
|
|
|
277
286
|
# Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
|
|
@@ -279,15 +288,13 @@ module Sequel
|
|
|
279
288
|
#
|
|
280
289
|
# Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
|
|
281
290
|
def string_to_datetime(string)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
datetime_class.parse(string)
|
|
287
|
-
end
|
|
288
|
-
rescue => e
|
|
289
|
-
raise convert_exception_class(e, InvalidValue)
|
|
291
|
+
if datetime_class == DateTime
|
|
292
|
+
DateTime.parse(string, convert_two_digit_years)
|
|
293
|
+
else
|
|
294
|
+
datetime_class.parse(string)
|
|
290
295
|
end
|
|
296
|
+
rescue => e
|
|
297
|
+
raise convert_exception_class(e, InvalidValue)
|
|
291
298
|
end
|
|
292
299
|
|
|
293
300
|
# Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
|
|
@@ -295,11 +302,9 @@ module Sequel
|
|
|
295
302
|
# v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
|
|
296
303
|
# DB.literal(v) # => '10:20:30'
|
|
297
304
|
def string_to_time(string)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
raise convert_exception_class(e, InvalidValue)
|
|
302
|
-
end
|
|
305
|
+
SQLTime.parse(string)
|
|
306
|
+
rescue => e
|
|
307
|
+
raise convert_exception_class(e, InvalidValue)
|
|
303
308
|
end
|
|
304
309
|
|
|
305
310
|
# Unless in single threaded mode, protects access to any mutable
|
|
@@ -389,6 +394,11 @@ module Sequel
|
|
|
389
394
|
|
|
390
395
|
private
|
|
391
396
|
|
|
397
|
+
# Return a hash of date information parsed from the given string.
|
|
398
|
+
def _date_parse(string)
|
|
399
|
+
Date._parse(string)
|
|
400
|
+
end
|
|
401
|
+
|
|
392
402
|
# Helper method that the database adapter class methods that are added to Sequel via
|
|
393
403
|
# metaprogramming use to parse arguments.
|
|
394
404
|
def adapter_method(adapter, *args, &block)
|
|
@@ -55,11 +55,11 @@ module Sequel
|
|
|
55
55
|
|
|
56
56
|
begin
|
|
57
57
|
db = c.new(opts)
|
|
58
|
-
if
|
|
58
|
+
if defined?(yield)
|
|
59
59
|
return yield(db)
|
|
60
60
|
end
|
|
61
61
|
ensure
|
|
62
|
-
if
|
|
62
|
+
if defined?(yield)
|
|
63
63
|
db.disconnect if db
|
|
64
64
|
Sequel.synchronize{::Sequel::DATABASES.delete(db)}
|
|
65
65
|
end
|
|
@@ -241,6 +241,30 @@ module Sequel
|
|
|
241
241
|
pool.servers
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
+
# Connect to the given server/shard. Handles database-generic post-connection
|
|
245
|
+
# setup not handled by #connect, using the :after_connect and :connect_sqls
|
|
246
|
+
# options.
|
|
247
|
+
def new_connection(server)
|
|
248
|
+
conn = connect(server)
|
|
249
|
+
opts = server_opts(server)
|
|
250
|
+
|
|
251
|
+
if ac = opts[:after_connect]
|
|
252
|
+
if ac.arity == 2
|
|
253
|
+
ac.call(conn, server)
|
|
254
|
+
else
|
|
255
|
+
ac.call(conn)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
if cs = opts[:connect_sqls]
|
|
260
|
+
cs.each do |sql|
|
|
261
|
+
log_connection_execute(conn, sql)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
conn
|
|
266
|
+
end
|
|
267
|
+
|
|
244
268
|
# Returns true if the database is using a single-threaded connection pool.
|
|
245
269
|
def single_threaded?
|
|
246
270
|
@single_threaded
|
data/lib/sequel/database/misc.rb
CHANGED
|
@@ -91,11 +91,23 @@ module Sequel
|
|
|
91
91
|
# The specific default size of string columns for this Sequel::Database, usually 255 by default.
|
|
92
92
|
attr_accessor :default_string_column_size
|
|
93
93
|
|
|
94
|
+
# Whether to check the bytesize of strings before typecasting (to avoid typecasting strings that
|
|
95
|
+
# would be too long for the given type), true by default. Strings that are too long will raise
|
|
96
|
+
# a typecasting error.
|
|
97
|
+
attr_accessor :check_string_typecast_bytesize
|
|
98
|
+
|
|
94
99
|
# Constructs a new instance of a database connection with the specified
|
|
95
100
|
# options hash.
|
|
96
101
|
#
|
|
97
102
|
# Accepts the following options:
|
|
103
|
+
# :after_connect :: A callable object called after each new connection is made, with the
|
|
104
|
+
# connection object (and server argument if the callable accepts 2 arguments),
|
|
105
|
+
# useful for customizations that you want to apply to all connections.
|
|
106
|
+
# :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
|
|
107
|
+
# but before any connections are created.
|
|
98
108
|
# :cache_schema :: Whether schema should be cached for this Database instance
|
|
109
|
+
# :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting.
|
|
110
|
+
# :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
|
|
99
111
|
# :default_string_column_size :: The default size of string columns, 255 by default.
|
|
100
112
|
# :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
|
|
101
113
|
# or string with extensions separated by columns. These extensions are loaded after
|
|
@@ -105,7 +117,7 @@ module Sequel
|
|
|
105
117
|
# :loggers :: An array of loggers to use.
|
|
106
118
|
# :log_connection_info :: Whether connection information should be logged when logging queries.
|
|
107
119
|
# :log_warn_duration :: The number of elapsed seconds after which queries should be logged at warn level.
|
|
108
|
-
# :name :: A name to use for the Database object, displayed in PoolTimeout
|
|
120
|
+
# :name :: A name to use for the Database object, displayed in PoolTimeout.
|
|
109
121
|
# :preconnect :: Automatically create the maximum number of connections, so that they don't
|
|
110
122
|
# need to be created as needed. This is useful when connecting takes a long time
|
|
111
123
|
# and you want to avoid possible latency during runtime.
|
|
@@ -114,13 +126,15 @@ module Sequel
|
|
|
114
126
|
# :preconnect_extensions :: Similar to the :extensions option, but loads the extensions before the
|
|
115
127
|
# connections are made by the :preconnect option.
|
|
116
128
|
# :quote_identifiers :: Whether to quote identifiers.
|
|
117
|
-
# :servers :: A hash specifying a server/shard specific options, keyed by shard symbol
|
|
129
|
+
# :servers :: A hash specifying a server/shard specific options, keyed by shard symbol.
|
|
118
130
|
# :single_threaded :: Whether to use a single-threaded connection pool.
|
|
119
131
|
# :sql_log_level :: Method to use to log SQL to a logger, :info by default.
|
|
120
132
|
#
|
|
133
|
+
# For sharded connection pools, :after_connect and :connect_sqls can be specified per-shard.
|
|
134
|
+
#
|
|
121
135
|
# All options given are also passed to the connection pool. Additional options respected by
|
|
122
|
-
# the connection pool are :
|
|
123
|
-
#
|
|
136
|
+
# the connection pool are :max_connections, :pool_timeout, :servers, and :servers_hash. See the
|
|
137
|
+
# connection pool documentation for details.
|
|
124
138
|
def initialize(opts = OPTS)
|
|
125
139
|
@opts ||= opts
|
|
126
140
|
@opts = connection_pool_default_options.merge(@opts)
|
|
@@ -130,6 +144,7 @@ module Sequel
|
|
|
130
144
|
@opts[:adapter_class] = self.class
|
|
131
145
|
@opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, Sequel.single_threaded))
|
|
132
146
|
@default_string_column_size = @opts[:default_string_column_size] || DEFAULT_STRING_COLUMN_SIZE
|
|
147
|
+
@check_string_typecast_bytesize = typecast_value_boolean(@opts.fetch(:check_string_typecast_bytesize, true))
|
|
133
148
|
|
|
134
149
|
@schemas = {}
|
|
135
150
|
@prepared_statements = {}
|
|
@@ -160,6 +175,10 @@ module Sequel
|
|
|
160
175
|
|
|
161
176
|
initialize_load_extensions(:preconnect_extensions)
|
|
162
177
|
|
|
178
|
+
if before_preconnect = @opts[:before_preconnect]
|
|
179
|
+
before_preconnect.call(self)
|
|
180
|
+
end
|
|
181
|
+
|
|
163
182
|
if typecast_value_boolean(@opts[:preconnect]) && @pool.respond_to?(:preconnect, true)
|
|
164
183
|
concurrent = typecast_value_string(@opts[:preconnect]) == "concurrently"
|
|
165
184
|
@pool.send(:preconnect, concurrent)
|
|
@@ -213,8 +232,7 @@ module Sequel
|
|
|
213
232
|
Sequel.extension(*exts)
|
|
214
233
|
exts.each do |ext|
|
|
215
234
|
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
|
216
|
-
|
|
217
|
-
Sequel.synchronize{@loaded_extensions << ext}
|
|
235
|
+
if Sequel.synchronize{@loaded_extensions.include?(ext) ? false : (@loaded_extensions << ext)}
|
|
218
236
|
pr.call(self)
|
|
219
237
|
end
|
|
220
238
|
else
|
|
@@ -460,6 +478,21 @@ module Sequel
|
|
|
460
478
|
# Don't rescue other exceptions, they will be raised normally.
|
|
461
479
|
end
|
|
462
480
|
|
|
481
|
+
# Check the bytesize of a string before conversion. There is no point
|
|
482
|
+
# trying to typecast strings that would be way too long.
|
|
483
|
+
def typecast_check_string_length(string, max_size)
|
|
484
|
+
if @check_string_typecast_bytesize && string.bytesize > max_size
|
|
485
|
+
raise InvalidValue, "string too long to typecast (bytesize: #{string.bytesize}, max: #{max_size})"
|
|
486
|
+
end
|
|
487
|
+
string
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# Check the bytesize of the string value, if value is a string.
|
|
491
|
+
def typecast_check_length(value, max_size)
|
|
492
|
+
typecast_check_string_length(value, max_size) if String === value
|
|
493
|
+
value
|
|
494
|
+
end
|
|
495
|
+
|
|
463
496
|
# Typecast the value to an SQL::Blob
|
|
464
497
|
def typecast_value_blob(value)
|
|
465
498
|
value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
|
|
@@ -483,9 +516,9 @@ module Sequel
|
|
|
483
516
|
when Date
|
|
484
517
|
value
|
|
485
518
|
when String
|
|
486
|
-
Sequel.string_to_date(value)
|
|
519
|
+
Sequel.string_to_date(typecast_check_string_length(value, 100))
|
|
487
520
|
when Hash
|
|
488
|
-
Date.new(*[:year, :month, :day].map{|x| (value[x] || value[x.to_s]).to_i})
|
|
521
|
+
Date.new(*[:year, :month, :day].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
|
|
489
522
|
else
|
|
490
523
|
raise InvalidValue, "invalid value for Date: #{value.inspect}"
|
|
491
524
|
end
|
|
@@ -493,7 +526,17 @@ module Sequel
|
|
|
493
526
|
|
|
494
527
|
# Typecast the value to a DateTime or Time depending on Sequel.datetime_class
|
|
495
528
|
def typecast_value_datetime(value)
|
|
496
|
-
|
|
529
|
+
case value
|
|
530
|
+
when String
|
|
531
|
+
Sequel.typecast_to_application_timestamp(typecast_check_string_length(value, 100))
|
|
532
|
+
when Hash
|
|
533
|
+
[:year, :month, :day, :hour, :minute, :second, :nanos, :offset].each do |x|
|
|
534
|
+
typecast_check_length(value[x] || value[x.to_s], 100)
|
|
535
|
+
end
|
|
536
|
+
Sequel.typecast_to_application_timestamp(value)
|
|
537
|
+
else
|
|
538
|
+
Sequel.typecast_to_application_timestamp(value)
|
|
539
|
+
end
|
|
497
540
|
end
|
|
498
541
|
|
|
499
542
|
if RUBY_VERSION >= '2.4'
|
|
@@ -526,18 +569,30 @@ module Sequel
|
|
|
526
569
|
when Numeric
|
|
527
570
|
BigDecimal(value.to_s)
|
|
528
571
|
when String
|
|
529
|
-
_typecast_value_string_to_decimal(value)
|
|
572
|
+
_typecast_value_string_to_decimal(typecast_check_string_length(value, 1000))
|
|
530
573
|
else
|
|
531
574
|
raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
|
|
532
575
|
end
|
|
533
576
|
end
|
|
534
577
|
|
|
535
578
|
# Typecast the value to a Float
|
|
536
|
-
|
|
579
|
+
def typecast_value_float(value)
|
|
580
|
+
Float(typecast_check_length(value, 1000))
|
|
581
|
+
end
|
|
537
582
|
|
|
538
583
|
# Typecast the value to an Integer
|
|
539
584
|
def typecast_value_integer(value)
|
|
540
|
-
|
|
585
|
+
case value
|
|
586
|
+
when String
|
|
587
|
+
typecast_check_string_length(value, 100)
|
|
588
|
+
if value =~ /\A-?0+(\d)/
|
|
589
|
+
Integer(value, 10)
|
|
590
|
+
else
|
|
591
|
+
Integer(value)
|
|
592
|
+
end
|
|
593
|
+
else
|
|
594
|
+
Integer(value)
|
|
595
|
+
end
|
|
541
596
|
end
|
|
542
597
|
|
|
543
598
|
# Typecast the value to a String
|
|
@@ -560,9 +615,9 @@ module Sequel
|
|
|
560
615
|
SQLTime.create(value.hour, value.min, value.sec, value.nsec/1000.0)
|
|
561
616
|
end
|
|
562
617
|
when String
|
|
563
|
-
Sequel.string_to_time(value)
|
|
618
|
+
Sequel.string_to_time(typecast_check_string_length(value, 100))
|
|
564
619
|
when Hash
|
|
565
|
-
SQLTime.create(*[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
|
|
620
|
+
SQLTime.create(*[:hour, :minute, :second].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
|
|
566
621
|
else
|
|
567
622
|
raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
|
|
568
623
|
end
|