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