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