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