sequel_core 2.2.0 → 3.8.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.
- metadata +30 -101
- data/CHANGELOG +0 -1519
- data/COPYING +0 -19
- data/README +0 -313
- data/Rakefile +0 -158
- data/bin/sequel +0 -117
- data/doc/cheat_sheet.rdoc +0 -225
- data/doc/dataset_filtering.rdoc +0 -182
- data/lib/sequel_core.rb +0 -136
- data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -68
- data/lib/sequel_core/adapters/ado.rb +0 -90
- data/lib/sequel_core/adapters/db2.rb +0 -160
- data/lib/sequel_core/adapters/dbi.rb +0 -127
- data/lib/sequel_core/adapters/informix.rb +0 -89
- data/lib/sequel_core/adapters/jdbc.rb +0 -110
- data/lib/sequel_core/adapters/mysql.rb +0 -486
- data/lib/sequel_core/adapters/odbc.rb +0 -167
- data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
- data/lib/sequel_core/adapters/openbase.rb +0 -76
- data/lib/sequel_core/adapters/oracle.rb +0 -182
- data/lib/sequel_core/adapters/postgres.rb +0 -560
- data/lib/sequel_core/adapters/sqlite.rb +0 -270
- data/lib/sequel_core/connection_pool.rb +0 -194
- data/lib/sequel_core/core_ext.rb +0 -197
- data/lib/sequel_core/core_sql.rb +0 -184
- data/lib/sequel_core/database.rb +0 -462
- data/lib/sequel_core/database/schema.rb +0 -156
- data/lib/sequel_core/dataset.rb +0 -457
- data/lib/sequel_core/dataset/callback.rb +0 -13
- data/lib/sequel_core/dataset/convenience.rb +0 -245
- data/lib/sequel_core/dataset/pagination.rb +0 -96
- data/lib/sequel_core/dataset/query.rb +0 -41
- data/lib/sequel_core/dataset/schema.rb +0 -15
- data/lib/sequel_core/dataset/sql.rb +0 -889
- data/lib/sequel_core/deprecated.rb +0 -26
- data/lib/sequel_core/exceptions.rb +0 -42
- data/lib/sequel_core/migration.rb +0 -187
- data/lib/sequel_core/object_graph.rb +0 -216
- data/lib/sequel_core/pretty_table.rb +0 -71
- data/lib/sequel_core/schema.rb +0 -2
- data/lib/sequel_core/schema/generator.rb +0 -239
- data/lib/sequel_core/schema/sql.rb +0 -326
- data/lib/sequel_core/sql.rb +0 -812
- data/lib/sequel_core/worker.rb +0 -68
- data/spec/adapters/informix_spec.rb +0 -96
- data/spec/adapters/mysql_spec.rb +0 -765
- data/spec/adapters/oracle_spec.rb +0 -222
- data/spec/adapters/postgres_spec.rb +0 -441
- data/spec/adapters/sqlite_spec.rb +0 -413
- data/spec/connection_pool_spec.rb +0 -363
- data/spec/core_ext_spec.rb +0 -156
- data/spec/core_sql_spec.rb +0 -427
- data/spec/database_spec.rb +0 -963
- data/spec/dataset_spec.rb +0 -2933
- data/spec/expression_filters_spec.rb +0 -316
- data/spec/migration_spec.rb +0 -261
- data/spec/object_graph_spec.rb +0 -230
- data/spec/pretty_table_spec.rb +0 -58
- data/spec/rcov.opts +0 -6
- data/spec/schema_generator_spec.rb +0 -122
- data/spec/schema_spec.rb +0 -422
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb +0 -7
- data/spec/spec_config.rb.example +0 -8
- data/spec/spec_helper.rb +0 -55
- data/spec/worker_spec.rb +0 -96
data/lib/sequel_core/core_ext.rb
DELETED
@@ -1,197 +0,0 @@
|
|
1
|
-
class Array
|
2
|
-
# True if the array is not empty and all of its elements are
|
3
|
-
# arrays of size 2. This is used to determine if the array
|
4
|
-
# could be a specifier of conditions, used similarly to a hash
|
5
|
-
# but allowing for duplicate keys.
|
6
|
-
#
|
7
|
-
# hash.to_a.all_two_pairs? # => true unless hash is empty
|
8
|
-
def all_two_pairs?
|
9
|
-
!empty? && all?{|i| (Array === i) && (i.length == 2)}
|
10
|
-
end
|
11
|
-
|
12
|
-
# Removes and returns the last member of the array if it is a hash. Otherwise,
|
13
|
-
# an empty hash is returned This method is useful when writing methods that
|
14
|
-
# take an options hash as the last parameter. For example:
|
15
|
-
#
|
16
|
-
# def validate_each(*args, &block)
|
17
|
-
# opts = args.extract_options!
|
18
|
-
# ...
|
19
|
-
# end
|
20
|
-
def extract_options!
|
21
|
-
last.is_a?(Hash) ? pop : {}
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module Enumerable
|
26
|
-
# Invokes the specified method for each item, along with the supplied
|
27
|
-
# arguments.
|
28
|
-
def send_each(sym, *args)
|
29
|
-
each{|i| i.send(sym, *args)}
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class FalseClass
|
34
|
-
# false is always blank
|
35
|
-
def blank?
|
36
|
-
true
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# Add some metaprogramming methods to avoid class << self
|
41
|
-
class Module
|
42
|
-
# Defines an instance method within a class/module
|
43
|
-
def class_def(name, &block)
|
44
|
-
class_eval{define_method(name, &block)}
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
# Define instance method(s) that calls class method(s) of the
|
50
|
-
# same name. Replaces the construct:
|
51
|
-
#
|
52
|
-
# define_method(meth){self.class.send(meth)}
|
53
|
-
def class_attr_reader(*meths)
|
54
|
-
meths.each{|meth| define_method(meth){self.class.send(meth)}}
|
55
|
-
end
|
56
|
-
|
57
|
-
# Create an alias for a singleton/class method.
|
58
|
-
# Replaces the construct:
|
59
|
-
#
|
60
|
-
# class << self
|
61
|
-
# alias_method to, from
|
62
|
-
# end
|
63
|
-
def metaalias(to, from)
|
64
|
-
meta_eval{alias_method to, from}
|
65
|
-
end
|
66
|
-
|
67
|
-
# Make a singleton/class attribute accessor method(s).
|
68
|
-
# Replaces the construct:
|
69
|
-
#
|
70
|
-
# class << self
|
71
|
-
# attr_accessor *meths
|
72
|
-
# end
|
73
|
-
def metaattr_accessor(*meths)
|
74
|
-
meta_eval{attr_accessor(*meths)}
|
75
|
-
end
|
76
|
-
|
77
|
-
# Make a singleton/class method(s) private.
|
78
|
-
# Make a singleton/class attribute reader method(s).
|
79
|
-
# Replaces the construct:
|
80
|
-
#
|
81
|
-
# class << self
|
82
|
-
# attr_reader *meths
|
83
|
-
# end
|
84
|
-
def metaattr_reader(*meths)
|
85
|
-
meta_eval{attr_reader(*meths)}
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# Helpers from Metaid and a bit more
|
90
|
-
class Object
|
91
|
-
# Objects are blank if they respond true to empty?
|
92
|
-
def blank?
|
93
|
-
respond_to?(:empty?) && empty?
|
94
|
-
end
|
95
|
-
|
96
|
-
# Returns true if the object is an instance of one of the classes
|
97
|
-
def is_one_of?(*classes)
|
98
|
-
!!classes.find{|c| is_a?(c)}
|
99
|
-
end
|
100
|
-
|
101
|
-
# Add methods to the object's metaclass
|
102
|
-
def meta_def(name, &block)
|
103
|
-
meta_eval{define_method(name, &block)}
|
104
|
-
end
|
105
|
-
|
106
|
-
# Evaluate the block in the context of the object's metaclass
|
107
|
-
def meta_eval(&block)
|
108
|
-
metaclass.instance_eval(&block)
|
109
|
-
end
|
110
|
-
|
111
|
-
# The hidden singleton lurks behind everyone
|
112
|
-
def metaclass
|
113
|
-
class << self
|
114
|
-
self
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
class NilClass
|
120
|
-
# nil is always blank
|
121
|
-
def blank?
|
122
|
-
true
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
class Numeric
|
127
|
-
# Numerics are never blank (not even 0)
|
128
|
-
def blank?
|
129
|
-
false
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
class Range
|
134
|
-
# Returns the interval between the beginning and end of the range.
|
135
|
-
#
|
136
|
-
# For exclusive ranges, is one less than the inclusive range:
|
137
|
-
#
|
138
|
-
# (0..10).interval # => 10
|
139
|
-
# (0...10).interval # => 9
|
140
|
-
#
|
141
|
-
# Only works for numeric ranges, for other ranges the result is undefined,
|
142
|
-
# and the method may raise an error.
|
143
|
-
def interval
|
144
|
-
last - first - (exclude_end? ? 1 : 0)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
class String
|
149
|
-
# Strings are blank if they are empty or include only whitespace
|
150
|
-
def blank?
|
151
|
-
strip.empty?
|
152
|
-
end
|
153
|
-
|
154
|
-
# Converts a string into a Date object.
|
155
|
-
def to_date
|
156
|
-
begin
|
157
|
-
Date.parse(self)
|
158
|
-
rescue => e
|
159
|
-
raise Sequel::Error::InvalidValue, "Invalid date value '#{self}' (#{e.message})"
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Converts a string into a DateTime object.
|
164
|
-
def to_datetime
|
165
|
-
begin
|
166
|
-
DateTime.parse(self)
|
167
|
-
rescue => e
|
168
|
-
raise Sequel::Error::InvalidValue, "Invalid date value '#{self}' (#{e.message})"
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
# Converts a string into a Time or DateTime object, depending on the
|
173
|
-
# value of Sequel.datetime_class
|
174
|
-
def to_sequel_time
|
175
|
-
begin
|
176
|
-
Sequel.datetime_class.parse(self)
|
177
|
-
rescue => e
|
178
|
-
raise Sequel::Error::InvalidValue, "Invalid time value '#{self}' (#{e.message})"
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
# Converts a string into a Time object.
|
183
|
-
def to_time
|
184
|
-
begin
|
185
|
-
Time.parse(self)
|
186
|
-
rescue => e
|
187
|
-
raise Sequel::Error::InvalidValue, "Invalid time value '#{self}' (#{e.message})"
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
class TrueClass
|
193
|
-
# true is never blank
|
194
|
-
def blank?
|
195
|
-
false
|
196
|
-
end
|
197
|
-
end
|
data/lib/sequel_core/core_sql.rb
DELETED
@@ -1,184 +0,0 @@
|
|
1
|
-
class Array
|
2
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, not matching any of the
|
3
|
-
# conditions.
|
4
|
-
def ~
|
5
|
-
sql_expr_if_all_two_pairs(:OR, true)
|
6
|
-
end
|
7
|
-
|
8
|
-
# Return a Sequel::SQL::CaseExpression with this array as the conditions and the given
|
9
|
-
# default value.
|
10
|
-
def case(default)
|
11
|
-
::Sequel::SQL::CaseExpression.new(self, default)
|
12
|
-
end
|
13
|
-
|
14
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, matching all of the
|
15
|
-
# conditions.
|
16
|
-
def sql_expr
|
17
|
-
sql_expr_if_all_two_pairs
|
18
|
-
end
|
19
|
-
|
20
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, matching none
|
21
|
-
# of the conditions.
|
22
|
-
def sql_negate
|
23
|
-
sql_expr_if_all_two_pairs(:AND, true)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, matching any of the
|
27
|
-
# conditions.
|
28
|
-
def sql_or
|
29
|
-
sql_expr_if_all_two_pairs(:OR)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Return a Sequel::SQL::BooleanExpression representing an SQL string made up of the
|
33
|
-
# concatenation of this array's elements. If an argument is passed
|
34
|
-
# it is used in between each element of the array in the SQL
|
35
|
-
# concatenation.
|
36
|
-
def sql_string_join(joiner=nil)
|
37
|
-
if joiner
|
38
|
-
args = self.inject([]) do |m, a|
|
39
|
-
m << a
|
40
|
-
m << joiner
|
41
|
-
end
|
42
|
-
args.pop
|
43
|
-
else
|
44
|
-
args = self
|
45
|
-
end
|
46
|
-
args = args.collect{|a| a.is_one_of?(Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass) ? a : a.to_s}
|
47
|
-
::Sequel::SQL::StringExpression.new(:'||', *args)
|
48
|
-
end
|
49
|
-
|
50
|
-
# Concatenates an array of strings into an SQL string. ANSI SQL and C-style
|
51
|
-
# comments are removed, as well as excessive white-space.
|
52
|
-
def to_sql
|
53
|
-
map {|l| ((m = /^(.*)--/.match(l)) ? m[1] : l).chomp}.join(' '). \
|
54
|
-
gsub(/\/\*.*\*\//, '').gsub(/\s+/, ' ').strip
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
# Raise an error if this array is not made up of all two pairs, otherwise create a Sequel::SQL::BooleanExpression from this array.
|
60
|
-
def sql_expr_if_all_two_pairs(*args)
|
61
|
-
raise(Sequel::Error, 'Not all elements of the array are arrays of size 2, so it cannot be converted to an SQL expression') unless all_two_pairs?
|
62
|
-
::Sequel::SQL::BooleanExpression.from_value_pairs(self, *args)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
class Hash
|
67
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching
|
68
|
-
# all of the conditions in this hash and the condition specified by
|
69
|
-
# the given argument.
|
70
|
-
def &(ce)
|
71
|
-
::Sequel::SQL::BooleanExpression.new(:AND, self, ce)
|
72
|
-
end
|
73
|
-
|
74
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching
|
75
|
-
# all of the conditions in this hash or the condition specified by
|
76
|
-
# the given argument.
|
77
|
-
def |(ce)
|
78
|
-
::Sequel::SQL::BooleanExpression.new(:OR, self, ce)
|
79
|
-
end
|
80
|
-
|
81
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, not matching any of the
|
82
|
-
# conditions.
|
83
|
-
def ~
|
84
|
-
::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR, true)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Return a Sequel::SQL::CaseExpression with this hash as the conditions and the given
|
88
|
-
# default value. Note that the order of the conditions will be arbitrary, so all
|
89
|
-
# conditions should be orthogonal.
|
90
|
-
def case(default)
|
91
|
-
::Sequel::SQL::CaseExpression.new(to_a, default)
|
92
|
-
end
|
93
|
-
|
94
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching all of the
|
95
|
-
# conditions.
|
96
|
-
def sql_expr
|
97
|
-
::Sequel::SQL::BooleanExpression.from_value_pairs(self)
|
98
|
-
end
|
99
|
-
|
100
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching none
|
101
|
-
# of the conditions.
|
102
|
-
def sql_negate
|
103
|
-
::Sequel::SQL::BooleanExpression.from_value_pairs(self, :AND, true)
|
104
|
-
end
|
105
|
-
|
106
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching any of the
|
107
|
-
# conditions.
|
108
|
-
def sql_or
|
109
|
-
::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
class String
|
114
|
-
include Sequel::SQL::AliasMethods
|
115
|
-
include Sequel::SQL::CastMethods
|
116
|
-
|
117
|
-
# Converts a string into an LiteralString, in order to override string
|
118
|
-
# literalization, e.g.:
|
119
|
-
#
|
120
|
-
# DB[:items].filter(:abc => 'def').sql #=>
|
121
|
-
# "SELECT * FROM items WHERE (abc = 'def')"
|
122
|
-
#
|
123
|
-
# DB[:items].filter(:abc => 'def'.lit).sql #=>
|
124
|
-
# "SELECT * FROM items WHERE (abc = def)"
|
125
|
-
#
|
126
|
-
def lit
|
127
|
-
Sequel::LiteralString.new(self)
|
128
|
-
end
|
129
|
-
alias_method :expr, :lit
|
130
|
-
|
131
|
-
# Splits a string into separate SQL statements, removing comments
|
132
|
-
# and excessive white-space.
|
133
|
-
def split_sql
|
134
|
-
to_sql.split(';').map {|s| s.strip}
|
135
|
-
end
|
136
|
-
|
137
|
-
# Converts a string into an SQL string by removing comments.
|
138
|
-
# See also Array#to_sql.
|
139
|
-
def to_sql
|
140
|
-
split("\n").to_sql
|
141
|
-
end
|
142
|
-
|
143
|
-
# Returns a Blob that holds the same data as this string. Blobs provide proper
|
144
|
-
# escaping of binary data.
|
145
|
-
def to_blob
|
146
|
-
::Sequel::SQL::Blob.new self
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
class Symbol
|
151
|
-
include Sequel::SQL::QualifyingMethods
|
152
|
-
include Sequel::SQL::IdentifierMethods
|
153
|
-
include Sequel::SQL::GenericExpressionMethods
|
154
|
-
|
155
|
-
# If no argument is given, returns a Sequel::SQL::ColumnAll object specifying all
|
156
|
-
# columns for this table.
|
157
|
-
# If an argument is given, returns a Sequel::SQL::NumericExpression using the *
|
158
|
-
# (multiplication) operator with this and the given argument.
|
159
|
-
def *(ce=(arg=false;nil))
|
160
|
-
return super(ce) unless arg == false
|
161
|
-
Sequel::SQL::ColumnAll.new(self);
|
162
|
-
end
|
163
|
-
|
164
|
-
# Returns a Sequel::SQL::Function with this as the function name,
|
165
|
-
# and the given arguments.
|
166
|
-
def [](*args)
|
167
|
-
Sequel::SQL::Function.new(self, *args)
|
168
|
-
end
|
169
|
-
|
170
|
-
# If the given argument is an Integer or an array containing an Integer, returns
|
171
|
-
# a Sequel::SQL::Subscript with this column and the given arg.
|
172
|
-
# Otherwise returns a Sequel::SQL::BooleanExpression where this column (which should be boolean)
|
173
|
-
# or the given argument is true.
|
174
|
-
def |(sub)
|
175
|
-
return super unless (Integer === sub) || ((Array === sub) && sub.any?{|x| Integer === x})
|
176
|
-
Sequel::SQL::Subscript.new(self, Array(sub))
|
177
|
-
end
|
178
|
-
|
179
|
-
# Delegate the creation of the resulting SQL to the given dataset,
|
180
|
-
# since it may be database dependent.
|
181
|
-
def to_column_ref(ds)
|
182
|
-
ds.symbol_to_column_ref(self)
|
183
|
-
end
|
184
|
-
end
|
data/lib/sequel_core/database.rb
DELETED
@@ -1,462 +0,0 @@
|
|
1
|
-
require 'sequel_core/database/schema'
|
2
|
-
|
3
|
-
module Sequel
|
4
|
-
# Array of all databases to which Sequel has connected. If you are
|
5
|
-
# developing an application that can connect to an arbitrary number of
|
6
|
-
# databases, delete the database objects from this or they will not get
|
7
|
-
# garbage collected.
|
8
|
-
DATABASES = []
|
9
|
-
|
10
|
-
# A Database object represents a virtual connection to a database.
|
11
|
-
# The Database class is meant to be subclassed by database adapters in order
|
12
|
-
# to provide the functionality needed for executing queries.
|
13
|
-
class Database
|
14
|
-
include Schema::SQL
|
15
|
-
|
16
|
-
# Array of supported database adapters
|
17
|
-
ADAPTERS = %w'ado db2 dbi informix jdbc mysql odbc odbc_mssql openbase oracle postgres sqlite'.collect{|x| x.to_sym}
|
18
|
-
|
19
|
-
SQL_BEGIN = 'BEGIN'.freeze
|
20
|
-
SQL_COMMIT = 'COMMIT'.freeze
|
21
|
-
SQL_ROLLBACK = 'ROLLBACK'.freeze
|
22
|
-
|
23
|
-
# Hash of adapters that have been used
|
24
|
-
@@adapters = Hash.new
|
25
|
-
|
26
|
-
# Whether to use the single threaded connection pool by default
|
27
|
-
@@single_threaded = false
|
28
|
-
|
29
|
-
# Whether to quote identifiers (columns and tables) by default
|
30
|
-
@@quote_identifiers = true
|
31
|
-
|
32
|
-
# Array of SQL loggers to use for this database
|
33
|
-
attr_accessor :loggers
|
34
|
-
|
35
|
-
# The options for this database
|
36
|
-
attr_reader :opts
|
37
|
-
|
38
|
-
# The connection pool for this database
|
39
|
-
attr_reader :pool
|
40
|
-
|
41
|
-
# Whether to quote identifiers (columns and tables) for this database
|
42
|
-
attr_writer :quote_identifiers
|
43
|
-
|
44
|
-
# Constructs a new instance of a database connection with the specified
|
45
|
-
# options hash.
|
46
|
-
#
|
47
|
-
# Sequel::Database is an abstract class that is not useful by itself.
|
48
|
-
def initialize(opts = {}, &block)
|
49
|
-
@opts = opts
|
50
|
-
|
51
|
-
@quote_identifiers = opts.include?(:quote_identifiers) ? opts[:quote_identifiers] : @@quote_identifiers
|
52
|
-
@single_threaded = opts.include?(:single_threaded) ? opts[:single_threaded] : @@single_threaded
|
53
|
-
@schemas = nil
|
54
|
-
@pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
|
55
|
-
@pool.connection_proc = proc {connect} unless block
|
56
|
-
|
57
|
-
@loggers = Array(opts[:logger]) + Array(opts[:loggers])
|
58
|
-
::Sequel::DATABASES.push(self)
|
59
|
-
end
|
60
|
-
|
61
|
-
### Class Methods ###
|
62
|
-
|
63
|
-
# The Database subclass for the given adapter scheme.
|
64
|
-
# Raises Sequel::Error::AdapterNotFound if the adapter
|
65
|
-
# could not be loaded.
|
66
|
-
def self.adapter_class(scheme)
|
67
|
-
scheme = scheme.to_s.gsub('-', '_').to_sym
|
68
|
-
|
69
|
-
if (klass = @@adapters[scheme]).nil?
|
70
|
-
# attempt to load the adapter file
|
71
|
-
begin
|
72
|
-
require "sequel_core/adapters/#{scheme}"
|
73
|
-
rescue LoadError => e
|
74
|
-
raise Error::AdapterNotFound, "Could not load #{scheme} adapter:\n #{e.message}"
|
75
|
-
end
|
76
|
-
|
77
|
-
# make sure we actually loaded the adapter
|
78
|
-
if (klass = @@adapters[scheme]).nil?
|
79
|
-
raise Error::AdapterNotFound, "Could not load #{scheme} adapter"
|
80
|
-
end
|
81
|
-
end
|
82
|
-
return klass
|
83
|
-
end
|
84
|
-
|
85
|
-
# Returns the scheme for the Database class.
|
86
|
-
def self.adapter_scheme
|
87
|
-
@scheme
|
88
|
-
end
|
89
|
-
|
90
|
-
# Connects to a database. See Sequel.connect.
|
91
|
-
def self.connect(conn_string, opts = nil, &block)
|
92
|
-
if conn_string.is_a?(String)
|
93
|
-
uri = URI.parse(conn_string)
|
94
|
-
scheme = uri.scheme
|
95
|
-
scheme = :dbi if scheme =~ /^dbi-(.+)/
|
96
|
-
c = adapter_class(scheme)
|
97
|
-
opts = c.uri_to_options(uri).merge(opts || {})
|
98
|
-
else
|
99
|
-
opts = conn_string.merge(opts || {})
|
100
|
-
c = adapter_class(opts[:adapter] || opts['adapter'])
|
101
|
-
end
|
102
|
-
# process opts a bit
|
103
|
-
opts = opts.inject({}) do |m, kv| k, v = *kv
|
104
|
-
k = :user if k.to_s == 'username'
|
105
|
-
m[k.to_sym] = v
|
106
|
-
m
|
107
|
-
end
|
108
|
-
if block
|
109
|
-
begin
|
110
|
-
yield(db = c.new(opts))
|
111
|
-
ensure
|
112
|
-
db.disconnect if db
|
113
|
-
::Sequel::DATABASES.delete(db)
|
114
|
-
end
|
115
|
-
nil
|
116
|
-
else
|
117
|
-
c.new(opts)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# Sets the default quote_identifiers mode for new databases.
|
122
|
-
# See Sequel.quote_identifiers=.
|
123
|
-
def self.quote_identifiers=(value)
|
124
|
-
@@quote_identifiers = value
|
125
|
-
end
|
126
|
-
|
127
|
-
# Sets the default single_threaded mode for new databases.
|
128
|
-
# See Sequel.single_threaded=.
|
129
|
-
def self.single_threaded=(value)
|
130
|
-
@@single_threaded = value
|
131
|
-
end
|
132
|
-
|
133
|
-
# Converts a uri to an options hash. These options are then passed
|
134
|
-
# to a newly created database object.
|
135
|
-
def self.uri_to_options(uri)
|
136
|
-
uri = URI.parse(uri) if uri.is_a?(String)
|
137
|
-
# special case for sqlite
|
138
|
-
opts = if uri.scheme == 'sqlite'
|
139
|
-
{ :user => uri.user,
|
140
|
-
:password => uri.password,
|
141
|
-
:database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
|
142
|
-
else
|
143
|
-
{ :user => uri.user,
|
144
|
-
:password => uri.password,
|
145
|
-
:host => uri.host,
|
146
|
-
:port => uri.port,
|
147
|
-
:database => (m = /\/(.*)/.match(uri.path)) && (m[1]) }
|
148
|
-
end
|
149
|
-
uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| opts[k.to_sym] = v} unless uri.query.blank?
|
150
|
-
opts
|
151
|
-
end
|
152
|
-
|
153
|
-
### Private Class Methods ###
|
154
|
-
|
155
|
-
# Sets the adapter scheme for the Database class. Call this method in
|
156
|
-
# descendnants of Database to allow connection using a URL. For example the
|
157
|
-
# following:
|
158
|
-
#
|
159
|
-
# class Sequel::MyDB::Database < Sequel::Database
|
160
|
-
# set_adapter_scheme :mydb
|
161
|
-
# ...
|
162
|
-
# end
|
163
|
-
#
|
164
|
-
# would allow connection using:
|
165
|
-
#
|
166
|
-
# Sequel.connect('mydb://user:password@dbserver/mydb')
|
167
|
-
def self.set_adapter_scheme(scheme) # :nodoc:
|
168
|
-
@scheme = scheme
|
169
|
-
@@adapters[scheme.to_sym] = self
|
170
|
-
end
|
171
|
-
|
172
|
-
private_class_method :set_adapter_scheme
|
173
|
-
|
174
|
-
### Instance Methods ###
|
175
|
-
|
176
|
-
# Executes the supplied SQL statement. The SQL can be supplied as a string
|
177
|
-
# or as an array of strings. If an array is given, comments and excessive
|
178
|
-
# white space are removed. See also Array#to_sql.
|
179
|
-
def <<(sql)
|
180
|
-
execute((Array === sql) ? sql.to_sql : sql)
|
181
|
-
end
|
182
|
-
|
183
|
-
# Returns a dataset from the database. If the first argument is a string,
|
184
|
-
# the method acts as an alias for Database#fetch, returning a dataset for
|
185
|
-
# arbitrary SQL:
|
186
|
-
#
|
187
|
-
# DB['SELECT * FROM items WHERE name = ?', my_name].print
|
188
|
-
#
|
189
|
-
# Otherwise, acts as an alias for Database#from, setting the primary
|
190
|
-
# table for the dataset:
|
191
|
-
#
|
192
|
-
# DB[:items].sql #=> "SELECT * FROM items"
|
193
|
-
def [](*args, &block)
|
194
|
-
(String === args.first) ? fetch(*args, &block) : from(*args, &block)
|
195
|
-
end
|
196
|
-
|
197
|
-
# Connects to the database. This method should be overridden by descendants.
|
198
|
-
def connect
|
199
|
-
raise NotImplementedError, "#connect should be overridden by adapters"
|
200
|
-
end
|
201
|
-
|
202
|
-
# Returns a blank dataset
|
203
|
-
def dataset
|
204
|
-
ds = Sequel::Dataset.new(self)
|
205
|
-
end
|
206
|
-
|
207
|
-
# Disconnects from the database. This method should be overridden by
|
208
|
-
# descendants.
|
209
|
-
def disconnect
|
210
|
-
raise NotImplementedError, "#disconnect should be overridden by adapters"
|
211
|
-
end
|
212
|
-
|
213
|
-
# Executes the given SQL. This method should be overridden in descendants.
|
214
|
-
def execute(sql)
|
215
|
-
raise NotImplementedError, "#execute should be overridden by adapters"
|
216
|
-
end
|
217
|
-
|
218
|
-
# Fetches records for an arbitrary SQL statement. If a block is given,
|
219
|
-
# it is used to iterate over the records:
|
220
|
-
#
|
221
|
-
# DB.fetch('SELECT * FROM items'){|r| p r}
|
222
|
-
#
|
223
|
-
# The method returns a dataset instance:
|
224
|
-
#
|
225
|
-
# DB.fetch('SELECT * FROM items').print
|
226
|
-
#
|
227
|
-
# Fetch can also perform parameterized queries for protection against SQL
|
228
|
-
# injection:
|
229
|
-
#
|
230
|
-
# DB.fetch('SELECT * FROM items WHERE name = ?', my_name).print
|
231
|
-
def fetch(sql, *args, &block)
|
232
|
-
ds = dataset
|
233
|
-
sql = sql.gsub('?') {|m| ds.literal(args.shift)}
|
234
|
-
ds.opts[:sql] = sql
|
235
|
-
ds.fetch_rows(sql, &block) if block
|
236
|
-
ds
|
237
|
-
end
|
238
|
-
alias_method :>>, :fetch
|
239
|
-
|
240
|
-
# Returns a new dataset with the from method invoked. If a block is given,
|
241
|
-
# it is used as a filter on the dataset.
|
242
|
-
def from(*args, &block)
|
243
|
-
ds = dataset.from(*args)
|
244
|
-
block ? ds.filter(&block) : ds
|
245
|
-
end
|
246
|
-
|
247
|
-
# Returns a single value from the database, e.g.:
|
248
|
-
#
|
249
|
-
# # SELECT 1
|
250
|
-
# DB.get(1) #=> 1
|
251
|
-
#
|
252
|
-
# # SELECT version()
|
253
|
-
# DB.get(:version[]) #=> ...
|
254
|
-
def get(expr)
|
255
|
-
dataset.get(expr)
|
256
|
-
end
|
257
|
-
|
258
|
-
# Returns a string representation of the database object including the
|
259
|
-
# class name and the connection URI (or the opts if the URI
|
260
|
-
# cannot be constructed).
|
261
|
-
def inspect
|
262
|
-
"#<#{self.class}: #{(uri rescue opts).inspect}>"
|
263
|
-
end
|
264
|
-
|
265
|
-
# Log a message at level info to all loggers. All SQL logging
|
266
|
-
# goes through this method.
|
267
|
-
def log_info(message)
|
268
|
-
@loggers.each{|logger| logger.info(message)}
|
269
|
-
end
|
270
|
-
|
271
|
-
# Return the first logger or nil if no loggers are being used.
|
272
|
-
# Should only be used for backwards compatibility.
|
273
|
-
def logger
|
274
|
-
@loggers.first
|
275
|
-
end
|
276
|
-
|
277
|
-
# Replace the array of loggers with the given logger(s).
|
278
|
-
def logger=(logger)
|
279
|
-
@loggers = Array(logger)
|
280
|
-
end
|
281
|
-
|
282
|
-
# Returns true unless the database is using a single-threaded connection pool.
|
283
|
-
def multi_threaded?
|
284
|
-
!@single_threaded
|
285
|
-
end
|
286
|
-
|
287
|
-
# Returns a dataset modified by the given query block. See Dataset#query.
|
288
|
-
def query(&block)
|
289
|
-
dataset.query(&block)
|
290
|
-
end
|
291
|
-
|
292
|
-
# Returns true if the database quotes identifiers.
|
293
|
-
def quote_identifiers?
|
294
|
-
@quote_identifiers
|
295
|
-
end
|
296
|
-
|
297
|
-
# Returns a new dataset with the select method invoked.
|
298
|
-
def select(*args)
|
299
|
-
dataset.select(*args)
|
300
|
-
end
|
301
|
-
|
302
|
-
# Default serial primary key options.
|
303
|
-
def serial_primary_key_options
|
304
|
-
{:primary_key => true, :type => :integer, :auto_increment => true}
|
305
|
-
end
|
306
|
-
|
307
|
-
# Returns true if the database is using a single-threaded connection pool.
|
308
|
-
def single_threaded?
|
309
|
-
@single_threaded
|
310
|
-
end
|
311
|
-
|
312
|
-
# Acquires a database connection, yielding it to the passed block.
|
313
|
-
def synchronize(&block)
|
314
|
-
@pool.hold(&block)
|
315
|
-
end
|
316
|
-
|
317
|
-
# Returns true if a table with the given name exists.
|
318
|
-
def table_exists?(name)
|
319
|
-
begin
|
320
|
-
if respond_to?(:tables)
|
321
|
-
tables.include?(name.to_sym)
|
322
|
-
else
|
323
|
-
from(name).first
|
324
|
-
true
|
325
|
-
end
|
326
|
-
rescue
|
327
|
-
false
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
|
-
# Attempts to acquire a database connection. Returns true if successful.
|
332
|
-
# Will probably raise an error if unsuccessful.
|
333
|
-
def test_connection
|
334
|
-
synchronize{|conn|}
|
335
|
-
true
|
336
|
-
end
|
337
|
-
|
338
|
-
# A simple implementation of SQL transactions. Nested transactions are not
|
339
|
-
# supported - calling #transaction within a transaction will reuse the
|
340
|
-
# current transaction. Should be overridden for databases that support nested
|
341
|
-
# transactions.
|
342
|
-
def transaction
|
343
|
-
@pool.hold do |conn|
|
344
|
-
@transactions ||= []
|
345
|
-
if @transactions.include? Thread.current
|
346
|
-
return yield(conn)
|
347
|
-
end
|
348
|
-
log_info(SQL_BEGIN)
|
349
|
-
conn.execute(SQL_BEGIN)
|
350
|
-
begin
|
351
|
-
@transactions << Thread.current
|
352
|
-
yield(conn)
|
353
|
-
rescue Exception => e
|
354
|
-
log_info(SQL_ROLLBACK)
|
355
|
-
conn.execute(SQL_ROLLBACK)
|
356
|
-
raise e unless Error::Rollback === e
|
357
|
-
ensure
|
358
|
-
unless e
|
359
|
-
log_info(SQL_COMMIT)
|
360
|
-
conn.execute(SQL_COMMIT)
|
361
|
-
end
|
362
|
-
@transactions.delete(Thread.current)
|
363
|
-
end
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
# Typecast the value to the given column_type. Can be overridden in
|
368
|
-
# adapters to support database specific column types.
|
369
|
-
def typecast_value(column_type, value)
|
370
|
-
return nil if value.nil?
|
371
|
-
case column_type
|
372
|
-
when :integer
|
373
|
-
Integer(value)
|
374
|
-
when :string
|
375
|
-
value.to_s
|
376
|
-
when :float
|
377
|
-
Float(value)
|
378
|
-
when :decimal
|
379
|
-
case value
|
380
|
-
when BigDecimal
|
381
|
-
value
|
382
|
-
when String, Float
|
383
|
-
value.to_d
|
384
|
-
when Integer
|
385
|
-
value.to_s.to_d
|
386
|
-
else
|
387
|
-
raise ArgumentError, "invalid value for BigDecimal: #{value.inspect}"
|
388
|
-
end
|
389
|
-
when :boolean
|
390
|
-
case value
|
391
|
-
when false, 0, "0", /\Af(alse)?\z/i
|
392
|
-
false
|
393
|
-
else
|
394
|
-
value.blank? ? nil : true
|
395
|
-
end
|
396
|
-
when :date
|
397
|
-
case value
|
398
|
-
when Date
|
399
|
-
value
|
400
|
-
when DateTime, Time
|
401
|
-
Date.new(value.year, value.month, value.day)
|
402
|
-
when String
|
403
|
-
value.to_date
|
404
|
-
else
|
405
|
-
raise ArgumentError, "invalid value for Date: #{value.inspect}"
|
406
|
-
end
|
407
|
-
when :time
|
408
|
-
case value
|
409
|
-
when Time
|
410
|
-
value
|
411
|
-
when String
|
412
|
-
value.to_time
|
413
|
-
else
|
414
|
-
raise ArgumentError, "invalid value for Time: #{value.inspect}"
|
415
|
-
end
|
416
|
-
when :datetime
|
417
|
-
raise(ArgumentError, "invalid value for #{tc}: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
|
418
|
-
if Sequel.datetime_class === value
|
419
|
-
# Already the correct class, no need to convert
|
420
|
-
value
|
421
|
-
else
|
422
|
-
# First convert it to standard ISO 8601 time, then
|
423
|
-
# parse that string using the time class.
|
424
|
-
(Time === value ? value.iso8601 : value.to_s).to_sequel_time
|
425
|
-
end
|
426
|
-
when :blob
|
427
|
-
value.to_blob
|
428
|
-
else
|
429
|
-
value
|
430
|
-
end
|
431
|
-
end
|
432
|
-
|
433
|
-
# Returns the URI identifying the database.
|
434
|
-
# This method can raise an error if the database used options
|
435
|
-
# instead of a connection string.
|
436
|
-
def uri
|
437
|
-
uri = URI::Generic.new(
|
438
|
-
self.class.adapter_scheme.to_s,
|
439
|
-
nil,
|
440
|
-
@opts[:host],
|
441
|
-
@opts[:port],
|
442
|
-
nil,
|
443
|
-
"/#{@opts[:database]}",
|
444
|
-
nil,
|
445
|
-
nil,
|
446
|
-
nil
|
447
|
-
)
|
448
|
-
uri.user = @opts[:user]
|
449
|
-
uri.password = @opts[:password] if uri.user
|
450
|
-
uri.to_s
|
451
|
-
end
|
452
|
-
alias_method :url, :uri
|
453
|
-
|
454
|
-
private
|
455
|
-
|
456
|
-
# The default options for the connection pool.
|
457
|
-
def connection_pool_default_options
|
458
|
-
{}
|
459
|
-
end
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|