colincasey-sequel 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +7 -1
  2. data/doc/advanced_associations.rdoc +614 -0
  3. data/doc/cheat_sheet.rdoc +223 -0
  4. data/doc/dataset_filtering.rdoc +158 -0
  5. data/doc/prepared_statements.rdoc +104 -0
  6. data/doc/release_notes/1.0.txt +38 -0
  7. data/doc/release_notes/1.1.txt +143 -0
  8. data/doc/release_notes/1.3.txt +101 -0
  9. data/doc/release_notes/1.4.0.txt +53 -0
  10. data/doc/release_notes/1.5.0.txt +155 -0
  11. data/doc/release_notes/2.0.0.txt +298 -0
  12. data/doc/release_notes/2.1.0.txt +271 -0
  13. data/doc/release_notes/2.10.0.txt +328 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/doc/schema.rdoc +29 -0
  23. data/doc/sharding.rdoc +113 -0
  24. data/lib/sequel.rb +1 -0
  25. data/lib/sequel_core/adapters/ado.rb +89 -0
  26. data/lib/sequel_core/adapters/db2.rb +143 -0
  27. data/lib/sequel_core/adapters/dbi.rb +112 -0
  28. data/lib/sequel_core/adapters/do/mysql.rb +38 -0
  29. data/lib/sequel_core/adapters/do/postgres.rb +92 -0
  30. data/lib/sequel_core/adapters/do/sqlite.rb +31 -0
  31. data/lib/sequel_core/adapters/do.rb +205 -0
  32. data/lib/sequel_core/adapters/firebird.rb +298 -0
  33. data/lib/sequel_core/adapters/informix.rb +85 -0
  34. data/lib/sequel_core/adapters/jdbc/h2.rb +69 -0
  35. data/lib/sequel_core/adapters/jdbc/mysql.rb +66 -0
  36. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  37. data/lib/sequel_core/adapters/jdbc/postgresql.rb +113 -0
  38. data/lib/sequel_core/adapters/jdbc/sqlite.rb +43 -0
  39. data/lib/sequel_core/adapters/jdbc.rb +491 -0
  40. data/lib/sequel_core/adapters/mysql.rb +369 -0
  41. data/lib/sequel_core/adapters/odbc.rb +174 -0
  42. data/lib/sequel_core/adapters/openbase.rb +68 -0
  43. data/lib/sequel_core/adapters/oracle.rb +107 -0
  44. data/lib/sequel_core/adapters/postgres.rb +456 -0
  45. data/lib/sequel_core/adapters/shared/ms_access.rb +110 -0
  46. data/lib/sequel_core/adapters/shared/mssql.rb +102 -0
  47. data/lib/sequel_core/adapters/shared/mysql.rb +325 -0
  48. data/lib/sequel_core/adapters/shared/oracle.rb +61 -0
  49. data/lib/sequel_core/adapters/shared/postgres.rb +715 -0
  50. data/lib/sequel_core/adapters/shared/progress.rb +31 -0
  51. data/lib/sequel_core/adapters/shared/sqlite.rb +265 -0
  52. data/lib/sequel_core/adapters/sqlite.rb +248 -0
  53. data/lib/sequel_core/connection_pool.rb +258 -0
  54. data/lib/sequel_core/core_ext.rb +217 -0
  55. data/lib/sequel_core/core_sql.rb +202 -0
  56. data/lib/sequel_core/database/schema.rb +164 -0
  57. data/lib/sequel_core/database.rb +691 -0
  58. data/lib/sequel_core/dataset/callback.rb +13 -0
  59. data/lib/sequel_core/dataset/convenience.rb +237 -0
  60. data/lib/sequel_core/dataset/pagination.rb +96 -0
  61. data/lib/sequel_core/dataset/prepared_statements.rb +220 -0
  62. data/lib/sequel_core/dataset/query.rb +41 -0
  63. data/lib/sequel_core/dataset/schema.rb +15 -0
  64. data/lib/sequel_core/dataset/sql.rb +1010 -0
  65. data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
  66. data/lib/sequel_core/dataset/unsupported.rb +43 -0
  67. data/lib/sequel_core/dataset.rb +511 -0
  68. data/lib/sequel_core/deprecated.rb +26 -0
  69. data/lib/sequel_core/exceptions.rb +44 -0
  70. data/lib/sequel_core/migration.rb +212 -0
  71. data/lib/sequel_core/object_graph.rb +230 -0
  72. data/lib/sequel_core/pretty_table.rb +71 -0
  73. data/lib/sequel_core/schema/generator.rb +320 -0
  74. data/lib/sequel_core/schema/sql.rb +325 -0
  75. data/lib/sequel_core/schema.rb +2 -0
  76. data/lib/sequel_core/sql.rb +887 -0
  77. data/lib/sequel_core/version.rb +11 -0
  78. data/lib/sequel_core.rb +172 -0
  79. data/lib/sequel_model/association_reflection.rb +267 -0
  80. data/lib/sequel_model/associations.rb +499 -0
  81. data/lib/sequel_model/base.rb +523 -0
  82. data/lib/sequel_model/caching.rb +82 -0
  83. data/lib/sequel_model/dataset_methods.rb +26 -0
  84. data/lib/sequel_model/eager_loading.rb +370 -0
  85. data/lib/sequel_model/exceptions.rb +7 -0
  86. data/lib/sequel_model/hooks.rb +101 -0
  87. data/lib/sequel_model/inflector.rb +281 -0
  88. data/lib/sequel_model/plugins.rb +62 -0
  89. data/lib/sequel_model/record.rb +568 -0
  90. data/lib/sequel_model/schema.rb +49 -0
  91. data/lib/sequel_model/validations.rb +429 -0
  92. data/lib/sequel_model.rb +91 -0
  93. data/spec/adapters/ado_spec.rb +46 -0
  94. data/spec/adapters/firebird_spec.rb +376 -0
  95. data/spec/adapters/informix_spec.rb +96 -0
  96. data/spec/adapters/mysql_spec.rb +881 -0
  97. data/spec/adapters/oracle_spec.rb +244 -0
  98. data/spec/adapters/postgres_spec.rb +687 -0
  99. data/spec/adapters/spec_helper.rb +10 -0
  100. data/spec/adapters/sqlite_spec.rb +555 -0
  101. data/spec/integration/dataset_test.rb +134 -0
  102. data/spec/integration/eager_loader_test.rb +696 -0
  103. data/spec/integration/prepared_statement_test.rb +130 -0
  104. data/spec/integration/schema_test.rb +180 -0
  105. data/spec/integration/spec_helper.rb +58 -0
  106. data/spec/integration/type_test.rb +96 -0
  107. data/spec/rcov.opts +6 -0
  108. data/spec/sequel_core/connection_pool_spec.rb +526 -0
  109. data/spec/sequel_core/core_ext_spec.rb +156 -0
  110. data/spec/sequel_core/core_sql_spec.rb +522 -0
  111. data/spec/sequel_core/database_spec.rb +1188 -0
  112. data/spec/sequel_core/dataset_spec.rb +3481 -0
  113. data/spec/sequel_core/expression_filters_spec.rb +363 -0
  114. data/spec/sequel_core/migration_spec.rb +261 -0
  115. data/spec/sequel_core/object_graph_spec.rb +272 -0
  116. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  117. data/spec/sequel_core/schema_generator_spec.rb +167 -0
  118. data/spec/sequel_core/schema_spec.rb +780 -0
  119. data/spec/sequel_core/spec_helper.rb +55 -0
  120. data/spec/sequel_core/version_spec.rb +7 -0
  121. data/spec/sequel_model/association_reflection_spec.rb +93 -0
  122. data/spec/sequel_model/associations_spec.rb +1767 -0
  123. data/spec/sequel_model/base_spec.rb +419 -0
  124. data/spec/sequel_model/caching_spec.rb +215 -0
  125. data/spec/sequel_model/dataset_methods_spec.rb +78 -0
  126. data/spec/sequel_model/eager_loading_spec.rb +1165 -0
  127. data/spec/sequel_model/hooks_spec.rb +485 -0
  128. data/spec/sequel_model/inflector_spec.rb +119 -0
  129. data/spec/sequel_model/model_spec.rb +588 -0
  130. data/spec/sequel_model/plugins_spec.rb +80 -0
  131. data/spec/sequel_model/record_spec.rb +1184 -0
  132. data/spec/sequel_model/schema_spec.rb +90 -0
  133. data/spec/sequel_model/spec_helper.rb +78 -0
  134. data/spec/sequel_model/validations_spec.rb +1067 -0
  135. data/spec/spec.opts +0 -0
  136. data/spec/spec_config.rb.example +10 -0
  137. metadata +177 -3
