sequel 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +56 -0
- data/README +1 -0
- data/Rakefile +1 -1
- data/lib/sequel_core.rb +9 -16
- data/lib/sequel_core/adapters/ado.rb +6 -15
- data/lib/sequel_core/adapters/db2.rb +8 -10
- data/lib/sequel_core/adapters/dbi.rb +6 -4
- data/lib/sequel_core/adapters/informix.rb +21 -22
- data/lib/sequel_core/adapters/jdbc.rb +69 -10
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +1 -0
- data/lib/sequel_core/adapters/mysql.rb +81 -13
- data/lib/sequel_core/adapters/odbc.rb +32 -4
- data/lib/sequel_core/adapters/openbase.rb +6 -5
- data/lib/sequel_core/adapters/oracle.rb +23 -7
- data/lib/sequel_core/adapters/postgres.rb +42 -32
- data/lib/sequel_core/adapters/shared/mssql.rb +37 -62
- data/lib/sequel_core/adapters/shared/mysql.rb +22 -7
- data/lib/sequel_core/adapters/shared/oracle.rb +27 -48
- data/lib/sequel_core/adapters/shared/postgres.rb +64 -43
- data/lib/sequel_core/adapters/shared/progress.rb +31 -0
- data/lib/sequel_core/adapters/shared/sqlite.rb +15 -4
- data/lib/sequel_core/adapters/sqlite.rb +6 -14
- data/lib/sequel_core/connection_pool.rb +47 -13
- data/lib/sequel_core/database.rb +60 -35
- data/lib/sequel_core/database/schema.rb +4 -4
- data/lib/sequel_core/dataset.rb +12 -3
- data/lib/sequel_core/dataset/convenience.rb +4 -13
- data/lib/sequel_core/dataset/prepared_statements.rb +30 -28
- data/lib/sequel_core/dataset/sql.rb +144 -85
- data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
- data/lib/sequel_core/dataset/unsupported.rb +31 -0
- data/lib/sequel_core/exceptions.rb +6 -0
- data/lib/sequel_core/schema/generator.rb +4 -3
- data/lib/sequel_core/schema/sql.rb +41 -23
- data/lib/sequel_core/sql.rb +29 -1
- data/lib/sequel_model/associations.rb +1 -1
- data/lib/sequel_model/record.rb +31 -28
- data/spec/adapters/mysql_spec.rb +37 -4
- data/spec/adapters/oracle_spec.rb +26 -4
- data/spec/adapters/sqlite_spec.rb +7 -0
- data/spec/integration/prepared_statement_test.rb +24 -0
- data/spec/integration/schema_test.rb +1 -1
- data/spec/sequel_core/connection_pool_spec.rb +49 -2
- data/spec/sequel_core/core_sql_spec.rb +9 -2
- data/spec/sequel_core/database_spec.rb +64 -14
- data/spec/sequel_core/dataset_spec.rb +105 -7
- data/spec/sequel_core/schema_spec.rb +40 -12
- data/spec/sequel_core/spec_helper.rb +1 -0
- data/spec/sequel_model/spec_helper.rb +1 -0
- metadata +6 -3
@@ -13,69 +13,48 @@ module Sequel
|
|
13
13
|
end
|
14
14
|
|
15
15
|
module DatasetMethods
|
16
|
+
include Dataset::UnsupportedIntersectExceptAll
|
17
|
+
|
18
|
+
SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having union intersect except order limit'.freeze
|
19
|
+
|
16
20
|
def empty?
|
17
21
|
db[:dual].where(exists).get(1) == nil
|
18
22
|
end
|
19
23
|
|
20
|
-
|
21
|
-
# options.
|
22
|
-
def select_sql(opts = nil)
|
23
|
-
opts = opts ? @opts.merge(opts) : @opts
|
24
|
-
|
25
|
-
if sql = opts[:sql]
|
26
|
-
return sql
|
27
|
-
end
|
28
|
-
|
29
|
-
columns = opts[:select]
|
30
|
-
select_columns = columns ? column_list(columns) : '*'
|
31
|
-
sql = opts[:distinct] ? \
|
32
|
-
"SELECT DISTINCT #{select_columns}" : \
|
33
|
-
"SELECT #{select_columns}"
|
34
|
-
|
35
|
-
if opts[:from]
|
36
|
-
sql << " FROM #{source_list(opts[:from])}"
|
37
|
-
end
|
38
|
-
|
39
|
-
if join = opts[:join]
|
40
|
-
join.each{|j| sql << literal(j)}
|
41
|
-
end
|
42
|
-
|
43
|
-
if where = opts[:where]
|
44
|
-
sql << " WHERE #{literal(where)}"
|
45
|
-
end
|
24
|
+
private
|
46
25
|
|
47
|
-
|
48
|
-
|
49
|
-
|
26
|
+
# Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
|
27
|
+
# the use of AS anywhere, so this disables it in all cases.
|
28
|
+
def as_sql(expression, aliaz)
|
29
|
+
"#{expression} #{quote_identifier(aliaz)}"
|
30
|
+
end
|
50
31
|
|
51
|
-
|
52
|
-
|
53
|
-
|
32
|
+
def select_clause_order
|
33
|
+
SELECT_CLAUSE_ORDER
|
34
|
+
end
|
54
35
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
sql <<
|
60
|
-
" INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
|
61
|
-
elsif except = opts[:except]
|
62
|
-
sql << (opts[:except_all] ? \
|
63
|
-
" EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
|
36
|
+
# Oracle doesn't support DISTINCT ON
|
37
|
+
def select_distinct_sql(sql, opts)
|
38
|
+
if opts[:distinct]
|
39
|
+
raise(Error, "DISTINCT ON not supported by Oracle") unless opts[:distinct].empty?
|
40
|
+
sql << " DISTINCT"
|
64
41
|
end
|
42
|
+
end
|
65
43
|
|
66
|
-
|
67
|
-
|
68
|
-
|
44
|
+
# Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
|
45
|
+
def select_except_sql(sql, opts)
|
46
|
+
sql << " MINUS #{opts[:except].sql}" if opts[:except]
|
47
|
+
end
|
69
48
|
|
49
|
+
# Oracle requires a subselect to do limit and offset
|
50
|
+
def select_limit_sql(sql, opts)
|
70
51
|
if limit = opts[:limit]
|
71
52
|
if (offset = opts[:offset]) && (offset > 0)
|
72
|
-
sql
|
53
|
+
sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
|
73
54
|
else
|
74
|
-
sql
|
55
|
+
sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
|
75
56
|
end
|
76
57
|
end
|
77
|
-
|
78
|
-
sql
|
79
58
|
end
|
80
59
|
end
|
81
60
|
end
|
@@ -4,6 +4,15 @@ module Sequel
|
|
4
4
|
# uses NativeExceptions, the native adapter uses PGError.
|
5
5
|
CONVERTED_EXCEPTIONS = []
|
6
6
|
|
7
|
+
@force_standard_strings = true
|
8
|
+
|
9
|
+
# By default, Sequel forces the use of standard strings, so that
|
10
|
+
# '\\' is interpreted as '\\' and not '\\'. While PostgreSQL defaults
|
11
|
+
# to interpreting plain strings as extended strings, this will change
|
12
|
+
# in a future version of PostgreSQL. Sequel assumes that SQL standard
|
13
|
+
# strings will be used.
|
14
|
+
metaattr_accessor :force_standard_strings
|
15
|
+
|
7
16
|
# Methods shared by adapter/connection instances.
|
8
17
|
module AdapterMethods
|
9
18
|
attr_writer :db
|
@@ -57,6 +66,20 @@ module Sequel
|
|
57
66
|
# to implement multi-level transactions with savepoints.
|
58
67
|
attr_accessor :transaction_depth
|
59
68
|
|
69
|
+
# Apply connection settings for this connection. Currently, turns
|
70
|
+
# standard_conforming_strings ON if Postgres.force_standard_strings
|
71
|
+
# is true.
|
72
|
+
def apply_connection_settings
|
73
|
+
if Postgres.force_standard_strings
|
74
|
+
sql = "SET standard_conforming_strings = ON"
|
75
|
+
@db.log_info(sql)
|
76
|
+
# This setting will only work on PostgreSQL 8.2 or greater
|
77
|
+
# and we don't know the server version at this point, so
|
78
|
+
# try it unconditionally and rescue any errors.
|
79
|
+
execute(sql) rescue nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
60
83
|
# Get the last inserted value for the given sequence.
|
61
84
|
def last_insert_id(sequence)
|
62
85
|
sql = SELECT_CURRVAL % sequence
|
@@ -67,6 +90,15 @@ module Sequel
|
|
67
90
|
end
|
68
91
|
end
|
69
92
|
|
93
|
+
# Get the primary key for the given table.
|
94
|
+
def primary_key(schema, table)
|
95
|
+
sql = SELECT_PK % [schema, table]
|
96
|
+
@db.log_info(sql)
|
97
|
+
execute(sql) do |r|
|
98
|
+
return single_value(r)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
70
102
|
# Get the primary key and sequence for the given table.
|
71
103
|
def sequence(schema, table)
|
72
104
|
sql = SELECT_SERIAL_SEQUENCE % [schema, table]
|
@@ -82,15 +114,6 @@ module Sequel
|
|
82
114
|
return single_value(r)
|
83
115
|
end
|
84
116
|
end
|
85
|
-
|
86
|
-
# Get the primary key for the given table.
|
87
|
-
def primary_key(schema, table)
|
88
|
-
sql = SELECT_PK % [schema, table]
|
89
|
-
@db.log_info(sql)
|
90
|
-
execute(sql) do |r|
|
91
|
-
return single_value(r)
|
92
|
-
end
|
93
|
-
end
|
94
117
|
end
|
95
118
|
|
96
119
|
# Methods shared by Database instances that connect to PostgreSQL.
|
@@ -110,11 +133,6 @@ module Sequel
|
|
110
133
|
@default_schema ||= :public
|
111
134
|
end
|
112
135
|
|
113
|
-
# Set a new default schema to use.
|
114
|
-
def default_schema=(schema)
|
115
|
-
@default_schema = schema
|
116
|
-
end
|
117
|
-
|
118
136
|
# Remove the cached entries for primary keys and sequences when dropping a table.
|
119
137
|
def drop_table(*names)
|
120
138
|
names.each do |name|
|
@@ -161,12 +179,11 @@ module Sequel
|
|
161
179
|
synchronize(server){|conn| primary_key_for_table(conn, table)}
|
162
180
|
end
|
163
181
|
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
182
|
+
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
183
|
+
# a rename table operation, so speciying a new schema in new_name will not have an effect.
|
184
|
+
def rename_table_sql(name, new_name)
|
185
|
+
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
186
|
+
end
|
170
187
|
|
171
188
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
172
189
|
# managing incrementing primary keys.
|
@@ -255,6 +272,11 @@ module Sequel
|
|
255
272
|
|
256
273
|
private
|
257
274
|
|
275
|
+
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers by default.
|
276
|
+
def upcase_identifiers_default
|
277
|
+
false
|
278
|
+
end
|
279
|
+
|
258
280
|
# The result of the insert for the given table and values. If values
|
259
281
|
# is an array, assume the first column is the primary key and return
|
260
282
|
# that. If values is a hash, lookup the primary key for the table. If
|
@@ -322,7 +344,7 @@ module Sequel
|
|
322
344
|
def schema_parse_tables(opts)
|
323
345
|
schemas = {}
|
324
346
|
schema_parser_dataset(nil, opts).each do |row|
|
325
|
-
(schemas[row.delete(:table)
|
347
|
+
(schemas[quote_schema_table(SQL::QualifiedIdentifier.new(row.delete(:schema), row.delete(:table)))] ||= []) << row
|
326
348
|
end
|
327
349
|
schemas.each do |table, rows|
|
328
350
|
schemas[table] = schema_parse_rows(rows)
|
@@ -348,9 +370,9 @@ module Sequel
|
|
348
370
|
if table_name
|
349
371
|
ds.filter!(:pg_class__relname=>table_name.to_s)
|
350
372
|
else
|
351
|
-
ds.select_more!(:pg_class__relname___table)
|
373
|
+
ds.select_more!(:pg_class__relname___table, :pg_namespace__nspname___schema)
|
374
|
+
ds.join!(:pg_namespace, :oid=>:pg_class__relnamespace, :nspname=>(opts[:schema] || default_schema).to_s)
|
352
375
|
end
|
353
|
-
ds.join!(:pg_namespace, :oid=>:pg_class__relnamespace, :nspname=>(opts[:schema] || default_schema).to_s) if opts[:schema] || !opts.include?(:schema)
|
354
376
|
ds
|
355
377
|
end
|
356
378
|
end
|
@@ -372,6 +394,7 @@ module Sequel
|
|
372
394
|
QUERY_PLAN = 'QUERY PLAN'.to_sym
|
373
395
|
ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
|
374
396
|
ROW_SHARE = 'ROW SHARE'.freeze
|
397
|
+
SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having intersect union except order limit lock'.freeze
|
375
398
|
SHARE = 'SHARE'.freeze
|
376
399
|
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
377
400
|
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
@@ -452,8 +475,10 @@ module Sequel
|
|
452
475
|
case v
|
453
476
|
when LiteralString
|
454
477
|
v
|
478
|
+
when SQL::Blob
|
479
|
+
db.synchronize{|c| "E'#{c.escape_bytea(v)}'"}
|
455
480
|
when String
|
456
|
-
db.synchronize{|c| "'#{
|
481
|
+
db.synchronize{|c| "'#{c.escape_string(v)}'"}
|
457
482
|
when Time
|
458
483
|
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
|
459
484
|
when DateTime
|
@@ -489,25 +514,6 @@ module Sequel
|
|
489
514
|
["INSERT INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
|
490
515
|
end
|
491
516
|
|
492
|
-
# PostgreSQL assumes unquoted identifiers are lower case by default,
|
493
|
-
# so do not upcase the identifier when quoting it.
|
494
|
-
def quoted_identifier(c)
|
495
|
-
"\"#{c}\""
|
496
|
-
end
|
497
|
-
|
498
|
-
# Support lock mode, allowing FOR SHARE and FOR UPDATE queries.
|
499
|
-
def select_sql(opts = nil)
|
500
|
-
row_lock_mode = opts ? opts[:lock] : @opts[:lock]
|
501
|
-
sql = super
|
502
|
-
case row_lock_mode
|
503
|
-
when :update
|
504
|
-
sql << FOR_UPDATE
|
505
|
-
when :share
|
506
|
-
sql << FOR_SHARE
|
507
|
-
end
|
508
|
-
sql
|
509
|
-
end
|
510
|
-
|
511
517
|
private
|
512
518
|
|
513
519
|
# Call execute_insert on the database object with the given values.
|
@@ -521,6 +527,21 @@ module Sequel
|
|
521
527
|
insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : 'NULL'.lit, *values)
|
522
528
|
end
|
523
529
|
|
530
|
+
# The order of clauses in the SELECT SQL statement
|
531
|
+
def select_clause_order
|
532
|
+
SELECT_CLAUSE_ORDER
|
533
|
+
end
|
534
|
+
|
535
|
+
# Support lock mode, allowing FOR SHARE and FOR UPDATE queries.
|
536
|
+
def select_lock_sql(sql, opts)
|
537
|
+
case opts[:lock]
|
538
|
+
when :update
|
539
|
+
sql << FOR_UPDATE
|
540
|
+
when :share
|
541
|
+
sql << FOR_SHARE
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
524
545
|
# The version of the database server
|
525
546
|
def server_version
|
526
547
|
db.server_version(@opts[:server])
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Progress
|
3
|
+
module DatabaseMethods
|
4
|
+
|
5
|
+
def dataset(opts = nil)
|
6
|
+
ds = super
|
7
|
+
ds.extend(DatasetMethods)
|
8
|
+
ds
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module DatasetMethods
|
13
|
+
include Dataset::UnsupportedIntersectExcept
|
14
|
+
|
15
|
+
SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where group order having union'.freeze
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def select_clause_order
|
20
|
+
SELECT_CLAUSE_ORDER
|
21
|
+
end
|
22
|
+
|
23
|
+
# Progress uses TOP for limit, but it is only supported in Progress 10.
|
24
|
+
# The Progress adapter targets Progress 9, so it silently ignores the option.
|
25
|
+
def select_limit_sql(sql, opts)
|
26
|
+
raise(Error, "OFFSET not supported") if opts[:offset]
|
27
|
+
#sql << " TOP #{opts[:limit]}" if opts[:limit]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -6,6 +6,12 @@ module Sequel
|
|
6
6
|
TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
|
7
7
|
TEMP_STORE = {'0' => :default, '1' => :file, '2' => :memory}.freeze
|
8
8
|
|
9
|
+
# Run all alter_table commands in a transaction. This is technically only
|
10
|
+
# needed for drop column.
|
11
|
+
def alter_table(name, generator=nil, &block)
|
12
|
+
transaction{super}
|
13
|
+
end
|
14
|
+
|
9
15
|
# SQLite supports limited table modification. You can add a column
|
10
16
|
# or an index. Dropping columns is supported by copying the table into
|
11
17
|
# a temporary table, dropping the table, and creating a new table without
|
@@ -18,14 +24,12 @@ module Sequel
|
|
18
24
|
index_definition_sql(table, op)
|
19
25
|
when :drop_column
|
20
26
|
columns_str = (schema_parse_table(table, {}).map{|c| c[0]} - Array(op[:name])).join(",")
|
21
|
-
|
22
|
-
"CREATE TEMPORARY TABLE #{table}_backup(#{columns_str})",
|
27
|
+
["CREATE TEMPORARY TABLE #{table}_backup(#{columns_str})",
|
23
28
|
"INSERT INTO #{table}_backup SELECT #{columns_str} FROM #{table}",
|
24
29
|
"DROP TABLE #{table}",
|
25
30
|
"CREATE TABLE #{table}(#{columns_str})",
|
26
31
|
"INSERT INTO #{table} SELECT #{columns_str} FROM #{table}_backup",
|
27
|
-
"DROP TABLE #{table}_backup"
|
28
|
-
"COMMIT"]
|
32
|
+
"DROP TABLE #{table}_backup"]
|
29
33
|
else
|
30
34
|
raise Error, "Unsupported ALTER TABLE operation"
|
31
35
|
end
|
@@ -82,6 +86,11 @@ module Sequel
|
|
82
86
|
|
83
87
|
private
|
84
88
|
|
89
|
+
# SQLite folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers by default.
|
90
|
+
def upcase_identifiers_default
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
85
94
|
# SQLite supports schema parsing using the table_info PRAGMA, so
|
86
95
|
# parse the output of that into the format Sequel expects.
|
87
96
|
def schema_parse_table(table_name, opts)
|
@@ -100,6 +109,8 @@ module Sequel
|
|
100
109
|
|
101
110
|
# Instance methods for datasets that connect to an SQLite database
|
102
111
|
module DatasetMethods
|
112
|
+
include Dataset::UnsupportedIntersectExceptAll
|
113
|
+
|
103
114
|
# SQLite does not support pattern matching via regular expressions.
|
104
115
|
# SQLite is case insensitive (depending on pragma), so use LIKE for
|
105
116
|
# ILIKE.
|
@@ -46,11 +46,6 @@ module Sequel
|
|
46
46
|
SQLite::Dataset.new(self, opts)
|
47
47
|
end
|
48
48
|
|
49
|
-
# Disconnect all connections from the database.
|
50
|
-
def disconnect
|
51
|
-
@pool.disconnect {|c| c.close}
|
52
|
-
end
|
53
|
-
|
54
49
|
# Run the given SQL with the given arguments and return the number of changed rows.
|
55
50
|
def execute_dui(sql, opts={})
|
56
51
|
_execute(sql, opts){|conn| conn.execute_batch(sql, opts[:arguments]); conn.changes}
|
@@ -110,6 +105,11 @@ module Sequel
|
|
110
105
|
o[:max_connections] = 1 if @opts[:database] == ':memory:' || @opts[:database].blank?
|
111
106
|
o
|
112
107
|
end
|
108
|
+
|
109
|
+
# Disconnect given connections from the database.
|
110
|
+
def disconnect_connection(c)
|
111
|
+
c.close
|
112
|
+
end
|
113
113
|
end
|
114
114
|
|
115
115
|
# Dataset class for SQLite datasets that use the ruby-sqlite3 driver.
|
@@ -135,14 +135,6 @@ module Sequel
|
|
135
135
|
|
136
136
|
private
|
137
137
|
|
138
|
-
# Work around for the default prepared statement and argument
|
139
|
-
# mapper code, which wants a hash that maps. SQLite doesn't
|
140
|
-
# need to do this, but still requires a value for the argument
|
141
|
-
# in order for the substitution to work correctly.
|
142
|
-
def prepared_args_hash
|
143
|
-
true
|
144
|
-
end
|
145
|
-
|
146
138
|
# SQLite uses a : before the name of the argument for named
|
147
139
|
# arguments.
|
148
140
|
def prepared_arg(k)
|
@@ -221,7 +213,7 @@ module Sequel
|
|
221
213
|
# Prepare the given type of query with the given name and store
|
222
214
|
# it in the database. Note that a new native prepared statement is
|
223
215
|
# created on each call to this prepared statement.
|
224
|
-
def prepare(type, name, values=nil)
|
216
|
+
def prepare(type, name=nil, values=nil)
|
225
217
|
ps = to_prepared_statement(type, values)
|
226
218
|
ps.extend(PreparedStatementMethods)
|
227
219
|
db.prepared_statements[name] = ps if name
|
@@ -5,6 +5,9 @@ class Sequel::ConnectionPool
|
|
5
5
|
# The proc used to create a new database connection.
|
6
6
|
attr_accessor :connection_proc
|
7
7
|
|
8
|
+
# The proc used to disconnect a database connection.
|
9
|
+
attr_accessor :disconnection_proc
|
10
|
+
|
8
11
|
# The maximum number of connections.
|
9
12
|
attr_reader :max_size
|
10
13
|
|
@@ -40,6 +43,7 @@ class Sequel::ConnectionPool
|
|
40
43
|
@max_size = opts[:max_connections] || 4
|
41
44
|
@mutex = Mutex.new
|
42
45
|
@connection_proc = block
|
46
|
+
@disconnection_proc = opts[:disconnection_proc]
|
43
47
|
@servers = [:default]
|
44
48
|
@servers += opts[:servers].keys - @servers if opts[:servers]
|
45
49
|
@available_connections = Hash.new{|h,k| h[:default]}
|
@@ -103,8 +107,11 @@ class Sequel::ConnectionPool
|
|
103
107
|
end
|
104
108
|
begin
|
105
109
|
yield conn
|
110
|
+
rescue Sequel::DatabaseDisconnectError => dde
|
111
|
+
remove(t, conn, server)
|
112
|
+
raise
|
106
113
|
ensure
|
107
|
-
release(t, conn, server)
|
114
|
+
release(t, conn, server) unless dde
|
108
115
|
end
|
109
116
|
rescue Exception => e
|
110
117
|
raise(@convert_exceptions && !e.is_a?(StandardError) ? RuntimeError.new(e.message) : e)
|
@@ -116,24 +123,19 @@ class Sequel::ConnectionPool
|
|
116
123
|
# disconnecting from the database, assuming that no connections are currently
|
117
124
|
# being used. Once a connection is requested using #hold, the connection pool
|
118
125
|
# creates new connections to the database.
|
119
|
-
def disconnect
|
126
|
+
def disconnect(&block)
|
127
|
+
block ||= @disconnection_proc
|
120
128
|
@mutex.synchronize do
|
121
129
|
@available_connections.each do |server, conns|
|
122
|
-
conns.each{|c|
|
130
|
+
conns.each{|c| block.call(c)} if block
|
123
131
|
conns.clear
|
124
|
-
|
132
|
+
set_created_count(server, allocated(server).length)
|
125
133
|
end
|
126
134
|
end
|
127
135
|
end
|
128
136
|
|
129
137
|
private
|
130
138
|
|
131
|
-
# Returns the connection owned by the supplied thread for the given server,
|
132
|
-
# if any.
|
133
|
-
def owned_connection(thread, server)
|
134
|
-
@mutex.synchronize{@allocated[server][thread]}
|
135
|
-
end
|
136
|
-
|
137
139
|
# Assigns a connection to the supplied thread for the given server, if one
|
138
140
|
# is available.
|
139
141
|
def acquire(thread, server)
|
@@ -154,12 +156,18 @@ class Sequel::ConnectionPool
|
|
154
156
|
# the server is less than the maximum size of the pool.
|
155
157
|
def make_new(server)
|
156
158
|
if @created_count[server] < @max_size
|
157
|
-
@created_count[server]
|
159
|
+
set_created_count(server, @created_count[server] + 1)
|
158
160
|
@connection_proc ? @connection_proc.call(server) : \
|
159
161
|
(raise Error, "No connection proc specified")
|
160
162
|
end
|
161
163
|
end
|
162
164
|
|
165
|
+
# Returns the connection owned by the supplied thread for the given server,
|
166
|
+
# if any.
|
167
|
+
def owned_connection(thread, server)
|
168
|
+
@mutex.synchronize{@allocated[server][thread]}
|
169
|
+
end
|
170
|
+
|
163
171
|
# Releases the connection assigned to the supplied thread and server.
|
164
172
|
def release(thread, conn, server)
|
165
173
|
@mutex.synchronize do
|
@@ -167,6 +175,21 @@ class Sequel::ConnectionPool
|
|
167
175
|
available_connections(server) << conn
|
168
176
|
end
|
169
177
|
end
|
178
|
+
|
179
|
+
# Removes the currently allocated connection from the connection pool.
|
180
|
+
def remove(thread, conn, server)
|
181
|
+
@mutex.synchronize do
|
182
|
+
allocated(server).delete(thread)
|
183
|
+
set_created_count(server, @created_count[server] - 1)
|
184
|
+
@disconnection_proc.call(conn) if @disconnection_proc
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Set the created count for the given server type
|
189
|
+
def set_created_count(server, value)
|
190
|
+
server = :default unless @created_count.include?(server)
|
191
|
+
@created_count[server] = value
|
192
|
+
end
|
170
193
|
end
|
171
194
|
|
172
195
|
# A SingleThreadedPool acts as a replacement for a ConnectionPool for use
|
@@ -179,6 +202,9 @@ class Sequel::SingleThreadedPool
|
|
179
202
|
# The proc used to create a new database connection
|
180
203
|
attr_writer :connection_proc
|
181
204
|
|
205
|
+
# The proc used to disconnect a database connection.
|
206
|
+
attr_accessor :disconnection_proc
|
207
|
+
|
182
208
|
# Initializes the instance with the supplied block as the connection_proc.
|
183
209
|
#
|
184
210
|
# The single threaded pool takes the following options:
|
@@ -187,6 +213,7 @@ class Sequel::SingleThreadedPool
|
|
187
213
|
# to RuntimeError exceptions (default true)
|
188
214
|
def initialize(opts={}, &block)
|
189
215
|
@connection_proc = block
|
216
|
+
@disconnection_proc = opts[:disconnection_proc]
|
190
217
|
@conns = {}
|
191
218
|
@convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
|
192
219
|
end
|
@@ -200,7 +227,13 @@ class Sequel::SingleThreadedPool
|
|
200
227
|
# This method simulates the ConnectionPool#hold API.
|
201
228
|
def hold(server=:default)
|
202
229
|
begin
|
203
|
-
|
230
|
+
begin
|
231
|
+
yield(c = (@conns[server] ||= @connection_proc.call(server)))
|
232
|
+
rescue Sequel::DatabaseDisconnectError => dde
|
233
|
+
@conns.delete(server)
|
234
|
+
@disconnection_proc.call(c) if @disconnection_proc
|
235
|
+
raise
|
236
|
+
end
|
204
237
|
rescue Exception => e
|
205
238
|
# if the error is not a StandardError it is converted into RuntimeError.
|
206
239
|
raise(@convert_exceptions && !e.is_a?(StandardError) ? RuntimeError.new(e.message) : e)
|
@@ -210,7 +243,8 @@ class Sequel::SingleThreadedPool
|
|
210
243
|
# Disconnects from the database. Once a connection is requested using
|
211
244
|
# #hold, the connection is reestablished.
|
212
245
|
def disconnect(&block)
|
213
|
-
|
246
|
+
block ||= @disconnection_proc
|
247
|
+
@conns.values.each{|conn| block.call(conn) if block}
|
214
248
|
@conns = {}
|
215
249
|
end
|
216
250
|
end
|