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