sequel 0.4.4.1 → 0.4.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/CHANGELOG +10 -0
  2. data/Rakefile +161 -159
  3. data/lib/sequel.rb +14 -10
  4. data/lib/sequel/adapters/adapter_skeleton.rb +2 -1
  5. data/lib/sequel/adapters/ado.rb +2 -1
  6. data/lib/sequel/adapters/db2.rb +5 -3
  7. data/lib/sequel/adapters/dbi.rb +2 -1
  8. data/lib/sequel/adapters/informix.rb +2 -1
  9. data/lib/sequel/adapters/jdbc.rb +3 -2
  10. data/lib/sequel/adapters/mysql.rb +268 -264
  11. data/lib/sequel/adapters/odbc.rb +7 -2
  12. data/lib/sequel/adapters/odbc_mssql.rb +1 -1
  13. data/lib/sequel/adapters/openbase.rb +2 -1
  14. data/lib/sequel/adapters/oracle.rb +2 -1
  15. data/lib/sequel/adapters/postgres.rb +32 -16
  16. data/lib/sequel/adapters/sqlite.rb +7 -6
  17. data/lib/sequel/array_keys.rb +295 -295
  18. data/lib/sequel/connection_pool.rb +1 -1
  19. data/lib/sequel/core_sql.rb +14 -5
  20. data/lib/sequel/database.rb +4 -4
  21. data/lib/sequel/dataset.rb +12 -10
  22. data/lib/sequel/dataset/convenience.rb +10 -8
  23. data/lib/sequel/dataset/sequelizer.rb +19 -16
  24. data/lib/sequel/dataset/sql.rb +43 -30
  25. data/lib/sequel/exceptions.rb +45 -0
  26. data/lib/sequel/migration.rb +7 -5
  27. data/lib/sequel/model.rb +1 -1
  28. data/lib/sequel/model/base.rb +3 -3
  29. data/lib/sequel/model/hooks.rb +0 -4
  30. data/lib/sequel/model/record.rb +9 -9
  31. data/lib/sequel/model/relations.rb +2 -2
  32. data/lib/sequel/pretty_table.rb +6 -3
  33. data/lib/sequel/schema/schema_sql.rb +11 -6
  34. data/lib/sequel/worker.rb +8 -7
  35. data/spec/adapters/sqlite_spec.rb +3 -3
  36. data/spec/array_keys_spec.rb +543 -543
  37. data/spec/connection_pool_spec.rb +6 -3
  38. data/spec/database_spec.rb +4 -4
  39. data/spec/dataset_spec.rb +25 -25
  40. data/spec/migration_spec.rb +1 -1
  41. data/spec/model_spec.rb +16 -16
  42. data/spec/sequelizer_spec.rb +7 -7
  43. data/spec/spec.opts +8 -0
  44. metadata +5 -5
  45. data/lib/sequel/error.rb +0 -22
@@ -107,7 +107,7 @@ module Sequel
107
107
  if @created_count < @max_size
108
108
  @created_count += 1
109
109
  @connection_proc ? @connection_proc.call : \
110
- (raise SequelError, "No connection proc specified")
110
+ (raise Error, "No connection proc specified")
111
111
  end
112
112
  end
113
113
 
@@ -47,7 +47,12 @@ class String
47
47
 
48
48
  # Converts a string into a Time object.
49
49
  def to_time
50
- Time.parse(self)
50
+ begin
51
+ Time.parse(self)
52
+ rescue Exception => e
53
+ raise Error::InvalidValue, "Invalid time value '#{self}' (#{e.message})"
54
+ end
55
+ # Why does Time.parse('0000-00-00') bork and not return nil or some such?
51
56
  end
52
57
  end
53
58
 
@@ -163,10 +168,14 @@ class Symbol
163
168
  #
164
169
  def to_column_ref(ds)
165
170
  case s = to_s
