sequel_core 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGELOG +74 -0
  2. data/COPYING +1 -0
  3. data/README +17 -6
  4. data/Rakefile +16 -21
  5. data/lib/sequel_core.rb +18 -28
  6. data/lib/sequel_core/adapters/ado.rb +3 -15
  7. data/lib/sequel_core/adapters/dbi.rb +1 -14
  8. data/lib/sequel_core/adapters/informix.rb +3 -3
  9. data/lib/sequel_core/adapters/jdbc.rb +2 -2
  10. data/lib/sequel_core/adapters/mysql.rb +39 -59
  11. data/lib/sequel_core/adapters/odbc.rb +18 -38
  12. data/lib/sequel_core/adapters/openbase.rb +1 -17
  13. data/lib/sequel_core/adapters/oracle.rb +1 -19
  14. data/lib/sequel_core/adapters/postgres.rb +20 -60
  15. data/lib/sequel_core/adapters/sqlite.rb +4 -8
  16. data/lib/sequel_core/connection_pool.rb +150 -0
  17. data/lib/sequel_core/core_ext.rb +41 -0
  18. data/lib/sequel_core/core_sql.rb +35 -38
  19. data/lib/sequel_core/database.rb +20 -17
  20. data/lib/sequel_core/dataset.rb +49 -80
  21. data/lib/sequel_core/dataset/callback.rb +11 -13
  22. data/lib/sequel_core/dataset/convenience.rb +18 -136
  23. data/lib/sequel_core/dataset/pagination.rb +81 -0
  24. data/lib/sequel_core/dataset/sequelizer.rb +5 -4
  25. data/lib/sequel_core/dataset/sql.rb +43 -33
  26. data/lib/sequel_core/deprecated.rb +200 -0
  27. data/lib/sequel_core/exceptions.rb +0 -14
  28. data/lib/sequel_core/object_graph.rb +199 -0
  29. data/lib/sequel_core/pretty_table.rb +27 -24
  30. data/lib/sequel_core/schema/generator.rb +16 -4
  31. data/lib/sequel_core/schema/sql.rb +5 -3
  32. data/lib/sequel_core/worker.rb +1 -1
  33. data/spec/adapters/informix_spec.rb +1 -47
  34. data/spec/adapters/mysql_spec.rb +85 -54
  35. data/spec/adapters/oracle_spec.rb +1 -57
  36. data/spec/adapters/postgres_spec.rb +66 -49
  37. data/spec/adapters/sqlite_spec.rb +4 -29
  38. data/spec/connection_pool_spec.rb +358 -0
  39. data/spec/core_sql_spec.rb +24 -19
  40. data/spec/database_spec.rb +13 -9
  41. data/spec/dataset_spec.rb +59 -78
  42. data/spec/object_graph_spec.rb +202 -0
  43. data/spec/pretty_table_spec.rb +1 -9
  44. data/spec/schema_generator_spec.rb +7 -1
  45. data/spec/schema_spec.rb +27 -0
  46. data/spec/sequelizer_spec.rb +2 -2
  47. data/spec/spec_helper.rb +4 -2
  48. metadata +16 -57
  49. data/lib/sequel_core/array_keys.rb +0 -322
  50. data/lib/sequel_core/model.rb +0 -8
  51. data/spec/array_keys_spec.rb +0 -682
@@ -1,19 +1,17 @@
1
1
  module Sequel
2
2
  class Dataset
3
+ # Module with empty methods that can be
4
+ # override to provide callback behavior
3
5
  module Callback
4
- # Add a symbol with the name of a method to the list of callbacks for name.
5
- def add_callback(name, sym)
6
- cbs = (@opts[:callbacks] ||= {})
7
- cb = (cbs[name] ||= [])
8
- cb.push(sym)
9
- end
10
-
11
- # Run all callbacks for name with the given args
12
- def run_callback(name, *args)
13
- return unless cbs = @opts[:callbacks]
14
- return unless cb = cbs[name]
15
- cb.each{|sym| send(sym, *args)}
16
- end
6
+ private
7
+ # This is run inside .all, after all
8
+ # of the records have been loaded
9
+ # via .each, but before any block passed
10
+ # to all is called. It is called with
11
+ # a single argument, an array of all
12
+ # returned records.
13
+ def post_load(all_records)
14
+ end
17
15
  end
18
16
  end
19
17
  end
