sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -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_hash_groups select_map select_order_map
15
+ set single_record single_value sum to_csv to_hash to_hash_groups 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
  #
@@ -114,23 +124,23 @@ module Sequel
114
124
  # running additional queries inside the provided block. If you are
115
125
  # running queries inside the block, you should use +all+ instead of +each+
116
126
  # for the outer queries, or use a separate thread or shard inside +each+:
117
- def each(&block)
127
+ def each
118
128
  if @opts[:graph]
119
- graph_each(&block)
129
+ graph_each{|r| yield r}
120
130
  elsif row_proc = @row_proc
121
131
  fetch_rows(select_sql){|r| yield row_proc.call(r)}
122
132
  else
123
- fetch_rows(select_sql, &block)
133
+ fetch_rows(select_sql){|r| yield r}
124
134
  end
125
135
  self
126
136
  end
127
137
 
128
138
  # Returns true if no records exist in the dataset, false otherwise
129
139
  #
130
- # DB[:table].empty? # SELECT 1 FROM table LIMIT 1
140
+ # DB[:table].empty? # SELECT 1 AS one FROM table LIMIT 1
131
141
  # # => false
132
142
  def empty?
133
- get(1).nil?
143
+ get(Sequel::SQL::AliasedExpression.new(1, :one)).nil?
134
144
  end
135
145
 
136
146
  # Executes a select query and fetches records, yielding each record to the
@@ -194,12 +204,12 @@ module Sequel
194
204
  #
195
205
  # ds.get{sum(id)} # SELECT sum(id) FROM table LIMIT 1
196
206
  # # => 6
197
- def get(column=nil, &block)
198
- if column
199
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
200
- select(column).single_value
201
- else
207
+ def get(column=(no_arg=true; nil), &block)
208
+ if block
209
+ raise(Error, ARG_BLOCK_ERROR_MSG) unless no_arg
202
210
  select(&block).single_value
211
+ else
212
+ select(column).single_value
203
213
  end
204
214
  end
205
215
 
@@ -219,43 +229,47 @@ module Sequel
219
229
  # DB[:table].import([:x, :y], DB[:table2].select(:a, :b))
220
230
  # # INSERT INTO table (x, y) SELECT a, b FROM table2
221
231
  #
222
- # The method also accepts a :slice or :commit_every option that specifies
223
- # the number of records to insert per transaction. This is useful especially
224
- # when inserting a large number of records, e.g.:
225
- #
226
- # # this will commit every 50 records
227
- # dataset.import([:x, :y], [[1, 2], [3, 4], ...], :slice => 50)
232
+ # Options:
233
+ # :commit_every :: Open a new transaction for every given number of records.
234
+ # For example, if you provide a value of 50, will commit
235
+ # after every 50 records.
236
+ # :server :: Set the server/shard to use for the transaction and insert
237
+ # queries.
238
+ # :slice :: Same as :commit_every, :commit_every takes precedence.
228
239
  def import(columns, values, opts={})
229
240
  return @db.transaction{insert(columns, values)} if values.is_a?(Dataset)
230
241
 
231
242
  return if values.empty?
232
243
  raise(Error, IMPORT_ERROR_MSG) if columns.empty?
244
+ ds = opts[:server] ? server(opts[:server]) : self
233
245
 
234
246
  if slice_size = opts[:commit_every] || opts[:slice]
235
247
  offset = 0
236
- loop do
237
- @db.transaction(opts){multi_insert_sql(columns, values[offset, slice_size]).each{|st| execute_dui(st)}}
248
+ rows = []
249
+ while offset < values.length
250
+ rows << ds._import(columns, values[offset, slice_size], opts)
238
251
  offset += slice_size
239
- break if offset >= values.length
240
252
  end
253
+ rows.flatten
241
254
  else
242
- statements = multi_insert_sql(columns, values)
243
- @db.transaction{statements.each{|st| execute_dui(st)}}
255
+ ds._import(columns, values, opts)
244
256
  end
245
257
  end
246
-
258
+
247
259
  # Inserts values into the associated table. The returned value is generally
248
260
  # the value of the primary key for the inserted row, but that is adapter dependent.
249
261
  #
250
262
  # +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.
263
+ # no arguments or single empty hash :: Uses DEFAULT VALUES
264
+ # single hash :: Most common format, treats keys as columns an values as values
265
+ # single array :: Treats entries as values, with no columns
266
+ # two arrays :: Treats first array as columns, second array as values
267
+ # single Dataset :: Treats as an insert based on a selection from the dataset given,
268
+ # with no columns
269
+ # array and dataset :: Treats as an insert based on a selection from the dataset
270
+ # given, with the columns given by the array.
271
+ #
272
+ # Examples:
259
273
  #
