epugh-sequel 0.0.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/README.rdoc +652 -0
- data/VERSION.yml +4 -0
- data/bin/sequel +104 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +85 -0
- data/lib/sequel/adapters/db2.rb +132 -0
- data/lib/sequel/adapters/dbi.rb +101 -0
- data/lib/sequel/adapters/do.rb +197 -0
- data/lib/sequel/adapters/do/mysql.rb +38 -0
- data/lib/sequel/adapters/do/postgres.rb +92 -0
- data/lib/sequel/adapters/do/sqlite.rb +31 -0
- data/lib/sequel/adapters/firebird.rb +307 -0
- data/lib/sequel/adapters/informix.rb +75 -0
- data/lib/sequel/adapters/jdbc.rb +485 -0
- data/lib/sequel/adapters/jdbc/h2.rb +62 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
- data/lib/sequel/adapters/mysql.rb +370 -0
- data/lib/sequel/adapters/odbc.rb +184 -0
- data/lib/sequel/adapters/openbase.rb +57 -0
- data/lib/sequel/adapters/oracle.rb +140 -0
- data/lib/sequel/adapters/postgres.rb +453 -0
- data/lib/sequel/adapters/shared/mssql.rb +93 -0
- data/lib/sequel/adapters/shared/mysql.rb +341 -0
- data/lib/sequel/adapters/shared/oracle.rb +62 -0
- data/lib/sequel/adapters/shared/postgres.rb +743 -0
- data/lib/sequel/adapters/shared/progress.rb +34 -0
- data/lib/sequel/adapters/shared/sqlite.rb +263 -0
- data/lib/sequel/adapters/sqlite.rb +243 -0
- data/lib/sequel/adapters/utils/date_format.rb +21 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
- data/lib/sequel/adapters/utils/unsupported.rb +62 -0
- data/lib/sequel/connection_pool.rb +258 -0
- data/lib/sequel/core.rb +204 -0
- data/lib/sequel/core_sql.rb +185 -0
- data/lib/sequel/database.rb +687 -0
- data/lib/sequel/database/schema_generator.rb +324 -0
- data/lib/sequel/database/schema_methods.rb +164 -0
- data/lib/sequel/database/schema_sql.rb +324 -0
- data/lib/sequel/dataset.rb +422 -0
- data/lib/sequel/dataset/convenience.rb +237 -0
- data/lib/sequel/dataset/prepared_statements.rb +220 -0
- data/lib/sequel/dataset/sql.rb +1105 -0
- data/lib/sequel/deprecated.rb +529 -0
- data/lib/sequel/exceptions.rb +44 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/sequel/extensions/inflector.rb +288 -0
- data/lib/sequel/extensions/pagination.rb +96 -0
- data/lib/sequel/extensions/pretty_table.rb +78 -0
- data/lib/sequel/extensions/query.rb +48 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +44 -0
- data/lib/sequel/migration.rb +212 -0
- data/lib/sequel/model.rb +142 -0
- data/lib/sequel/model/association_reflection.rb +263 -0
- data/lib/sequel/model/associations.rb +1024 -0
- data/lib/sequel/model/base.rb +911 -0
- data/lib/sequel/model/deprecated.rb +188 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +384 -0
- data/lib/sequel/model/errors.rb +37 -0
- data/lib/sequel/model/exceptions.rb +7 -0
- data/lib/sequel/model/inflections.rb +230 -0
- data/lib/sequel/model/plugins.rb +74 -0
- data/lib/sequel/object_graph.rb +230 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +373 -0
- data/lib/sequel/sql.rb +854 -0
- data/lib/sequel/version.rb +11 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/ado_spec.rb +46 -0
- data/spec/adapters/firebird_spec.rb +376 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +875 -0
- data/spec/adapters/oracle_spec.rb +272 -0
- data/spec/adapters/postgres_spec.rb +692 -0
- data/spec/adapters/spec_helper.rb +10 -0
- data/spec/adapters/sqlite_spec.rb +550 -0
- data/spec/core/connection_pool_spec.rb +526 -0
- data/spec/core/core_ext_spec.rb +156 -0
- data/spec/core/core_sql_spec.rb +528 -0
- data/spec/core/database_spec.rb +1214 -0
- data/spec/core/dataset_spec.rb +3513 -0
- data/spec/core/expression_filters_spec.rb +363 -0
- data/spec/core/migration_spec.rb +261 -0
- data/spec/core/object_graph_spec.rb +280 -0
- data/spec/core/pretty_table_spec.rb +58 -0
- data/spec/core/schema_generator_spec.rb +167 -0
- data/spec/core/schema_spec.rb +778 -0
- data/spec/core/spec_helper.rb +82 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/inflector_spec.rb +122 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/schema_spec.rb +111 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/extensions/spec_helper.rb +90 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/validation_class_methods_spec.rb +1054 -0
- data/spec/integration/dataset_test.rb +160 -0
- data/spec/integration/eager_loader_test.rb +683 -0
- data/spec/integration/prepared_statement_test.rb +130 -0
- data/spec/integration/schema_test.rb +183 -0
- data/spec/integration/spec_helper.rb +75 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +93 -0
- data/spec/model/associations_spec.rb +1780 -0
- data/spec/model/base_spec.rb +494 -0
- data/spec/model/caching_spec.rb +217 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1165 -0
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/model/model_spec.rb +588 -0
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/model/record_spec.rb +1243 -0
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +202 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Module containing overrides for Sequel's standard date/time literalization
|
|
2
|
+
# to use the SQL standrd. The SQL standard is used by fewer databases than
|
|
3
|
+
# the defacto standard (which is just a normal string).
|
|
4
|
+
module Sequel::Dataset::SQLStandardDateFormat
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
# Use SQL standard syntax for Date
|
|
8
|
+
def literal_date(v)
|
|
9
|
+
v.strftime("DATE '%Y-%m-%d'")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Use SQL standard syntax for DateTime
|
|
13
|
+
def literal_datetime(v)
|
|
14
|
+
v.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Use SQL standard syntax for Time
|
|
18
|
+
def literal_time(v)
|
|
19
|
+
v.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
class Dataset
|
|
3
|
+
module StoredProcedureMethods
|
|
4
|
+
SQL_QUERY_TYPE = Hash.new{|h,k| h[k] = k}
|
|
5
|
+
SQL_QUERY_TYPE[:first] = SQL_QUERY_TYPE[:all] = :select
|
|
6
|
+
|
|
7
|
+
# The name of the stored procedure to call
|
|
8
|
+
attr_accessor :sproc_name
|
|
9
|
+
|
|
10
|
+
# Call the prepared statement
|
|
11
|
+
def call(*args, &block)
|
|
12
|
+
@sproc_args = args
|
|
13
|
+
case @sproc_type
|
|
14
|
+
when :select, :all
|
|
15
|
+
all(&block)
|
|
16
|
+
when :first
|
|
17
|
+
first
|
|
18
|
+
when :insert
|
|
19
|
+
insert
|
|
20
|
+
when :update
|
|
21
|
+
update
|
|
22
|
+
when :delete
|
|
23
|
+
delete
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Programmer friendly string showing this is a stored procedure,
|
|
28
|
+
# showing the name of the procedure.
|
|
29
|
+
def inspect
|
|
30
|
+
"<#{self.class.name}/StoredProcedure name=#{@sproc_name}>"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Set the type of the sproc and override the corresponding _sql
|
|
34
|
+
# method to return the empty string (since the result will be
|
|
35
|
+
# ignored anyway).
|
|
36
|
+
def sproc_type=(type)
|
|
37
|
+
@sproc_type = type
|
|
38
|
+
meta_def("#{sql_query_type}_sql"){|*a| ''}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# The type of query (:select, :insert, :delete, :update).
|
|
44
|
+
def sql_query_type
|
|
45
|
+
SQL_QUERY_TYPE[@sproc_type]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module StoredProcedures
|
|
50
|
+
# For the given type (:select, :first, :insert, :update, or :delete),
|
|
51
|
+
# run the database stored procedure with the given name with the given
|
|
52
|
+
# arguments.
|
|
53
|
+
def call_sproc(type, name, *args)
|
|
54
|
+
prepare_sproc(type, name).call(*args)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Transform this dataset into a stored procedure that you can call
|
|
58
|
+
# multiple times with new arguments.
|
|
59
|
+
def prepare_sproc(type, name)
|
|
60
|
+
sp = clone
|
|
61
|
+
prepare_extend_sproc(sp)
|
|
62
|
+
sp.sproc_type = type
|
|
63
|
+
sp.sproc_name = name
|
|
64
|
+
sp
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# Extend the dataset with the stored procedure methods.
|
|
70
|
+
def prepare_extend_sproc(ds)
|
|
71
|
+
ds.extend(StoredProcedureMethods)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
class Sequel::Dataset
|
|
2
|
+
# This module should be included in the dataset class for all databases that
|
|
3
|
+
# don't support INTERSECT or EXCEPT.
|
|
4
|
+
module UnsupportedIntersectExcept
|
|
5
|
+
# Raise an Error if EXCEPT is used
|
|
6
|
+
def except(ds, all=false)
|
|
7
|
+
raise(Sequel::Error, "EXCEPT not supported")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Raise an Error if INTERSECT is used
|
|
11
|
+
def intersect(ds, all=false)
|
|
12
|
+
raise(Sequel::Error, "INTERSECT not supported")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Since EXCEPT and INTERSECT are not supported, and order shouldn't matter
|
|
18
|
+
# when UNION is used, don't worry about parantheses. This may potentially
|
|
19
|
+
# give incorrect results if UNION ALL is used.
|
|
20
|
+
def select_compounds_sql(sql, opts)
|
|
21
|
+
return unless opts[:compounds]
|
|
22
|
+
opts[:compounds].each do |type, dataset, all|
|
|
23
|
+
sql << " #{type.to_s.upcase}#{' ALL' if all} #{subselect_sql(dataset)}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# This module should be included in the dataset class for all databases that
|
|
29
|
+
# don't support INTERSECT ALL or EXCEPT ALL.
|
|
30
|
+
module UnsupportedIntersectExceptAll
|
|
31
|
+
# Raise an Error if EXCEPT is used
|
|
32
|
+
def except(ds, all=false)
|
|
33
|
+
raise(Sequel::Error, "EXCEPT ALL not supported") if all
|
|
34
|
+
super(ds)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Raise an Error if INTERSECT is used
|
|
38
|
+
def intersect(ds, all=false)
|
|
39
|
+
raise(Sequel::Error, "INTERSECT ALL not supported") if all
|
|
40
|
+
super(ds)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# This module should be included in the dataset class for all databases that
|
|
45
|
+
# don't support IS [NOT] (TRUE|FALSE)
|
|
46
|
+
module UnsupportedIsTrue
|
|
47
|
+
# Use an = construct instead of IS and an != OR IS NULL construct instead of IS NOT.
|
|
48
|
+
def complex_expression_sql(op, args)
|
|
49
|
+
case op
|
|
50
|
+
when :IS, :'IS NOT'
|
|
51
|
+
isnot = op != :IS
|
|
52
|
+
return super if (v1 = args.at(1)).nil?
|
|
53
|
+
v0 = literal(args.at(0))
|
|
54
|
+
s = "(#{v0} #{'!' if isnot}= #{literal(v1)})"
|
|
55
|
+
s = "(#{s} OR (#{v0} IS NULL))" if isnot
|
|
56
|
+
s
|
|
57
|
+
else
|
|
58
|
+
super(op, args)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# A ConnectionPool manages access to database connections by keeping
|
|
2
|
+
# multiple connections and giving threads exclusive access to each
|
|
3
|
+
# connection.
|
|
4
|
+
class Sequel::ConnectionPool
|
|
5
|
+
# The proc used to create a new database connection.
|
|
6
|
+
attr_accessor :connection_proc
|
|
7
|
+
|
|
8
|
+
# The proc used to disconnect a database connection.
|
|
9
|
+
attr_accessor :disconnection_proc
|
|
10
|
+
|
|
11
|
+
# The maximum number of connections.
|
|
12
|
+
attr_reader :max_size
|
|
13
|
+
|
|
14
|
+
# The mutex that protects access to the other internal vairables. You must use
|
|
15
|
+
# this if you want to manipulate the variables safely.
|
|
16
|
+
attr_reader :mutex
|
|
17
|
+
|
|
18
|
+
# Constructs a new pool with a maximum size. If a block is supplied, it
|
|
19
|
+
# is used to create new connections as they are needed.
|
|
20
|
+
#
|
|
21
|
+
# pool = ConnectionPool.new(:max_connections=>10) {MyConnection.new(opts)}
|
|
22
|
+
#
|
|
23
|
+
# The connection creation proc can be changed at any time by assigning a
|
|
24
|
+
# Proc to pool#connection_proc.
|
|
25
|
+
#
|
|
26
|
+
# pool = ConnectionPool.new(:max_connections=>10)
|
|
27
|
+
# pool.connection_proc = proc {MyConnection.new(opts)}
|
|
28
|
+
#
|
|
29
|
+
# The connection pool takes the following options:
|
|
30
|
+
#
|
|
31
|
+
# * :max_connections - The maximum number of connections the connection pool
|
|
32
|
+
# will open (default 4)
|
|
33
|
+
# * :pool_convert_exceptions - Whether to convert non-StandardError based exceptions
|
|
34
|
+
# to RuntimeError exceptions (default true)
|
|
35
|
+
# * :pool_sleep_time - The amount of time to sleep before attempting to acquire
|
|
36
|
+
# a connection again (default 0.001)
|
|
37
|
+
# * :pool_timeout - The amount of seconds to wait to acquire a connection
|
|
38
|
+
# before raising a PoolTimeoutError (default 5)
|
|
39
|
+
# * :servers - A hash of servers to use. Keys should be symbols. If not
|
|
40
|
+
# present, will use a single :default server. The server name symbol will
|
|
41
|
+
# be passed to the connection_proc.
|
|
42
|
+
def initialize(opts = {}, &block)
|
|
43
|
+
@max_size = opts[:max_connections] || 4
|
|
44
|
+
@mutex = Mutex.new
|
|
45
|
+
@connection_proc = block
|
|
46
|
+
@disconnection_proc = opts[:disconnection_proc]
|
|
47
|
+
@servers = [:default]
|
|
48
|
+
@servers += opts[:servers].keys - @servers if opts[:servers]
|
|
49
|
+
@available_connections = Hash.new{|h,k| h[:default]}
|
|
50
|
+
@allocated = Hash.new{|h,k| h[:default]}
|
|
51
|
+
@created_count = Hash.new{|h,k| h[:default]}
|
|
52
|
+
@servers.each do |s|
|
|
53
|
+
@available_connections[s] = []
|
|
54
|
+
@allocated[s] = {}
|
|
55
|
+
@created_count[s] = 0
|
|
56
|
+
end
|
|
57
|
+
@timeout = opts[:pool_timeout] || 5
|
|
58
|
+
@sleep_time = opts[:pool_sleep_time] || 0.001
|
|
59
|
+
@convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A hash of connections currently being used for the given server, key is the
|
|
63
|
+
# Thread, value is the connection.
|
|
64
|
+
def allocated(server=:default)
|
|
65
|
+
@allocated[server]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# An array of connections opened but not currently used, for the given
|
|
69
|
+
# server.
|
|
70
|
+
def available_connections(server=:default)
|
|
71
|
+
@available_connections[server]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The total number of connections opened for the given server, should
|
|
75
|
+
# be equal to available_connections.length + allocated.length
|
|
76
|
+
def created_count(server=:default)
|
|
77
|
+
@created_count[server]
|
|
78
|
+
end
|
|
79
|
+
alias size created_count
|
|
80
|
+
|
|
81
|
+
# Chooses the first available connection to the given server, or if none are
|
|
82
|
+
# available, creates a new connection. Passes the connection to the supplied
|
|
83
|
+
# block:
|
|
84
|
+
#
|
|
85
|
+
# pool.hold {|conn| conn.execute('DROP TABLE posts')}
|
|
86
|
+
#
|
|
87
|
+
# Pool#hold is re-entrant, meaning it can be called recursively in
|
|
88
|
+
# the same thread without blocking.
|
|
89
|
+
#
|
|
90
|
+
# If no connection is immediately available and the pool is already using the maximum
|
|
91
|
+
# number of connections, Pool#hold will block until a connection
|
|
92
|
+
# is available or the timeout expires. If the timeout expires before a
|
|
93
|
+
# connection can be acquired, a Sequel::Error::PoolTimeoutError is
|
|
94
|
+
# raised.
|
|
95
|
+
def hold(server=:default)
|
|
96
|
+
begin
|
|
97
|
+
t = Thread.current
|
|
98
|
+
time = Time.new
|
|
99
|
+
timeout = time + @timeout
|
|
100
|
+
sleep_time = @sleep_time
|
|
101
|
+
if conn = owned_connection(t, server)
|
|
102
|
+
return yield(conn)
|
|
103
|
+
end
|
|
104
|
+
until conn = acquire(t, server)
|
|
105
|
+
raise(::Sequel::Error::PoolTimeoutError) if Time.new > timeout
|
|
106
|
+
sleep sleep_time
|
|
107
|
+
end
|
|
108
|
+
begin
|
|
109
|
+
yield conn
|
|
110
|
+
rescue Sequel::DatabaseDisconnectError => dde
|
|
111
|
+
remove(t, conn, server)
|
|
112
|
+
raise
|
|
113
|
+
ensure
|
|
114
|
+
release(t, conn, server) unless dde
|
|
115
|
+
end
|
|
116
|
+
rescue Exception => e
|
|
117
|
+
raise(@convert_exceptions && !e.is_a?(StandardError) ? RuntimeError.new(e.message) : e)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Removes all connection currently available on all servers, optionally
|
|
122
|
+
# yielding each connection to the given block. This method has the effect of
|
|
123
|
+
# disconnecting from the database, assuming that no connections are currently
|
|
124
|
+
# being used. Once a connection is requested using #hold, the connection pool
|
|
125
|
+
# creates new connections to the database.
|
|
126
|
+
def disconnect(&block)
|
|
127
|
+
block ||= @disconnection_proc
|
|
128
|
+
@mutex.synchronize do
|
|
129
|
+
@available_connections.each do |server, conns|
|
|
130
|
+
conns.each{|c| block.call(c)} if block
|
|
131
|
+
conns.clear
|
|
132
|
+
set_created_count(server, allocated(server).length)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
# Assigns a connection to the supplied thread for the given server, if one
|
|
140
|
+
# is available.
|
|
141
|
+
def acquire(thread, server)
|
|
142
|
+
@mutex.synchronize do
|
|
143
|
+
if conn = available(server)
|
|
144
|
+
allocated(server)[thread] = conn
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Returns an available connection to the given server. If no connection is
|
|
150
|
+
# available, tries to create a new connection.
|
|
151
|
+
def available(server)
|
|
152
|
+
available_connections(server).pop || make_new(server)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Creates a new connection to the given server if the size of the pool for
|
|
156
|
+
# the server is less than the maximum size of the pool.
|
|
157
|
+
def make_new(server)
|
|
158
|
+
if @created_count[server] < @max_size
|
|
159
|
+
raise(Sequel::Error, "No connection proc specified") unless @connection_proc
|
|
160
|
+
begin
|
|
161
|
+
conn = @connection_proc.call(server)
|
|
162
|
+
rescue Exception=>exception
|
|
163
|
+
e = Sequel::DatabaseConnectionError.new("#{exception.class} #{exception.message}")
|
|
164
|
+
e.set_backtrace(exception.backtrace)
|
|
165
|
+
raise e
|
|
166
|
+
end
|
|
167
|
+
raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
|
|
168
|
+
set_created_count(server, @created_count[server] + 1)
|
|
169
|
+
conn
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Returns the connection owned by the supplied thread for the given server,
|
|
174
|
+
# if any.
|
|
175
|
+
def owned_connection(thread, server)
|
|
176
|
+
@mutex.synchronize{@allocated[server][thread]}
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Releases the connection assigned to the supplied thread and server.
|
|
180
|
+
def release(thread, conn, server)
|
|
181
|
+
@mutex.synchronize do
|
|
182
|
+
allocated(server).delete(thread)
|
|
183
|
+
available_connections(server) << conn
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Removes the currently allocated connection from the connection pool.
|
|
188
|
+
def remove(thread, conn, server)
|
|
189
|
+
@mutex.synchronize do
|
|
190
|
+
allocated(server).delete(thread)
|
|
191
|
+
set_created_count(server, @created_count[server] - 1)
|
|
192
|
+
@disconnection_proc.call(conn) if @disconnection_proc
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Set the created count for the given server type
|
|
197
|
+
def set_created_count(server, value)
|
|
198
|
+
server = :default unless @created_count.include?(server)
|
|
199
|
+
@created_count[server] = value
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# A SingleThreadedPool acts as a replacement for a ConnectionPool for use
|
|
204
|
+
# in single-threaded applications. ConnectionPool imposes a substantial
|
|
205
|
+
# performance penalty, so SingleThreadedPool is used to gain some speed.
|
|
206
|
+
#
|
|
207
|
+
# Note that using a single threaded pool with some adapters can cause
|
|
208
|
+
# errors in certain cases, see Sequel.single_threaded=.
|
|
209
|
+
class Sequel::SingleThreadedPool
|
|
210
|
+
# The proc used to create a new database connection
|
|
211
|
+
attr_writer :connection_proc
|
|
212
|
+
|
|
213
|
+
# The proc used to disconnect a database connection.
|
|
214
|
+
attr_accessor :disconnection_proc
|
|
215
|
+
|
|
216
|
+
# Initializes the instance with the supplied block as the connection_proc.
|
|
217
|
+
#
|
|
218
|
+
# The single threaded pool takes the following options:
|
|
219
|
+
#
|
|
220
|
+
# * :pool_convert_exceptions - Whether to convert non-StandardError based exceptions
|
|
221
|
+
# to RuntimeError exceptions (default true)
|
|
222
|
+
def initialize(opts={}, &block)
|
|
223
|
+
@connection_proc = block
|
|
224
|
+
@disconnection_proc = opts[:disconnection_proc]
|
|
225
|
+
@conns = {}
|
|
226
|
+
@convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# The connection for the given server.
|
|
230
|
+
def conn(server=:default)
|
|
231
|
+
@conns[server]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Yields the connection to the supplied block for the given server.
|
|
235
|
+
# This method simulates the ConnectionPool#hold API.
|
|
236
|
+
def hold(server=:default)
|
|
237
|
+
begin
|
|
238
|
+
begin
|
|
239
|
+
yield(c = (@conns[server] ||= @connection_proc.call(server)))
|
|
240
|
+
rescue Sequel::DatabaseDisconnectError => dde
|
|
241
|
+
@conns.delete(server)
|
|
242
|
+
@disconnection_proc.call(c) if @disconnection_proc
|
|
243
|
+
raise
|
|
244
|
+
end
|
|
245
|
+
rescue Exception => e
|
|
246
|
+
# if the error is not a StandardError it is converted into RuntimeError.
|
|
247
|
+
raise(@convert_exceptions && !e.is_a?(StandardError) ? RuntimeError.new(e.message) : e)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Disconnects from the database. Once a connection is requested using
|
|
252
|
+
# #hold, the connection is reestablished.
|
|
253
|
+
def disconnect(&block)
|
|
254
|
+
block ||= @disconnection_proc
|
|
255
|
+
@conns.values.each{|conn| block.call(conn) if block}
|
|
256
|
+
@conns = {}
|
|
257
|
+
end
|
|
258
|
+
end
|
data/lib/sequel/core.rb
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
%w'bigdecimal bigdecimal/util date enumerator thread time uri yaml'.each do |f|
|
|
2
|
+
require f
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
# Top level module for Sequel
|
|
6
|
+
#
|
|
7
|
+
# There are some class methods that are added via metaprogramming, one for
|
|
8
|
+
# each supported adapter. For example:
|
|
9
|
+
#
|
|
10
|
+
# DB = Sequel.sqlite # Memory database
|
|
11
|
+
# DB = Sequel.sqlite('blog.db')
|
|
12
|
+
# DB = Sequel.postgres('database_name', :user=>'user',
|
|
13
|
+
# :password=>'password', :host=>'host', :port=>5432,
|
|
14
|
+
# :max_connections=>10)
|
|
15
|
+
#
|
|
16
|
+
# If a block is given to these methods, it is passed the opened Database
|
|
17
|
+
# object, which is closed (disconnected) when the block exits. For example:
|
|
18
|
+
#
|
|
19
|
+
# Sequel.sqlite('blog.db'){|db| puts db.users.count}
|
|
20
|
+
#
|
|
21
|
+
# Sequel converts the column type tinyint to a boolean by default,
|
|
22
|
+
# you can override the conversion to use tinyint as an integer:
|
|
23
|
+
#
|
|
24
|
+
# Sequel.convert_tinyint_to_bool = false
|
|
25
|
+
#
|
|
26
|
+
# Sequel converts two digit years in Dates and DateTimes by default,
|
|
27
|
+
# so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
|
|
28
|
+
# as December 13, 1999.. You can override this # to treat those dates as
|
|
29
|
+
# January 2nd, 0003 and December 13, 0099, respectively, by setting:
|
|
30
|
+
#
|
|
31
|
+
# Sequel.convert_two_digit_years = false
|
|
32
|
+
#
|
|
33
|
+
# Sequel can use either Time or DateTime for times returned from the
|
|
34
|
+
# database. It defaults to Time. To change it to DateTime, use:
|
|
35
|
+
#
|
|
36
|
+
# Sequel.datetime_class = DateTime
|
|
37
|
+
#
|
|
38
|
+
# Sequel currently does not use instance_eval for virtual row blocks by default
|
|
39
|
+
# (e.g. the block passed to Dataset#filter, #select, #order and other similar
|
|
40
|
+
# methods). If you want to use instance_eval for these blocks, don't have any
|
|
41
|
+
# block arguments, and set:
|
|
42
|
+
#
|
|
43
|
+
# Sequel.virtual_row_instance_eval = true
|
|
44
|
+
#
|
|
45
|
+
# When this is set, you can do:
|
|
46
|
+
#
|
|
47
|
+
# dataset.filter{|o| o.column > 0} # no instance_eval
|
|
48
|
+
# dataset.filter{column > 0} # instance eval
|
|
49
|
+
#
|
|
50
|
+
# When the virtual_row_instance_eval is false, using a virtual row block without a block
|
|
51
|
+
# argument will generate a deprecation message.
|
|
52
|
+
#
|
|
53
|
+
# The option to not use instance_eval for a block with no arguments will be removed in a future version.
|
|
54
|
+
# If you have any virtual row blocks that you don't want to use instance_eval for,
|
|
55
|
+
# make sure the blocks have block arguments.
|
|
56
|
+
module Sequel
|
|
57
|
+
@convert_tinyint_to_bool = true
|
|
58
|
+
@convert_two_digit_years = true
|
|
59
|
+
@datetime_class = Time
|
|
60
|
+
@virtual_row_instance_eval = false
|
|
61
|
+
|
|
62
|
+
class << self
|
|
63
|
+
attr_accessor :convert_tinyint_to_bool, :convert_two_digit_years, :datetime_class, :virtual_row_instance_eval
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Creates a new database object based on the supplied connection string
|
|
67
|
+
# and optional arguments. The specified scheme determines the database
|
|
68
|
+
# class used, and the rest of the string specifies the connection options.
|
|
69
|
+
# For example:
|
|
70
|
+
#
|
|
71
|
+
# DB = Sequel.connect('sqlite:/') # Memory database
|
|
72
|
+
# DB = Sequel.connect('sqlite://blog.db') # ./blog.db
|
|
73
|
+
# DB = Sequel.connect('sqlite:///blog.db') # /blog.db
|
|
74
|
+
# DB = Sequel.connect('postgres://user:password@host:port/database_name')
|
|
75
|
+
# DB = Sequel.connect('sqlite:///blog.db', :max_connections=>10)
|
|
76
|
+
#
|
|
77
|
+
# If a block is given, it is passed the opened Database object, which is
|
|
78
|
+
# closed when the block exits. For example:
|
|
79
|
+
#
|
|
80
|
+
# Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
|
|
81
|
+
def self.connect(*args, &block)
|
|
82
|
+
Database.connect(*args, &block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Set the method to call on identifiers going into the database. This affects
|
|
86
|
+
# the literalization of identifiers by calling this method on them before they are input.
|
|
87
|
+
# Sequel upcases identifiers in all SQL strings for most databases, so to turn that off:
|
|
88
|
+
#
|
|
89
|
+
# Sequel.identifier_input_method = nil
|
|
90
|
+
#
|
|
91
|
+
# to downcase instead:
|
|
92
|
+
#
|
|
93
|
+
# Sequel.identifier_input_method = :downcase
|
|
94
|
+
#
|
|
95
|
+
# Other string methods work as well.
|
|
96
|
+
def self.identifier_input_method=(value)
|
|
97
|
+
Database.identifier_input_method = value
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Set the method to call on identifiers coming out of the database. This affects
|
|
101
|
+
# the literalization of identifiers by calling this method on them when they are
|
|
102
|
+
# retrieved from the database. Sequel downcases identifiers retrieved for most
|
|
103
|
+
# databases, so to turn that off:
|
|
104
|
+
#
|
|
105
|
+
# Sequel.identifier_output_method = nil
|
|
106
|
+
#
|
|
107
|
+
# to upcase instead:
|
|
108
|
+
#
|
|
109
|
+
# Sequel.identifier_output_method = :upcase
|
|
110
|
+
#
|
|
111
|
+
# Other string methods work as well.
|
|
112
|
+
def self.identifier_output_method=(value)
|
|
113
|
+
Database.identifier_output_method = value
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Set whether to quote identifiers for all databases by default. By default,
|
|
117
|
+
# Sequel quotes identifiers in all SQL strings, so to turn that off:
|
|
118
|
+
#
|
|
119
|
+
# Sequel.quote_identifiers = false
|
|
120
|
+
def self.quote_identifiers=(value)
|
|
121
|
+
Database.quote_identifiers = value
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Require all given files which should be in the same or a subdirectory of
|
|
125
|
+
# this file
|
|
126
|
+
def self.require(files, subdir=nil)
|
|
127
|
+
Array(files).each{|f| super("#{File.dirname(__FILE__)}/#{"#{subdir}/" if subdir}#{f}")}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Set whether to set the single threaded mode for all databases by default. By default,
|
|
131
|
+
# Sequel uses a threadsafe connection pool, which isn't as fast as the
|
|
132
|
+
# single threaded connection pool. If your program will only have one thread,
|
|
133
|
+
# and speed is a priority, you may want to set this to true:
|
|
134
|
+
#
|
|
135
|
+
# Sequel.single_threaded = true
|
|
136
|
+
def self.single_threaded=(value)
|
|
137
|
+
Database.single_threaded = value
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Converts a string into a Date object.
|
|
141
|
+
def self.string_to_date(s)
|
|
142
|
+
begin
|
|
143
|
+
Date.parse(s, Sequel.convert_two_digit_years)
|
|
144
|
+
rescue => e
|
|
145
|
+
raise Error::InvalidValue, "Invalid Date value '#{self}' (#{e.message})"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Converts a string into a Time or DateTime object, depending on the
|
|
150
|
+
# value of Sequel.datetime_class.
|
|
151
|
+
def self.string_to_datetime(s)
|
|
152
|
+
begin
|
|
153
|
+
if datetime_class == DateTime
|
|
154
|
+
DateTime.parse(s, convert_two_digit_years)
|
|
155
|
+
else
|
|
156
|
+
datetime_class.parse(s)
|
|
157
|
+
end
|
|
158
|
+
rescue => e
|
|
159
|
+
raise Error::InvalidValue, "Invalid #{datetime_class} value '#{self}' (#{e.message})"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Converts a string into a Time object.
|
|
164
|
+
def self.string_to_time(s)
|
|
165
|
+
begin
|
|
166
|
+
Time.parse(s)
|
|
167
|
+
rescue => e
|
|
168
|
+
raise Error::InvalidValue, "Invalid Time value '#{self}' (#{e.message})"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
### Private Class Methods ###
|
|
173
|
+
|
|
174
|
+
# Helper method that the database adapter class methods that are added to Sequel via
|
|
175
|
+
# metaprogramming use to parse arguments.
|
|
176
|
+
def self.adapter_method(adapter, *args, &block) # :nodoc:
|
|
177
|
+
raise(::Sequel::Error, "Wrong number of arguments, 0-2 arguments valid") if args.length > 2
|
|
178
|
+
opts = {:adapter=>adapter.to_sym}
|
|
179
|
+
opts[:database] = args.shift if args.length >= 1 && !(args[0].is_a?(Hash))
|
|
180
|
+
if Hash === (arg = args[0])
|
|
181
|
+
opts.merge!(arg)
|
|
182
|
+
elsif !arg.nil?
|
|
183
|
+
raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
|
|
184
|
+
end
|
|
185
|
+
connect(opts, &block)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Method that adds a database adapter class method to Sequel that calls
|
|
189
|
+
# Sequel.adapter_method.
|
|
190
|
+
def self.def_adapter_method(*adapters) # :nodoc:
|
|
191
|
+
adapters.each do |adapter|
|
|
192
|
+
instance_eval("def #{adapter}(*args, &block); adapter_method('#{adapter}', *args, &block) end")
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private_class_method :adapter_method, :def_adapter_method
|
|
197
|
+
|
|
198
|
+
require(%w"metaprogramming sql core_sql connection_pool exceptions dataset migration database object_graph version deprecated")
|
|
199
|
+
require(%w"schema_generator schema_methods schema_sql", 'database')
|
|
200
|
+
require(%w"convenience prepared_statements sql", 'dataset')
|
|
201
|
+
|
|
202
|
+
# Add the database adapter class methods to Sequel via metaprogramming
|
|
203
|
+
def_adapter_method(*Database::ADAPTERS)
|
|
204
|
+
end
|