@@ -3,15 +3,9 @@ require 'enumerator'
3
3
  module Sequel
4
4
  class Dataset
5
5
  module Convenience
6
- # Iterates through each record, converting it into a hash.
7
- def each_hash(&block)
8
- each {|a| block[a.to_hash]}
9
- end
10
-
11
6
  # Returns true if no records exists in the dataset
12
7
  def empty?
13
8
  db.dataset.where(exists).get(1) == nil
14
- # count == 0
15
9
  end
16
10
 
17
11
  # Returns the first record in the dataset.
@@ -23,10 +17,10 @@ module Sequel
23
17
  NAKED_HASH = {:naked => true}.freeze
24
18
 
25
19
  # Returns the first value of the first reecord in the dataset.
26
- # Returns nill if dataset is empty.
20
+ # Returns nil if dataset is empty.
27
21
  def single_value(opts = nil)
28
22
  opts = opts ? NAKED_HASH.merge(opts) : NAKED_HASH
29
- # reset the columns cache so it won't fuck subsequent calls to columns
23
+ # don't cache the columns
30
24
  each(opts) {|r| @columns = nil; return r.values.first}
31
25
  nil
32
26
  end
@@ -104,97 +98,27 @@ module Sequel
104
98
  end
105
99
  end
106
100
 
107
- # Returns a paginated dataset. The resulting dataset also provides the
108
- # total number of pages (Dataset#page_count) and the current page number
109
- # (Dataset#current_page), as well as Dataset#prev_page and Dataset#next_page
110
- # for implementing pagination controls.
111
- def paginate(page_no, page_size)
112
- record_count = count
113
- total_pages = (record_count / page_size.to_f).ceil
114
- paginated = limit(page_size, (page_no - 1) * page_size)
115
- paginated.set_pagination_info(page_no, page_size, record_count)
116
- paginated
117
- end
118
-
119
- # Sets the pagination info
120
- def set_pagination_info(page_no, page_size, record_count)
121
- @current_page = page_no
122
- @page_size = page_size
123
- @pagination_record_count = record_count
124
- @page_count = (record_count / page_size.to_f).ceil
125
- end
126
-
127
- def each_page(page_size)
128
- record_count = count
129
- total_pages = (record_count / page_size.to_f).ceil
130
-
131
- (1..total_pages).each do |page_no|
132
- paginated = limit(page_size, (page_no - 1) * page_size)
133
- paginated.set_pagination_info(page_no, page_size, record_count)
134
- yield paginated
135
- end
136
-
137
- self
138
- end
139
-
140
- attr_accessor :page_size, :page_count, :current_page, :pagination_record_count
141
-
142
- # Returns the previous page number or nil if the current page is the first
143
- def prev_page
144
- current_page > 1 ? (current_page - 1) : nil
145
- end
146
-
147
- # Returns the next page number or nil if the current page is the last page
148
- def next_page
149
- current_page < page_count ? (current_page + 1) : nil
150
- end
151
-
152
- # Returns the page range
153
- def page_range
154
- 1..page_count
155
- end
156
-
157
- # Returns the record range for the current page
158
- def current_page_record_range
159
- return (0..0) if @current_page > @page_count
160
-
161
- a = 1 + (@current_page - 1) * @page_size
162
- b = a + @page_size - 1
163
- b = @pagination_record_count if b > @pagination_record_count
164
- a..b
165
- end
166
-
167
- # Returns the number of records in the current page
168
- def current_page_record_count
169
- return 0 if @current_page > @page_count
170
-
171
- a = 1 + (@current_page - 1) * @page_size
172
- b = a + @page_size - 1
173
- b = @pagination_record_count if b > @pagination_record_count
174
- b - a + 1
175
- end
176
-
177
101
  # Returns the minimum value for the given column.
178
102
  def min(column)
179
- single_value(:select => [column.MIN.AS(:v)])
103
+ single_value(:select => [:min[column].as(:v)])
180
104
  end
181
105
 
182
106
  # Returns the maximum value for the given column.
183
107
  def max(column)
184
- single_value(:select => [column.MAX.AS(:v)])
108
+ single_value(:select => [:max[column].as(:v)])
185
109
  end
186
110
 
187
111
  # Returns the sum for the given column.
188
112
  def sum(column)
189
- single_value(:select => [column.SUM.AS(:v)])
113
+ single_value(:select => [:sum[column].as(:v)])
190
114
  end