260
274
  # DB[:items].insert
261
275
  # # INSERT INTO items DEFAULT VALUES
@@ -277,27 +291,35 @@ module Sequel
277
291
  #
278
292
  # DB[:items].insert([:a, :b], DB[:old_items])
279
293
  # # INSERT INTO items (a, b) SELECT * FROM old_items
280
- def insert(*values)
281
- execute_insert(insert_sql(*values))
294
+ def insert(*values, &block)
295
+ sql = insert_sql(*values)
296
+ if uses_returning?(:insert)
297
+ returning_fetch_rows(sql, &block)
298
+ else
299
+ execute_insert(sql)
300
+ end
282
301
  end
283
302
 
284
303
  # Inserts multiple values. If a block is given it is invoked for each
285
304
  # item in the given array before inserting it. See +multi_insert+ as
286
- # a possible faster version that inserts multiple records in one
287
- # SQL statement.
305
+ # a possibly faster version that may be able to insert multiple
306
+ # records in one SQL statement (if supported by the database).
307
+ # Returns an array of primary keys of inserted rows.
288
308
  #
289
309
  # DB[:table].insert_multiple([{:x=>1}, {:x=>2}])
310
+ # # => [4, 5]
290
311
  # # INSERT INTO table (x) VALUES (1)
291
312
  # # INSERT INTO table (x) VALUES (2)
292
313
  #
293
314
  # DB[:table].insert_multiple([{:x=>1}, {:x=>2}]){|row| row[:y] = row[:x] * 2}
315
+ # # => [6, 7]
294
316
  # # INSERT INTO table (x, y) VALUES (1, 2)
295
317
  # # INSERT INTO table (x, y) VALUES (2, 4)
296
318
  def insert_multiple(array, &block)
297
319
  if block
298
- array.each {|i| insert(block[i])}
320
+ array.map{|i| insert(block.call(i))}
299
321
  else
300
- array.each {|i| insert(i)}
322
+ array.map{|i| insert(i)}
301
323
  end
302
324
  end
303
325
 
@@ -310,7 +332,7 @@ module Sequel
310
332
  aggregate_dataset.get{max(column) - min(column)}
311
333
  end
312
334
 
313
- # Reverses the order and then runs first. Note that this
335
+ # Reverses the order and then runs #first with the given arguments and block. Note that this
314
336
  # will not necessarily give you the last record in the dataset,
315
337
  # unless you have an unambiguous order. If there is not
316
338
  # currently an order for this dataset, raises an +Error+.
@@ -334,10 +356,20 @@ module Sequel
334
356
  #
335
357
  # DB[:table].map{|r| r[:id] * 2} # SELECT * FROM table
336
358
  # # => [2, 4, 6, ...]
359
+ #
360
+ # You can also provide an array of column names:
361
+ #
362
+ # DB[:table].map([:id, :name]) # SELECT * FROM table
363
+ # # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
337
364
  def map(column=nil, &block)
338
365
  if column
339
366
  raise(Error, ARG_BLOCK_ERROR_MSG) if block
340
- super(){|r| r[column]}
367
+ return naked.map(column) if row_proc
368
+ if column.is_a?(Array)
369
+ super(){|r| r.values_at(*column)}
370
+ else
371
+ super(){|r| r[column]}
372
+ end
341
373
  else
342
374
  super(&block)
343
375
  end
@@ -370,7 +402,7 @@ module Sequel
370
402
  # otherwise some columns could be missed or set to null instead of to default
371
403
  # values.
372
404
  #
373
- # You can also use the :slice or :commit_every option that import accepts.
405
+ # This respects the same options as #import.
374
406
  def multi_insert(hashes, opts={})
375
407
  return if hashes.empty?
376
408
  columns = hashes.first.keys
@@ -389,33 +421,65 @@ module Sequel
389
421
  end
390
422
 
391
423
  # Returns a hash with key_column values as keys and value_column values as
392
- # values. Similar to to_hash, but only selects the two columns.
424
+ # values. Similar to to_hash, but only selects the columns given.
393
425
  #
394
426
  # DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
395
427
  # # => {1=>'a', 2=>'b', ...}
428
+ #
429
+ # You can also provide an array of column names for either the key_column,
430
+ # the value column, or both:
431
+ #
432
+ # DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
433
+ # # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
434
+ #
435
+ # When using this method, you must be sure that each expression has an alias
436
+ # that Sequel can determine. Usually you can do this by calling the #as method
437
+ # on the expression and providing an alias.
396
438
  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))