166
- when COLUMN_REF_RE1: "#{$1}.#{ds.quote_column_ref($2)} AS #{ds.quote_column_ref($3)}"
167
- when COLUMN_REF_RE2: "#{ds.quote_column_ref($1)} AS #{ds.quote_column_ref($2)}"
168
- when COLUMN_REF_RE3: "#{$1}.#{ds.quote_column_ref($2)}"
169
- else ds.quote_column_ref(s)
171
+ when COLUMN_REF_RE1
172
+ "#{$1}.#{ds.quote_column_ref($2)} AS #{ds.quote_column_ref($3)}"
173
+ when COLUMN_REF_RE2
174
+ "#{ds.quote_column_ref($1)} AS #{ds.quote_column_ref($2)}"
175
+ when COLUMN_REF_RE3
176
+ "#{$1}.#{ds.quote_column_ref($2)}"
177
+ else
178
+ ds.quote_column_ref(s)
170
179
  end
171
180
  end
172
181
 
@@ -135,9 +135,9 @@ module Sequel
135
135
  (String === args.first) ? fetch(*args) : from(*args)
136
136
  end
137
137
 
138
- # Raises a NotImplementedError. This method is overriden in descendants.
138
+ # Raises a Sequel::Error::NotImplemented. This method is overriden in descendants.
139
139
  def execute(sql)
140
- raise NotImplementedError
140
+ raise NotImplementedError, "#execute should be overriden by adapters"
141
141
  end
142
142
 
143
143
  # Executes the supplied SQL statement. The SQL can be supplied as a string
@@ -272,7 +272,7 @@ module Sequel
272
272
  result
273
273
  rescue => e
274
274
  conn.execute(SQL_ROLLBACK)
275
- raise e unless SequelRollbackError === e
275
+ raise e unless Error::Rollback === e
276
276
  ensure
277
277
  @transactions.delete(Thread.current)
278
278
  end
@@ -318,7 +318,7 @@ module Sequel
318
318
  require File.join(File.dirname(__FILE__), "adapters/#{scheme}")
319
319
  c = @@adapters[scheme.to_sym]
320
320
  end
321
- raise SequelError, "Invalid database scheme" unless c
321
+ raise Error::InvalidDatabaseScheme, "Invalid database scheme" unless c
322
322
  c
323
323
  end
324
324
 
@@ -226,28 +226,28 @@ module Sequel
226
226
  def set_model(key, *args)
227
227
  # pattern matching
228
228
  case key
229
- when nil: # set_model(nil) => no
229
+ when nil # set_model(nil) => no
230
230
  # no argument provided, so the dataset is denuded
231
231
  @opts.merge!(:naked => true, :models => nil, :polymorphic_key => nil)
232
232
  remove_row_proc
233
233
  # extend_with_stock_each
234
- when Class:
234
+ when Class
235
235
  # isomorphic model
236
236
  @opts.merge!(:naked => nil, :models => {nil => key}, :polymorphic_key => nil)
237
237
  set_row_proc {|h| key.new(h, *args)}
238
238
  extend_with_destroy
239
- when Symbol:
239
+ when Symbol
240
240
  # polymorphic model
241
- hash = args.shift || raise(SequelError, "No class hash supplied for polymorphic model")
241
+ hash = args.shift || raise(ArgumentError, "No class hash supplied for polymorphic model")
242
242
  @opts.merge!(:naked => true, :models => hash, :polymorphic_key => key)
243
243
  set_row_proc do |h|
244
244
  c = hash[h[key]] || hash[nil] || \
245
- raise(SequelError, "No matching model class for record (#{polymorphic_key} => #{h[polymorphic_key].inspect})")
245
+ raise(Error, "No matching model class for record (#{polymorphic_key} => #{h[polymorphic_key].inspect})")
246
246
  c.new(h, *args)
247
247
  end
248
248
  extend_with_destroy
249
249
  else
250
- raise SequelError, "Invalid parameters specified"
250
+ raise ArgumentError, "Invalid model specified"
251
251
  end
252
252
  self
253
253
  end
@@ -300,13 +300,13 @@ module Sequel
300
300
  @transform = t
301
301
  t.each do |k, v|
302
302
  case v
