sequel_core 1.4.0 → 1.5.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 (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}"