439
+ _select_hash(:to_hash, key_column, value_column)
398
440
  end
399
441
 
442
+ # Returns a hash with key_column values as keys and an array of value_column values.
443
+ # Similar to to_hash_groups, but only selects the columns given.
444
+ #
445
+ # DB[:table].select_hash(:name, :id) # SELECT id, name FROM table
446
+ # # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
447
+ #
448
+ # You can also provide an array of column names for either the key_column,
449
+ # the value column, or both:
450
+ #
451
+ # DB[:table].select_hash([:first, :middle], [:last, :id]) # SELECT * FROM table
452
+ # # {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
453
+ #
454
+ # When using this method, you must be sure that each expression has an alias
455
+ # that Sequel can determine. Usually you can do this by calling the #as method
456
+ # on the expression and providing an alias.
457
+ def select_hash_groups(key_column, value_column)
458
+ _select_hash(:to_hash_groups, key_column, value_column)
459
+ end
460
+
400
461
  # Selects the column given (either as an argument or as a block), and
401
462
  # returns an array of all values of that column in the dataset. If you
402
463
  # give a block argument that returns an array with multiple entries,
403
- # the contents of the resulting array are undefined.
464
+ # the contents of the resulting array are undefined. Raises an Error
465
+ # if called with both an argument and a block.
404
466
  #
405
467
  # DB[:table].select_map(:id) # SELECT id FROM table
406
468
  # # => [3, 5, 8, 1, ...]
407
469
  #
408
- # DB[:table].select_map{abs(id)} # SELECT abs(id) FROM table
409
- # # => [3, 5, 8, 1, ...]
470
+ # DB[:table].select_map{id * 2} # SELECT (id * 2) FROM table
471
+ # # => [6, 10, 16, 2, ...]
472
+ #
473
+ # You can also provide an array of column names:
474
+ #
475
+ # DB[:table].select_map([:id, :name]) # SELECT id, name FROM table
476
+ # # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
477
+ #
478
+ # If you provide an array of expressions, you must be sure that each entry
479
+ # in the array has an alias that Sequel can determine. Usually you can do this
480
+ # by calling the #as method on the expression and providing an alias.
410
481
  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}
482
+ _select_map(column, false, &block)
419
483
  end
420
484
 
421
485
  # The same as select_map, but in addition orders the array by the column.
@@ -423,19 +487,21 @@ module Sequel
423
487
  # DB[:table].select_order_map(:id) # SELECT id FROM table ORDER BY id
424
488
  # # => [1, 2, 3, 4, ...]
425
489
  #
426
- # DB[:table].select_order_map{abs(id)} # SELECT abs(id) FROM table ORDER BY abs(id)
427
- # # => [1, 2, 3, 4, ...]
490
+ # DB[:table].select_order_map{id * 2} # SELECT (id * 2) FROM table ORDER BY (id * 2)
491
+ # # => [2, 4, 6, 8, ...]
492
+ #
493
+ # You can also provide an array of column names:
494
+ #
495
+ # DB[:table].select_order_map([:id, :name]) # SELECT id, name FROM table ORDER BY id, name
496
+ # # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
497
+ #
498
+ # If you provide an array of expressions, you must be sure that each entry
499
+ # in the array has an alias that Sequel can determine. Usually you can do this
500
+ # by calling the #as method on the expression and providing an alias.
428
501
  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}
502
+ _select_map(column, true, &block)
437
503
  end
438
-
504
+
439
505
  # Alias for update, but not aliased directly so subclasses
440
506
  # don't have to override both methods.
441
507
  def set(*args)
@@ -499,11 +565,81 @@ module Sequel
499
565
  #
500
566
  # DB[:table].to_hash(:id) # SELECT * FROM table
501
567
  # # {1=>{:id=>1, :name=>'Jim'}, 2=>{:id=>2, :name=>'Bob'}, ...}
568
+ #
569
+ # You can also provide an array of column names for either the key_column,
570
+ # the value column, or both:
571
+ #
572
+ # DB[:table].to_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
573
+ # # {[1, 3]=>['Jim', 'bo'], [2, 4]=>['Bob', 'be'], ...}
574
+ #
575
+ # DB[:table].to_hash([:id, :name]) # SELECT * FROM table
576
+ # # {[1, 'Jim']=>{:id=>1, :name=>'Jim'}, [2, 'Bob'=>{:id=>2, :name=>'Bob'}, ...}
502
577
  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