191
115
 
192
116
  # Returns the average value for the given column.
193
117
  def avg(column)
194
- single_value(:select => [column.AVG.AS(:v)])
118
+ single_value(:select => [:avg[column].as(:v)])
195
119
  end
196
120
 
197
- COUNT_OF_ALL_AS_COUNT = :count['*'.lit].AS(:count)
121
+ COUNT_OF_ALL_AS_COUNT = :count['*'.lit].as(:count)
198
122
 
199
123
  # Returns a dataset grouped by the given column with count by group.
200
124
  def group_and_count(*columns)
@@ -204,15 +128,17 @@ module Sequel
204
128
  # Returns a Range object made from the minimum and maximum values for the
205
129
  # given column.
206
130
  def range(column)
207
- r = select(column.MIN.AS(:v1), column.MAX.AS(:v2)).first
208
- r && (r[:v1]..r[:v2])
131
+ if r = select(:min[column].as(:v1), :max[column].as(:v2)).first
132
+ (r[:v1]..r[:v2])
133
+ end
209
134
  end
210
135
 
211
136
  # Returns the interval between minimum and maximum values for the given
212
137
  # column.
213
138
  def interval(column)
214
- r = select("(max(#{literal(column)}) - min(#{literal(column)})) AS v".lit).first
215
- r && r[:v]
139
+ if r = select("(max(#{literal(column)}) - min(#{literal(column)})) AS v".lit).first
140
+ r[:v]
141
+ end
216
142
  end
217
143
 
218
144
  # Pretty prints the records in the dataset as plain-text table.
@@ -227,12 +153,11 @@ module Sequel
227
153
  # first line. You can turn that off by passing false as the
228
154
  # include_column_titles argument.
229
155
  def to_csv(include_column_titles = true)
230
- records = naked.to_a
156
+ n = naked
157
+ cols = n.columns
231
158
  csv = ''
232
- if include_column_titles
233
- csv << "#{@columns.join(COMMA_SEPARATOR)}\r\n"
234
- end
235
- records.each {|r| csv << "#{r.join(COMMA_SEPARATOR)}\r\n"}
159
+ csv << "#{cols.join(COMMA_SEPARATOR)}\r\n" if include_column_titles
160
+ n.each{|r| csv << "#{cols.collect{|c| r[c]}.join(COMMA_SEPARATOR)}\r\n"}
236
161
  csv
237
162
  end
238
163
 
@@ -317,49 +242,6 @@ module Sequel
317
242
  clone(copy.opts)
318
243
  end
319
244
 
320
- MUTATION_RE = /^(.+)!$/.freeze
321
-
322
- # Provides support for mutation methods (filter!, order!, etc.) and magic
323
- # methods.
324
- def method_missing(m, *args, &block)
325
- if m.to_s =~ MUTATION_RE
326
- m = $1.to_sym
327
- super unless respond_to?(m)
328
- copy = send(m, *args, &block)
329
- super if copy.class != self.class
330
- @opts.merge!(copy.opts)
331
- self
332
- elsif magic_method_missing(m)
333
- send(m, *args)
334
- else
335
- super
336
- end
337
- end
338
-
339
- MAGIC_METHODS = {
340
- /^order_by_(.+)$/ => proc {|c| proc {order(c)}},
341
- /^first_by_(.+)$/ => proc {|c| proc {order(c).first}},
342
- /^last_by_(.+)$/ => proc {|c| proc {order(c).last}},
343
- /^filter_by_(.+)$/ => proc {|c| proc {|v| filter(c => v)}},
344
- /^all_by_(.+)$/ => proc {|c| proc {|v| filter(c => v).all}},
345
- /^find_by_(.+)$/ => proc {|c| proc {|v| filter(c => v).first}},
346
- /^group_by_(.+)$/ => proc {|c| proc {group(c)}},
347
- /^count_by_(.+)$/ => proc {|c| proc {group_and_count(c)}}
348
- }
349
-
350
- # Checks if the given method name represents a magic method and
351
- # defines it. Otherwise, nil is returned.
352
- def magic_method_missing(m)
353
- method_name = m.to_s
354
- MAGIC_METHODS.each_pair do |r, p|
355
- if method_name =~ r
356
- impl = p[$1.to_sym]
357
- return Dataset.class_def(m, &impl)
358
- end
359
- end
360
- nil
361
- end
362
-
363
245
  def create_view(name)