303
- when Array:
303
+ when Array
304
304
  if (v.size != 2) || !v.first.is_a?(Proc) && !v.last.is_a?(Proc)
305
- raise SequelError, "Invalid transform specified"
305
+ raise Error::InvalidTransform, "Invalid transform specified"
306
306
  end
307
307
  else
308
308
  unless v = STOCK_TRANSFORMS[v]
309
- raise SequelError, "Invalid transform specified"
309
+ raise Error::InvalidTransform, "Invalid transform specified"
310
310
  else
311
311
  t[k] = v
312
312
  end
@@ -384,7 +384,9 @@ module Sequel
384
384
  def extend_with_destroy
385
385
  unless respond_to?(:destroy)
386
386
  meta_def(:destroy) do
387
- raise SequelError, 'Dataset not associated with model' unless @opts[:models]
387
+ unless @opts[:models]
388
+ raise Error, "No model associated with this dataset"
389
+ end
388
390
  count = 0
389
391
  @db.transaction {each {|r| count += 1; r.destroy}}
390
392
  count
@@ -38,8 +38,10 @@ module Sequel
38
38
  end
39
39
  args = args.empty? ? 1 : (args.size == 1) ? args.first : args
40
40
  case args
41
- when 1: single_record(:limit => 1)
42
- when Fixnum: limit(args).all
41
+ when 1
42
+ single_record(:limit => 1)
43
+ when Fixnum
44
+ limit(args).all
43
45
  else
44
46
  filter(args, &block).single_record(:limit => 1)
45
47
  end
@@ -59,13 +61,13 @@ module Sequel
59
61
  # record is returned. Otherwise an array is returned with the last
60
62
  # <i>num</i> records.
61
63
  def last(*args)
62
- raise SequelError, 'No order specified' unless
64
+ raise Error, 'No order specified' unless
63
65
  @opts[:order] || (opts && opts[:order])
64
66
 
65
67
  args = args.empty? ? 1 : (args.size == 1) ? args.first : args
66
68
 
67
69
  case args
68
- when Fixnum:
70
+ when Fixnum
69
71
  l = {:limit => args}
70
72
  opts = {:order => invert_order(@opts[:order])}. \
71
73
  merge(opts ? opts.merge(l) : l)
@@ -238,10 +240,10 @@ module Sequel
238
240
  end
239
241
 
240
242
  module QueryBlockCopy #:nodoc:
241
- def each(*args); raise SequelError, "#each cannot be invoked inside a query block."; end
242
- def insert(*args); raise SequelError, "#insert cannot be invoked inside a query block."; end
243
- def update(*args); raise SequelError, "#update cannot be invoked inside a query block."; end
244
- def delete(*args); raise SequelError, "#delete cannot be invoked inside a query block."; end
243
+ def each(*args); raise Error, "#each cannot be invoked inside a query block."; end
244
+ def insert(*args); raise Error, "#insert cannot be invoked inside a query block."; end
245
+ def update(*args); raise Error, "#update cannot be invoked inside a query block."; end
246
+ def delete(*args); raise Error, "#delete cannot be invoked inside a query block."; end
245
247
 
246
248
  def clone_merge(opts)
247
249
  @opts.merge!(opts)
@@ -38,17 +38,17 @@ class Sequel::Dataset
38
38
  # "(id = 3)"
39
39
  def compare_expr(l, r)
40
40
  case r
41
- when Range:
41
+ when Range
42
42
  r.exclude_end? ? \
43
43
  "(#{literal(l)} >= #{literal(r.begin)} AND #{literal(l)} < #{literal(r.end)})" : \
44
44
  "(#{literal(l)} >= #{literal(r.begin)} AND #{literal(l)} <= #{literal(r.end)})"
45
- when Array:
45
+ when Array
46
46
  "(#{literal(l)} IN (#{literal(r)}))"
47
- when Sequel::Dataset:
47
+ when Sequel::Dataset
48
48
  "(#{literal(l)} IN (#{r.sql}))"
49
- when NilClass:
49
+ when NilClass
50
50
  "(#{literal(l)} IS NULL)"