578
+ h = {}
579
+ if value_column
580
+ return naked.to_hash(key_column, value_column) if row_proc
581
+ if value_column.is_a?(Array)
582
+ if key_column.is_a?(Array)
583
+ each{|r| h[r.values_at(*key_column)] = r.values_at(*value_column)}
584
+ else
585
+ each{|r| h[r[key_column]] = r.values_at(*value_column)}
586
+ end
587
+ else
588
+ if key_column.is_a?(Array)
589
+ each{|r| h[r.values_at(*key_column)] = r[value_column]}
590
+ else
591
+ each{|r| h[r[key_column]] = r[value_column]}
592
+ end
593
+ end
594
+ elsif key_column.is_a?(Array)
595
+ each{|r| h[r.values_at(*key_column)] = r}
596
+ else
597
+ each{|r| h[r[key_column]] = r}
506
598
  end
599
+ h
600
+ end
601
+
602
+ # Returns a hash with one column used as key and the values being an
603
+ # array of column values. If the value_column is not given or nil, uses
604
+ # the entire hash as the value.
605
+ #
606
+ # DB[:table].to_hash(:name, :id) # SELECT * FROM table
607
+ # # {'Jim'=>[1, 4, 16, ...], 'Bob'=>[2], ...}
608
+ #
609
+ # DB[:table].to_hash(:name) # SELECT * FROM table
610
+ # # {'Jim'=>[{:id=>1, :name=>'Jim'}, {:id=>4, :name=>'Jim'}, ...], 'Bob'=>[{:id=>2, :name=>'Bob'}], ...}
611
+ #
612
+ # You can also provide an array of column names for either the key_column,
613
+ # the value column, or both:
614
+ #
615
+ # DB[:table].to_hash([:first, :middle], [:last, :id]) # SELECT * FROM table
616
+ # # {['Jim', 'Bob']=>[['Smith', 1], ['Jackson', 4], ...], ...}
617
+ #
618
+ # DB[:table].to_hash([:first, :middle]) # SELECT * FROM table
619
+ # # {['Jim', 'Bob']=>[{:id=>1, :first=>'Jim', :middle=>'Bob', :last=>'Smith'}, ...], ...}
620
+ def to_hash_groups(key_column, value_column = nil)
621
+ h = {}
622
+ if value_column
623
+ return naked.to_hash_groups(key_column, value_column) if row_proc
624
+ if value_column.is_a?(Array)
625
+ if key_column.is_a?(Array)
626
+ each{|r| (h[r.values_at(*key_column)] ||= []) << r.values_at(*value_column)}
627
+ else
628
+ each{|r| (h[r[key_column]] ||= []) << r.values_at(*value_column)}
629
+ end
630
+ else
631
+ if key_column.is_a?(Array)
632
+ each{|r| (h[r.values_at(*key_column)] ||= []) << r[value_column]}
633
+ else
634
+ each{|r| (h[r[key_column]] ||= []) << r[value_column]}
635
+ end
636
+ end
637
+ elsif key_column.is_a?(Array)
638
+ each{|r| (h[r.values_at(*key_column)] ||= []) << r}
639
+ else
640
+ each{|r| (h[r[key_column]] ||= []) << r}
641
+ end
642
+ h
507
643
  end
508
644
 
509
645
  # Truncates the dataset. Returns nil.
@@ -522,14 +658,81 @@ module Sequel
522
658
  # DB[:table].update(:x=>nil) # UPDATE table SET x = NULL
523
659
  # # => 10
524
660
  #
525
- # DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1), :y = 0
661
+ # DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1), y = 0
526
662
  # # => 10
527
- def update(values={})
528
- execute_dui(update_sql(values))
663
+ def update(values={}, &block)
664
+ sql = update_sql(values)
665
+ if uses_returning?(:update)
666
+ returning_fetch_rows(sql, &block)
667
+ else
668
+ execute_dui(sql)
669
+ end
529
670
  end
530
671
 
672
+ # Execute the given SQL and return the number of rows deleted. This exists
673
+ # solely as an optimization, replacing with_sql(sql).delete. It's significantly
674
+ # faster as it does not require cloning the current dataset.
675
+ def with_sql_delete(sql)
676
+ execute_dui(sql)
677
+ end
678
+
679
+ protected
680
+
681
+ # Internals of #import. If primary key values are requested, use
682
+ # separate insert commands for each row. Otherwise, call #multi_insert_sql
683
+ # and execute each statement it gives separately.
684
+ def _import(columns, values, opts)
685
+ trans_opts = opts.merge(:server=>@opts[:server])
686
+ if opts[:return] == :primary_key
687
+ @db.transaction(trans_opts){values.map{|v| insert(columns, v)}}
688
+ else
689
+ stmts = multi_insert_sql(columns, values)
690
+ @db.transaction(trans_opts){stmts.each{|st| execute_dui(st)}}
691
+ end
692
+ end
693
+
694
+ # Return an array of arrays of values given by the symbols in ret_cols.
695
+ def _select_map_multiple(ret_cols)
696
+ map{|r| r.values_at(*ret_cols)}
697
+ end
698
+
699
+ # Returns an array of the first value in each row.
700
+ def _select_map_single
701
+ map{|r| r.values.first}
702
+ end
703
+
531
704
  private