364
246
  @db.create_view(name, self)
365
247
  end
@@ -386,4 +268,4 @@ module Sequel
386
268
  end
387
269
  end
388
270
  end
389
- end
271
+ end
@@ -0,0 +1,81 @@
1
+ require 'enumerator'
2
+
3
+ module Sequel
4
+ class Dataset
5
+ # Returns a paginated dataset. The resulting dataset also provides the
6
+ # total number of pages (Dataset#page_count) and the current page number
7
+ # (Dataset#current_page), as well as Dataset#prev_page and Dataset#next_page
8
+ # for implementing pagination controls.
9
+ def paginate(page_no, page_size)
10
+ raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
11
+ record_count = count
12
+ total_pages = (record_count / page_size.to_f).ceil
13
+ paginated = limit(page_size, (page_no - 1) * page_size)
14
+ paginated.extend(Pagination)
15
+ paginated.set_pagination_info(page_no, page_size, record_count)
16
+ paginated
17
+ end
18
+
19
+ def each_page(page_size)
20
+ raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
21
+ record_count = count
22
+ total_pages = (record_count / page_size.to_f).ceil
23
+
24
+ (1..total_pages).each do |page_no|
25
+ paginated = limit(page_size, (page_no - 1) * page_size)
26
+ paginated.extend(Pagination)
27
+ paginated.set_pagination_info(page_no, page_size, record_count)
28
+ yield paginated
29
+ end
30
+
31
+ self
32
+ end
33
+
34
+ module Pagination
35
+ attr_accessor :page_size, :page_count, :current_page, :pagination_record_count
36
+
37
+ # Sets the pagination info
38
+ def set_pagination_info(page_no, page_size, record_count)
39
+ @current_page = page_no
40
+ @page_size = page_size
41
+ @pagination_record_count = record_count
42
+ @page_count = (record_count / page_size.to_f).ceil
43
+ end
44
+
45
+ # Returns the previous page number or nil if the current page is the first
46
+ def prev_page
47
+ current_page > 1 ? (current_page - 1) : nil
48
+ end
49
+
50
+ # Returns the next page number or nil if the current page is the last page
51
+ def next_page
52
+ current_page < page_count ? (current_page + 1) : nil
53
+ end
54
+
55
+ # Returns the page range
56
+ def page_range
57
+ 1..page_count
58
+ end
59
+
60
+ # Returns the record range for the current page
61
+ def current_page_record_range
62
+ return (0..0) if @current_page > @page_count
63
+
64
+ a = 1 + (@current_page - 1) * @page_size
65
+ b = a + @page_size - 1
66
+ b = @pagination_record_count if b > @pagination_record_count
67
+ a..b
68
+ end
69
+
70
+ # Returns the number of records in the current page
71
+ def current_page_record_count
72
+ return 0 if @current_page > @page_count
73
+
74
+ a = 1 + (@current_page - 1) * @page_size
75
+ b = a + @page_size - 1
76
+ b = @pagination_record_count if b > @pagination_record_count
77
+ b - a + 1
78
+ end
79
+ end
80
+ end
81
+ end
@@ -4,7 +4,7 @@ class Sequel::Dataset
4
4
  # blocks, e.g.:
5
5
  #
6
6
  # DB[:items].filter {:price < 100}
7
- # DB[:items].filter {:category == 'ruby' && :date < 3.days.ago}
7
+ # DB[:items].filter {:category == 'ruby' && :date < Date.today - 7}
8
8
  #
9
9
  # Block filters can refer to literals, variables, constants, arguments,
10
10
  # instance variables or anything else in order to create parameterized
@@ -20,6 +20,7 @@ class Sequel::Dataset
20
20
  # sudo gem install parsetree
21
21
  # sudo gem install ruby2ruby
22
22
  module Sequelizer
23
+ private
23
24
  # Formats an comparison expression involving a left value and a right
24
25
  # value. Comparison expressions differ according to the class of the right
25
26
  # value. The stock implementation supports Range (inclusive and exclusive),
@@ -311,7 +312,7 @@ class Sequel::Dataset
311
312
 
312
313
  JOIN_AND = " AND ".freeze
313
314
  JOIN_COMMA = ", ".freeze
314
-
315
+
315
316
  def pt_expr(e, b, opts = {}) #:nodoc:
