sequel 3.40.0 → 3.41.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.
- data/CHANGELOG +40 -0
- data/README.rdoc +2 -2
- data/doc/advanced_associations.rdoc +12 -0
- data/doc/bin_sequel.rdoc +144 -0
- data/doc/migration.rdoc +1 -1
- data/doc/object_model.rdoc +29 -0
- data/doc/release_notes/3.41.0.txt +155 -0
- data/lib/sequel/adapters/ado.rb +4 -4
- data/lib/sequel/adapters/amalgalite.rb +0 -5
- data/lib/sequel/adapters/cubrid.rb +2 -2
- data/lib/sequel/adapters/db2.rb +9 -5
- data/lib/sequel/adapters/dbi.rb +4 -6
- data/lib/sequel/adapters/do.rb +4 -5
- data/lib/sequel/adapters/firebird.rb +8 -4
- data/lib/sequel/adapters/ibmdb.rb +2 -3
- data/lib/sequel/adapters/informix.rb +0 -6
- data/lib/sequel/adapters/jdbc.rb +11 -7
- data/lib/sequel/adapters/jdbc/db2.rb +22 -0
- data/lib/sequel/adapters/jdbc/derby.rb +5 -5
- data/lib/sequel/adapters/jdbc/h2.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +6 -0
- data/lib/sequel/adapters/mock.rb +3 -3
- data/lib/sequel/adapters/mysql.rb +7 -7
- data/lib/sequel/adapters/mysql2.rb +0 -5
- data/lib/sequel/adapters/odbc.rb +4 -4
- data/lib/sequel/adapters/openbase.rb +4 -6
- data/lib/sequel/adapters/oracle.rb +14 -6
- data/lib/sequel/adapters/postgres.rb +12 -8
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/firebird.rb +10 -0
- data/lib/sequel/adapters/shared/mssql.rb +43 -1
- data/lib/sequel/adapters/shared/mysql.rb +1 -0
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +12 -0
- data/lib/sequel/adapters/shared/sqlite.rb +32 -0
- data/lib/sequel/adapters/sqlite.rb +9 -8
- data/lib/sequel/adapters/swift.rb +3 -8
- data/lib/sequel/adapters/tinytds.rb +5 -5
- data/lib/sequel/connection_pool.rb +13 -19
- data/lib/sequel/connection_pool/sharded_single.rb +12 -12
- data/lib/sequel/connection_pool/sharded_threaded.rb +37 -17
- data/lib/sequel/connection_pool/single.rb +6 -3
- data/lib/sequel/connection_pool/threaded.rb +33 -13
- data/lib/sequel/database/connecting.rb +28 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +2 -5
- data/lib/sequel/database/query.rb +2 -2
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +3 -0
- data/lib/sequel/dataset/query.rb +8 -4
- data/lib/sequel/dataset/sql.rb +7 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +1 -1
- data/lib/sequel/extensions/connection_validator.rb +109 -0
- data/lib/sequel/extensions/pg_array.rb +2 -0
- data/lib/sequel/extensions/pg_hstore.rb +2 -0
- data/lib/sequel/extensions/pg_json.rb +4 -0
- data/lib/sequel/extensions/pg_range.rb +1 -0
- data/lib/sequel/extensions/pg_row.rb +4 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +53 -10
- data/lib/sequel/plugins/touch.rb +18 -6
- data/lib/sequel/plugins/validation_class_methods.rb +1 -0
- data/lib/sequel/plugins/validation_helpers.rb +3 -1
- data/lib/sequel/sql.rb +61 -19
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/firebird_spec.rb +52 -38
- data/spec/adapters/mssql_spec.rb +67 -0
- data/spec/adapters/mysql_spec.rb +192 -116
- data/spec/adapters/postgres_spec.rb +133 -70
- data/spec/adapters/spec_helper.rb +7 -0
- data/spec/adapters/sqlite_spec.rb +34 -1
- data/spec/core/connection_pool_spec.rb +79 -75
- data/spec/core/database_spec.rb +9 -4
- data/spec/core/dataset_spec.rb +15 -0
- data/spec/core/expression_filters_spec.rb +40 -2
- data/spec/extensions/connection_validator_spec.rb +118 -0
- data/spec/extensions/pg_array_spec.rb +4 -0
- data/spec/extensions/single_table_inheritance_spec.rb +42 -0
- data/spec/extensions/touch_spec.rb +40 -0
- data/spec/extensions/validation_class_methods_spec.rb +19 -1
- data/spec/extensions/validation_helpers_spec.rb +17 -0
- data/spec/integration/database_test.rb +14 -0
- data/spec/integration/dataset_test.rb +3 -3
- data/spec/integration/plugin_test.rb +41 -12
- data/spec/integration/schema_test.rb +14 -0
- data/spec/integration/spec_helper.rb +7 -0
- data/spec/integration/type_test.rb +3 -0
- metadata +9 -3
@@ -8,13 +8,12 @@ Sequel.require 'connection_pool/threaded'
|
|
8
8
|
class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
9
9
|
# The following additional options are respected:
|
10
10
|
# * :servers - A hash of servers to use. Keys should be symbols. If not
|
11
|
-
# present, will use a single :default server.
|
12
|
-
# be passed to the connection_proc.
|
11
|
+
# present, will use a single :default server.
|
13
12
|
# * :servers_hash - The base hash to use for the servers. By default,
|
14
13
|
# Sequel uses Hash.new(:default). You can use a hash with a default proc
|
15
14
|
# that raises an error if you want to catch all cases where a nonexistent
|
16
15
|
# server is used.
|
17
|
-
def initialize(opts = {}
|
16
|
+
def initialize(db, opts = {})
|
18
17
|
super
|
19
18
|
@available_connections = {}
|
20
19
|
@connections_to_remove = []
|
@@ -87,11 +86,10 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
87
86
|
# creates new connections to the database. Options:
|
88
87
|
# * :server - Should be a symbol specifing the server to disconnect from,
|
89
88
|
# or an array of symbols to specify multiple servers.
|
90
|
-
def disconnect(opts={}
|
91
|
-
block ||= @disconnection_proc
|
89
|
+
def disconnect(opts={})
|
92
90
|
sync do
|
93
91
|
(opts[:server] ? Array(opts[:server]) : @servers.keys).each do |s|
|
94
|
-
disconnect_server(s
|
92
|
+
disconnect_server(s)
|
95
93
|
end
|
96
94
|
end
|
97
95
|
end
|
@@ -145,7 +143,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
145
143
|
raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
|
146
144
|
servers.each do |server|
|
147
145
|
if @servers.include?(server)
|
148
|
-
disconnect_server(server
|
146
|
+
disconnect_server(server)
|
149
147
|
@available_connections.delete(server)
|
150
148
|
@allocated.delete(server)
|
151
149
|
@servers.delete(server)
|
@@ -159,6 +157,10 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
159
157
|
sync{@servers.keys}
|
160
158
|
end
|
161
159
|
|
160
|
+
def pool_type
|
161
|
+
:sharded_threaded
|
162
|
+
end
|
163
|
+
|
162
164
|
private
|
163
165
|
|
164
166
|
# Assigns a connection to the supplied thread for the given server, if one
|
@@ -176,16 +178,30 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
176
178
|
# available, tries to create a new connection. The calling code should already
|
177
179
|
# have the mutex before calling this.
|
178
180
|
def available(server)
|
179
|
-
|
181
|
+
next_available(server) || make_new(server)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Return a connection to the pool of available connections for the server,
|
185
|
+
# returns the connection. The calling code should already have the mutex
|
186
|
+
# before calling this.
|
187
|
+
def checkin_connection(server, conn)
|
188
|
+
case @connection_handling
|
189
|
+
when :queue
|
190
|
+
available_connections(server).unshift(conn)
|
191
|
+
else
|
192
|
+
available_connections(server) << conn
|
193
|
+
end
|
194
|
+
|
195
|
+
conn
|
180
196
|
end
|
181
197
|
|
182
198
|
# Disconnect from the given server. Disconnects available connections
|
183
199
|
# immediately, and schedules currently allocated connections for disconnection
|
184
200
|
# as soon as they are returned to the pool. The calling code should already
|
185
201
|
# have the mutex before calling this.
|
186
|
-
def disconnect_server(server
|
202
|
+
def disconnect_server(server)
|
187
203
|
if conns = available_connections(server)
|
188
|
-
conns.each{|conn|
|
204
|
+
conns.each{|conn| db.disconnect_connection(conn)}
|
189
205
|
conns.clear
|
190
206
|
end
|
191
207
|
@connections_to_remove.concat(allocated(server).values)
|
@@ -201,6 +217,13 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
201
217
|
end
|
202
218
|
default_make_new(server) if (n || size(server)) < @max_size
|
203
219
|
end
|
220
|
+
|
221
|
+
# Return the next available connection in the pool for the given server, or nil
|
222
|
+
# if there is not currently an available connection for the server.
|
223
|
+
# The calling code should already have the mutex before calling this.
|
224
|
+
def next_available(server)
|
225
|
+
available_connections(server).pop
|
226
|
+
end
|
204
227
|
|
205
228
|
# Returns the connection owned by the supplied thread for the given server,
|
206
229
|
# if any. The calling code should NOT already have the mutex before calling this.
|
@@ -223,13 +246,10 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
223
246
|
else
|
224
247
|
conn = allocated(server).delete(thread)
|
225
248
|
|
226
|
-
|
227
|
-
|
228
|
-
available_connections(server).unshift(conn)
|
229
|
-
when :disconnect
|
230
|
-
@disconnection_proc.call(conn) if @disconnection_proc
|
249
|
+
if @connection_handling == :disconnect
|
250
|
+
db.disconnect_connection(conn)
|
231
251
|
else
|
232
|
-
|
252
|
+
checkin_connection(server, conn)
|
233
253
|
end
|
234
254
|
end
|
235
255
|
end
|
@@ -239,7 +259,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
239
259
|
def remove(thread, conn, server)
|
240
260
|
@connections_to_remove.delete(conn)
|
241
261
|
allocated(server).delete(thread) if @servers.include?(server)
|
242
|
-
|
262
|
+
db.disconnect_connection(conn)
|
243
263
|
end
|
244
264
|
|
245
265
|
CONNECTION_POOL_MAP[[false, true]] = self
|
@@ -14,10 +14,9 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# Disconnect the connection from the database.
|
17
|
-
def disconnect(opts=nil
|
17
|
+
def disconnect(opts=nil)
|
18
18
|
return unless @conn
|
19
|
-
|
20
|
-
block.call(@conn) if block
|
19
|
+
db.disconnect_connection(@conn)
|
21
20
|
@conn = nil
|
22
21
|
end
|
23
22
|
|
@@ -31,5 +30,9 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
|
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
33
|
+
def pool_type
|
34
|
+
:single
|
35
|
+
end
|
36
|
+
|
34
37
|
CONNECTION_POOL_MAP[[true, false]] = self
|
35
38
|
end
|
@@ -22,7 +22,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
22
22
|
# a connection again (default 0.001)
|
23
23
|
# * :pool_timeout - The amount of seconds to wait to acquire a connection
|
24
24
|
# before raising a PoolTimeoutError (default 5)
|
25
|
-
def initialize(opts = {}
|
25
|
+
def initialize(db, opts = {})
|
26
26
|
super
|
27
27
|
@max_size = Integer(opts[:max_connections] || 4)
|
28
28
|
raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
|
@@ -64,10 +64,9 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
64
64
|
#
|
65
65
|
# Once a connection is requested using #hold, the connection pool
|
66
66
|
# creates new connections to the database.
|
67
|
-
def disconnect(opts={}
|
68
|
-
block ||= @disconnection_proc
|
67
|
+
def disconnect(opts={})
|
69
68
|
sync do
|
70
|
-
@available_connections.each{|conn|
|
69
|
+
@available_connections.each{|conn| db.disconnect_connection(conn)}
|
71
70
|
@available_connections.clear
|
72
71
|
end
|
73
72
|
end
|
@@ -106,13 +105,17 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
106
105
|
rescue Sequel::DatabaseDisconnectError
|
107
106
|
oconn = conn
|
108
107
|
conn = nil
|
109
|
-
|
108
|
+
db.disconnect_connection(oconn) if oconn
|
110
109
|
@allocated.delete(t)
|
111
110
|
raise
|
112
111
|
ensure
|
113
112
|
sync{release(t)} if conn
|
114
113
|
end
|
115
114
|
end
|
115
|
+
|
116
|
+
def pool_type
|
117
|
+
:threaded
|
118
|
+
end
|
116
119
|
|
117
120
|
private
|
118
121
|
|
@@ -131,7 +134,20 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
131
134
|
# available, tries to create a new connection. The calling code should already
|
132
135
|
# have the mutex before calling this.
|
133
136
|
def available
|
134
|
-
|
137
|
+
next_available || make_new(DEFAULT_SERVER)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return a connection to the pool of available connections, returns the connection.
|
141
|
+
# The calling code should already have the mutex before calling this.
|
142
|
+
def checkin_connection(conn)
|
143
|
+
case @connection_handling
|
144
|
+
when :queue
|
145
|
+
@available_connections.unshift(conn)
|
146
|
+
else
|
147
|
+
@available_connections << conn
|
148
|
+
end
|
149
|
+
|
150
|
+
conn
|
135
151
|
end
|
136
152
|
|
137
153
|
# Alias the default make_new method, so subclasses can call it directly.
|
@@ -147,7 +163,14 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
147
163
|
end
|
148
164
|
super if (n || size) < @max_size
|
149
165
|
end
|
150
|
-
|
166
|
+
|
167
|
+
# Return the next available connection in the pool, or nil if there
|
168
|
+
# is not currently an available connection. The calling code should already
|
169
|
+
# have the mutex before calling this.
|
170
|
+
def next_available
|
171
|
+
@available_connections.pop
|
172
|
+
end
|
173
|
+
|
151
174
|
# Returns the connection owned by the supplied thread,
|
152
175
|
# if any. The calling code should NOT already have the mutex before calling this.
|
153
176
|
def owned_connection(thread)
|
@@ -159,13 +182,10 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
159
182
|
def release(thread)
|
160
183
|
conn = @allocated.delete(thread)
|
161
184
|
|
162
|
-
|
163
|
-
|
164
|
-
@available_connections.unshift(conn)
|
165
|
-
when :disconnect
|
166
|
-
@disconnection_proc.call(conn) if @disconnection_proc
|
185
|
+
if @connection_handling == :disconnect
|
186
|
+
db.disconnect_connection(conn)
|
167
187
|
else
|
168
|
-
|
188
|
+
checkin_connection(conn)
|
169
189
|
end
|
170
190
|
end
|
171
191
|
|
@@ -140,7 +140,7 @@ module Sequel
|
|
140
140
|
@pool.add_servers(servers.keys)
|
141
141
|
end
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
# Connects to the database. This method should be overridden by descendants.
|
145
145
|
def connect(server)
|
146
146
|
raise NotImplemented, "#connect should be overridden by adapters"
|
@@ -173,6 +173,13 @@ module Sequel
|
|
173
173
|
pool.disconnect(opts)
|
174
174
|
end
|
175
175
|
|
176
|
+
# Should only be called by the connection pool code to disconnect a connection.
|
177
|
+
# By default, calls the close method on the connection object, since most
|
178
|
+
# adapters use that, but should be overwritten on other adapters.
|
179
|
+
def disconnect_connection(conn)
|
180
|
+
conn.close
|
181
|
+
end
|
182
|
+
|
176
183
|
# Yield a new Database instance for every server in the connection pool.
|
177
184
|
# Intended for use in sharded environments where there is a need to make schema
|
178
185
|
# modifications (DDL queries) on each shard.
|
@@ -243,6 +250,21 @@ module Sequel
|
|
243
250
|
true
|
244
251
|
end
|
245
252
|
|
253
|
+
# Check whether the given connection is currently valid, by
|
254
|
+
# running a query against it. If the query fails, the
|
255
|
+
# connection should probably be removed from the connection
|
256
|
+
# pool.
|
257
|
+
def valid_connection?(conn)
|
258
|
+
sql = valid_connection_sql
|
259
|
+
begin
|
260
|
+
log_connection_execute(conn, sql)
|
261
|
+
rescue Sequel::DatabaseError, *database_error_classes
|
262
|
+
false
|
263
|
+
else
|
264
|
+
true
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
246
268
|
private
|
247
269
|
|
248
270
|
# The default options for the connection pool.
|
@@ -271,5 +293,10 @@ module Sequel
|
|
271
293
|
opts.delete(:servers)
|
272
294
|
opts
|
273
295
|
end
|
296
|
+
|
297
|
+
# The SQL query to issue to check if a connection is valid.
|
298
|
+
def valid_connection_sql
|
299
|
+
@valid_connection_sql ||= select(nil).sql
|
300
|
+
end
|
274
301
|
end
|
275
302
|
end
|
@@ -19,7 +19,7 @@ module Sequel
|
|
19
19
|
|
20
20
|
# Log a message at error level, with information about the exception.
|
21
21
|
def log_exception(exception, message)
|
22
|
-
log_each(:error, "#{exception.class}: #{exception.message.strip}: #{message}")
|
22
|
+
log_each(:error, "#{exception.class}: #{exception.message.strip if exception.message}: #{message}")
|
23
23
|
end
|
24
24
|
|
25
25
|
# Log a message at level info to all loggers.
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -49,7 +49,6 @@ module Sequel
|
|
49
49
|
#
|
50
50
|
# Accepts the following options:
|
51
51
|
# :default_schema :: The default schema to use, see #default_schema.
|
52
|
-
# :disconnection_proc :: A proc used to disconnect the connection
|
53
52
|
# :identifier_input_method :: A string method symbol to call on identifiers going into the database
|
54
53
|
# :identifier_output_method :: A string method symbol to call on identifiers coming from the database
|
55
54
|
# :logger :: A specific logger to use
|
@@ -59,14 +58,12 @@ module Sequel
|
|
59
58
|
# :single_threaded :: Whether to use a single-threaded connection pool
|
60
59
|
# :sql_log_level :: Method to use to log SQL to a logger, :info by default.
|
61
60
|
#
|
62
|
-
# All options given are also passed to the connection pool.
|
63
|
-
# is given, it is used as the connection_proc for the ConnectionPool.
|
61
|
+
# All options given are also passed to the connection pool.
|
64
62
|
def initialize(opts = {}, &block)
|
65
63
|
@opts ||= opts
|
66
64
|
@opts = connection_pool_default_options.merge(@opts)
|
67
65
|
@loggers = Array(@opts[:logger]) + Array(@opts[:loggers])
|
68
66
|
self.log_warn_duration = @opts[:log_warn_duration]
|
69
|
-
@opts[:disconnection_proc] ||= proc{|conn| disconnect_connection(conn)}
|
70
67
|
block ||= proc{|server| connect(server)}
|
71
68
|
@opts[:servers] = {} if @opts[:servers].is_a?(String)
|
72
69
|
@opts[:adapter_class] = self.class
|
@@ -84,7 +81,7 @@ module Sequel
|
|
84
81
|
@cache_schema = typecast_value_boolean(@opts.fetch(:cache_schema, true))
|
85
82
|
@dataset_modules = []
|
86
83
|
self.sql_log_level = @opts[:sql_log_level] ? @opts[:sql_log_level].to_sym : :info
|
87
|
-
@pool = ConnectionPool.get_pool(@opts
|
84
|
+
@pool = ConnectionPool.get_pool(self, @opts)
|
88
85
|
|
89
86
|
Sequel.synchronize{::Sequel::DATABASES.push(self)}
|
90
87
|
end
|
@@ -605,7 +605,7 @@ module Sequel
|
|
605
605
|
# such as :integer or :string.
|
606
606
|
def schema_column_type(db_type)
|
607
607
|
case db_type
|
608
|
-
when /\A(character( varying)?|n?(var)?char|n?text|string)/io
|
608
|
+
when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/io
|
609
609
|
:string
|
610
610
|
when /\A(int(eger)?|(big|small|tiny)int)/io
|
611
611
|
:integer
|
@@ -621,7 +621,7 @@ module Sequel
|
|
621
621
|
:float
|
622
622
|
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?)|(?:small)?money)\z/io
|
623
623
|
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
624
|
-
when /bytea|
|
624
|
+
when /bytea|blob|image|(var)?binary/io
|
625
625
|
:blob
|
626
626
|
when /\Aenum/io
|
627
627
|
:enum
|
@@ -177,7 +177,7 @@ module Sequel
|
|
177
177
|
# :concurrently :: Create the index concurrently, so it doesn't block
|
178
178
|
# operations on the table while the index is being
|
179
179
|
# built.
|
180
|
-
# :
|
180
|
+
# :opclass :: Use a specific operator class in the index.
|
181
181
|
#
|
182
182
|
# Microsoft SQL Server specific options:
|
183
183
|
#
|
@@ -154,6 +154,9 @@ module Sequel
|
|
154
154
|
# :collate :: The collation to use for the table.
|
155
155
|
# :engine :: The table engine to use for the table.
|
156
156
|
#
|
157
|
+
# PostgreSQL specific options:
|
158
|
+
# :unlogged :: Create the table as an unlogged table.
|
159
|
+
#
|
157
160
|
# See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
|
158
161
|
def create_table(name, options={}, &block)
|
159
162
|
remove_cached_schema(name)
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -592,7 +592,7 @@ module Sequel
|
|
592
592
|
# DB[:items].limit(10...20) # SELECT * FROM items LIMIT 10 OFFSET 10
|
593
593
|
# DB[:items].limit(10..20) # SELECT * FROM items LIMIT 11 OFFSET 10
|
594
594
|
# DB[:items].limit(nil, 20) # SELECT * FROM items OFFSET 20
|
595
|
-
def limit(l, o = nil)
|
595
|
+
def limit(l, o = (no_offset = true; nil))
|
596
596
|
return from_self.limit(l, o) if @opts[:sql]
|
597
597
|
|
598
598
|
if Range === l
|
@@ -610,6 +610,8 @@ module Sequel
|
|
610
610
|
raise(Error, 'Offsets must be greater than or equal to 0') unless o >= 0
|
611
611
|
end
|
612
612
|
opts[:offset] = o
|
613
|
+
elsif !no_offset
|
614
|
+
opts[:offset] = nil
|
613
615
|
end
|
614
616
|
clone(opts)
|
615
617
|
end
|
@@ -753,15 +755,17 @@ module Sequel
|
|
753
755
|
# given, the existing order is inverted.
|
754
756
|
#
|
755
757
|
# DB[:items].reverse(:id) # SELECT * FROM items ORDER BY id DESC
|
758
|
+
# DB[:items].reverse{foo(bar)} # SELECT * FROM items ORDER BY foo(bar) DESC
|
756
759
|
# DB[:items].order(:id).reverse # SELECT * FROM items ORDER BY id DESC
|
757
760
|
# DB[:items].order(:id).reverse(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name ASC
|
758
|
-
def reverse(*order)
|
761
|
+
def reverse(*order, &block)
|
762
|
+
virtual_row_columns(order, block)
|
759
763
|
order(*invert_order(order.empty? ? @opts[:order] : order))
|
760
764
|
end
|
761
765
|
|
762
766
|
# Alias of +reverse+
|
763
|
-
def reverse_order(*order)
|
764
|
-
reverse(*order)
|
767
|
+
def reverse_order(*order, &block)
|
768
|
+
reverse(*order, &block)
|
765
769
|
end
|
766
770
|
|
767
771
|
# Returns a copy of the dataset with the columns selected changed
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -290,6 +290,7 @@ module Sequel
|
|
290
290
|
column_all_sql
|
291
291
|
complex_expression_sql
|
292
292
|
constant_sql
|
293
|
+
delayed_evaluation_sql
|
293
294
|
function_sql
|
294
295
|
join_clause_sql
|
295
296
|
join_on_clause_sql
|
@@ -495,6 +496,12 @@ module Sequel
|
|
495
496
|
sql << constant.to_s
|
496
497
|
end
|
497
498
|
|
499
|
+
# SQL fragment for delayed evaluations, evaluating the
|
500
|
+
# object and literalizing the returned value.
|
501
|
+
def delayed_evaluation_sql_append(sql, callable)
|
502
|
+
literal_append(sql, callable.call)
|
503
|
+
end
|
504
|
+
|
498
505
|
# SQL fragment specifying an emulated SQL function call.
|
499
506
|
# By default, assumes just the function name may need to
|
500
507
|
# be emulated, adapters should set an EMULATED_FUNCTION_MAP
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# The connection_validator extension modifies a database's
|
2
|
+
# connection pool to validate that connections checked out
|
3
|
+
# from the pool are still valid, before yielding them for
|
4
|
+
# use. If it detects an invalid connection, it removes it
|
5
|
+
# from the pool and tries the next available connection,
|
6
|
+
# creating a new connection if no available connection is
|
7
|
+
# valid. Example of use:
|
8
|
+
#
|
9
|
+
# DB.extension(:connection_validator)
|
10
|
+
#
|
11
|
+
# As checking connections for validity involves issuing a
|
12
|
+
# query, which is potentially an expensive operation,
|
13
|
+
# the validation checks are only run if the connection has
|
14
|
+
# been idle for longer than a certain threshold. By default,
|
15
|
+
# that threshold is 3600 seconds (1 hour), but it can be
|
16
|
+
# modified by the user, set to -1 to always validate
|
17
|
+
# connections on checkout:
|
18
|
+
#
|
19
|
+
# DB.pool.connection_validation_timeout = -1
|
20
|
+
#
|
21
|
+
# Note that if you set the timeout to validate connections
|
22
|
+
# on every checkout, you should probably manually control
|
23
|
+
# connection checkouts on a coarse basis, using
|
24
|
+
# Database#synchonrize. In a web application, the optimal
|
25
|
+
# place for that would be a rack middleware. Validating
|
26
|
+
# connections on every checkout without setting up coarse
|
27
|
+
# connection checkouts will hurt performance, in some cases
|
28
|
+
# significantly. Note that setting up coarse connection
|
29
|
+
# checkouts reduces the concurrency level acheivable. For
|
30
|
+
# example, in a web application, using Database#synchronize
|
31
|
+
# in a rack middleware will limit the number of concurrent
|
32
|
+
# web requests to the number to connections in the database
|
33
|
+
# connection pool.
|
34
|
+
#
|
35
|
+
# Note that this extension only affects the default threaded
|
36
|
+
# and the sharded threaded connection pool. The single
|
37
|
+
# threaded and sharded single threaded connection pools are
|
38
|
+
# not affected. As the only reason to use the single threaded
|
39
|
+
# pools is for speed, and this extension makes the connection
|
40
|
+
# pool slower, there's not much point in modifying this
|
41
|
+
# extension to work with the single threaded pools. The
|
42
|
+
# threaded pools work fine even in single threaded code, so if
|
43
|
+
# you are currently using a single threaded pool and want to
|
44
|
+
# use this extension, switch to using a threaded pool.
|
45
|
+
|
46
|
+
module Sequel
|
47
|
+
module ConnectionValidator
|
48
|
+
class Retry < Error; end
|
49
|
+
|
50
|
+
# The number of seconds that need to pass since
|
51
|
+
# connection checkin before attempting to validate
|
52
|
+
# the connection when checking it out from the pool.
|
53
|
+
# Defaults to 3600 seconds (1 hour).
|
54
|
+
attr_accessor :connection_validation_timeout
|
55
|
+
|
56
|
+
# Initialize the data structures used by this extension.
|
57
|
+
def self.extended(pool)
|
58
|
+
pool.instance_eval do
|
59
|
+
@connection_timestamps ||= {}
|
60
|
+
@connection_validation_timeout = 3600
|
61
|
+
end
|
62
|
+
|
63
|
+
# Make sure the valid connection SQL query is precached,
|
64
|
+
# otherwise it's possible it will happen at runtime. While
|
65
|
+
# it should work correctly at runtime, it's better to avoid
|
66
|
+
# the possibility of failure altogether.
|
67
|
+
pool.db.send(:valid_connection_sql)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Record the time the connection was checked back into the pool.
|
73
|
+
def checkin_connection(*)
|
74
|
+
conn = super
|
75
|
+
@connection_timestamps[conn] = Time.now
|
76
|
+
conn
|
77
|
+
end
|
78
|
+
|
79
|
+
# When acquiring a connection, if it has been
|
80
|
+
# idle for longer than the connection validation timeout,
|
81
|
+
# test the connection for validity. If it is not valid,
|
82
|
+
# disconnect the connection, and retry with a new connection.
|
83
|
+
def acquire(*a)
|
84
|
+
begin
|
85
|
+
if (conn = super) &&
|
86
|
+
(t = sync{@connection_timestamps.delete(conn)}) &&
|
87
|
+
Time.now - t > @connection_validation_timeout &&
|
88
|
+
!db.valid_connection?(conn)
|
89
|
+
|
90
|
+
if pool_type == :sharded_threaded
|
91
|
+
sync{allocated(a.last).delete(Thread.current)}
|
92
|
+
else
|
93
|
+
sync{@allocated.delete(Thread.current)}
|
94
|
+
end
|
95
|
+
|
96
|
+
db.disconnect_connection(conn)
|
97
|
+
raise Retry
|
98
|
+
end
|
99
|
+
rescue Retry
|
100
|
+
retry
|
101
|
+
end
|
102
|
+
|
103
|
+
conn
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
Database.register_extension(:connection_validator){|db| db.pool.extend(ConnectionValidator)}
|
108
|
+
end
|
109
|
+
|