/
misc.rb
252 lines (225 loc) · 7.85 KB
/
misc.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
module Sequel
class Database
# ---------------------
# :section: Miscellaneous methods
# These methods don't fit neatly into another category.
# ---------------------
# Converts a uri to an options hash. These options are then passed
# to a newly created database object.
def self.uri_to_options(uri) # :nodoc:
{ :user => uri.user,
:password => uri.password,
:host => uri.host,
:port => uri.port,
:database => (m = /\/(.*)/.match(uri.path)) && (m[1]) }
end
private_class_method :uri_to_options
# The options for this database
attr_reader :opts
# Constructs a new instance of a database connection with the specified
# options hash.
#
# Sequel::Database is an abstract class that is not useful by itself.
#
# Takes the following options:
# * :default_schema : The default schema to use, should generally be nil
# * :disconnection_proc: A proc used to disconnect the connection.
# * :identifier_input_method: A string method symbol to call on identifiers going into the database
# * :identifier_output_method: A string method symbol to call on identifiers coming from the database
# * :loggers : An array of loggers to use.
# * :quote_identifiers : Whether to quote identifiers
# * :single_threaded : Whether to use a single-threaded connection pool
#
# All options given are also passed to the ConnectionPool. If a block
# is given, it is used as the connection_proc for the ConnectionPool.
def initialize(opts = {}, &block)
@opts ||= opts
@opts = connection_pool_default_options.merge(@opts)
@loggers = Array(@opts[:logger]) + Array(@opts[:loggers])
self.log_warn_duration = @opts[:log_warn_duration]
@opts[:disconnection_proc] ||= proc{|conn| disconnect_connection(conn)}
block ||= proc{|server| connect(server)}
@opts[:servers] = {} if @opts[:servers].is_a?(String)
@opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, @@single_threaded))
@schemas = {}
@default_schema = @opts.fetch(:default_schema, default_schema_default)
@prepared_statements = {}
@transactions = []
@identifier_input_method = nil
@identifier_output_method = nil
@quote_identifiers = nil
@pool = ConnectionPool.get_pool(@opts, &block)
::Sequel::DATABASES.push(self)
end
# Cast the given type to a literal type
def cast_type_literal(type)
type_literal(:type=>type)
end
# Returns a string representation of the database object including the
# class name and the connection URI (or the opts if the URI
# cannot be constructed).
def inspect
"#<#{self.class}: #{(uri rescue opts).inspect}>"
end
# Proxy the literal call to the dataset, used for default values.
def literal(v)
schema_utility_dataset.literal(v)
end
# Default serial primary key options.
def serial_primary_key_options
{:primary_key => true, :type => Integer, :auto_increment => true}
end
# Whether the database and adapter support prepared transactions
# (two-phase commit), false by default
def supports_prepared_transactions?
false
end
# Whether the database and adapter support savepoints, false by default
def supports_savepoints?
false
end
# Typecast the value to the given column_type. Calls
# typecast_value_#{column_type} if the method exists,
# otherwise returns the value.
# This method should raise Sequel::InvalidValue if assigned value
# is invalid.
def typecast_value(column_type, value)
return nil if value.nil?
meth = "typecast_value_#{column_type}"
begin
respond_to?(meth, true) ? send(meth, value) : value
rescue ArgumentError, TypeError => e
raise Sequel.convert_exception_class(e, InvalidValue)
end
end
# Returns the URI identifying the database.
# This method can raise an error if the database used options
# instead of a connection string.
def uri
uri = URI::Generic.new(
self.class.adapter_scheme.to_s,
nil,
@opts[:host],
@opts[:port],
nil,
"/#{@opts[:database]}",
nil,
nil,
nil
)
uri.user = @opts[:user]
uri.password = @opts[:password] if uri.user
uri.to_s
end
# Explicit alias of uri for easier subclassing.
def url
uri
end
private
# Returns true when the object is considered blank.
# The only objects that are blank are nil, false,
# strings with all whitespace, and ones that respond
# true to empty?
def blank_object?(obj)
return obj.blank? if obj.respond_to?(:blank?)
case obj
when NilClass, FalseClass
true
when Numeric, TrueClass
false
when String
obj.strip.empty?
else
obj.respond_to?(:empty?) ? obj.empty? : false
end
end
# Which transaction errors to translate, blank by default.
def database_error_classes
[]
end
# Convert the given exception to a DatabaseError, keeping message
# and traceback.
def raise_error(exception, opts={})
if !opts[:classes] || Array(opts[:classes]).any?{|c| exception.is_a?(c)}
raise Sequel.convert_exception_class(exception, opts[:disconnect] ? DatabaseDisconnectError : DatabaseError)
else
raise exception
end
end
# Typecast the value to an SQL::Blob
def typecast_value_blob(value)
value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
end
# Typecast the value to true, false, or nil
def typecast_value_boolean(value)
case value
when false, 0, "0", /\Af(alse)?\z/i
false
else
blank_object?(value) ? nil : true
end
end
# Typecast the value to a Date
def typecast_value_date(value)
case value
when Date
value
when DateTime, Time
Date.new(value.year, value.month, value.day)
when String
Sequel.string_to_date(value)
when Hash
Date.new(*[:year, :month, :day].map{|x| (value[x] || value[x.to_s]).to_i})
else
raise InvalidValue, "invalid value for Date: #{value.inspect}"
end
end
# Typecast the value to a DateTime or Time depending on Sequel.datetime_class
def typecast_value_datetime(value)
raise(Sequel::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless [DateTime, Date, Time, String, Hash].any?{|c| value.is_a?(c)}
klass = Sequel.datetime_class
if value.is_a?(Hash)
klass.send(klass == Time ? :mktime : :new, *[:year, :month, :day, :hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
else
Sequel.typecast_to_application_timestamp(value)
end
end
# Typecast the value to a BigDecimal
def typecast_value_decimal(value)
case value
when BigDecimal
value
when String, Numeric
BigDecimal.new(value.to_s)
else
raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
end
end
# Typecast the value to a Float
def typecast_value_float(value)
Float(value)
end
# Typecast the value to an Integer
def typecast_value_integer(value)
Integer(value)
end
# Typecast the value to a String
def typecast_value_string(value)
value.to_s
end
# Typecast the value to a Time
def typecast_value_time(value)
case value
when Time
value
when String
Sequel.string_to_time(value)
when Hash
t = Time.now
Time.mktime(t.year, t.month, t.day, *[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
else
raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
end
end
end
end