316
317
  case e[0]
317
318
  when :not # negation: !x, (x != y), (x !~ y)
@@ -346,7 +347,7 @@ end
346
347
 
347
348
  class Proc
348
349
  def to_sql(dataset, opts = {})
349
- dataset.pt_expr(to_sexp[2], self.binding, opts)
350
+ dataset.send(:pt_expr, to_sexp[2], self.binding, opts)
350
351
  end
351
352
  end
352
353
 
@@ -379,4 +380,4 @@ class Proc
379
380
  c = Class.new {define_method(:m, &block)}
380
381
  ParseTree.translate(c, :m)[2]
381
382
  end
382
- end
383
+ end
@@ -8,8 +8,8 @@ module Sequel
8
8
  # behavior.
9
9
  def quote_column_ref(name); name.to_s; end
10
10
 
11
- ALIASED_REGEXP = /^(.*)\s(.*)$/.freeze
12
- QUALIFIED_REGEXP = /^(.*)\.(.*)$/.freeze
11
+ ALIASED_REGEXP = /\A(.*)\s(.*)\z/.freeze
12
+ QUALIFIED_REGEXP = /\A(.*)\.(.*)\z/.freeze
13
13
 
14
14
  # Returns a qualified column name (including a table name) if the column
15
15
  # name isn't already qualified.
@@ -100,7 +100,7 @@ module Sequel
100
100
  # Time (as an SQL TIMESTAMP), Date (as an SQL DATE), Dataset (as a
101
101
  # subquery) and nil (AS NULL).
102
102
  #
103
- # dataset.literal("abc'def") #=> "'abc''def'"
103
+ # dataset.literal("abc'def\\") #=> "'abc''def\\\\'"
104
104
  # dataset.literal(:items__id) #=> "items.id"
105
105
  # dataset.literal([1, 2, 3]) => "(1, 2, 3)"
106
106
  # dataset.literal(DB[:items]) => "(SELECT * FROM items)"
@@ -111,7 +111,7 @@ module Sequel
111
111
  when LiteralString
112
112
  v
113
113
  when String
114
- "'#{v.gsub(/'/, "''")}'"
114
+ "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
115
115
  when Integer, Float
116
116
  v.to_s
117
117
  when BigDecimal
@@ -161,6 +161,7 @@ module Sequel
161
161
  end
162
162
  parenthesize ? "(#{fmt})" : fmt
163
163
  end
164
+ private :qualified_column_name, :column_list, :table_ref, :source_list, :expression_list
164
165
 
165
166
  # Returns a copy of the dataset with the source changed.
166
167
  def from(*source)
@@ -407,24 +408,18 @@ module Sequel
407
408
  }
408
409
 
409
410
  # Returns a join clause based on the specified join type and condition.
410
- def join_expr(type, table, expr)
411
- join_type = JOIN_TYPES[type || :inner]
412
- unless join_type
413
- raise Error::InvalidJoinType, "Invalid join type: #{type}"
414
- end
411
+ def join_expr(type, table, expr, options)
412
+ raise(Error::InvalidJoinType, "Invalid join type: #{type}") unless join_type = JOIN_TYPES[type || :inner]
415
413
 
416
- table = table.table_name if table.respond_to?(:table_name)
414
+ table_alias = options[:table_alias]
417
415
 
418
416
  join_conditions = {}
419
417
  expr.each do |k, v|
420
- k = qualified_column_name(k, table) if k.is_a?(Symbol)
418
+ k = qualified_column_name(k, table_alias || table) if k.is_a?(Symbol)
421
419
  v = qualified_column_name(v, @opts[:last_joined_table] || first_source) if v.is_a?(Symbol)
422
420
  join_conditions[k] = v
423
421
  end
424
- if table.is_a?(Dataset)
425
- table = "(#{table.sql}) t1"
426
- end
427
- " #{join_type} #{table} ON #{expression_list(join_conditions)}"
422
+ " #{join_type} #{table} #{"#{table_alias} " if table_alias}ON #{expression_list(join_conditions)}"
428
423
  end
429
424
 
430
425
  # Returns a joined dataset with the specified join type and condition.
@@ -432,9 +427,20 @@ module Sequel
432
427
  unless expr.is_a?(Hash)
433
428
  expr = {expr => :id}
434
429
  end