51
- when Regexp:
51
+ when Regexp
52
52
  match_expr(l, r)
53
53
  else
54
54
  "(#{literal(l)} = #{literal(r)})"
@@ -60,10 +60,10 @@ class Sequel::Dataset
60
60
  # can override this method to provide support for regular expressions.
61
61
  def match_expr(l, r)
62
62
  case r
63
- when String:
63
+ when String
64
64
  "(#{literal(l)} LIKE #{literal(r)})"
65
65
  else
66
- raise SequelError, "Unsupported match pattern class (#{r.class})."
66
+ raise Sequel::Error, "Unsupported match pattern class (#{r.class})."
67
67
  end
68
68
  end
69
69
 
@@ -250,9 +250,9 @@ class Sequel::Dataset
250
250
  vcall_expr(e, b, opts)
251
251
  when :ivar, :cvar, :dvar, :const, :gvar # local ref
252
252
  eval(e[1].to_s, b)
253
- when :nth_ref:
253
+ when :nth_ref
254
254
  eval("$#{e[1]}", b)
255
- when :lvar: # local context
255
+ when :lvar # local context
256
256
  if e[1] == :block
257
257
  pr = eval(e[1].to_s, b)
258
258
  "#{proc_to_sql(pr)}"
@@ -267,9 +267,12 @@ class Sequel::Dataset
267
267
  eval_expr(e[1], b, opts)...eval_expr(e[2], b, opts)
268
268
  when :colon2 # qualified constant ref
269
269
  eval_expr(e[1], b, opts).const_get(e[2])
270
- when :false: false
271
- when :true: true
272
- when :nil: nil
270
+ when :false
271
+ false
272
+ when :true
273
+ true
274
+ when :nil
275
+ nil
273
276
  when :array
274
277
  # array
275
278
  e[1..-1].map {|i| eval_expr(i, b, opts)}
@@ -284,11 +287,11 @@ class Sequel::Dataset
284
287
  # assignment
285
288
  l = e[1]
286
289
  r = eval_expr(e[2], b, opts)
287
- raise SequelError, "Invalid expression #{l} = #{r}. Did you mean :#{l} == #{r}?"
290
+ raise Sequel::Error::InvalidExpression, "#{l} = #{r}. Did you mean :#{l} == #{r}?"
288
291
  when :if, :dstr
289
292
  ext_expr(e, b, opts)
290
293
  else
291
- raise SequelError, "Invalid expression tree: #{e.inspect}"
294
+ raise Sequel::Error::InvalidExpression, "Invalid expression tree: #{e.inspect}"
292
295
  end
293
296
  end
294
297
 
@@ -338,7 +341,7 @@ begin
338
341
  rescue Exception
339
342
  module Sequel::Dataset::Sequelizer
340
343
  def proc_to_sql(proc)
341
- raise SequelError, "You must have the ParseTree gem installed in order to use block filters."
344
+ raise Sequel::Error, "You must have the ParseTree gem installed in order to use block filters."
342
345
  end
343
346
  end
344
347
  end
@@ -348,7 +351,7 @@ begin
348
351
  rescue Exception
349
352
  module Sequel::Dataset::Sequelizer
350
353
  def ext_expr(e)
351
- raise SequelError, "You must have the Ruby2Ruby gem installed in order to use this block filter."
354
+ raise Sequel::Error, "You must have the Ruby2Ruby gem installed in order to use this block filter."
352
355
  end
353
356
  end
354
357
  end
@@ -45,15 +45,15 @@ module Sequel
45
45
  # Converts an array of sources names into into a comma separated list.
46
46
  def source_list(source)
47
47
  if source.nil? || source.empty?
48
- raise SequelError, 'No source specified for query'
48
+ raise Error, 'No source specified for query'
49
49
  end
50
50
  auto_alias_count = 0
51
51
  m = source.map do |i|
52
52
  case i
53
- when Dataset:
53
+ when Dataset
54
54
  auto_alias_count += 1
55
55
  i.to_table_reference(auto_alias_count)