@@ -0,0 +1,691 @@
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 do firebird informix jdbc mysql odbc 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
+ # The identifier input method to use by default
27
+ @@identifier_input_method = nil
28
+
29
+ # The identifier output method to use by default
30
+ @@identifier_output_method = nil
31
+
32
+ # Whether to use the single threaded connection pool by default
33
+ @@single_threaded = false
34
+
35
+ # Whether to quote identifiers (columns and tables) by default
36
+ @@quote_identifiers = nil
37
+
38
+ # The default schema to use
39
+ attr_accessor :default_schema
40
+
41
+ # Array of SQL loggers to use for this database
42
+ attr_accessor :loggers
43
+
44
+ # The options for this database
45
+ attr_reader :opts
46
+
47
+ # The connection pool for this database
48
+ attr_reader :pool
49
+
50
+ # The prepared statement objects for this database, keyed by name
51
+ attr_reader :prepared_statements
52
+
53
+ # Constructs a new instance of a database connection with the specified
54
+ # options hash.
55
+ #
56
+ # Sequel::Database is an abstract class that is not useful by itself.
57
+ #
58
+ # Takes the following options:
59
+ # * :default_schema : The default schema to use, should generally be nil
60
+ # * :disconnection_proc: A proc used to disconnect the connection.
61
+ # * :identifier_input_method: A string method symbol to call on identifiers going into the database
62
+ # * :identifier_output_method: A string method symbol to call on identifiers coming from the database
63
+ # * :loggers : An array of loggers to use.
64
+ # * :quote_identifiers : Whether to quote identifiers
65
+ # * :single_threaded : Whether to use a single-threaded connection pool
66
+ # * :upcase_identifiers : Whether to upcase identifiers going into the database
67
+ #
68
+ # All options given are also passed to the ConnectionPool. If a block
69
+ # is given, it is used as the connection_proc for the ConnectionPool.
70
+ def initialize(opts = {}, &block)
71
+ @opts ||= opts
72
+
73
+ @single_threaded = opts.include?(:single_threaded) ? opts[:single_threaded] : @@single_threaded
74
+ @schemas = nil
75
+ @default_schema = opts.include?(:default_schema) ? opts[:default_schema] : default_schema_default
76
+ @prepared_statements = {}
77
+ @transactions = []
78
+ @identifier_input_method = nil
79
+ @identifier_output_method = nil
80
+ @quote_identifiers = nil
81
+ if opts.include?(:upcase_identifiers)
82
+ @identifier_input_method = opts[:upcase_identifiers] ? :upcase : ""
83
+ end
84
+ @pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
85
+ @pool.connection_proc = proc{|server| connect(server)} unless block
86
+ @pool.disconnection_proc = proc{|conn| disconnect_connection(conn)} unless opts[:disconnection_proc]
87
+
88
+ @loggers = Array(opts[:logger]) + Array(opts[:loggers])
89
+ ::Sequel::DATABASES.push(self)
90
+ end
91
+
92
+ ### Class Methods ###
93
+
94
+ # The Database subclass for the given adapter scheme.
95
+ # Raises Sequel::Error::AdapterNotFound if the adapter
96
+ # could not be loaded.
97
+ def self.adapter_class(scheme)
98
+ scheme = scheme.to_s.gsub('-', '_').to_sym
99
+
100
+ if (klass = @@adapters[scheme]).nil?
101
+ # attempt to load the adapter file
102
+ begin
103
+ require "sequel_core/adapters/#{scheme}"
104
+ rescue LoadError => e
105
+ raise Error::AdapterNotFound, "Could not load #{scheme} adapter:\n #{e.message}"
106
+ end
107
+
108
+ # make sure we actually loaded the adapter
109
+ if (klass = @@adapters[scheme]).nil?
110
+ raise Error::AdapterNotFound, "Could not load #{scheme} adapter"
111
+ end
112
+ end
113
+ return klass
114
+ end
115
+
116
+ # Returns the scheme for the Database class.
117
+ def self.adapter_scheme
118
+ @scheme
119
+ end
120
+
121
+ # Connects to a database. See Sequel.connect.
122
+ def self.connect(conn_string, opts = {}, &block)
123
+ if conn_string.is_a?(String)
124
+ if conn_string =~ /\Ajdbc:/
125
+ c = adapter_class(:jdbc)
126
+ opts = {:uri=>conn_string}.merge(opts)
127
+ elsif conn_string =~ /\Ado:/
128
+ c = adapter_class(:do)
129
+ opts = {:uri=>conn_string}.merge(opts)
130
+ else
131
+ uri = URI.parse(conn_string)
132
+ scheme = uri.scheme
133
+ scheme = :dbi if scheme =~ /\Adbi-/
134
+ c = adapter_class(scheme)
135
+ uri_options = {}
136
+ uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.blank?
137
+ opts = c.send(:uri_to_options, uri).merge(uri_options).merge(opts)
138
+ end
139
+ else
140
+ opts = conn_string.merge(opts)
141
+ c = adapter_class(opts[:adapter] || opts['adapter'])
142
+ end
143
+ # process opts a bit
144
+ opts = opts.inject({}) do |m, kv| k, v = *kv
145
+ k = :user if k.to_s == 'username'
146
+ m[k.to_sym] = v
147
+ m
148
+ end
149
+ if block
150
+ begin
151
+ yield(db = c.new(opts))
152
+ ensure
153
+ db.disconnect if db
154
+ ::Sequel::DATABASES.delete(db)
155
+ end
156
+ nil
157
+ else
158
+ c.new(opts)
159
+ end
160
+ end
161
+
162
+ # The method to call on identifiers going into the database
163
+ def self.identifier_input_method
164
+ @@identifier_input_method
165
+ end
166
+
167
+ # Set the method to call on identifiers going into the database
168
+ def self.identifier_input_method=(v)
169
+ @@identifier_input_method = v || ""
170
+ end
171
+
172
+ # The method to call on identifiers coming from the database
173
+ def self.identifier_output_method
174
+ @@identifier_output_method
175
+ end
176
+
177
+ # Set the method to call on identifiers coming from the database
178
+ def self.identifier_output_method=(v)
179
+ @@identifier_output_method = v || ""
180
+ end
181
+
182
+ # Sets the default quote_identifiers mode for new databases.
183
+ # See Sequel.quote_identifiers=.
184
+ def self.quote_identifiers=(value)
185
+ @@quote_identifiers = value
186
+ end
187
+
188
+ # Sets the default single_threaded mode for new databases.
189
+ # See Sequel.single_threaded=.
190
+ def self.single_threaded=(value)
191
+ @@single_threaded = value
192
+ end
193
+
194
+ # Sets the default quote_identifiers mode for new databases.
195
+ # See Sequel.quote_identifiers=.
196
+ def self.upcase_identifiers=(value)
197
+ self.identifier_input_method = value ? :upcase : nil
198
+ end
199
+
200
+ ### Private Class Methods ###
201
+
202
+ # Sets the adapter scheme for the Database class. Call this method in
203
+ # descendnants of Database to allow connection using a URL. For example the
204
+ # following:
205
+ #
206
+ # class Sequel::MyDB::Database < Sequel::Database
207
+ # set_adapter_scheme :mydb
208
+ # ...
209
+ # end
210
+ #
211
+ # would allow connection using:
212
+ #
213
+ # Sequel.connect('mydb://user:password@dbserver/mydb')
214
+ def self.set_adapter_scheme(scheme) # :nodoc:
215
+ @scheme = scheme
216
+ @@adapters[scheme.to_sym] = self
217
+ end
218
+
219
+ # Converts a uri to an options hash. These options are then passed
220
+ # to a newly created database object.
221
+ def self.uri_to_options(uri) # :nodoc:
222
+ { :user => uri.user,
223
+ :password => uri.password,
224
+ :host => uri.host,
225
+ :port => uri.port,
226
+ :database => (m = /\/(.*)/.match(uri.path)) && (m[1]) }
227
+ end
228
+
229
+ private_class_method :set_adapter_scheme, :uri_to_options
230
+
231
+ ### Instance Methods ###
232
+
233
+ # Executes the supplied SQL statement. The SQL can be supplied as a string
234
+ # or as an array of strings. If an array is given, comments and excessive
235
+ # white space are removed. See also Array#to_sql.
236
+ def <<(sql)
237
+ execute_ddl((Array === sql) ? sql.to_sql : sql)
238
+ end
239
+
240
+ # Returns a dataset from the database. If the first argument is a string,
241
+ # the method acts as an alias for Database#fetch, returning a dataset for
242
+ # arbitrary SQL:
243
+ #
244
+ # DB['SELECT * FROM items WHERE name = ?', my_name].print
245
+ #
246
+ # Otherwise, acts as an alias for Database#from, setting the primary
247
+ # table for the dataset:
248
+ #
249
+ # DB[:items].sql #=> "SELECT * FROM items"
250
+ def [](*args, &block)
251
+ (String === args.first) ? fetch(*args, &block) : from(*args, &block)
252
+ end
253
+
254
+ # Call the prepared statement with the given name with the given hash
255
+ # of arguments.
256
+ def call(ps_name, hash={})
257
+ prepared_statements[ps_name].call(hash)
258
+ end
259
+
260
+ # Connects to the database. This method should be overridden by descendants.
261
+ def connect
262
+ raise NotImplementedError, "#connect should be overridden by adapters"
263
+ end
264
+
265
+ # Returns a blank dataset
266
+ def dataset
267
+ ds = Sequel::Dataset.new(self)
268
+ end
269
+
270
+ # Disconnects all available connections from the connection pool. If any
271
+ # connections are currently in use, they will not be disconnected.
272
+ def disconnect
273
+ pool.disconnect
274
+ end
275
+
276
+ # Executes the given SQL. This method should be overridden in descendants.
277
+ def execute(sql, opts={})
278
+ raise NotImplementedError, "#execute should be overridden by adapters"
279
+ end
280
+
281
+ # Method that should be used when submitting any DDL (Data Definition
282
+ # Language) SQL. By default, calls execute_dui.
283
+ def execute_ddl(sql, opts={}, &block)
284
+ execute_dui(sql, opts, &block)
285
+ end
286
+
287
+ # Method that should be used when issuing a DELETE, UPDATE, or INSERT
288
+ # statement. By default, calls execute.
289
+ def execute_dui(sql, opts={}, &block)
290
+ execute(sql, opts, &block)
291
+ end
292
+
293
+ # Method that should be used when issuing a INSERT
294
+ # statement. By default, calls execute_dui.
295
+ def execute_insert(sql, opts={}, &block)
296
+ execute_dui(sql, opts, &block)
297
+ end
298
+
299
+ # Fetches records for an arbitrary SQL statement. If a block is given,
300
+ # it is used to iterate over the records:
301
+ #
302
+ # DB.fetch('SELECT * FROM items'){|r| p r}
303
+ #
304
+ # The method returns a dataset instance:
305
+ #
306
+ # DB.fetch('SELECT * FROM items').print
307
+ #
308
+ # Fetch can also perform parameterized queries for protection against SQL
309
+ # injection:
310
+ #
311
+ # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).print
312
+ def fetch(sql, *args, &block)
313
+ ds = dataset
314
+ ds.opts[:sql] = Sequel::SQL::PlaceholderLiteralString.new(sql, args)
315
+ ds.each(&block) if block
316
+ ds
317
+ end
318
+ alias_method :>>, :fetch
319
+
320
+ # Returns a new dataset with the from method invoked. If a block is given,
321
+ # it is used as a filter on the dataset.
322
+ def from(*args, &block)
323
+ ds = dataset.from(*args)
324
+ block ? ds.filter(&block) : ds
325
+ end
326
+
327
+ # Returns a single value from the database, e.g.:
328
+ #
329
+ # # SELECT 1
330
+ # DB.get(1) #=> 1
331
+ #
332
+ # # SELECT version()
333
+ # DB.get(:version.sql_function) #=> ...
334
+ def get(expr)
335
+ dataset.get(expr)
336
+ end
337
+
338
+ # The method to call on identifiers going into the database
339
+ def identifier_input_method
340
+ case @identifier_input_method
341
+ when nil
342
+ @identifier_input_method = @opts.include?(:identifier_input_method) ? @opts[:identifier_input_method] : (@@identifier_input_method.nil? ? identifier_input_method_default : @@identifier_input_method)
343
+ @identifier_input_method == "" ? nil : @identifier_input_method
344
+ when ""
345
+ nil
346
+ else
347
+ @identifier_input_method
348
+ end
349
+ end
350
+
351
+ # Set the method to call on identifiers going into the database
352
+ def identifier_input_method=(v)
353
+ reset_schema_utility_dataset
354
+ @identifier_input_method = v || ""
355
+ end
356
+
357
+ # The method to call on identifiers coming from the database
358
+ def identifier_output_method
359
+ case @identifier_output_method
360
+ when nil
361
+ @identifier_output_method = @opts.include?(:identifier_output_method) ? @opts[:identifier_output_method] : (@@identifier_output_method.nil? ? identifier_output_method_default : @@identifier_output_method)
362
+ @identifier_output_method == "" ? nil : @identifier_output_method
363
+ when ""
364
+ nil
365
+ else
366
+ @identifier_output_method
367
+ end
368
+ end
369
+
370
+ # Set the method to call on identifiers coming from the database
371
+ def identifier_output_method=(v)
372
+ reset_schema_utility_dataset
373
+ @identifier_output_method = v || ""
374
+ end
375
+
376
+ # Returns a string representation of the database object including the
377
+ # class name and the connection URI (or the opts if the URI
378
+ # cannot be constructed).
379
+ def inspect
380
+ "#<#{self.class}: #{(uri rescue opts).inspect}>"
381
+ end
382
+
383
+ # Log a message at level info to all loggers. All SQL logging
384
+ # goes through this method.
385
+ def log_info(message, args=nil)
386
+ message = "#{message}; #{args.inspect}" if args
387
+ @loggers.each{|logger| logger.info(message)}
388
+ end
389
+
390
+ # Return the first logger or nil if no loggers are being used.
391
+ # Should only be used for backwards compatibility.
392
+ def logger
393
+ @loggers.first
394
+ end
395
+
396
+ # Replace the array of loggers with the given logger(s).
397
+ def logger=(logger)
398
+ @loggers = Array(logger)
399
+ end
400
+
401
+ # Returns true unless the database is using a single-threaded connection pool.
402
+ def multi_threaded?
403
+ !@single_threaded
404
+ end
405
+
406
+ # Returns a dataset modified by the given query block. See Dataset#query.
407
+ def query(&block)
408
+ dataset.query(&block)
409
+ end
410
+
411
+ # Whether to quote identifiers (columns and tables) for this database
412
+ def quote_identifiers=(v)
413
+ reset_schema_utility_dataset
414
+ @quote_identifiers = v
415
+ end
416
+
417
+ # Returns true if the database quotes identifiers.
418
+ def quote_identifiers?
419
+ return @quote_identifiers unless @quote_identifiers.nil?
420
+ @quote_identifiers = @opts.include?(:quote_identifiers) ? @opts[:quote_identifiers] : (@@quote_identifiers.nil? ? quote_identifiers_default : @@quote_identifiers)
421
+ end
422
+
423
+ # Returns a new dataset with the select method invoked.
424
+ def select(*args)
425
+ dataset.select(*args)
426
+ end
427
+
428
+ # Default serial primary key options.
429
+ def serial_primary_key_options
430
+ {:primary_key => true, :type => Integer, :auto_increment => true}
431
+ end
432
+
433
+ # Returns true if the database is using a single-threaded connection pool.
434
+ def single_threaded?
435
+ @single_threaded
436
+ end
437
+
438
+ # Acquires a database connection, yielding it to the passed block.
439
+ def synchronize(server=nil, &block)
440
+ @pool.hold(server || :default, &block)
441
+ end
442
+
443
+ # Returns true if a table with the given name exists. This requires a query
444
+ # to the database unless this database object already has the schema for
445
+ # the given table name.
446
+ def table_exists?(name)
447
+ if @schemas && @schemas[name]
448
+ true
449
+ else
450
+ begin
451
+ from(name).first
452
+ true
453
+ rescue
454
+ false
455
+ end
456
+ end
457
+ end
458
+
459
+ # Attempts to acquire a database connection. Returns true if successful.
460
+ # Will probably raise an error if unsuccessful.
461
+ def test_connection(server=nil)
462
+ synchronize(server){|conn|}
463
+ true
464
+ end
465
+
466
+ # A simple implementation of SQL transactions. Nested transactions are not
467
+ # supported - calling #transaction within a transaction will reuse the
468
+ # current transaction. Should be overridden for databases that support nested
469
+ # transactions.
470
+ def transaction(server=nil)
471
+ synchronize(server) do |conn|
472
+ return yield(conn) if @transactions.include?(Thread.current)
473
+ log_info(begin_transaction_sql)
474
+ conn.execute(begin_transaction_sql)
475
+ begin
476
+ @transactions << Thread.current
477
+ yield(conn)
478
+ rescue Exception => e
479
+ log_info(rollback_transaction_sql)
480
+ conn.execute(rollback_transaction_sql)
481
+ transaction_error(e)
482
+ ensure
483
+ unless e
484
+ log_info(commit_transaction_sql)
485
+ conn.execute(commit_transaction_sql)
486
+ end
487
+ @transactions.delete(Thread.current)
488
+ end
489
+ end
490
+ end
491
+
492
+ # Typecast the value to the given column_type. Can be overridden in
493
+ # adapters to support database specific column types.
494
+ # This method should raise Sequel::Error::InvalidValue if assigned value
495
+ # is invalid.
496
+ def typecast_value(column_type, value)
497
+ return nil if value.nil?
498
+ begin
499
+ case column_type
500
+ when :integer
501
+ Integer(value)
502
+ when :string
503
+ value.to_s
504
+ when :float
505
+ Float(value)
506
+ when :decimal
507
+ case value
508
+ when BigDecimal
509
+ value
510
+ when String, Float
511
+ value.to_d
512
+ when Integer
513
+ value.to_s.to_d
514
+ else
515
+ raise Sequel::Error::InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
516
+ end
517
+ when :boolean
518
+ case value
519
+ when false, 0, "0", /\Af(alse)?\z/i
520
+ false
521
+ else
522
+ value.blank? ? nil : true
523
+ end
524
+ when :date
525
+ case value
526
+ when Date
527
+ value
528
+ when DateTime, Time
529
+ Date.new(value.year, value.month, value.day)
530
+ when String
531
+ value.to_date
532
+ else
533
+ raise Sequel::Error::InvalidValue, "invalid value for Date: #{value.inspect}"
534
+ end
535
+ when :time
536
+ case value
537
+ when Time
538
+ value
539
+ when String
540
+ value.to_time
541
+ else
542
+ raise Sequel::Error::InvalidValue, "invalid value for Time: #{value.inspect}"
543
+ end
544
+ when :datetime
545
+ raise(Sequel::Error::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
546
+ if Sequel.datetime_class === value
547
+ # Already the correct class, no need to convert
548
+ value
549
+ else
550
+ # First convert it to standard ISO 8601 time, then
551
+ # parse that string using the time class.
552
+ (Time === value ? value.iso8601 : value.to_s).to_sequel_time
553
+ end
554
+ when :blob
555
+ ::Sequel::SQL::Blob.new(value)
556
+ else
557
+ value
558
+ end
559
+ rescue ArgumentError => exp
560
+ e = Sequel::Error::InvalidValue.new("#{exp.class} #{exp.message}")
561
+ e.set_backtrace(exp.backtrace)
562
+ raise e
563
+ end
564
+ end
565
+
566
+ # Set whether to upcase identifiers going into the database.
567
+ def upcase_identifiers=(v)
568
+ self.identifier_input_method = v ? :upcase : nil
569
+ end
570
+
571
+ # Returns true if the database upcases identifiers.
572
+ def upcase_identifiers?
573
+ identifier_input_method == :upcase
574
+ end
575
+
576
+ # Returns the URI identifying the database.
577
+ # This method can raise an error if the database used options
578
+ # instead of a connection string.
579
+ def uri
580
+ uri = URI::Generic.new(
581
+ self.class.adapter_scheme.to_s,
582
+ nil,
583
+ @opts[:host],
584
+ @opts[:port],
585
+ nil,
586
+ "/#{@opts[:database]}",
587
+ nil,
588
+ nil,
589
+ nil
590
+ )
591
+ uri.user = @opts[:user]
592
+ uri.password = @opts[:password] if uri.user
593
+ uri.to_s
594
+ end
595
+
596
+ # Explicit alias of uri for easier subclassing.
597
+ def url
598
+ uri
599
+ end
600
+
601
+ private
602
+
603
+ # SQL to BEGIN a transaction.
604
+ def begin_transaction_sql
605
+ SQL_BEGIN
606
+ end
607
+
608
+ # SQL to COMMIT a transaction.
609
+ def commit_transaction_sql
610
+ SQL_COMMIT
611
+ end
612
+
613
+ # The default options for the connection pool.
614
+ def connection_pool_default_options
615
+ {}
616
+ end
617
+
618
+ # The default value for default_schema.
619
+ def default_schema_default
620
+ nil
621
+ end
622
+
623
+ # The method to apply to identifiers going into the database by default.
624
+ # Should be overridden in subclasses for databases that fold unquoted
625
+ # identifiers to lower case instead of uppercase, such as
626
+ # MySQL, PostgreSQL, and SQLite.
627
+ def identifier_input_method_default
628
+ :upcase
629
+ end
630
+
631
+ # The method to apply to identifiers coming the database by default.
632
+ # Should be overridden in subclasses for databases that fold unquoted
633
+ # identifiers to lower case instead of uppercase, such as
634
+ # MySQL, PostgreSQL, and SQLite.
635
+ def identifier_output_method_default
636
+ :downcase
637
+ end
638
+
639
+ def quote_identifiers_default
640
+ true
641
+ end
642
+
643
+ # SQL to ROLLBACK a transaction.
644
+ def rollback_transaction_sql
645
+ SQL_ROLLBACK
646
+ end
647
+
648
+ # Convert the given exception to a DatabaseError, keeping message
649
+ # and traceback.
650
+ def raise_error(exception, opts={})
651
+ if !opts[:classes] || exception.is_one_of?(*opts[:classes])
652
+ e = DatabaseError.new("#{exception.class} #{exception.message}")
653
+ e.set_backtrace(exception.backtrace)
654
+ raise e
655
+ else
656
+ raise exception
657
+ end
658
+ end
659
+
660
+ # Split the schema information from the table
661
+ def schema_and_table(table_name)
662
+ schema_utility_dataset.schema_and_table(table_name)
663
+ end
664
+
665
+ # Return the options for the given server by merging the generic
666
+ # options for all server with the specific options for the given
667
+ # server specified in the :servers option.
668
+ def server_opts(server)
669
+ opts = if @opts[:servers] && server_options = @opts[:servers][server]
670
+ case server_options
671
+ when Hash
672
+ @opts.merge(server_options)
673
+ when Proc
674
+ @opts.merge(server_options.call(self))
675
+ else
676
+ raise Error, 'Server opts should be a hash or proc'
677
+ end
678
+ else
679
+ @opts.dup
680
+ end
681
+ opts.delete(:servers)
682
+ opts
683
+ end
684
+
685
+ # Raise a database error unless the exception is an Error::Rollback.
686
+ def transaction_error(e, *classes)
687
+ raise_error(e, :classes=>classes) unless Error::Rollback === e
688
+ end
689
+ end
690
+ end
691
+
@@ -0,0 +1,13 @@
1
+ module Sequel
2
+ class Dataset
3
+ private
4
+ # This is run inside .all, after all
5
+ # of the records have been loaded
6
+ # via .each, but before any block passed
7
+ # to all is called. It is called with
8
+ # a single argument, an array of all
9
+ # returned records.
10
+ def post_load(all_records)
11
+ end
12
+ end
13
+ end