sequel 2.2.0 → 2.3.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.
Files changed (98) hide show
  1. data/CHANGELOG +1551 -4
  2. data/README +306 -19
  3. data/Rakefile +84 -56
  4. data/bin/sequel +106 -0
  5. data/doc/cheat_sheet.rdoc +225 -0
  6. data/doc/dataset_filtering.rdoc +182 -0
  7. data/lib/sequel_core.rb +136 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +54 -0
  9. data/lib/sequel_core/adapters/ado.rb +80 -0
  10. data/lib/sequel_core/adapters/db2.rb +148 -0
  11. data/lib/sequel_core/adapters/dbi.rb +117 -0
  12. data/lib/sequel_core/adapters/informix.rb +78 -0
  13. data/lib/sequel_core/adapters/jdbc.rb +186 -0
  14. data/lib/sequel_core/adapters/jdbc/mysql.rb +55 -0
  15. data/lib/sequel_core/adapters/jdbc/postgresql.rb +66 -0
  16. data/lib/sequel_core/adapters/jdbc/sqlite.rb +47 -0
  17. data/lib/sequel_core/adapters/mysql.rb +231 -0
  18. data/lib/sequel_core/adapters/odbc.rb +155 -0
  19. data/lib/sequel_core/adapters/odbc_mssql.rb +106 -0
  20. data/lib/sequel_core/adapters/openbase.rb +64 -0
  21. data/lib/sequel_core/adapters/oracle.rb +170 -0
  22. data/lib/sequel_core/adapters/postgres.rb +199 -0
  23. data/lib/sequel_core/adapters/shared/mysql.rb +275 -0
  24. data/lib/sequel_core/adapters/shared/postgres.rb +351 -0
  25. data/lib/sequel_core/adapters/shared/sqlite.rb +146 -0
  26. data/lib/sequel_core/adapters/sqlite.rb +138 -0
  27. data/lib/sequel_core/connection_pool.rb +194 -0
  28. data/lib/sequel_core/core_ext.rb +203 -0
  29. data/lib/sequel_core/core_sql.rb +184 -0
  30. data/lib/sequel_core/database.rb +471 -0
  31. data/lib/sequel_core/database/schema.rb +156 -0
  32. data/lib/sequel_core/dataset.rb +457 -0
  33. data/lib/sequel_core/dataset/callback.rb +13 -0
  34. data/lib/sequel_core/dataset/convenience.rb +245 -0
  35. data/lib/sequel_core/dataset/pagination.rb +96 -0
  36. data/lib/sequel_core/dataset/query.rb +41 -0
  37. data/lib/sequel_core/dataset/schema.rb +15 -0
  38. data/lib/sequel_core/dataset/sql.rb +889 -0
  39. data/lib/sequel_core/deprecated.rb +26 -0
  40. data/lib/sequel_core/exceptions.rb +42 -0
  41. data/lib/sequel_core/migration.rb +187 -0
  42. data/lib/sequel_core/object_graph.rb +216 -0
  43. data/lib/sequel_core/pretty_table.rb +71 -0
  44. data/lib/sequel_core/schema.rb +2 -0
  45. data/lib/sequel_core/schema/generator.rb +239 -0
  46. data/lib/sequel_core/schema/sql.rb +325 -0
  47. data/lib/sequel_core/sql.rb +812 -0
  48. data/lib/sequel_model.rb +5 -1
  49. data/lib/sequel_model/association_reflection.rb +3 -8
  50. data/lib/sequel_model/base.rb +15 -10
  51. data/lib/sequel_model/inflector.rb +3 -5
  52. data/lib/sequel_model/plugins.rb +1 -1
  53. data/lib/sequel_model/record.rb +11 -3
  54. data/lib/sequel_model/schema.rb +4 -4
  55. data/lib/sequel_model/validations.rb +6 -1
  56. data/spec/adapters/ado_spec.rb +17 -0
  57. data/spec/adapters/informix_spec.rb +96 -0
  58. data/spec/adapters/mysql_spec.rb +764 -0
  59. data/spec/adapters/oracle_spec.rb +222 -0
  60. data/spec/adapters/postgres_spec.rb +441 -0
  61. data/spec/adapters/spec_helper.rb +7 -0
  62. data/spec/adapters/sqlite_spec.rb +400 -0
  63. data/spec/integration/dataset_test.rb +51 -0
  64. data/spec/integration/eager_loader_test.rb +702 -0
  65. data/spec/integration/schema_test.rb +102 -0
  66. data/spec/integration/spec_helper.rb +44 -0
  67. data/spec/integration/type_test.rb +43 -0
  68. data/spec/rcov.opts +2 -0
  69. data/spec/sequel_core/connection_pool_spec.rb +363 -0
  70. data/spec/sequel_core/core_ext_spec.rb +156 -0
  71. data/spec/sequel_core/core_sql_spec.rb +427 -0
  72. data/spec/sequel_core/database_spec.rb +964 -0
  73. data/spec/sequel_core/dataset_spec.rb +2977 -0
  74. data/spec/sequel_core/expression_filters_spec.rb +346 -0
  75. data/spec/sequel_core/migration_spec.rb +261 -0
  76. data/spec/sequel_core/object_graph_spec.rb +234 -0
  77. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  78. data/spec/sequel_core/schema_generator_spec.rb +122 -0
  79. data/spec/sequel_core/schema_spec.rb +497 -0
  80. data/spec/sequel_core/spec_helper.rb +51 -0
  81. data/spec/{association_reflection_spec.rb → sequel_model/association_reflection_spec.rb} +6 -6
  82. data/spec/{associations_spec.rb → sequel_model/associations_spec.rb} +47 -18
  83. data/spec/{base_spec.rb → sequel_model/base_spec.rb} +2 -1
  84. data/spec/{caching_spec.rb → sequel_model/caching_spec.rb} +0 -0
  85. data/spec/{dataset_methods_spec.rb → sequel_model/dataset_methods_spec.rb} +13 -1
  86. data/spec/{eager_loading_spec.rb → sequel_model/eager_loading_spec.rb} +75 -14
  87. data/spec/{hooks_spec.rb → sequel_model/hooks_spec.rb} +4 -4
  88. data/spec/sequel_model/inflector_spec.rb +119 -0
  89. data/spec/{model_spec.rb → sequel_model/model_spec.rb} +30 -11
  90. data/spec/{plugins_spec.rb → sequel_model/plugins_spec.rb} +0 -0
  91. data/spec/{record_spec.rb → sequel_model/record_spec.rb} +47 -6
  92. data/spec/{schema_spec.rb → sequel_model/schema_spec.rb} +18 -4
  93. data/spec/{spec_helper.rb → sequel_model/spec_helper.rb} +3 -2
  94. data/spec/{validations_spec.rb → sequel_model/validations_spec.rb} +37 -17
  95. data/spec/spec_config.rb +9 -0
  96. data/spec/spec_config.rb.example +10 -0
  97. metadata +110 -37
  98. data/spec/inflector_spec.rb +0 -34