56
- when Hash:
56
+ when Hash
57
57
  i.map {|k, v| "#{k.is_a?(Dataset) ? k.to_table_reference : k} #{v}"}.
58
58
  join(COMMA_SEPARATOR)
59
59
  else
@@ -84,21 +84,34 @@ module Sequel
84
84
  # If an unsupported object is given, an exception is raised.
85
85
  def literal(v)
86
86
  case v
87
- when LiteralString: v
88
- when String: "'#{v.gsub(/'/, "''")}'"
89
- when Integer, Float: v.to_s
90
- when BigDecimal: v.to_s("F")
91
- when NilClass: NULL
92
- when TrueClass: TRUE
93
- when FalseClass: FALSE
94
- when Symbol: v.to_column_ref(self)
95
- when Sequel::SQL::Expression: v.to_s(self)
96
- when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
97
- when Time: v.strftime(TIMESTAMP_FORMAT)
98
- when Date: v.strftime(DATE_FORMAT)
99
- when Dataset: "(#{v.sql})"
87
+ when LiteralString
88
+ v
89
+ when String
90
+ "'#{v.gsub(/'/, "''")}'"
91
+ when Integer, Float
92
+ v.to_s
93
+ when BigDecimal
94
+ v.to_s("F")
95
+ when NilClass
96
+ NULL
97
+ when TrueClass
98
+ TRUE
99
+ when FalseClass
100
+ FALSE
101
+ when Symbol
102
+ v.to_column_ref(self)
103
+ when Sequel::SQL::Expression
104
+ v.to_s(self)
105
+ when Array
106
+ v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
107
+ when Time
108
+ v.strftime(TIMESTAMP_FORMAT)
109
+ when Date
110
+ v.strftime(DATE_FORMAT)
111
+ when Dataset
112
+ "(#{v.sql})"
100
113
  else
101
- raise SequelError, "can't express #{v.inspect} as a SQL literal"
114
+ raise Error, "can't express #{v.inspect} as a SQL literal"
102
115
  end
103
116
  end
104
117
 
@@ -109,12 +122,12 @@ module Sequel
109
122
  # generated clause will be enclosed in a set of parentheses.
110
123
  def expression_list(expr, parenthesize = false)
111
124
  case expr
112
- when Hash:
125
+ when Hash
113
126
  parenthesize = false if expr.size == 1
114
127
  fmt = expr.map {|i| compare_expr(i[0], i[1])}.join(AND_SEPARATOR)
115
- when Array:
128
+ when Array
116
129
  fmt = expr.shift.gsub(QUESTION_MARK) {literal(expr.shift)}
117
- when Proc:
130
+ when Proc
118
131
  fmt = proc_to_sql(expr)
119
132
  else
120
133
  # if the expression is compound, it should be parenthesized in order for
@@ -204,7 +217,7 @@ module Sequel
204
217
  clause = (@opts[:group] ? :having : :where)
205
218
  cond = cond.first if cond.size == 1
206
219
  if cond === true || cond === false
207
- raise SequelError, "Invalid filter specified. Did you mean to supply a block?"
220
+ raise Error::InvalidFilter, "Invalid filter specified. Did you mean to supply a block?"
208
221
  end
209
222
  parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
210
223
  filter = cond.is_a?(Hash) && cond
@@ -228,7 +241,7 @@ module Sequel
228
241
  r = expression_list(block || cond, parenthesize)
229
242
  clone_merge(clause => "#{l} OR #{r}")
230
243
  else
231
- raise SequelError, "No existing filter found."
244
+ raise Error::NoExistingFilter, "No existing filter found."
232
245
  end
233
246
  end
234
247
 
@@ -238,7 +251,7 @@ module Sequel
238
251
  def and(*cond, &block)
239
252
  clause = (@opts[:group] ? :having : :where)
240
253
  unless @opts[clause]
241
- raise SequelError, "No existing filter found."
254
+ raise Error::NoExistingFilter, "No existing filter found."
242
255
  end
243
256
  filter(*cond, &block)
244
257
  end