532
705
 
706
+ # Internals of +select_hash+ and +select_hash_groups+
707
+ def _select_hash(meth, key_column, value_column)
708
+ if key_column.is_a?(Array)
709
+ if value_column.is_a?(Array)
710
+ select(*(key_column + value_column)).send(meth, key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
711
+ else
712
+ select(*(key_column + [value_column])).send(meth, key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
713
+ end
714
+ elsif value_column.is_a?(Array)
715
+ select(key_column, *value_column).send(meth, hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
716
+ else
717
+ select(key_column, value_column).send(meth, hash_key_symbol(key_column), hash_key_symbol(value_column))
718
+ end
719
+ end
720
+
721
+ # Internals of +select_map+ and +select_order_map+
722
+ def _select_map(column, order, &block)
723
+ ds = naked.ungraphed
724
+ columns = Array(column)
725
+ virtual_row_columns(columns, block)
726
+ select_cols = order ? columns.map{|c| c.is_a?(SQL::OrderedExpression) ? c.expression : c} : columns
727
+ ds = ds.select(*select_cols)
728
+ ds = ds.order(*columns.map{|c| unaliased_identifier(c)}) if order
729
+ if column.is_a?(Array) || (columns.length > 1)
730
+ ds._select_map_multiple(select_cols.map{|c| hash_key_symbol(c)})
731
+ else
732
+ ds._select_map_single
733
+ end
734
+ end
735
+
533
736
  # Set the server to use to :default unless it is already set in the passed opts
534
737
  def default_server_opts(opts)
535
738
  {:server=>@opts[:server] || :default}.merge(opts)
@@ -561,9 +764,21 @@ module Sequel
561
764
  # specifying the symbol that is likely to be used as the hash key
562
765
  # for the column when records are returned.
563
766
  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
767
+ case s
768
+ when Symbol
769
+ _, c, a = split_symbol(s)
770
+ (a || c).to_sym
771
+ when SQL::Identifier
772
+ hash_key_symbol(s.value)
773
+ when SQL::QualifiedIdentifier
774
+ hash_key_symbol(s.column)
775
+ when SQL::AliasedExpression
776
+ hash_key_symbol(s.aliaz)
777
+ when String
778
+ s.to_sym
779
+ else
780
+ raise(Error, "#{s.inspect} is not supported, should be a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or SQL::AliasedExpression")
781
+ end
567
782
  end
568
783
 
569
784
  # Modify the identifier returned from the database based on the
@@ -580,6 +795,20 @@ module Sequel
580
795
  def post_load(all_records)
581
796
  end
582
797
 
798
+ # Called by insert/update/delete when returning is used.
799
+ # Yields each row as a plain hash to the block if one is given, or returns
800
+ # an array of plain hashes for all rows if a block is not given
801
+ def returning_fetch_rows(sql, &block)
802
+ if block
803
+ default_server.fetch_rows(sql, &block)
804
+ nil
805
+ else
806
+ rows = []
807
+ default_server.fetch_rows(sql){|r| rows << r}
808
+ rows
809
+ end
810
+ end
811
+
583
812
  # Return the unaliased part of the identifier. Handles both
584
813
  # implicit aliases in symbols, as well as SQL::AliasedExpression
585
814
  # objects. Other objects are returned as is.
@@ -590,6 +819,14 @@ module Sequel
590
819
  c_table ? SQL::QualifiedIdentifier.new(c_table, column.to_sym) : column.to_sym
591
820
  when SQL::AliasedExpression
592
821
  c.expression
822
+ when SQL::OrderedExpression
823
+ expr = c.expression
824
+ if expr.is_a?(Symbol)
825
+ expr = unaliased_identifier(expr)
826
+ SQL::OrderedExpression.new(unaliased_identifier(c.expression), c.descending, :nulls=>c.nulls)
827
+ else
828
+ c
829
+ end
593
830
  else
594
831
  c
595
832
  end