sequel 5.61.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 +46 -0
- data/README.rdoc +20 -19
- data/doc/advanced_associations.rdoc +13 -13
- data/doc/association_basics.rdoc +21 -15
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +4 -4
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +1 -1
- 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 +13 -13
- data/doc/testing.rdoc +13 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/postgres.rb +4 -0
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/mssql.rb +9 -5
- data/lib/sequel/adapters/shared/mysql.rb +7 -0
- data/lib/sequel/adapters/shared/oracle.rb +7 -0
- data/lib/sequel/adapters/shared/postgres.rb +275 -152
- data/lib/sequel/adapters/shared/sqlanywhere.rb +7 -0
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- 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/database/connecting.rb +24 -0
- data/lib/sequel/database/misc.rb +8 -2
- data/lib/sequel/database/query.rb +37 -0
- data/lib/sequel/dataset/actions.rb +56 -11
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/query.rb +9 -9
- data/lib/sequel/dataset/sql.rb +5 -1
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +11 -11
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +1 -1
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/named_timezones.rb +21 -5
- data/lib/sequel/extensions/pg_array.rb +22 -3
- data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
- data/lib/sequel/extensions/pg_extended_date_support.rb +12 -0
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +5 -0
- data/lib/sequel/extensions/pg_inet.rb +9 -10
- data/lib/sequel/extensions/pg_interval.rb +9 -10
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_multirange.rb +5 -10
- data/lib/sequel/extensions/pg_range.rb +5 -10
- data/lib/sequel/extensions/pg_row.rb +18 -13
- data/lib/sequel/model/associations.rb +9 -4
- data/lib/sequel/model/base.rb +6 -5
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/composition.rb +2 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/finder.rb +3 -1
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +14 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +20 -0
- data/lib/sequel/version.rb +1 -1
- metadata +14 -6
|
@@ -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
|
|
@@ -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
|
@@ -100,10 +100,14 @@ module Sequel
|
|
|
100
100
|
# options hash.
|
|
101
101
|
#
|
|
102
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.
|
|
103
106
|
# :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
|
|
104
107
|
# but before any connections are created.
|
|
105
108
|
# :cache_schema :: Whether schema should be cached for this Database instance
|
|
106
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.
|
|
107
111
|
# :default_string_column_size :: The default size of string columns, 255 by default.
|
|
108
112
|
# :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
|
|
109
113
|
# or string with extensions separated by columns. These extensions are loaded after
|
|
@@ -126,9 +130,11 @@ module Sequel
|
|
|
126
130
|
# :single_threaded :: Whether to use a single-threaded connection pool.
|
|
127
131
|
# :sql_log_level :: Method to use to log SQL to a logger, :info by default.
|
|
128
132
|
#
|
|
133
|
+
# For sharded connection pools, :after_connect and :connect_sqls can be specified per-shard.
|
|
134
|
+
#
|
|
129
135
|
# All options given are also passed to the connection pool. Additional options respected by
|
|
130
|
-
# the connection pool are :
|
|
131
|
-
#
|
|
136
|
+
# the connection pool are :max_connections, :pool_timeout, :servers, and :servers_hash. See the
|
|
137
|
+
# connection pool documentation for details.
|
|
132
138
|
def initialize(opts = OPTS)
|
|
133
139
|
@opts ||= opts
|
|
134
140
|
@opts = connection_pool_default_options.merge(@opts)
|
|
@@ -175,6 +175,9 @@ module Sequel
|
|
|
175
175
|
if !c[:max_length] && c[:type] == :string && (max_length = column_schema_max_length(c[:db_type]))
|
|
176
176
|
c[:max_length] = max_length
|
|
177
177
|
end
|
|
178
|
+
if !c[:max_value] && !c[:min_value] && c[:type] == :integer && (min_max = column_schema_integer_min_max_values(c[:db_type]))
|
|
179
|
+
c[:min_value], c[:max_value] = min_max
|
|
180
|
+
end
|
|
178
181
|
end
|
|
179
182
|
schema_post_process(cols)
|
|
180
183
|
|
|
@@ -272,6 +275,40 @@ module Sequel
|
|
|
272
275
|
column_schema_default_to_ruby_value(default, type) rescue nil
|
|
273
276
|
end
|
|
274
277
|
|
|
278
|
+
INTEGER1_MIN_MAX = [-128, 127].freeze
|
|
279
|
+
INTEGER2_MIN_MAX = [-32768, 32767].freeze
|
|
280
|
+
INTEGER3_MIN_MAX = [-8388608, 8388607].freeze
|
|
281
|
+
INTEGER4_MIN_MAX = [-2147483648, 2147483647].freeze
|
|
282
|
+
INTEGER8_MIN_MAX = [-9223372036854775808, 9223372036854775807].freeze
|
|
283
|
+
UNSIGNED_INTEGER1_MIN_MAX = [0, 255].freeze
|
|
284
|
+
UNSIGNED_INTEGER2_MIN_MAX = [0, 65535].freeze
|
|
285
|
+
UNSIGNED_INTEGER3_MIN_MAX = [0, 16777215].freeze
|
|
286
|
+
UNSIGNED_INTEGER4_MIN_MAX = [0, 4294967295].freeze
|
|
287
|
+
UNSIGNED_INTEGER8_MIN_MAX = [0, 18446744073709551615].freeze
|
|
288
|
+
|
|
289
|
+
# Look at the db_type and guess the minimum and maximum integer values for
|
|
290
|
+
# the column.
|
|
291
|
+
def column_schema_integer_min_max_values(db_type)
|
|
292
|
+
unsigned = /unsigned/i =~ db_type
|
|
293
|
+
case db_type
|
|
294
|
+
when /big|int8/i
|
|
295
|
+
unsigned ? UNSIGNED_INTEGER8_MIN_MAX : INTEGER8_MIN_MAX
|
|
296
|
+
when /medium/i
|
|
297
|
+
unsigned ? UNSIGNED_INTEGER3_MIN_MAX : INTEGER3_MIN_MAX
|
|
298
|
+
when /small|int2/i
|
|
299
|
+
unsigned ? UNSIGNED_INTEGER2_MIN_MAX : INTEGER2_MIN_MAX
|
|
300
|
+
when /tiny/i
|
|
301
|
+
(unsigned || column_schema_tinyint_type_is_unsigned?) ? UNSIGNED_INTEGER1_MIN_MAX : INTEGER1_MIN_MAX
|
|
302
|
+
else
|
|
303
|
+
unsigned ? UNSIGNED_INTEGER4_MIN_MAX : INTEGER4_MIN_MAX
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Whether the tinyint type (if supported by the database) is unsigned by default.
|
|
308
|
+
def column_schema_tinyint_type_is_unsigned?
|
|
309
|
+
false
|
|
310
|
+
end
|
|
311
|
+
|
|
275
312
|
# Look at the db_type and guess the maximum length of the column.
|
|
276
313
|
# This assumes types such as varchar(255).
|
|
277
314
|
def column_schema_max_length(db_type)
|
|
@@ -127,6 +127,18 @@ module Sequel
|
|
|
127
127
|
#
|
|
128
128
|
# DB[:table].delete # DELETE * FROM table
|
|
129
129
|
# # => 3
|
|
130
|
+
#
|
|
131
|
+
# Some databases support using multiple tables in a DELETE query. This requires
|
|
132
|
+
# multiple FROM tables (JOINs can also be used). As multiple FROM tables use
|
|
133
|
+
# an implicit CROSS JOIN, you should make sure your WHERE condition uses the
|
|
134
|
+
# appropriate filters for the FROM tables:
|
|
135
|
+
#
|
|
136
|
+
# DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>c[:h]}}.
|
|
137
|
+
# delete
|
|
138
|
+
# # DELETE FROM a
|
|
139
|
+
# # USING b
|
|
140
|
+
# # INNER JOIN c ON (c.d = b.e)
|
|
141
|
+
# # WHERE ((a.f = b.g) AND (a.id = c.h))
|
|
130
142
|
def delete(&block)
|
|
131
143
|
sql = delete_sql
|
|
132
144
|
if uses_returning?(:delete)
|
|
@@ -313,14 +325,18 @@ module Sequel
|
|
|
313
325
|
|
|
314
326
|
# Inserts multiple records into the associated table. This method can be
|
|
315
327
|
# used to efficiently insert a large number of records into a table in a
|
|
316
|
-
# single query if the database supports it. Inserts
|
|
317
|
-
#
|
|
328
|
+
# single query if the database supports it. Inserts are automatically
|
|
329
|
+
# wrapped in a transaction if necessary.
|
|
318
330
|
#
|
|
319
331
|
# This method is called with a columns array and an array of value arrays:
|
|
320
332
|
#
|
|
321
333
|
# DB[:table].import([:x, :y], [[1, 2], [3, 4]])
|
|
322
334
|
# # INSERT INTO table (x, y) VALUES (1, 2)
|
|
323
|
-
# # INSERT INTO table (x, y) VALUES (3, 4)
|
|
335
|
+
# # INSERT INTO table (x, y) VALUES (3, 4)
|
|
336
|
+
#
|
|
337
|
+
# or, if the database supports it:
|
|
338
|
+
#
|
|
339
|
+
# # INSERT INTO table (x, y) VALUES (1, 2), (3, 4)
|
|
324
340
|
#
|
|
325
341
|
# This method also accepts a dataset instead of an array of value arrays:
|
|
326
342
|
#
|
|
@@ -328,9 +344,13 @@ module Sequel
|
|
|
328
344
|
# # INSERT INTO table (x, y) SELECT a, b FROM table2
|
|
329
345
|
#
|
|
330
346
|
# Options:
|
|
331
|
-
# :commit_every :: Open a new transaction for every given number of
|
|
332
|
-
# For example, if you provide a value of 50,
|
|
333
|
-
# after every 50 records.
|
|
347
|
+
# :commit_every :: Open a new transaction for every given number of
|
|
348
|
+
# records. For example, if you provide a value of 50,
|
|
349
|
+
# will commit after every 50 records. When a
|
|
350
|
+
# transaction is not required, this option controls
|
|
351
|
+
# the maximum number of values to insert with a single
|
|
352
|
+
# statement; it does not force the use of a
|
|
353
|
+
# transaction.
|
|
334
354
|
# :return :: When this is set to :primary_key, returns an array of
|
|
335
355
|
# autoincremented primary key values for the rows inserted.
|
|
336
356
|
# This does not have an effect if +values+ is a Dataset.
|
|
@@ -576,7 +596,7 @@ module Sequel
|
|
|
576
596
|
# # SELECT * FROM table ORDER BY id LIMIT 1000 OFFSET 1000
|
|
577
597
|
# # ...
|
|
578
598
|
#
|
|
579
|
-
# DB[:table].order(:id).paged_each(:
|
|
599
|
+
# DB[:table].order(:id).paged_each(rows_per_fetch: 100){|row| }
|
|
580
600
|
# # SELECT * FROM table ORDER BY id LIMIT 100
|
|
581
601
|
# # SELECT * FROM table ORDER BY id LIMIT 100 OFFSET 100
|
|
582
602
|
# # ...
|
|
@@ -918,6 +938,19 @@ module Sequel
|
|
|
918
938
|
#
|
|
919
939
|
# DB[:table].update(x: Sequel[:x]+1, y: 0) # UPDATE table SET x = (x + 1), y = 0
|
|
920
940
|
# # => 10
|
|
941
|
+
#
|
|
942
|
+
# Some databases support using multiple tables in an UPDATE query. This requires
|
|
943
|
+
# multiple FROM tables (JOINs can also be used). As multiple FROM tables use
|
|
944
|
+
# an implicit CROSS JOIN, you should make sure your WHERE condition uses the
|
|
945
|
+
# appropriate filters for the FROM tables:
|
|
946
|
+
#
|
|
947
|
+
# DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>10}}.
|
|
948
|
+
# update(:f=>Sequel[:c][:h])
|
|
949
|
+
# # UPDATE a
|
|
950
|
+
# # SET f = c.h
|
|
951
|
+
# # FROM b
|
|
952
|
+
# # INNER JOIN c ON (c.d = b.e)
|
|
953
|
+
# # WHERE ((a.f = b.g) AND (a.id = 10))
|
|
921
954
|
def update(values=OPTS, &block)
|
|
922
955
|
sql = update_sql(values)
|
|
923
956
|
if uses_returning?(:update)
|
|
@@ -1023,18 +1056,19 @@ module Sequel
|
|
|
1023
1056
|
|
|
1024
1057
|
# Internals of #import. If primary key values are requested, use
|
|
1025
1058
|
# separate insert commands for each row. Otherwise, call #multi_insert_sql
|
|
1026
|
-
# and execute each statement it gives separately.
|
|
1059
|
+
# and execute each statement it gives separately. A transaction is only used
|
|
1060
|
+
# if there are multiple statements to execute.
|
|
1027
1061
|
def _import(columns, values, opts)
|
|
1028
1062
|
trans_opts = Hash[opts]
|
|
1029
1063
|
trans_opts[:server] = @opts[:server]
|
|
1030
1064
|
if opts[:return] == :primary_key
|
|
1031
|
-
|
|
1065
|
+
_import_transaction(values, trans_opts){values.map{|v| insert(columns, v)}}
|
|
1032
1066
|
else
|
|
1033
1067
|
stmts = multi_insert_sql(columns, values)
|
|
1034
|
-
|
|
1068
|
+
_import_transaction(stmts, trans_opts){stmts.each{|st| execute_dui(st)}}
|
|
1035
1069
|
end
|
|
1036
1070
|
end
|
|
1037
|
-
|
|
1071
|
+
|
|
1038
1072
|
# Return an array of arrays of values given by the symbols in ret_cols.
|
|
1039
1073
|
def _select_map_multiple(ret_cols)
|
|
1040
1074
|
map{|r| r.values_at(*ret_cols)}
|
|
@@ -1073,6 +1107,17 @@ module Sequel
|
|
|
1073
1107
|
end
|
|
1074
1108
|
end
|
|
1075
1109
|
|
|
1110
|
+
# Use a transaction when yielding to the block if multiple values/statements
|
|
1111
|
+
# are provided. When only a single value or statement is provided, then yield
|
|
1112
|
+
# without using a transaction.
|
|
1113
|
+
def _import_transaction(values, trans_opts, &block)
|
|
1114
|
+
if values.length > 1
|
|
1115
|
+
@db.transaction(trans_opts, &block)
|
|
1116
|
+
else
|
|
1117
|
+
yield
|
|
1118
|
+
end
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1076
1121
|
# Internals of +select_hash+ and +select_hash_groups+
|
|
1077
1122
|
def _select_hash(meth, key_column, value_column, opts=OPTS)
|
|
1078
1123
|
select(*(key_column.is_a?(Array) ? key_column : [key_column]) + (value_column.is_a?(Array) ? value_column : [value_column])).
|
|
@@ -152,6 +152,11 @@ module Sequel
|
|
|
152
152
|
supports_distinct_on?
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
+
# Whether placeholder literalizers are supported, true by default.
|
|
156
|
+
def supports_placeholder_literalizer?
|
|
157
|
+
true
|
|
158
|
+
end
|
|
159
|
+
|
|
155
160
|
# Whether the dataset supports pattern matching by regular expressions, false by default.
|
|
156
161
|
def supports_regexp?
|
|
157
162
|
false
|