@@ -265,7 +278,7 @@ module Sequel
265
278
  # if the dataset has been grouped. See also #filter.
266
279
  def where(*cond, &block)
267
280
  if @opts[:group]
268
- raise SequelError, "Can't specify a WHERE clause once the dataset has been grouped"
281
+ raise Error, "Can't specify a WHERE clause once the dataset has been grouped"
269
282
  else
270
283
  filter(*cond, &block)
271
284
  end
@@ -275,7 +288,7 @@ module Sequel
275
288
  # if the dataset has not been grouped. See also #filter
276
289
  def having(*cond, &block)
277
290
  unless @opts[:group]
278
- raise SequelError, "Can only specify a HAVING clause on a grouped dataset"
291
+ raise Error, "Can only specify a HAVING clause on a grouped dataset"
279
292
  else
280
293
  filter(*cond, &block)
281
294
  end
@@ -310,7 +323,7 @@ module Sequel
310
323
  def join_expr(type, table, expr)
311
324
  join_type = JOIN_TYPES[type || :inner]
312
325
  unless join_type
313
- raise SequelError, "Invalid join type: #{type}"
326
+ raise Error::InvalidJoinType, "Invalid join type: #{type}"
314
327
  end
315
328
 
316
329
  join_conditions = {}
@@ -471,9 +484,9 @@ module Sequel
471
484
  opts = opts ? @opts.merge(opts) : @opts
472
485
 
473
486
  if opts[:group]
474
- raise SequelError, "Can't update a grouped dataset"
487
+ raise Error::InvalidOperation, "A grouped dataset cannot be updated"
475
488
  elsif (opts[:from].size > 1) or opts[:join]
476
- raise SequelError, "Can't update a joined dataset"
489
+ raise Error::InvalidOperation, "A joined dataset cannot be updated"
477
490
  end
478
491
 
479
492
  sql = "UPDATE #{@opts[:from]} SET "
@@ -507,9 +520,9 @@ module Sequel
507
520
  opts = opts ? @opts.merge(opts) : @opts
508
521
 
509
522
  if opts[:group]
510
- raise SequelError, "Can't delete from a grouped dataset"
523
+ raise Error::InvalidOperation, "Grouped datasets cannot be deleted from"
511
524
  elsif opts[:from].is_a?(Array) && opts[:from].size > 1
512
- raise SequelError, "Can't delete from a joined dataset"
525
+ raise Error::InvalidOperation, "Joined datasets cannot be deleted from"
513
526
  end
514
527
 
515
528
  sql = "DELETE FROM #{opts[:from]}"
@@ -0,0 +1,45 @@
1
+ module Sequel
2
+ # Represents an error raised in Sequel code.
3
+ class Error < StandardError
4
+
5
+ # Rollback is a special error used to rollback a transactions.
6
+ # A transaction block will catch this error and wont pass further up the stack.
7
+ class Rollback < Error ; end
8
+
9
+ class InvalidDatabaseScheme < Error; end
10
+
11
+ # Represents an invalid value stored in the database.
12
+ class InvalidValue < Error ; end
13
+
14
+ # Represents an Invalid transform.
15
+ class InvalidTransform < Error ; end
16
+
17
+ # Represents an Invalid filter.
18
+ class InvalidFilter < Error ; end
19
+
20
+ class InvalidExpression < Error; end
21
+
22
+ # Represents an attempt to performing filter operations when no filter has been specified yet.
23
+ class NoExistingFilter < Error ; end
24
+
25
+ # Represents an invalid join type.
26
+ class InvalidJoinType < Error ; end
27
+
28
+ class WorkerStop < RuntimeError ; end
29
+
30
+ end
31
+ end
32
+
33
+ # Object extensions
34
+ class Object
35
+ # Cancels the current transaction without an error:
36
+ #
37
+ # DB.tranaction do
38
+ # ...
39
+ # rollback! if failed_to_contact_client
40
+ # ...
41
+ # end
42
+ def rollback!
43
+ raise Sequel::Error::Rollback
44
+ end
45
+ end