@@ -0,0 +1,184 @@
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
@@ -0,0 +1,471 @@
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 = {}, &block)
92
+ if conn_string.is_a?(String)
93
+ if conn_string =~ /\Ajdbc:/
94
+ c = adapter_class(:jdbc)
95
+ opts = {:uri=>conn_string}.merge(opts)
96
+ else
97
+ uri = URI.parse(conn_string)
98
+ scheme = uri.scheme
99
+ scheme = :dbi if scheme =~ /\Adbi-/
100
+ c = adapter_class(scheme)
101
+ uri_options = {}
102
+ uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.blank?
103
+ opts = c.send(:uri_to_options, uri).merge(uri_options).merge(opts)
104
+ end
105
+ else
106
+ opts = conn_string.merge(opts)
107
+ c = adapter_class(opts[:adapter] || opts['adapter'])
108
+ end
109
+ # process opts a bit
110
+ opts = opts.inject({}) do |m, kv| k, v = *kv
111
+ k = :user if k.to_s == 'username'
112
+ m[k.to_sym] = v
113
+ m
114
+ end
115
+ if block
116
+ begin
117
+ yield(db = c.new(opts))
118
+ ensure
119
+ db.disconnect if db
120
+ ::Sequel::DATABASES.delete(db)
121
+ end
122
+ nil
123
+ else
124
+ c.new(opts)
125
+ end
126
+ end
127
+
128
+ # Sets the default quote_identifiers mode for new databases.
129
+ # See Sequel.quote_identifiers=.
130
+ def self.quote_identifiers=(value)
131
+ @@quote_identifiers = value
132
+ end
133
+
134
+ # Sets the default single_threaded mode for new databases.
135
+ # See Sequel.single_threaded=.
136
+ def self.single_threaded=(value)
137
+ @@single_threaded = value
138
+ end
139
+
140
+ ### Private Class Methods ###
141
+
142
+ # Sets the adapter scheme for the Database class. Call this method in
143
+ # descendnants of Database to allow connection using a URL. For example the
144
+ # following:
145
+ #
146
+ # class Sequel::MyDB::Database < Sequel::Database
147
+ # set_adapter_scheme :mydb
148
+ # ...
149
+ # end
150
+ #
151
+ # would allow connection using:
152
+ #
153
+ # Sequel.connect('mydb://user:password@dbserver/mydb')
154
+ def self.set_adapter_scheme(scheme) # :nodoc:
155
+ @scheme = scheme
156
+ @@adapters[scheme.to_sym] = self
157
+ end
158
+
159
+ # Converts a uri to an options hash. These options are then passed
160
+ # to a newly created database object.
161
+ def self.uri_to_options(uri) # :nodoc:
162
+ { :user => uri.user,
163
+ :password => uri.password,
164
+ :host => uri.host,
165
+ :port => uri.port,
166
+ :database => (m = /\/(.*)/.match(uri.path)) && (m[1]) }
167
+ end
168
+
169
+ private_class_method :set_adapter_scheme, :uri_to_options
170
+
171
+ ### Instance Methods ###
172
+
173
+ # Executes the supplied SQL statement. The SQL can be supplied as a string
174
+ # or as an array of strings. If an array is given, comments and excessive
175
+ # white space are removed. See also Array#to_sql.
176
+ def <<(sql)
177
+ execute_ddl((Array === sql) ? sql.to_sql : sql)
178
+ end
179
+
180
+ # Returns a dataset from the database. If the first argument is a string,
181
+ # the method acts as an alias for Database#fetch, returning a dataset for
182
+ # arbitrary SQL:
183
+ #
184
+ # DB['SELECT * FROM items WHERE name = ?', my_name].print
185
+ #
186
+ # Otherwise, acts as an alias for Database#from, setting the primary
187
+ # table for the dataset:
188
+ #
189
+ # DB[:items].sql #=> "SELECT * FROM items"
190
+ def [](*args, &block)
191
+ (String === args.first) ? fetch(*args, &block) : from(*args, &block)
192
+ end
193
+
194
+ # Connects to the database. This method should be overridden by descendants.
195
+ def connect
196
+ raise NotImplementedError, "#connect should be overridden by adapters"
197
+ end
198
+
199
+ # Returns a blank dataset
200
+ def dataset
201
+ ds = Sequel::Dataset.new(self)
202
+ end
203
+
204
+ # Disconnects from the database. This method should be overridden by
205
+ # descendants.
206
+ def disconnect
207
+ raise NotImplementedError, "#disconnect should be overridden by adapters"
208
+ end
209
+
210
+ # Executes the given SQL. This method should be overridden in descendants.
211
+ def execute(sql)
212
+ raise NotImplementedError, "#execute should be overridden by adapters"
213
+ end
214
+
215
+ # Method that should be used when submitting any DDL (Data Definition
216
+ # Language) SQL. By default, calls execute_dui.
217
+ def execute_ddl(sql)
218
+ execute_dui(sql)
219
+ end
220
+
221
+ # Method that should be used when issuing a DELETE, UPDATE, or INSERT
222
+ # statement. By default, calls execute.
223
+ def execute_dui(sql)
224
+ execute(sql)
225
+ end
226
+
227
+ # Fetches records for an arbitrary SQL statement. If a block is given,
228
+ # it is used to iterate over the records:
229
+ #
230
+ # DB.fetch('SELECT * FROM items'){|r| p r}
231
+ #
232
+ # The method returns a dataset instance:
233
+ #
234
+ # DB.fetch('SELECT * FROM items').print
235
+ #
236
+ # Fetch can also perform parameterized queries for protection against SQL
237
+ # injection:
238
+ #
239
+ # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).print
240
+ def fetch(sql, *args, &block)
241
+ ds = dataset
242
+ sql = sql.gsub('?') {|m| ds.literal(args.shift)}
243
+ ds.opts[:sql] = sql
244
+ ds.fetch_rows(sql, &block) if block
245
+ ds
246
+ end
247
+ alias_method :>>, :fetch
248
+
249
+ # Returns a new dataset with the from method invoked. If a block is given,
250
+ # it is used as a filter on the dataset.
251
+ def from(*args, &block)
252
+ ds = dataset.from(*args)
253
+ block ? ds.filter(&block) : ds
254
+ end
255
+
256
+ # Returns a single value from the database, e.g.:
257
+ #
258
+ # # SELECT 1
259
+ # DB.get(1) #=> 1
260
+ #
261
+ # # SELECT version()
262
+ # DB.get(:version[]) #=> ...
263
+ def get(expr)
264
+ dataset.get(expr)
265
+ end
266
+
267
+ # Returns a string representation of the database object including the
268
+ # class name and the connection URI (or the opts if the URI
269
+ # cannot be constructed).
270
+ def inspect
271
+ "#<#{self.class}: #{(uri rescue opts).inspect}>"
272
+ end
273
+
274
+ # Log a message at level info to all loggers. All SQL logging
275
+ # goes through this method.
276
+ def log_info(message)
277
+ @loggers.each{|logger| logger.info(message)}
278
+ end
279
+
280
+ # Return the first logger or nil if no loggers are being used.
281
+ # Should only be used for backwards compatibility.
282
+ def logger
283
+ @loggers.first
284
+ end
285
+
286
+ # Replace the array of loggers with the given logger(s).
287
+ def logger=(logger)
288
+ @loggers = Array(logger)
289
+ end
290
+
291
+ # Returns true unless the database is using a single-threaded connection pool.
292
+ def multi_threaded?
293
+ !@single_threaded
294
+ end
295
+
296
+ # Returns a dataset modified by the given query block. See Dataset#query.
297
+ def query(&block)
298
+ dataset.query(&block)
299
+ end
300
+
301
+ # Returns true if the database quotes identifiers.
302
+ def quote_identifiers?
303
+ @quote_identifiers
304
+ end
305
+
306
+ # Returns a new dataset with the select method invoked.
307
+ def select(*args)
308
+ dataset.select(*args)
309
+ end
310
+
311
+ # Default serial primary key options.
312
+ def serial_primary_key_options
313
+ {:primary_key => true, :type => :integer, :auto_increment => true}
314
+ end
315
+
316
+ # Returns true if the database is using a single-threaded connection pool.
317
+ def single_threaded?
318
+ @single_threaded
319
+ end
320
+
321
+ # Acquires a database connection, yielding it to the passed block.
322
+ def synchronize(&block)
323
+ @pool.hold(&block)
324
+ end
325
+
326
+ # Returns true if a table with the given name exists.
327
+ def table_exists?(name)
328
+ begin
329
+ if respond_to?(:tables)
330
+ tables.include?(name.to_sym)
331
+ else
332
+ from(name).first
333
+ true
334
+ end
335
+ rescue
336
+ false
337
+ end
338
+ end
339
+
340
+ # Attempts to acquire a database connection. Returns true if successful.
341
+ # Will probably raise an error if unsuccessful.
342
+ def test_connection
343
+ synchronize{|conn|}
344
+ true
345
+ end
346
+
347
+ # A simple implementation of SQL transactions. Nested transactions are not
348
+ # supported - calling #transaction within a transaction will reuse the
349
+ # current transaction. Should be overridden for databases that support nested
350
+ # transactions.
351
+ def transaction
352
+ @pool.hold do |conn|
353
+ @transactions ||= []
354
+ if @transactions.include? Thread.current
355
+ return yield(conn)
356
+ end
357
+ log_info(SQL_BEGIN)
358
+ conn.execute(SQL_BEGIN)
359
+ begin
360
+ @transactions << Thread.current
361
+ yield(conn)
362
+ rescue Exception => e
363
+ log_info(SQL_ROLLBACK)
364
+ conn.execute(SQL_ROLLBACK)
365
+ raise e unless Error::Rollback === e
366
+ ensure
367
+ unless e
368
+ log_info(SQL_COMMIT)
369
+ conn.execute(SQL_COMMIT)
370
+ end
371
+ @transactions.delete(Thread.current)
372
+ end
373
+ end
374
+ end
375
+
376
+ # Typecast the value to the given column_type. Can be overridden in
377
+ # adapters to support database specific column types.
378
+ def typecast_value(column_type, value)
379
+ return nil if value.nil?
380
+ case column_type
381
+ when :integer
382
+ Integer(value)
383
+ when :string
384
+ value.to_s
385
+ when :float
386
+ Float(value)
387
+ when :decimal
388
+ case value
389
+ when BigDecimal
390
+ value
391
+ when String, Float
392
+ value.to_d
393
+ when Integer
394
+ value.to_s.to_d
395
+ else
396
+ raise ArgumentError, "invalid value for BigDecimal: #{value.inspect}"
397
+ end
398
+ when :boolean
399
+ case value
400
+ when false, 0, "0", /\Af(alse)?\z/i
401
+ false
402
+ else
403
+ value.blank? ? nil : true
404
+ end
405
+ when :date
406
+ case value
407
+ when Date
408
+ value
409
+ when DateTime, Time
410
+ Date.new(value.year, value.month, value.day)
411
+ when String
412
+ value.to_date
413
+ else
414
+ raise ArgumentError, "invalid value for Date: #{value.inspect}"
415
+ end
416
+ when :time
417
+ case value
418
+ when Time
419
+ value
420
+ when String
421
+ value.to_time
422
+ else
423
+ raise ArgumentError, "invalid value for Time: #{value.inspect}"
424
+ end
425
+ when :datetime
426
+ raise(ArgumentError, "invalid value for #{tc}: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
427
+ if Sequel.datetime_class === value
428
+ # Already the correct class, no need to convert
429
+ value
430
+ else
431
+ # First convert it to standard ISO 8601 time, then
432
+ # parse that string using the time class.
433
+ (Time === value ? value.iso8601 : value.to_s).to_sequel_time
434
+ end
435
+ when :blob
436
+ value.to_blob
437
+ else
438
+ value
439
+ end
440
+ end
441
+
442
+ # Returns the URI identifying the database.
443
+ # This method can raise an error if the database used options
444
+ # instead of a connection string.
445
+ def uri
446
+ uri = URI::Generic.new(
447
+ self.class.adapter_scheme.to_s,
448
+ nil,
449
+ @opts[:host],
450
+ @opts[:port],
451
+ nil,
452
+ "/#{@opts[:database]}",
453
+ nil,
454
+ nil,
455
+ nil
456
+ )
457
+ uri.user = @opts[:user]
458
+ uri.password = @opts[:password] if uri.user
459
+ uri.to_s
460
+ end
461
+ alias_method :url, :uri
462
+
463
+ private
464
+
465
+ # The default options for the connection pool.
466
+ def connection_pool_default_options
467
+ {}
468
+ end
469
+ end
470
+ end
471
+