sequel 3.21.0 → 3.29.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 (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -1,22 +1,27 @@
1
1
  module Sequel
2
2
  class Dataset
3
3
  # ---------------------
4
- # :section: Methods that execute code on the database
4
+ # :section: 2 - Methods that execute code on the database
5
5
  # These methods all execute the dataset's SQL on the database.
6
6
  # They don't return modified datasets, so if used in a method chain
7
7
  # they should be the last method called.
8
8
  # ---------------------
9
9
 
10
10
  # Action methods defined by Sequel that execute code on the database.
11
- ACTION_METHODS = %w'<< [] []= all avg count columns columns! delete each
12
- empty? fetch_rows first get import insert insert_multiple interval last
13
- map max min multi_insert range select_hash select_map select_order_map
14
- set single_record single_value sum to_csv to_hash truncate update'.map{|x| x.to_sym}
11
+ ACTION_METHODS = (<<-METHS).split.map{|x| x.to_sym}
12
+ << [] []= all avg count columns columns! delete each
13
+ empty? fetch_rows first get import insert insert_multiple interval last
14
+ map max min multi_insert range select_hash select_map select_order_map
15
+ set single_record single_value sum to_csv to_hash truncate update
16
+ METHS
15
17
 
16
- # Alias for insert, but not aliased directly so subclasses
17
- # don't have to override both methods.
18
- def <<(*args)
19
- insert(*args)
18
+ # Inserts the given argument into the database. Returns self so it
19
+ # can be used safely when chaining:
20
+ #
21
+ # DB[:items] << {:id=>0, :name=>'Zero'} << DB[:old_items].select(:id, name)
22
+ def <<(arg)
23
+ insert(arg)
24
+ self
20
25
  end
21
26
 
22
27
  # Returns the first record matching the conditions. Examples:
@@ -72,7 +77,7 @@ module Sequel
72
77
  # # => [:id, :name]
73
78
  def columns
74
79
  return @columns if @columns
75
- ds = unfiltered.unordered.clone(:distinct => nil, :limit => 1)
80
+ ds = unfiltered.unordered.clone(:distinct => nil, :limit => 1, :offset=>nil)
76
81
  ds.each{break}
77
82
  @columns = ds.instance_variable_get(:@columns)
78
83
  @columns || []
@@ -101,10 +106,15 @@ module Sequel
101
106
  #
102
107
  # DB[:table].delete # DELETE * FROM table
103
108
  # # => 3
104
- def delete
105
- execute_dui(delete_sql)
109
+ def delete(&block)
110
+ sql = delete_sql
111
+ if uses_returning?(:delete)
112
+ returning_fetch_rows(sql, &block)
113
+ else
114
+ execute_dui(sql)
115
+ end
106
116
  end
107
-
117
+
108
118
  # Iterates over the records in the dataset as they are yielded from the
109
119
  # database adapter, and returns self.
110
120
  #
@@ -248,14 +258,16 @@ module Sequel
248
258
  # the value of the primary key for the inserted row, but that is adapter dependent.
249
259
  #
250
260
  # +insert+ handles a number of different argument formats:
251
- # * No arguments, single empty hash - Uses DEFAULT VALUES
252
- # * Single hash - Most common format, treats keys as columns an values as values
253
- # * Single array - Treats entries as values, with no columns
254
- # * Two arrays - Treats first array as columns, second array as values
255
- # * Single Dataset - Treats as an insert based on a selection from the dataset given,
256
- # with no columns
257
- # * Array and dataset - Treats as an insert based on a selection from the dataset
258
- # given, with the columns given by the array.
261
+ # no arguments or single empty hash :: Uses DEFAULT VALUES
262
+ # single hash :: Most common format, treats keys as columns an values as values
263
+ # single array :: Treats entries as values, with no columns
264
+ # two arrays :: Treats first array as columns, second array as values
265
+ # single Dataset :: Treats as an insert based on a selection from the dataset given,
266
+ # with no columns
267
+ # array and dataset :: Treats as an insert based on a selection from the dataset
268
+ # given, with the columns given by the array.
269
+ #
270
+ # Examples:
259
271
  #
260
272
  # DB[:items].insert
261
273
  # # INSERT INTO items DEFAULT VALUES
@@ -277,8 +289,13 @@ module Sequel
277
289
  #
278
290
  # DB[:items].insert([:a, :b], DB[:old_items])
279
291
  # # INSERT INTO items (a, b) SELECT * FROM old_items
280
- def insert(*values)
281
- execute_insert(insert_sql(*values))
292
+ def insert(*values, &block)
293
+ sql = insert_sql(*values)
294
+ if uses_returning?(:insert)
295
+ returning_fetch_rows(sql, &block)
296
+ else
297
+ execute_insert(sql)
298
+ end
282
299
  end
283
300
 
284
301
  # Inserts multiple values. If a block is given it is invoked for each
@@ -310,7 +327,7 @@ module Sequel
310
327
  aggregate_dataset.get{max(column) - min(column)}
311
328
  end
312
329
 
313
- # Reverses the order and then runs first. Note that this
330
+ # Reverses the order and then runs #first with the given arguments and block. Note that this
314
331
  # will not necessarily give you the last record in the dataset,
315
332
  # unless you have an unambiguous order. If there is not
316
333
  # currently an order for this dataset, raises an +Error+.
@@ -334,10 +351,20 @@ module Sequel
334
351
  #
335
352
  # DB[:table].map{|r| r[:id] * 2} # SELECT * FROM table
336
353
  # # => [2, 4, 6, ...]
354
+ #
355
+ # You can also provide an array of column names:
356
+ #
357
+ # DB[:table].map([:id, :name]) # SELECT * FROM table
358
+ # # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
337
359
  def map(column=nil, &block)
338
360
  if column
339
361
  raise(Error, ARG_BLOCK_ERROR_MSG) if block
340
- super(){|r| r[column]}
362
+ return naked.map(column) if row_proc
363
+ if column.is_a?(Array)
364
+ super(){|r| r.values_at(*column)}
365
+ else
366
+ super(){|r| r[column]}
367
+ end
341
368
  else
342
369
  super(&block)
343
370
  end
@@ -393,49 +420,63 @@ module Sequel
393
420
  #
394
421
  # DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
395
422
  # # => {1=>'a', 2=>'b', ...}
423
+ #
424
+ # You can also provide an array of column names for either the key_column,
425
+ # the value column, or both:
426
+ #
427
+ # DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
428
+ # # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
396
429
  def select_hash(key_column, value_column)
397
- select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
430
+ if key_column.is_a?(Array)
431
+ if value_column.is_a?(Array)
432
+ select(*(key_column + value_column)).to_hash(key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
433
+ else
434
+ select(*(key_column + [value_column])).to_hash(key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
435
+ end
436
+ elsif value_column.is_a?(Array)
437
+ select(key_column, *value_column).to_hash(hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
438
+ else
439
+ select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
440
+ end
398
441
  end
399
442
 
400
443
  # Selects the column given (either as an argument or as a block), and
401
444
  # returns an array of all values of that column in the dataset. If you
402
445
  # give a block argument that returns an array with multiple entries,
403
- # the contents of the resulting array are undefined.
446
+ # the contents of the resulting array are undefined. Raises an Error
447
+ # if called with both an argument and a block.
404
448
  #
405
449
  # DB[:table].select_map(:id) # SELECT id FROM table
406
450
  # # => [3, 5, 8, 1, ...]
407
451
  #
408
- # DB[:table].select_map{abs(id)} # SELECT abs(id) FROM table
409
- # # => [3, 5, 8, 1, ...]
452
+ # DB[:table].select_map{id * 2} # SELECT (id * 2) FROM table
453
+ # # => [6, 10, 16, 2, ...]
454
+ #
455
+ # You can also provide an array of column names:
456
+ #
457
+ # DB[:table].select_map([:id, :name]) # SELECT id, name FROM table
458
+ # # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
410
459
  def select_map(column=nil, &block)
411
- ds = naked.ungraphed
412
- ds = if column
413
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
414
- ds.select(column)
415
- else
416
- ds.select(&block)
417
- end
418
- ds.map{|r| r.values.first}
460
+ _select_map(column, false, &block)
419
461
  end
462
+
420
463
 
421
464
  # The same as select_map, but in addition orders the array by the column.
422
465
  #
423
466
  # DB[:table].select_order_map(:id) # SELECT id FROM table ORDER BY id
424
467
  # # => [1, 2, 3, 4, ...]
425
468
  #
426
- # DB[:table].select_order_map{abs(id)} # SELECT abs(id) FROM table ORDER BY abs(id)
427
- # # => [1, 2, 3, 4, ...]
469
+ # DB[:table].select_order_map{id * 2} # SELECT (id * 2) FROM table ORDER BY (id * 2)
470
+ # # => [2, 4, 6, 8, ...]
471
+ #
472
+ # You can also provide an array of column names:
473
+ #
474
+ # DB[:table].select_order_map([:id, :name]) # SELECT id, name FROM table ORDER BY id, name
475
+ # # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
428
476
  def select_order_map(column=nil, &block)
429
- ds = naked.ungraphed
430
- ds = if column
431
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
432
- ds.select(column).order(unaliased_identifier(column))
433
- else
434
- ds.select(&block).order(&block)
435
- end
436
- ds.map{|r| r.values.first}
477
+ _select_map(column, true, &block)
437
478
  end
438
-
479
+
439
480
  # Alias for update, but not aliased directly so subclasses
440
481
  # don't have to override both methods.
441
482
  def set(*args)
@@ -499,11 +540,38 @@ module Sequel
499
540
  #
500
541
  # DB[:table].to_hash(:id) # SELECT * FROM table
501
542
  # # {1=>{:id=>1, :name=>'Jim'}, 2=>{:id=>2, :name=>'Bob'}, ...}
543
+ #
544
+ # You can also provide an array of column names for either the key_column,
545
+ # the value column, or both:
546
+ #
547
+ # DB[:table].to_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
548
+ # # {[1, 3]=>['Jim', 'bo'], [2, 4]=>['Bob', 'be'], ...}
549
+ #
550
+ # DB[:table].to_hash([:id, :name]) # SELECT * FROM table
551
+ # # {[1, 'Jim']=>{:id=>1, :name=>'Jim'}, [2, 'Bob'=>{:id=>2, :name=>'Bob'}, ...}
502
552
  def to_hash(key_column, value_column = nil)
503
- inject({}) do |m, r|
504
- m[r[key_column]] = value_column ? r[value_column] : r
505
- m
553
+ h = {}
554
+ if value_column
555
+ return naked.to_hash(key_column, value_column) if row_proc
556
+ if value_column.is_a?(Array)
557
+ if key_column.is_a?(Array)
558
+ each{|r| h[r.values_at(*key_column)] = r.values_at(*value_column)}
559
+ else
560
+ each{|r| h[r[key_column]] = r.values_at(*value_column)}
561
+ end
562
+ else
563
+ if key_column.is_a?(Array)
564
+ each{|r| h[r.values_at(*key_column)] = r[value_column]}
565
+ else
566
+ each{|r| h[r[key_column]] = r[value_column]}
567
+ end
568
+ end
569
+ elsif key_column.is_a?(Array)
570
+ each{|r| h[r.values_at(*key_column)] = r}
571
+ else
572
+ each{|r| h[r[key_column]] = r}
506
573
  end
574
+ h
507
575
  end
508
576
 
509
577
  # Truncates the dataset. Returns nil.
@@ -522,14 +590,51 @@ module Sequel
522
590
  # DB[:table].update(:x=>nil) # UPDATE table SET x = NULL
523
591
  # # => 10
524
592
  #
525
- # DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1), :y = 0
593
+ # DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1), y = 0
526
594
  # # => 10
527
- def update(values={})
528
- execute_dui(update_sql(values))
595
+ def update(values={}, &block)
596
+ sql = update_sql(values)
597
+ if uses_returning?(:update)
598
+ returning_fetch_rows(sql, &block)
599
+ else
600
+ execute_dui(sql)
601
+ end
529
602
  end
530
603
 
604
+ protected
605
+
606
+ # Return an array of arrays of values given by the symbols in ret_cols.
607
+ def _select_map_multiple(ret_cols)
608
+ map{|r| r.values_at(*ret_cols)}
609
+ end
610
+
611
+ # Returns an array of the first value in each row.
612
+ def _select_map_single
613
+ map{|r| r.values.first}
614
+ end
615
+
531
616
  private
532
617
 
618
+ # Internals of +select_map+ and +select_order_map+
619
+ def _select_map(column, order, &block)
620
+ ds = naked.ungraphed
621
+ if column
622
+ raise(Error, ARG_BLOCK_ERROR_MSG) if block
623
+ columns = Array(column)
624
+ select_cols = order ? columns.map{|c| c.is_a?(SQL::OrderedExpression) ? c.expression : c} : columns
625
+ ds = ds.select(*select_cols)
626
+ ds = ds.order(*columns.map{|c| unaliased_identifier(c)}) if order
627
+ else
628
+ ds = ds.select(&block)
629
+ ds = ds.order(&block) if order
630
+ end
631
+ if column.is_a?(Array)
632
+ ds._select_map_multiple(select_cols.map{|c| hash_key_symbol(c)})
633
+ else
634
+ ds._select_map_single
635
+ end
636
+ end
637
+
533
638
  # Set the server to use to :default unless it is already set in the passed opts
534
639
  def default_server_opts(opts)
535
640
  {:server=>@opts[:server] || :default}.merge(opts)
@@ -561,9 +666,19 @@ module Sequel
561
666
  # specifying the symbol that is likely to be used as the hash key
562
667
  # for the column when records are returned.
563
668
  def hash_key_symbol(s)
564
- raise(Error, "#{s.inspect} is not a symbol") unless s.is_a?(Symbol)
565
- _, c, a = split_symbol(s)
566
- (a || c).to_sym
669
+ case s
670
+ when Symbol
671
+ _, c, a = split_symbol(s)
672
+ (a || c).to_sym
673
+ when SQL::Identifier
674
+ hash_key_symbol(s.value)
675
+ when SQL::QualifiedIdentifier
676
+ hash_key_symbol(s.column)
677
+ when SQL::AliasedExpression
678
+ hash_key_symbol(s.aliaz)
679
+ else
680
+ raise(Error, "#{s.inspect} is not supported, should be a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or SQL::AliasedExpression")
681
+ end
567
682
  end
568
683
 
569
684
  # Modify the identifier returned from the database based on the
@@ -580,6 +695,20 @@ module Sequel
580
695
  def post_load(all_records)
581
696
  end
582
697
 
698
+ # Called by insert/update/delete when returning is used.
699
+ # Yields each row as a plain hash to the block if one is given, or returns
700
+ # an array of plain hashes for all rows if a block is not given
701
+ def returning_fetch_rows(sql, &block)
702
+ if block
703
+ default_server.fetch_rows(sql, &block)
704
+ nil
705
+ else
706
+ rows = []
707
+ default_server.fetch_rows(sql){|r| rows << r}
708
+ rows
709
+ end
710
+ end
711
+
583
712
  # Return the unaliased part of the identifier. Handles both
584
713
  # implicit aliases in symbols, as well as SQL::AliasedExpression
585
714
  # objects. Other objects are returned as is.
@@ -590,6 +719,14 @@ module Sequel
590
719
  c_table ? SQL::QualifiedIdentifier.new(c_table, column.to_sym) : column.to_sym
591
720
  when SQL::AliasedExpression
592
721
  c.expression
722
+ when SQL::OrderedExpression
723
+ expr = c.expression
724
+ if expr.is_a?(Symbol)
725
+ expr = unaliased_identifier(expr)
726
+ SQL::OrderedExpression.new(unaliased_identifier(c.expression), c.descending, :nulls=>c.nulls)
727
+ else
728
+ c
729
+ end
593
730
  else
594
731
  c
595
732
  end
@@ -1,17 +1,20 @@
1
1
  module Sequel
2
2
  class Dataset
3
3
  # ---------------------
4
- # :section: Methods that describe what the dataset supports
4
+ # :section: 4 - Methods that describe what the dataset supports
5
5
  # These methods all return booleans, with most describing whether or not the
6
6
  # dataset supports a feature.
7
7
  # ---------------------
8
8
 
9
- # Method used to check if WITH is supported
10
- WITH_SUPPORTED=:select_with_sql
11
-
12
9
  # Whether this dataset quotes identifiers.
13
10
  def quote_identifiers?
14
- @quote_identifiers
11
+ if defined?(@quote_identifiers)
12
+ @quote_identifiers
13
+ elsif db.respond_to?(:quote_identifiers?)
14
+ @quote_identifiers = db.quote_identifiers?
15
+ else
16
+ @quote_identifiers = false
17
+ end
15
18
  end
16
19
 
17
20
  # Whether this dataset will provide accurate number of rows matched for
@@ -21,22 +24,49 @@ module Sequel
21
24
  true
22
25
  end
23
26
 
27
+ # Whether you must use a column alias list for recursive CTEs (false by
28
+ # default).
29
+ def recursive_cte_requires_column_aliases?
30
+ false
31
+ end
32
+
24
33
  # Whether the dataset requires SQL standard datetimes (false by default,
25
34
  # as most allow strings with ISO 8601 format).
26
35
  def requires_sql_standard_datetimes?
27
36
  false
28
37
  end
29
38
 
39
+ # Whether type specifiers are required for prepared statement/bound
40
+ # variable argument placeholders (i.e. :bv__integer)
41
+ def requires_placeholder_type_specifiers?
42
+ false
43
+ end
44
+
30
45
  # Whether the dataset supports common table expressions (the WITH clause).
31
- def supports_cte?
32
- select_clause_methods.include?(WITH_SUPPORTED)
46
+ # If given, +type+ can be :select, :insert, :update, or :delete, in which case it
47
+ # determines whether WITH is supported for the respective statement type.
48
+ def supports_cte?(type=:select)
49
+ send(:"#{type}_clause_methods").include?(:"#{type}_with_sql")
50
+ end
51
+
52
+ # Whether the dataset supports common table expressions (the WITH clause)
53
+ # in subqueries. If false, applies the WITH clause to the main query, which can cause issues
54
+ # if multiple WITH clauses use the same name.
55
+ def supports_cte_in_subqueries?
56
+ false
33
57
  end
34
58
 
35
- # Whether the dataset supports the DISTINCT ON clause, false by default.
59
+ # Whether the dataset supports or can emulate the DISTINCT ON clause, false by default.
36
60
  def supports_distinct_on?
37
61
  false
38
62
  end
39
63
 
64
+ # Whether this dataset supports the +insert_select+ method for returning all columns values
65
+ # directly from an insert query.
66
+ def supports_insert_select?
67
+ supports_returning?(:insert)
68
+ end
69
+
40
70
  # Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
41
71
  def supports_intersect_except?
42
72
  true
@@ -67,7 +97,24 @@ module Sequel
67
97
  def supports_multiple_column_in?
68
98
  true
69
99
  end
100
+
101
+ # Whether the dataset supports or can fully emulate the DISTINCT ON clause,
102
+ # including respecting the ORDER BY clause, false by default
103
+ def supports_ordered_distinct_on?
104
+ supports_distinct_on?
105
+ end
70
106
 
107
+ # Whether the RETURNING clause is supported for the given type of query.
108
+ # +type+ can be :insert, :update, or :delete.
109
+ def supports_returning?(type)
110
+ send(:"#{type}_clause_methods").include?(:"#{type}_returning_sql")
111
+ end
112
+
113
+ # Whether the database supports SELECT *, column FROM table
114
+ def supports_select_all_and_column?
115
+ true
116
+ end
117
+
71
118
  # Whether the dataset supports timezones in literal timestamps
72
119
  def supports_timestamp_timezones?
73
120
  false
@@ -82,5 +129,31 @@ module Sequel
82
129
  def supports_window_functions?
83
130
  false
84
131
  end
132
+
133
+ # Whether the dataset supports WHERE TRUE (or WHERE 1 for databases that
134
+ # that use 1 for true).
135
+ def supports_where_true?
136
+ true
137
+ end
138
+
139
+ private
140
+
141
+ # Whether insert(nil) or insert({}) must be emulated by
142
+ # using at least one value, false by default.
143
+ def insert_supports_empty_values?
144
+ true
145
+ end
146
+
147
+ # Whether using an offset returns an extra row number column that should be
148
+ # eliminated, false by default.
149
+ def offset_returns_row_number_column?
150
+ false
151
+ end
152
+
153
+ # Whether the RETURNING clause is used for the given dataset.
154
+ # +type+ can be :insert, :update, or :delete.
155
+ def uses_returning?(type)
156
+ opts[:returning] && !@opts[:sql] && supports_returning?(type)
157
+ end
85
158
  end
86
159
  end
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  class Dataset
3
3
  # ---------------------
4
- # :section: Methods related to dataset graphing
4
+ # :section: 5 - Methods related to dataset graphing
5
5
  # Dataset graphing changes the dataset to yield hashes where keys are table
6
6
  # name symbols and values are hashes representing the columns related to
7
7
  # that table. All of these methods return modified copies of the receiver.
@@ -113,6 +113,8 @@ module Sequel
113
113
  add_table = options[:select] == false ? false : true
114
114
  # Whether to add the columns to the list of column aliases
115
115
  add_columns = !ds.opts.include?(:graph_aliases)
116
+ # columns to select
117
+ select = (opts[:select] || []).dup
116
118
 
117
119
  # Setup the initial graph data structure if it doesn't exist
118
120
  unless graph = opts[:graph]
@@ -130,7 +132,6 @@ module Sequel
130
132
  # aliased, but are not included if set_graph_aliases
131
133
  # has been used.
132
134
  if add_columns
133
- select = opts[:select] = []
134
135
  columns.each do |column|
135
136
  column_aliases[column] = [master, column]
136
137
  select.push(SQL::QualifiedIdentifier.new(master, column))
@@ -147,7 +148,6 @@ module Sequel
147
148
 
148
149
  # Add the columns to the selection unless we are ignoring them
149
150
  if add_table && add_columns
150
- select = opts[:select]
151
151
  column_aliases = graph[:column_aliases]
152
152
  ca_num = graph[:column_alias_num]
153
153
  # Which columns to add to the result set
@@ -165,15 +165,16 @@ module Sequel
165
165
  column_alias = :"#{column_alias}_#{column_alias_num}"
166
166
  ca_num[column_alias] += 1
167
167
  end
168
- [column_alias, SQL::QualifiedIdentifier.new(table_alias, column).as(column_alias)]
168
+ [column_alias, SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table_alias, column), column_alias)]
169
169
  else
170
- [column, SQL::QualifiedIdentifier.new(table_alias, column)]
170
+ ident = SQL::QualifiedIdentifier.new(table_alias, column)
171
+ [column, ident]
171
172
  end
172
173
  column_aliases[col_alias] = [table_alias, column]
173
174
  select.push(identifier)
174
175
  end
175
176
  end
176
- ds
177
+ ds.select(*select)
177
178
  end
178
179
 
179
180
  # This allows you to manually specify the graph aliases to use
@@ -225,7 +226,7 @@ module Sequel
225
226
  column ||= col_alias
226
227
  gas[col_alias] = [table, column]
227
228
  identifier = value || SQL::QualifiedIdentifier.new(table, column)
228
- identifier = SQL::AliasedExpression.new(identifier, col_alias) if value or column != col_alias
229
+ identifier = SQL::AliasedExpression.new(identifier, col_alias) if value || column != col_alias
229
230
  identifier
230
231
  end
231
232
  [identifiers, gas]