435
- clause = join_expr(type, table, expr)
436
- join = @opts[:join] ? @opts[:join] + clause : clause
437
- clone(:join => join, :last_joined_table => table)
430
+ options = {}
431
+
432
+ if Dataset === table
433
+ table = "(#{table.sql})"
434
+ table_alias_num = @opts[:num_dataset_joins] || 1
435
+ options[:table_alias] = "t#{table_alias_num}"
436
+ elsif table.respond_to?(:table_name)
437
+ table = table.table_name
438
+ end
439
+
440
+ clause = join_expr(type, table, expr, options)
441
+ opts = {:join => @opts[:join] ? @opts[:join] + clause : clause, :last_joined_table => options[:table_alias] || table}
442
+ opts[:num_dataset_joins] = table_alias_num + 1 if table_alias_num
443
+ clone(opts)
438
444
  end
439
445
 
440
446
  # Returns a LEFT OUTER joined dataset.
@@ -525,6 +531,11 @@ module Sequel
525
531
  end
526
532
  alias_method :sql, :select_sql
527
533
 
534
+ # Returns the SQL for formatting an insert statement with default values
535
+ def insert_default_values_sql
536
+ "INSERT INTO #{source_list(@opts[:from])} DEFAULT VALUES"
537
+ end
538
+
528
539
  # Formats an INSERT statement using the given values. If a hash is given,
529
540
  # the resulting statement includes column names. If no values are given,
530
541
  # the resulting statement includes a DEFAULT VALUES clause.
@@ -535,7 +546,7 @@ module Sequel
535
546
  # 'INSERT INTO items (a, b) VALUES (1, 2)'
536
547
  def insert_sql(*values)
537
548
  if values.empty?
538
- "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
549
+ insert_default_values_sql
539
550
  else
540
551
  values = values[0] if values.size == 1
541
552
 
@@ -543,32 +554,31 @@ module Sequel
543
554
  if @transform && (values.is_a?(Hash) || (values.is_a?(Array) && values.keys))
544
555
  values = transform_save(values)
545
556
  end
557
+ from = source_list(@opts[:from])
546
558
 
547
559
  case values
548
- when Sequel::Model
549
- insert_sql(values.values)
550
560
  when Array
551
561
  if values.empty?
552
- "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
553
- elsif values.keys
554
- fl = values.keys.map {|f| literal(f.is_a?(String) ? f.to_sym : f)}
555
- vl = values.values.map {|v| literal(v)}
556
- "INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
562
+ insert_default_values_sql
557
563
  else
558
- "INSERT INTO #{@opts[:from]} VALUES (#{literal(values)})"
564
+ "INSERT INTO #{from} VALUES (#{literal(values)})"
559
565
  end
560
566
  when Hash
561
567
  if values.empty?
562
- "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
568
+ insert_default_values_sql
563
569
  else
564
570
  fl, vl = [], []
565
571
  values.each {|k, v| fl << literal(k.is_a?(String) ? k.to_sym : k); vl << literal(v)}
566
- "INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
572
+ "INSERT INTO #{from} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
567
573
  end
568
574
  when Dataset
569
- "INSERT INTO #{@opts[:from]} #{literal(values)}"
575
+ "INSERT INTO #{from} #{literal(values)}"
570
576
  else
571
- "INSERT INTO #{@opts[:from]} VALUES (#{literal(values)})"
577
+ if values.respond_to?(:values)
578
+ insert_sql(values.values)
579
+ else
580
+ "INSERT INTO #{from} VALUES (#{literal(values)})"
581
+ end
572
582
  end
573
583
  end
574
584
  end
@@ -599,7 +609,7 @@ module Sequel
599
609
  raise Error::InvalidOperation, "A joined dataset cannot be updated"
600
610
  end
601
611
 
602
- sql = "UPDATE #{@opts[:from]} SET "
612
+ sql = "UPDATE #{source_list(@opts[:from])} SET "
603
613
  if block
604
614
  sql << block.to_sql(self, :comma_separated => true)
605
615
  else
@@ -639,7 +649,7 @@ module Sequel
639
649
  raise Error::InvalidOperation, "Joined datasets cannot be deleted from"
640
650
  end
641
651
 
642
- sql = "DELETE FROM #{opts[:from]}"
652
+ sql = "DELETE FROM #{source_list(opts[:from])}"
643
653
 
644
654
  if where = opts[:where]
645
655
  sql << " WHERE #{where}"