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.
- data/CHANGELOG +10 -0
- data/Rakefile +161 -159
- data/lib/sequel.rb +14 -10
- data/lib/sequel/adapters/adapter_skeleton.rb +2 -1
- data/lib/sequel/adapters/ado.rb +2 -1
- data/lib/sequel/adapters/db2.rb +5 -3
- data/lib/sequel/adapters/dbi.rb +2 -1
- data/lib/sequel/adapters/informix.rb +2 -1
- data/lib/sequel/adapters/jdbc.rb +3 -2
- data/lib/sequel/adapters/mysql.rb +268 -264
- data/lib/sequel/adapters/odbc.rb +7 -2
- data/lib/sequel/adapters/odbc_mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +2 -1
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/postgres.rb +32 -16
- data/lib/sequel/adapters/sqlite.rb +7 -6
- data/lib/sequel/array_keys.rb +295 -295
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core_sql.rb +14 -5
- data/lib/sequel/database.rb +4 -4
- data/lib/sequel/dataset.rb +12 -10
- data/lib/sequel/dataset/convenience.rb +10 -8
- data/lib/sequel/dataset/sequelizer.rb +19 -16
- data/lib/sequel/dataset/sql.rb +43 -30
- data/lib/sequel/exceptions.rb +45 -0
- data/lib/sequel/migration.rb +7 -5
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/model/hooks.rb +0 -4
- data/lib/sequel/model/record.rb +9 -9
- data/lib/sequel/model/relations.rb +2 -2
- data/lib/sequel/pretty_table.rb +6 -3
- data/lib/sequel/schema/schema_sql.rb +11 -6
- data/lib/sequel/worker.rb +8 -7
- data/spec/adapters/sqlite_spec.rb +3 -3
- data/spec/array_keys_spec.rb +543 -543
- data/spec/connection_pool_spec.rb +6 -3
- data/spec/database_spec.rb +4 -4
- data/spec/dataset_spec.rb +25 -25
- data/spec/migration_spec.rb +1 -1
- data/spec/model_spec.rb +16 -16
- data/spec/sequelizer_spec.rb +7 -7
- data/spec/spec.opts +8 -0
- metadata +5 -5
- data/lib/sequel/error.rb +0 -22
data/lib/sequel/core_sql.rb
CHANGED
@@ -47,7 +47,12 @@ class String
|
|
47
47
|
|
48
48
|
# Converts a string into a Time object.
|
49
49
|
def to_time
|
50
|
-
|
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
|
167
|
-
|
168
|
-
when
|
169
|
-
|
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
|
|
data/lib/sequel/database.rb
CHANGED
@@ -135,9 +135,9 @@ module Sequel
|
|
135
135
|
(String === args.first) ? fetch(*args) : from(*args)
|
136
136
|
end
|
137
137
|
|
138
|
-
# Raises a
|
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
|
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
|
321
|
+
raise Error::InvalidDatabaseScheme, "Invalid database scheme" unless c
|
322
322
|
c
|
323
323
|
end
|
324
324
|
|
data/lib/sequel/dataset.rb
CHANGED
@@ -226,28 +226,28 @@ module Sequel
|
|
226
226
|
def set_model(key, *args)
|
227
227
|
# pattern matching
|
228
228
|
case key
|
229
|
-
when nil
|
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(
|
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(
|
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
|
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
|
305
|
+
raise Error::InvalidTransform, "Invalid transform specified"
|
306
306
|
end
|
307
307
|
else
|
308
308
|
unless v = STOCK_TRANSFORMS[v]
|
309
|
-
raise
|
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
|
-
|
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
|
42
|
-
|
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
|
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
|
242
|
-
def insert(*args); raise
|
243
|
-
def update(*args); raise
|
244
|
-
def delete(*args); raise
|
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
|
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
|
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
|
271
|
-
|
272
|
-
when :
|
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
|
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
|
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
|
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
|
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
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -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
|
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
|
88
|
-
|
89
|
-
when
|
90
|
-
|
91
|
-
when
|
92
|
-
|
93
|
-
when
|
94
|
-
|
95
|
-
when
|
96
|
-
|
97
|
-
when
|
98
|
-
|
99
|
-
when
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
487
|
+
raise Error::InvalidOperation, "A grouped dataset cannot be updated"
|
475
488
|
elsif (opts[:from].size > 1) or opts[:join]
|
476
|
-
raise
|
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
|
523
|
+
raise Error::InvalidOperation, "Grouped datasets cannot be deleted from"
|
511
524
|
elsif opts[:from].is_a?(Array) && opts[:from].size > 1
|
512
|
-
raise
|
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
|