sequel 4.36.0 → 5.61.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 (760) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG +548 -5749
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +265 -159
  5. data/bin/sequel +34 -12
  6. data/doc/advanced_associations.rdoc +228 -187
  7. data/doc/association_basics.rdoc +281 -291
  8. data/doc/bin_sequel.rdoc +5 -3
  9. data/doc/cheat_sheet.rdoc +86 -51
  10. data/doc/code_order.rdoc +25 -19
  11. data/doc/core_extensions.rdoc +104 -63
  12. data/doc/dataset_basics.rdoc +12 -21
  13. data/doc/dataset_filtering.rdoc +99 -86
  14. data/doc/extensions.rdoc +3 -10
  15. data/doc/fork_safety.rdoc +84 -0
  16. data/doc/mass_assignment.rdoc +74 -31
  17. data/doc/migration.rdoc +59 -51
  18. data/doc/model_dataset_method_design.rdoc +129 -0
  19. data/doc/model_hooks.rdoc +15 -25
  20. data/doc/model_plugins.rdoc +12 -12
  21. data/doc/mssql_stored_procedures.rdoc +3 -3
  22. data/doc/object_model.rdoc +58 -68
  23. data/doc/opening_databases.rdoc +85 -95
  24. data/doc/postgresql.rdoc +263 -38
  25. data/doc/prepared_statements.rdoc +29 -24
  26. data/doc/querying.rdoc +189 -167
  27. data/doc/reflection.rdoc +5 -6
  28. data/doc/release_notes/5.0.0.txt +159 -0
  29. data/doc/release_notes/5.1.0.txt +31 -0
  30. data/doc/release_notes/5.10.0.txt +84 -0
  31. data/doc/release_notes/5.11.0.txt +83 -0
  32. data/doc/release_notes/5.12.0.txt +141 -0
  33. data/doc/release_notes/5.13.0.txt +27 -0
  34. data/doc/release_notes/5.14.0.txt +63 -0
  35. data/doc/release_notes/5.15.0.txt +39 -0
  36. data/doc/release_notes/5.16.0.txt +110 -0
  37. data/doc/release_notes/5.17.0.txt +31 -0
  38. data/doc/release_notes/5.18.0.txt +69 -0
  39. data/doc/release_notes/5.19.0.txt +28 -0
  40. data/doc/release_notes/5.2.0.txt +33 -0
  41. data/doc/release_notes/5.20.0.txt +89 -0
  42. data/doc/release_notes/5.21.0.txt +87 -0
  43. data/doc/release_notes/5.22.0.txt +48 -0
  44. data/doc/release_notes/5.23.0.txt +56 -0
  45. data/doc/release_notes/5.24.0.txt +56 -0
  46. data/doc/release_notes/5.25.0.txt +32 -0
  47. data/doc/release_notes/5.26.0.txt +35 -0
  48. data/doc/release_notes/5.27.0.txt +21 -0
  49. data/doc/release_notes/5.28.0.txt +16 -0
  50. data/doc/release_notes/5.29.0.txt +22 -0
  51. data/doc/release_notes/5.3.0.txt +121 -0
  52. data/doc/release_notes/5.30.0.txt +20 -0
  53. data/doc/release_notes/5.31.0.txt +148 -0
  54. data/doc/release_notes/5.32.0.txt +46 -0
  55. data/doc/release_notes/5.33.0.txt +24 -0
  56. data/doc/release_notes/5.34.0.txt +40 -0
  57. data/doc/release_notes/5.35.0.txt +56 -0
  58. data/doc/release_notes/5.36.0.txt +60 -0
  59. data/doc/release_notes/5.37.0.txt +30 -0
  60. data/doc/release_notes/5.38.0.txt +28 -0
  61. data/doc/release_notes/5.39.0.txt +19 -0
  62. data/doc/release_notes/5.4.0.txt +80 -0
  63. data/doc/release_notes/5.40.0.txt +40 -0
  64. data/doc/release_notes/5.41.0.txt +25 -0
  65. data/doc/release_notes/5.42.0.txt +136 -0
  66. data/doc/release_notes/5.43.0.txt +98 -0
  67. data/doc/release_notes/5.44.0.txt +32 -0
  68. data/doc/release_notes/5.45.0.txt +34 -0
  69. data/doc/release_notes/5.46.0.txt +87 -0
  70. data/doc/release_notes/5.47.0.txt +59 -0
  71. data/doc/release_notes/5.48.0.txt +14 -0
  72. data/doc/release_notes/5.49.0.txt +59 -0
  73. data/doc/release_notes/5.5.0.txt +61 -0
  74. data/doc/release_notes/5.50.0.txt +78 -0
  75. data/doc/release_notes/5.51.0.txt +47 -0
  76. data/doc/release_notes/5.52.0.txt +87 -0
  77. data/doc/release_notes/5.53.0.txt +23 -0
  78. data/doc/release_notes/5.54.0.txt +27 -0
  79. data/doc/release_notes/5.55.0.txt +21 -0
  80. data/doc/release_notes/5.56.0.txt +51 -0
  81. data/doc/release_notes/5.57.0.txt +23 -0
  82. data/doc/release_notes/5.58.0.txt +31 -0
  83. data/doc/release_notes/5.59.0.txt +73 -0
  84. data/doc/release_notes/5.6.0.txt +31 -0
  85. data/doc/release_notes/5.60.0.txt +22 -0
  86. data/doc/release_notes/5.61.0.txt +43 -0
  87. data/doc/release_notes/5.7.0.txt +108 -0
  88. data/doc/release_notes/5.8.0.txt +170 -0
  89. data/doc/release_notes/5.9.0.txt +99 -0
  90. data/doc/schema_modification.rdoc +95 -75
  91. data/doc/security.rdoc +109 -80
  92. data/doc/sharding.rdoc +74 -47
  93. data/doc/sql.rdoc +147 -122
  94. data/doc/testing.rdoc +43 -20
  95. data/doc/thread_safety.rdoc +2 -4
  96. data/doc/transactions.rdoc +97 -18
  97. data/doc/validations.rdoc +52 -50
  98. data/doc/virtual_rows.rdoc +90 -109
  99. data/lib/sequel/adapters/ado/access.rb +15 -17
  100. data/lib/sequel/adapters/ado/mssql.rb +6 -15
  101. data/lib/sequel/adapters/ado.rb +150 -20
  102. data/lib/sequel/adapters/amalgalite.rb +11 -23
  103. data/lib/sequel/adapters/ibmdb.rb +47 -55
  104. data/lib/sequel/adapters/jdbc/db2.rb +29 -39
  105. data/lib/sequel/adapters/jdbc/derby.rb +58 -54
  106. data/lib/sequel/adapters/jdbc/h2.rb +93 -35
  107. data/lib/sequel/adapters/jdbc/hsqldb.rb +24 -31
  108. data/lib/sequel/adapters/jdbc/jtds.rb +2 -10
  109. data/lib/sequel/adapters/jdbc/mssql.rb +3 -11
  110. data/lib/sequel/adapters/jdbc/mysql.rb +17 -20
  111. data/lib/sequel/adapters/jdbc/oracle.rb +22 -18
  112. data/lib/sequel/adapters/jdbc/postgresql.rb +69 -71
  113. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +11 -23
  114. data/lib/sequel/adapters/jdbc/sqlite.rb +47 -11
  115. data/lib/sequel/adapters/jdbc/sqlserver.rb +34 -9
  116. data/lib/sequel/adapters/jdbc/transactions.rb +22 -38
  117. data/lib/sequel/adapters/jdbc.rb +145 -130
  118. data/lib/sequel/adapters/mock.rb +100 -111
  119. data/lib/sequel/adapters/mysql.rb +114 -122
  120. data/lib/sequel/adapters/mysql2.rb +147 -63
  121. data/lib/sequel/adapters/odbc/db2.rb +1 -1
  122. data/lib/sequel/adapters/odbc/mssql.rb +8 -14
  123. data/lib/sequel/adapters/odbc/oracle.rb +11 -0
  124. data/lib/sequel/adapters/odbc.rb +20 -25
  125. data/lib/sequel/adapters/oracle.rb +50 -56
  126. data/lib/sequel/adapters/postgres.rb +305 -327
  127. data/lib/sequel/adapters/postgresql.rb +1 -1
  128. data/lib/sequel/adapters/shared/access.rb +74 -78
  129. data/lib/sequel/adapters/shared/db2.rb +118 -71
  130. data/lib/sequel/adapters/shared/mssql.rb +301 -220
  131. data/lib/sequel/adapters/shared/mysql.rb +299 -217
  132. data/lib/sequel/adapters/shared/oracle.rb +226 -65
  133. data/lib/sequel/adapters/shared/postgres.rb +935 -395
  134. data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -126
  135. data/lib/sequel/adapters/shared/sqlite.rb +447 -173
  136. data/lib/sequel/adapters/sqlanywhere.rb +48 -35
  137. data/lib/sequel/adapters/sqlite.rb +156 -111
  138. data/lib/sequel/adapters/tinytds.rb +30 -38
  139. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  140. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +3 -6
  141. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -2
  142. data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
  143. data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
  144. data/lib/sequel/adapters/utils/replace.rb +1 -4
  145. data/lib/sequel/adapters/utils/stored_procedures.rb +7 -22
  146. data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
  147. data/lib/sequel/ast_transformer.rb +17 -89
  148. data/lib/sequel/connection_pool/sharded_single.rb +18 -15
  149. data/lib/sequel/connection_pool/sharded_threaded.rb +130 -111
  150. data/lib/sequel/connection_pool/single.rb +18 -13
  151. data/lib/sequel/connection_pool/threaded.rb +121 -120
  152. data/lib/sequel/connection_pool.rb +48 -29
  153. data/lib/sequel/core.rb +351 -301
  154. data/lib/sequel/database/connecting.rb +69 -57
  155. data/lib/sequel/database/dataset.rb +13 -5
  156. data/lib/sequel/database/dataset_defaults.rb +18 -102
  157. data/lib/sequel/database/features.rb +18 -4
  158. data/lib/sequel/database/logging.rb +12 -11
  159. data/lib/sequel/database/misc.rb +180 -122
  160. data/lib/sequel/database/query.rb +47 -27
  161. data/lib/sequel/database/schema_generator.rb +178 -84
  162. data/lib/sequel/database/schema_methods.rb +172 -97
  163. data/lib/sequel/database/transactions.rb +205 -44
  164. data/lib/sequel/database.rb +17 -2
  165. data/lib/sequel/dataset/actions.rb +339 -155
  166. data/lib/sequel/dataset/dataset_module.rb +46 -0
  167. data/lib/sequel/dataset/features.rb +90 -35
  168. data/lib/sequel/dataset/graph.rb +80 -58
  169. data/lib/sequel/dataset/misc.rb +137 -47
  170. data/lib/sequel/dataset/placeholder_literalizer.rb +63 -25
  171. data/lib/sequel/dataset/prepared_statements.rb +188 -85
  172. data/lib/sequel/dataset/query.rb +530 -222
  173. data/lib/sequel/dataset/sql.rb +590 -368
  174. data/lib/sequel/dataset.rb +26 -16
  175. data/lib/sequel/deprecated.rb +12 -2
  176. data/lib/sequel/exceptions.rb +46 -16
  177. data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
  178. data/lib/sequel/extensions/_model_pg_row.rb +43 -0
  179. data/lib/sequel/extensions/_pretty_table.rb +2 -5
  180. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  181. data/lib/sequel/extensions/arbitrary_servers.rb +10 -10
  182. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  183. data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
  184. data/lib/sequel/extensions/blank.rb +8 -0
  185. data/lib/sequel/extensions/caller_logging.rb +79 -0
  186. data/lib/sequel/extensions/columns_introspection.rb +4 -3
  187. data/lib/sequel/extensions/connection_expiration.rb +20 -10
  188. data/lib/sequel/extensions/connection_validator.rb +11 -10
  189. data/lib/sequel/extensions/constant_sql_override.rb +65 -0
  190. data/lib/sequel/extensions/constraint_validations.rb +62 -39
  191. data/lib/sequel/extensions/core_extensions.rb +42 -48
  192. data/lib/sequel/extensions/core_refinements.rb +80 -59
  193. data/lib/sequel/extensions/current_datetime_timestamp.rb +1 -4
  194. data/lib/sequel/extensions/date_arithmetic.rb +98 -39
  195. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  196. data/lib/sequel/extensions/datetime_parse_to_time.rb +41 -0
  197. data/lib/sequel/extensions/duplicate_columns_handler.rb +21 -14
  198. data/lib/sequel/extensions/empty_array_consider_nulls.rb +2 -2
  199. data/lib/sequel/extensions/escaped_like.rb +100 -0
  200. data/lib/sequel/extensions/eval_inspect.rb +12 -15
  201. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  202. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  203. data/lib/sequel/extensions/freeze_datasets.rb +3 -0
  204. data/lib/sequel/extensions/from_block.rb +1 -34
  205. data/lib/sequel/extensions/graph_each.rb +4 -4
  206. data/lib/sequel/extensions/identifier_mangling.rb +180 -0
  207. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  208. data/lib/sequel/extensions/index_caching.rb +109 -0
  209. data/lib/sequel/extensions/inflector.rb +13 -5
  210. data/lib/sequel/extensions/integer64.rb +32 -0
  211. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  212. data/lib/sequel/extensions/looser_typecasting.rb +17 -8
  213. data/lib/sequel/extensions/migration.rb +119 -78
  214. data/lib/sequel/extensions/named_timezones.rb +88 -23
  215. data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -82
  216. data/lib/sequel/extensions/null_dataset.rb +8 -8
  217. data/lib/sequel/extensions/pagination.rb +32 -29
  218. data/lib/sequel/extensions/pg_array.rb +221 -287
  219. data/lib/sequel/extensions/pg_array_ops.rb +17 -9
  220. data/lib/sequel/extensions/pg_enum.rb +63 -23
  221. data/lib/sequel/extensions/pg_extended_date_support.rb +241 -0
  222. data/lib/sequel/extensions/pg_hstore.rb +45 -54
  223. data/lib/sequel/extensions/pg_hstore_ops.rb +58 -6
  224. data/lib/sequel/extensions/pg_inet.rb +31 -12
  225. data/lib/sequel/extensions/pg_inet_ops.rb +2 -2
  226. data/lib/sequel/extensions/pg_interval.rb +56 -29
  227. data/lib/sequel/extensions/pg_json.rb +417 -140
  228. data/lib/sequel/extensions/pg_json_ops.rb +270 -18
  229. data/lib/sequel/extensions/pg_loose_count.rb +4 -2
  230. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  231. data/lib/sequel/extensions/pg_range.rb +131 -191
  232. data/lib/sequel/extensions/pg_range_ops.rb +42 -13
  233. data/lib/sequel/extensions/pg_row.rb +48 -81
  234. data/lib/sequel/extensions/pg_row_ops.rb +33 -14
  235. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
  236. data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
  237. data/lib/sequel/extensions/query.rb +9 -7
  238. data/lib/sequel/extensions/round_timestamps.rb +0 -6
  239. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  240. data/lib/sequel/extensions/s.rb +60 -0
  241. data/lib/sequel/extensions/schema_caching.rb +10 -1
  242. data/lib/sequel/extensions/schema_dumper.rb +71 -48
  243. data/lib/sequel/extensions/select_remove.rb +4 -4
  244. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
  245. data/lib/sequel/extensions/server_block.rb +51 -27
  246. data/lib/sequel/extensions/split_array_nil.rb +4 -4
  247. data/lib/sequel/extensions/sql_comments.rb +119 -7
  248. data/lib/sequel/extensions/sql_expr.rb +2 -1
  249. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  250. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  251. data/lib/sequel/extensions/string_agg.rb +11 -8
  252. data/lib/sequel/extensions/string_date_time.rb +19 -23
  253. data/lib/sequel/extensions/symbol_aref.rb +55 -0
  254. data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
  255. data/lib/sequel/extensions/symbol_as.rb +23 -0
  256. data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
  257. data/lib/sequel/extensions/synchronize_sql.rb +45 -0
  258. data/lib/sequel/extensions/to_dot.rb +10 -4
  259. data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
  260. data/lib/sequel/model/associations.rb +1006 -284
  261. data/lib/sequel/model/base.rb +560 -805
  262. data/lib/sequel/model/dataset_module.rb +11 -10
  263. data/lib/sequel/model/default_inflections.rb +1 -1
  264. data/lib/sequel/model/errors.rb +10 -3
  265. data/lib/sequel/model/exceptions.rb +8 -10
  266. data/lib/sequel/model/inflections.rb +7 -20
  267. data/lib/sequel/model/plugins.rb +114 -0
  268. data/lib/sequel/model.rb +32 -82
  269. data/lib/sequel/plugins/active_model.rb +30 -14
  270. data/lib/sequel/plugins/after_initialize.rb +1 -1
  271. data/lib/sequel/plugins/association_dependencies.rb +25 -18
  272. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  273. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  274. data/lib/sequel/plugins/association_pks.rb +147 -70
  275. data/lib/sequel/plugins/association_proxies.rb +33 -9
  276. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  277. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  278. data/lib/sequel/plugins/auto_validations.rb +95 -28
  279. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  280. data/lib/sequel/plugins/before_after_save.rb +0 -42
  281. data/lib/sequel/plugins/blacklist_security.rb +21 -12
  282. data/lib/sequel/plugins/boolean_readers.rb +5 -5
  283. data/lib/sequel/plugins/boolean_subsets.rb +13 -8
  284. data/lib/sequel/plugins/caching.rb +25 -16
  285. data/lib/sequel/plugins/class_table_inheritance.rb +179 -100
  286. data/lib/sequel/plugins/column_conflicts.rb +16 -3
  287. data/lib/sequel/plugins/column_encryption.rb +728 -0
  288. data/lib/sequel/plugins/column_select.rb +7 -5
  289. data/lib/sequel/plugins/columns_updated.rb +42 -0
  290. data/lib/sequel/plugins/composition.rb +42 -26
  291. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  292. data/lib/sequel/plugins/constraint_validations.rb +20 -14
  293. data/lib/sequel/plugins/csv_serializer.rb +56 -35
  294. data/lib/sequel/plugins/dataset_associations.rb +40 -17
  295. data/lib/sequel/plugins/def_dataset_method.rb +90 -0
  296. data/lib/sequel/plugins/defaults_setter.rb +65 -10
  297. data/lib/sequel/plugins/delay_add_association.rb +1 -1
  298. data/lib/sequel/plugins/dirty.rb +62 -24
  299. data/lib/sequel/plugins/eager_each.rb +3 -3
  300. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  301. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  302. data/lib/sequel/plugins/enum.rb +124 -0
  303. data/lib/sequel/plugins/error_splitter.rb +17 -12
  304. data/lib/sequel/plugins/finder.rb +246 -0
  305. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  306. data/lib/sequel/plugins/force_encoding.rb +7 -12
  307. data/lib/sequel/plugins/hook_class_methods.rb +37 -54
  308. data/lib/sequel/plugins/input_transformer.rb +18 -10
  309. data/lib/sequel/plugins/insert_conflict.rb +76 -0
  310. data/lib/sequel/plugins/insert_returning_select.rb +2 -2
  311. data/lib/sequel/plugins/instance_filters.rb +10 -8
  312. data/lib/sequel/plugins/instance_hooks.rb +34 -17
  313. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  314. data/lib/sequel/plugins/inverted_subsets.rb +22 -13
  315. data/lib/sequel/plugins/json_serializer.rb +124 -64
  316. data/lib/sequel/plugins/lazy_attributes.rb +21 -14
  317. data/lib/sequel/plugins/list.rb +35 -21
  318. data/lib/sequel/plugins/many_through_many.rb +134 -21
  319. data/lib/sequel/plugins/modification_detection.rb +15 -5
  320. data/lib/sequel/plugins/mssql_optimistic_locking.rb +6 -5
  321. data/lib/sequel/plugins/nested_attributes.rb +61 -31
  322. data/lib/sequel/plugins/optimistic_locking.rb +3 -3
  323. data/lib/sequel/plugins/pg_array_associations.rb +103 -53
  324. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +350 -0
  325. data/lib/sequel/plugins/pg_row.rb +5 -51
  326. data/lib/sequel/plugins/prepared_statements.rb +60 -72
  327. data/lib/sequel/plugins/prepared_statements_safe.rb +9 -4
  328. data/lib/sequel/plugins/rcte_tree.rb +68 -82
  329. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  330. data/lib/sequel/plugins/serialization.rb +43 -46
  331. data/lib/sequel/plugins/serialization_modification_detection.rb +3 -2
  332. data/lib/sequel/plugins/sharding.rb +15 -10
  333. data/lib/sequel/plugins/single_table_inheritance.rb +67 -28
  334. data/lib/sequel/plugins/skip_create_refresh.rb +3 -3
  335. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  336. data/lib/sequel/plugins/split_values.rb +11 -6
  337. data/lib/sequel/plugins/sql_comments.rb +189 -0
  338. data/lib/sequel/plugins/static_cache.rb +77 -53
  339. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  340. data/lib/sequel/plugins/string_stripper.rb +3 -3
  341. data/lib/sequel/plugins/subclasses.rb +43 -10
  342. data/lib/sequel/plugins/subset_conditions.rb +15 -5
  343. data/lib/sequel/plugins/table_select.rb +2 -2
  344. data/lib/sequel/plugins/tactical_eager_loading.rb +96 -12
  345. data/lib/sequel/plugins/throw_failures.rb +110 -0
  346. data/lib/sequel/plugins/timestamps.rb +20 -8
  347. data/lib/sequel/plugins/touch.rb +19 -8
  348. data/lib/sequel/plugins/tree.rb +62 -32
  349. data/lib/sequel/plugins/typecast_on_load.rb +12 -4
  350. data/lib/sequel/plugins/unlimited_update.rb +1 -7
  351. data/lib/sequel/plugins/unused_associations.rb +521 -0
  352. data/lib/sequel/plugins/update_or_create.rb +4 -4
  353. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  354. data/lib/sequel/plugins/update_refresh.rb +26 -15
  355. data/lib/sequel/plugins/uuid.rb +7 -11
  356. data/lib/sequel/plugins/validate_associated.rb +18 -0
  357. data/lib/sequel/plugins/validation_class_methods.rb +38 -19
  358. data/lib/sequel/plugins/validation_contexts.rb +49 -0
  359. data/lib/sequel/plugins/validation_helpers.rb +57 -41
  360. data/lib/sequel/plugins/whitelist_security.rb +122 -0
  361. data/lib/sequel/plugins/xml_serializer.rb +30 -31
  362. data/lib/sequel/sql.rb +471 -331
  363. data/lib/sequel/timezones.rb +78 -47
  364. data/lib/sequel/version.rb +7 -2
  365. data/lib/sequel.rb +1 -1
  366. metadata +217 -521
  367. data/Rakefile +0 -164
  368. data/doc/active_record.rdoc +0 -928
  369. data/doc/release_notes/1.0.txt +0 -38
  370. data/doc/release_notes/1.1.txt +0 -143
  371. data/doc/release_notes/1.3.txt +0 -101
  372. data/doc/release_notes/1.4.0.txt +0 -53
  373. data/doc/release_notes/1.5.0.txt +0 -155
  374. data/doc/release_notes/2.0.0.txt +0 -298
  375. data/doc/release_notes/2.1.0.txt +0 -271
  376. data/doc/release_notes/2.10.0.txt +0 -328
  377. data/doc/release_notes/2.11.0.txt +0 -215
  378. data/doc/release_notes/2.12.0.txt +0 -534
  379. data/doc/release_notes/2.2.0.txt +0 -253
  380. data/doc/release_notes/2.3.0.txt +0 -88
  381. data/doc/release_notes/2.4.0.txt +0 -106
  382. data/doc/release_notes/2.5.0.txt +0 -137
  383. data/doc/release_notes/2.6.0.txt +0 -157
  384. data/doc/release_notes/2.7.0.txt +0 -166
  385. data/doc/release_notes/2.8.0.txt +0 -171
  386. data/doc/release_notes/2.9.0.txt +0 -97
  387. data/doc/release_notes/3.0.0.txt +0 -221
  388. data/doc/release_notes/3.1.0.txt +0 -406
  389. data/doc/release_notes/3.10.0.txt +0 -286
  390. data/doc/release_notes/3.11.0.txt +0 -254
  391. data/doc/release_notes/3.12.0.txt +0 -304
  392. data/doc/release_notes/3.13.0.txt +0 -210
  393. data/doc/release_notes/3.14.0.txt +0 -118
  394. data/doc/release_notes/3.15.0.txt +0 -78
  395. data/doc/release_notes/3.16.0.txt +0 -45
  396. data/doc/release_notes/3.17.0.txt +0 -58
  397. data/doc/release_notes/3.18.0.txt +0 -120
  398. data/doc/release_notes/3.19.0.txt +0 -67
  399. data/doc/release_notes/3.2.0.txt +0 -268
  400. data/doc/release_notes/3.20.0.txt +0 -41
  401. data/doc/release_notes/3.21.0.txt +0 -87
  402. data/doc/release_notes/3.22.0.txt +0 -39
  403. data/doc/release_notes/3.23.0.txt +0 -172
  404. data/doc/release_notes/3.24.0.txt +0 -420
  405. data/doc/release_notes/3.25.0.txt +0 -88
  406. data/doc/release_notes/3.26.0.txt +0 -88
  407. data/doc/release_notes/3.27.0.txt +0 -82
  408. data/doc/release_notes/3.28.0.txt +0 -304
  409. data/doc/release_notes/3.29.0.txt +0 -459
  410. data/doc/release_notes/3.3.0.txt +0 -192
  411. data/doc/release_notes/3.30.0.txt +0 -135
  412. data/doc/release_notes/3.31.0.txt +0 -146
  413. data/doc/release_notes/3.32.0.txt +0 -202
  414. data/doc/release_notes/3.33.0.txt +0 -157
  415. data/doc/release_notes/3.34.0.txt +0 -671
  416. data/doc/release_notes/3.35.0.txt +0 -144
  417. data/doc/release_notes/3.36.0.txt +0 -245
  418. data/doc/release_notes/3.37.0.txt +0 -338
  419. data/doc/release_notes/3.38.0.txt +0 -234
  420. data/doc/release_notes/3.39.0.txt +0 -237
  421. data/doc/release_notes/3.4.0.txt +0 -325
  422. data/doc/release_notes/3.40.0.txt +0 -73
  423. data/doc/release_notes/3.41.0.txt +0 -155
  424. data/doc/release_notes/3.42.0.txt +0 -74
  425. data/doc/release_notes/3.43.0.txt +0 -105
  426. data/doc/release_notes/3.44.0.txt +0 -152
  427. data/doc/release_notes/3.45.0.txt +0 -179
  428. data/doc/release_notes/3.46.0.txt +0 -122
  429. data/doc/release_notes/3.47.0.txt +0 -270
  430. data/doc/release_notes/3.48.0.txt +0 -477
  431. data/doc/release_notes/3.5.0.txt +0 -510
  432. data/doc/release_notes/3.6.0.txt +0 -366
  433. data/doc/release_notes/3.7.0.txt +0 -179
  434. data/doc/release_notes/3.8.0.txt +0 -151
  435. data/doc/release_notes/3.9.0.txt +0 -233
  436. data/doc/release_notes/4.0.0.txt +0 -262
  437. data/doc/release_notes/4.1.0.txt +0 -85
  438. data/doc/release_notes/4.10.0.txt +0 -226
  439. data/doc/release_notes/4.11.0.txt +0 -147
  440. data/doc/release_notes/4.12.0.txt +0 -105
  441. data/doc/release_notes/4.13.0.txt +0 -169
  442. data/doc/release_notes/4.14.0.txt +0 -68
  443. data/doc/release_notes/4.15.0.txt +0 -56
  444. data/doc/release_notes/4.16.0.txt +0 -36
  445. data/doc/release_notes/4.17.0.txt +0 -38
  446. data/doc/release_notes/4.18.0.txt +0 -36
  447. data/doc/release_notes/4.19.0.txt +0 -45
  448. data/doc/release_notes/4.2.0.txt +0 -129
  449. data/doc/release_notes/4.20.0.txt +0 -79
  450. data/doc/release_notes/4.21.0.txt +0 -94
  451. data/doc/release_notes/4.22.0.txt +0 -72
  452. data/doc/release_notes/4.23.0.txt +0 -65
  453. data/doc/release_notes/4.24.0.txt +0 -99
  454. data/doc/release_notes/4.25.0.txt +0 -181
  455. data/doc/release_notes/4.26.0.txt +0 -44
  456. data/doc/release_notes/4.27.0.txt +0 -78
  457. data/doc/release_notes/4.28.0.txt +0 -57
  458. data/doc/release_notes/4.29.0.txt +0 -41
  459. data/doc/release_notes/4.3.0.txt +0 -40
  460. data/doc/release_notes/4.30.0.txt +0 -37
  461. data/doc/release_notes/4.31.0.txt +0 -57
  462. data/doc/release_notes/4.32.0.txt +0 -132
  463. data/doc/release_notes/4.33.0.txt +0 -88
  464. data/doc/release_notes/4.34.0.txt +0 -86
  465. data/doc/release_notes/4.35.0.txt +0 -130
  466. data/doc/release_notes/4.36.0.txt +0 -116
  467. data/doc/release_notes/4.4.0.txt +0 -92
  468. data/doc/release_notes/4.5.0.txt +0 -34
  469. data/doc/release_notes/4.6.0.txt +0 -30
  470. data/doc/release_notes/4.7.0.txt +0 -103
  471. data/doc/release_notes/4.8.0.txt +0 -175
  472. data/doc/release_notes/4.9.0.txt +0 -190
  473. data/lib/sequel/adapters/cubrid.rb +0 -144
  474. data/lib/sequel/adapters/do/mysql.rb +0 -66
  475. data/lib/sequel/adapters/do/postgres.rb +0 -44
  476. data/lib/sequel/adapters/do/sqlite3.rb +0 -42
  477. data/lib/sequel/adapters/do.rb +0 -158
  478. data/lib/sequel/adapters/jdbc/as400.rb +0 -84
  479. data/lib/sequel/adapters/jdbc/cubrid.rb +0 -64
  480. data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -36
  481. data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -33
  482. data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -33
  483. data/lib/sequel/adapters/odbc/progress.rb +0 -10
  484. data/lib/sequel/adapters/shared/cubrid.rb +0 -245
  485. data/lib/sequel/adapters/shared/firebird.rb +0 -247
  486. data/lib/sequel/adapters/shared/informix.rb +0 -54
  487. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -152
  488. data/lib/sequel/adapters/shared/progress.rb +0 -40
  489. data/lib/sequel/adapters/swift/mysql.rb +0 -49
  490. data/lib/sequel/adapters/swift/postgres.rb +0 -47
  491. data/lib/sequel/adapters/swift/sqlite.rb +0 -49
  492. data/lib/sequel/adapters/swift.rb +0 -160
  493. data/lib/sequel/adapters/utils/pg_types.rb +0 -70
  494. data/lib/sequel/dataset/mutation.rb +0 -111
  495. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -5
  496. data/lib/sequel/extensions/filter_having.rb +0 -63
  497. data/lib/sequel/extensions/hash_aliases.rb +0 -49
  498. data/lib/sequel/extensions/meta_def.rb +0 -35
  499. data/lib/sequel/extensions/query_literals.rb +0 -84
  500. data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -24
  501. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -122
  502. data/lib/sequel/extensions/set_overrides.rb +0 -76
  503. data/lib/sequel/no_core_ext.rb +0 -3
  504. data/lib/sequel/plugins/association_autoreloading.rb +0 -9
  505. data/lib/sequel/plugins/identifier_columns.rb +0 -47
  506. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -9
  507. data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -81
  508. data/lib/sequel/plugins/prepared_statements_associations.rb +0 -119
  509. data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -61
  510. data/lib/sequel/plugins/schema.rb +0 -82
  511. data/lib/sequel/plugins/scissors.rb +0 -35
  512. data/spec/adapter_spec.rb +0 -4
  513. data/spec/adapters/db2_spec.rb +0 -160
  514. data/spec/adapters/firebird_spec.rb +0 -411
  515. data/spec/adapters/informix_spec.rb +0 -100
  516. data/spec/adapters/mssql_spec.rb +0 -733
  517. data/spec/adapters/mysql_spec.rb +0 -1319
  518. data/spec/adapters/oracle_spec.rb +0 -313
  519. data/spec/adapters/postgres_spec.rb +0 -3790
  520. data/spec/adapters/spec_helper.rb +0 -49
  521. data/spec/adapters/sqlanywhere_spec.rb +0 -170
  522. data/spec/adapters/sqlite_spec.rb +0 -688
  523. data/spec/bin_spec.rb +0 -258
  524. data/spec/core/connection_pool_spec.rb +0 -1045
  525. data/spec/core/database_spec.rb +0 -2636
  526. data/spec/core/dataset_spec.rb +0 -5175
  527. data/spec/core/deprecated_spec.rb +0 -70
  528. data/spec/core/expression_filters_spec.rb +0 -1247
  529. data/spec/core/mock_adapter_spec.rb +0 -464
  530. data/spec/core/object_graph_spec.rb +0 -303
  531. data/spec/core/placeholder_literalizer_spec.rb +0 -163
  532. data/spec/core/schema_generator_spec.rb +0 -203
  533. data/spec/core/schema_spec.rb +0 -1676
  534. data/spec/core/spec_helper.rb +0 -34
  535. data/spec/core/version_spec.rb +0 -7
  536. data/spec/core_extensions_spec.rb +0 -699
  537. data/spec/core_model_spec.rb +0 -2
  538. data/spec/core_spec.rb +0 -1
  539. data/spec/extensions/accessed_columns_spec.rb +0 -51
  540. data/spec/extensions/active_model_spec.rb +0 -85
  541. data/spec/extensions/after_initialize_spec.rb +0 -24
  542. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  543. data/spec/extensions/association_dependencies_spec.rb +0 -117
  544. data/spec/extensions/association_pks_spec.rb +0 -405
  545. data/spec/extensions/association_proxies_spec.rb +0 -86
  546. data/spec/extensions/auto_validations_spec.rb +0 -192
  547. data/spec/extensions/before_after_save_spec.rb +0 -40
  548. data/spec/extensions/blacklist_security_spec.rb +0 -88
  549. data/spec/extensions/blank_spec.rb +0 -69
  550. data/spec/extensions/boolean_readers_spec.rb +0 -93
  551. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  552. data/spec/extensions/caching_spec.rb +0 -270
  553. data/spec/extensions/class_table_inheritance_spec.rb +0 -444
  554. data/spec/extensions/column_conflicts_spec.rb +0 -60
  555. data/spec/extensions/column_select_spec.rb +0 -108
  556. data/spec/extensions/columns_introspection_spec.rb +0 -91
  557. data/spec/extensions/composition_spec.rb +0 -242
  558. data/spec/extensions/connection_expiration_spec.rb +0 -121
  559. data/spec/extensions/connection_validator_spec.rb +0 -127
  560. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -288
  561. data/spec/extensions/constraint_validations_spec.rb +0 -389
  562. data/spec/extensions/core_refinements_spec.rb +0 -519
  563. data/spec/extensions/csv_serializer_spec.rb +0 -180
  564. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  565. data/spec/extensions/dataset_associations_spec.rb +0 -343
  566. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  567. data/spec/extensions/date_arithmetic_spec.rb +0 -167
  568. data/spec/extensions/defaults_setter_spec.rb +0 -102
  569. data/spec/extensions/delay_add_association_spec.rb +0 -74
  570. data/spec/extensions/dirty_spec.rb +0 -180
  571. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -110
  572. data/spec/extensions/eager_each_spec.rb +0 -66
  573. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  574. data/spec/extensions/error_splitter_spec.rb +0 -18
  575. data/spec/extensions/error_sql_spec.rb +0 -20
  576. data/spec/extensions/eval_inspect_spec.rb +0 -73
  577. data/spec/extensions/filter_having_spec.rb +0 -40
  578. data/spec/extensions/force_encoding_spec.rb +0 -114
  579. data/spec/extensions/from_block_spec.rb +0 -21
  580. data/spec/extensions/graph_each_spec.rb +0 -119
  581. data/spec/extensions/hash_aliases_spec.rb +0 -24
  582. data/spec/extensions/hook_class_methods_spec.rb +0 -429
  583. data/spec/extensions/identifier_columns_spec.rb +0 -17
  584. data/spec/extensions/inflector_spec.rb +0 -183
  585. data/spec/extensions/input_transformer_spec.rb +0 -54
  586. data/spec/extensions/insert_returning_select_spec.rb +0 -46
  587. data/spec/extensions/instance_filters_spec.rb +0 -79
  588. data/spec/extensions/instance_hooks_spec.rb +0 -276
  589. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  590. data/spec/extensions/json_serializer_spec.rb +0 -304
  591. data/spec/extensions/lazy_attributes_spec.rb +0 -170
  592. data/spec/extensions/list_spec.rb +0 -278
  593. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  594. data/spec/extensions/many_through_many_spec.rb +0 -2172
  595. data/spec/extensions/meta_def_spec.rb +0 -21
  596. data/spec/extensions/migration_spec.rb +0 -728
  597. data/spec/extensions/modification_detection_spec.rb +0 -80
  598. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
  599. data/spec/extensions/named_timezones_spec.rb +0 -108
  600. data/spec/extensions/nested_attributes_spec.rb +0 -697
  601. data/spec/extensions/no_auto_literal_strings_spec.rb +0 -65
  602. data/spec/extensions/null_dataset_spec.rb +0 -85
  603. data/spec/extensions/optimistic_locking_spec.rb +0 -128
  604. data/spec/extensions/pagination_spec.rb +0 -118
  605. data/spec/extensions/pg_array_associations_spec.rb +0 -736
  606. data/spec/extensions/pg_array_ops_spec.rb +0 -143
  607. data/spec/extensions/pg_array_spec.rb +0 -390
  608. data/spec/extensions/pg_enum_spec.rb +0 -92
  609. data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
  610. data/spec/extensions/pg_hstore_spec.rb +0 -206
  611. data/spec/extensions/pg_inet_ops_spec.rb +0 -101
  612. data/spec/extensions/pg_inet_spec.rb +0 -52
  613. data/spec/extensions/pg_interval_spec.rb +0 -76
  614. data/spec/extensions/pg_json_ops_spec.rb +0 -275
  615. data/spec/extensions/pg_json_spec.rb +0 -218
  616. data/spec/extensions/pg_loose_count_spec.rb +0 -17
  617. data/spec/extensions/pg_range_ops_spec.rb +0 -58
  618. data/spec/extensions/pg_range_spec.rb +0 -473
  619. data/spec/extensions/pg_row_ops_spec.rb +0 -60
  620. data/spec/extensions/pg_row_plugin_spec.rb +0 -62
  621. data/spec/extensions/pg_row_spec.rb +0 -360
  622. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
  623. data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
  624. data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
  625. data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
  626. data/spec/extensions/prepared_statements_spec.rb +0 -103
  627. data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
  628. data/spec/extensions/pretty_table_spec.rb +0 -92
  629. data/spec/extensions/query_literals_spec.rb +0 -183
  630. data/spec/extensions/query_spec.rb +0 -102
  631. data/spec/extensions/rcte_tree_spec.rb +0 -392
  632. data/spec/extensions/round_timestamps_spec.rb +0 -43
  633. data/spec/extensions/schema_caching_spec.rb +0 -41
  634. data/spec/extensions/schema_dumper_spec.rb +0 -814
  635. data/spec/extensions/schema_spec.rb +0 -117
  636. data/spec/extensions/scissors_spec.rb +0 -26
  637. data/spec/extensions/select_remove_spec.rb +0 -38
  638. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
  639. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  640. data/spec/extensions/serialization_spec.rb +0 -362
  641. data/spec/extensions/server_block_spec.rb +0 -90
  642. data/spec/extensions/server_logging_spec.rb +0 -45
  643. data/spec/extensions/set_overrides_spec.rb +0 -61
  644. data/spec/extensions/sharding_spec.rb +0 -198
  645. data/spec/extensions/shared_caching_spec.rb +0 -175
  646. data/spec/extensions/single_table_inheritance_spec.rb +0 -297
  647. data/spec/extensions/singular_table_names_spec.rb +0 -22
  648. data/spec/extensions/skip_create_refresh_spec.rb +0 -17
  649. data/spec/extensions/spec_helper.rb +0 -71
  650. data/spec/extensions/split_array_nil_spec.rb +0 -24
  651. data/spec/extensions/split_values_spec.rb +0 -22
  652. data/spec/extensions/sql_comments_spec.rb +0 -27
  653. data/spec/extensions/sql_expr_spec.rb +0 -60
  654. data/spec/extensions/static_cache_spec.rb +0 -361
  655. data/spec/extensions/string_agg_spec.rb +0 -85
  656. data/spec/extensions/string_date_time_spec.rb +0 -95
  657. data/spec/extensions/string_stripper_spec.rb +0 -68
  658. data/spec/extensions/subclasses_spec.rb +0 -66
  659. data/spec/extensions/subset_conditions_spec.rb +0 -38
  660. data/spec/extensions/table_select_spec.rb +0 -71
  661. data/spec/extensions/tactical_eager_loading_spec.rb +0 -136
  662. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  663. data/spec/extensions/timestamps_spec.rb +0 -175
  664. data/spec/extensions/to_dot_spec.rb +0 -154
  665. data/spec/extensions/touch_spec.rb +0 -203
  666. data/spec/extensions/tree_spec.rb +0 -274
  667. data/spec/extensions/typecast_on_load_spec.rb +0 -80
  668. data/spec/extensions/unlimited_update_spec.rb +0 -20
  669. data/spec/extensions/update_or_create_spec.rb +0 -87
  670. data/spec/extensions/update_primary_key_spec.rb +0 -100
  671. data/spec/extensions/update_refresh_spec.rb +0 -53
  672. data/spec/extensions/uuid_spec.rb +0 -106
  673. data/spec/extensions/validate_associated_spec.rb +0 -52
  674. data/spec/extensions/validation_class_methods_spec.rb +0 -1027
  675. data/spec/extensions/validation_helpers_spec.rb +0 -554
  676. data/spec/extensions/xml_serializer_spec.rb +0 -207
  677. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  678. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  679. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  680. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  681. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  682. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  683. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  684. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  685. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  686. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  687. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  688. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  689. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  690. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  691. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  692. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  693. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  694. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  695. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  696. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  697. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  698. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  699. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  700. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  701. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  702. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  703. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  704. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  705. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  706. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  707. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  708. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  709. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  710. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  711. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  712. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  713. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  714. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  715. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  716. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  717. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  718. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  719. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  720. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  721. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  722. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  723. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  724. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  725. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  726. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  727. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  728. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  729. data/spec/guards_helper.rb +0 -55
  730. data/spec/integration/associations_test.rb +0 -2506
  731. data/spec/integration/database_test.rb +0 -113
  732. data/spec/integration/dataset_test.rb +0 -1858
  733. data/spec/integration/eager_loader_test.rb +0 -687
  734. data/spec/integration/migrator_test.rb +0 -262
  735. data/spec/integration/model_test.rb +0 -230
  736. data/spec/integration/plugin_test.rb +0 -2297
  737. data/spec/integration/prepared_statement_test.rb +0 -467
  738. data/spec/integration/schema_test.rb +0 -815
  739. data/spec/integration/spec_helper.rb +0 -56
  740. data/spec/integration/timezone_test.rb +0 -86
  741. data/spec/integration/transaction_test.rb +0 -406
  742. data/spec/integration/type_test.rb +0 -133
  743. data/spec/model/association_reflection_spec.rb +0 -565
  744. data/spec/model/associations_spec.rb +0 -4589
  745. data/spec/model/base_spec.rb +0 -759
  746. data/spec/model/class_dataset_methods_spec.rb +0 -150
  747. data/spec/model/dataset_methods_spec.rb +0 -149
  748. data/spec/model/eager_loading_spec.rb +0 -2197
  749. data/spec/model/hooks_spec.rb +0 -604
  750. data/spec/model/inflector_spec.rb +0 -26
  751. data/spec/model/model_spec.rb +0 -1097
  752. data/spec/model/plugins_spec.rb +0 -299
  753. data/spec/model/record_spec.rb +0 -2162
  754. data/spec/model/spec_helper.rb +0 -46
  755. data/spec/model/validations_spec.rb +0 -193
  756. data/spec/model_no_assoc_spec.rb +0 -1
  757. data/spec/model_spec.rb +0 -1
  758. data/spec/plugin_spec.rb +0 -1
  759. data/spec/sequel_coverage.rb +0 -15
  760. data/spec/spec_config.rb +0 -10
@@ -12,30 +12,31 @@ module Sequel
12
12
  # in the extension).
13
13
  EXTENSIONS = {}
14
14
 
15
- # The dataset options that require the removal of cached columns
16
- # if changed.
15
+ EMPTY_ARRAY = [].freeze
16
+
17
+ # The dataset options that require the removal of cached columns if changed.
17
18
  COLUMN_CHANGE_OPTS = [:select, :sql, :from, :join].freeze
18
19
 
19
20
  # Which options don't affect the SQL generation. Used by simple_select_all?
20
21
  # to determine if this is a simple SELECT * FROM table.
21
- NON_SQL_OPTIONS = [:server, :defaults, :overrides, :graph, :eager, :eager_graph, :graph_aliases]
22
+ NON_SQL_OPTIONS = [:server, :graph, :row_proc, :quote_identifiers, :skip_symbol_cache].freeze
22
23
 
23
24
  # These symbols have _join methods created (e.g. inner_join) that
24
25
  # call join_table with the symbol, passing along the arguments and
25
26
  # block from the method call.
26
- CONDITIONED_JOIN_TYPES = [:inner, :full_outer, :right_outer, :left_outer, :full, :right, :left]
27
+ CONDITIONED_JOIN_TYPES = [:inner, :full_outer, :right_outer, :left_outer, :full, :right, :left].freeze
27
28
 
28
29
  # These symbols have _join methods created (e.g. natural_join).
29
30
  # They accept a table argument and options hash which is passed to join_table,
30
31
  # and they raise an error if called with a block.
31
- UNCONDITIONED_JOIN_TYPES = [:natural, :natural_left, :natural_right, :natural_full, :cross]
32
+ UNCONDITIONED_JOIN_TYPES = [:natural, :natural_left, :natural_right, :natural_full, :cross].freeze
32
33
 
33
34
  # All methods that return modified datasets with a joined table added.
34
- JOIN_METHODS = (CONDITIONED_JOIN_TYPES + UNCONDITIONED_JOIN_TYPES).map{|x| "#{x}_join".to_sym} + [:join, :join_table]
35
+ JOIN_METHODS = ((CONDITIONED_JOIN_TYPES + UNCONDITIONED_JOIN_TYPES).map{|x| "#{x}_join".to_sym} + [:join, :join_table]).freeze
35
36
 
36
37
  # Methods that return modified datasets
37
- QUERY_METHODS = (<<-METHS).split.map(&:to_sym) + JOIN_METHODS
38
- add_graph_aliases and distinct except exclude exclude_having exclude_where
38
+ QUERY_METHODS = ((<<-METHS).split.map(&:to_sym) + JOIN_METHODS).freeze
39
+ add_graph_aliases distinct except exclude exclude_having
39
40
  filter for_update from from_self graph grep group group_and_count group_append group_by having intersect invert
40
41
  limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
41
42
  reverse reverse_order select select_all select_append select_group select_more server
@@ -64,24 +65,44 @@ module Sequel
64
65
  Sequel.synchronize{EXTENSIONS[ext] = block}
65
66
  end
66
67
 
67
- # Alias for where.
68
- def and(*cond, &block)
69
- where(*cond, &block)
70
- end
71
-
72
- # Returns a new clone of the dataset with the given options merged.
73
- # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
74
- # columns are deleted. This method should generally not be called
75
- # directly by user code.
76
- def clone(opts = nil)
77
- c = super()
78
- if opts
79
- c.instance_variable_set(:@opts, Hash[@opts].merge!(opts))
80
- c.instance_variable_set(:@columns, nil) if @columns && !opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
81
- else
82
- c.instance_variable_set(:@opts, Hash[@opts])
68
+ # On Ruby 2.4+, use clone(:freeze=>false) to create clones, because
69
+ # we use true freezing in that case, and we need to modify the opts
70
+ # in the frozen copy.
71
+ #
72
+ # On Ruby <2.4, just use Object#clone directly, since we don't
73
+ # use true freezing as it isn't possible.
74
+ if TRUE_FREEZE
75
+ # Save original clone implementation, as some other methods need
76
+ # to call it internally.
77
+ alias _clone clone
78
+ private :_clone
79
+
80
+ # Returns a new clone of the dataset with the given options merged.
81
+ # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
82
+ # columns are deleted. This method should generally not be called
83
+ # directly by user code.
84
+ def clone(opts = nil || (return self))
85
+ # return self used above because clone is called by almost all
86
+ # other query methods, and it is the fastest approach
87
+ c = super(:freeze=>false)
88
+ c.opts.merge!(opts)
89
+ unless opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
90
+ c.clear_columns_cache
91
+ end
92
+ c.freeze
93
+ end
94
+ else
95
+ # :nocov:
96
+ def clone(opts = OPTS) # :nodoc:
97
+ c = super()
98
+ c.opts.merge!(opts)
99
+ unless opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
100
+ c.clear_columns_cache
101
+ end
102
+ c.opts.freeze
103
+ c
83
104
  end
84
- c
105
+ # :nocov:
85
106
  end
86
107
 
87
108
  # Returns a copy of the dataset with the SQL DISTINCT clause. The DISTINCT
@@ -94,10 +115,19 @@ module Sequel
94
115
  # DB[:items].distinct # SQL: SELECT DISTINCT * FROM items
95
116
  # DB[:items].order(:id).distinct(:id) # SQL: SELECT DISTINCT ON (id) * FROM items ORDER BY id
96
117
  # DB[:items].order(:id).distinct{func(:id)} # SQL: SELECT DISTINCT ON (func(id)) * FROM items ORDER BY id
118
+ #
119
+ # There is support for emualting the DISTINCT ON support in MySQL, but it
120
+ # does not support the ORDER of the dataset, and also doesn't work in many
121
+ # cases if the ONLY_FULL_GROUP_BY sql_mode is used, which is the default on
122
+ # MySQL 5.7.5+.
97
123
  def distinct(*args, &block)
98
124
  virtual_row_columns(args, block)
99
- raise(InvalidOperation, "DISTINCT ON not supported") if !args.empty? && !supports_distinct_on?
100
- clone(:distinct => args)
125
+ if args.empty?
126
+ cached_dataset(:_distinct_ds){clone(:distinct => EMPTY_ARRAY)}
127
+ else
128
+ raise(InvalidOperation, "DISTINCT ON not supported") unless supports_distinct_on?
129
+ clone(:distinct => args.freeze)
130
+ end
101
131
  end
102
132
 
103
133
  # Adds an EXCEPT clause using a second dataset object.
@@ -112,10 +142,10 @@ module Sequel
112
142
  # DB[:items].except(DB[:other_items])
113
143
  # # SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS t1
114
144
  #
115
- # DB[:items].except(DB[:other_items], :all=>true, :from_self=>false)
145
+ # DB[:items].except(DB[:other_items], all: true, from_self: false)
116
146
  # # SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
117
147
  #
118
- # DB[:items].except(DB[:other_items], :alias=>:i)
148
+ # DB[:items].except(DB[:other_items], alias: :i)
119
149
  # # SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS i
120
150
  def except(dataset, opts=OPTS)
121
151
  raise(InvalidOperation, "EXCEPT not supported") unless supports_intersect_except?
@@ -126,31 +156,59 @@ module Sequel
126
156
  # Performs the inverse of Dataset#where. Note that if you have multiple filter
127
157
  # conditions, this is not the same as a negation of all conditions.
128
158
  #
129
- # DB[:items].exclude(:category => 'software')
159
+ # DB[:items].exclude(category: 'software')
130
160
  # # SELECT * FROM items WHERE (category != 'software')
131
161
  #
132
- # DB[:items].exclude(:category => 'software', :id=>3)
162
+ # DB[:items].exclude(category: 'software', id: 3)
133
163
  # # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
164
+ #
165
+ # Also note that SQL uses 3-valued boolean logic (+true+, +false+, +NULL+), so
166
+ # the inverse of a true condition is a false condition, and will still
167
+ # not match rows that were NULL originally. If you take the earlier
168
+ # example:
169
+ #
170
+ # DB[:items].exclude(category: 'software')
171
+ # # SELECT * FROM items WHERE (category != 'software')
172
+ #
173
+ # Note that this does not match rows where +category+ is +NULL+. This
174
+ # is because +NULL+ is an unknown value, and you do not know whether
175
+ # or not the +NULL+ category is +software+. You can explicitly
176
+ # specify how to handle +NULL+ values if you want:
177
+ #
178
+ # DB[:items].exclude(Sequel.~(category: nil) & {category: 'software'})
179
+ # # SELECT * FROM items WHERE ((category IS NULL) OR (category != 'software'))
134
180
  def exclude(*cond, &block)
135
- _filter_or_exclude(true, :where, *cond, &block)
181
+ add_filter(:where, cond, true, &block)
136
182
  end
137
183
 
138
184
  # Inverts the given conditions and adds them to the HAVING clause.
139
185
  #
140
186
  # DB[:items].select_group(:name).exclude_having{count(name) < 2}
141
187
  # # SELECT name FROM items GROUP BY name HAVING (count(name) >= 2)
188
+ #
189
+ # See documentation for exclude for how inversion is handled in regards
190
+ # to SQL 3-valued boolean logic.
142
191
  def exclude_having(*cond, &block)
143
- _filter_or_exclude(true, :having, *cond, &block)
192
+ add_filter(:having, cond, true, &block)
144
193
  end
145
194
 
146
- # Alias for exclude.
147
- def exclude_where(*cond, &block)
148
- exclude(*cond, &block)
149
- end
150
-
151
- # Return a clone of the dataset loaded with the extensions, see #extension!.
152
- def extension(*exts)
153
- clone.extension!(*exts)
195
+ if TRUE_FREEZE
196
+ # Return a clone of the dataset loaded with the given dataset extensions.
197
+ # If no related extension file exists or the extension does not have
198
+ # specific support for Dataset objects, an Error will be raised.
199
+ def extension(*a)
200
+ c = _clone(:freeze=>false)
201
+ c.send(:_extension!, a)
202
+ c.freeze
203
+ end
204
+ else
205
+ # :nocov:
206
+ def extension(*exts) # :nodoc:
207
+ c = clone
208
+ c.send(:_extension!, exts)
209
+ c
210
+ end
211
+ # :nocov:
154
212
  end
155
213
 
156
214
  # Alias for where.
@@ -162,7 +220,7 @@ module Sequel
162
220
  #
163
221
  # DB[:table].for_update # SELECT * FROM table FOR UPDATE
164
222
  def for_update
165
- lock_style(:update)
223
+ cached_dataset(:_for_update_ds){lock_style(:update)}
166
224
  end
167
225
 
168
226
  # Returns a copy of the dataset with the source changed. If no
@@ -199,14 +257,17 @@ module Sequel
199
257
  s
200
258
  end
201
259
  end
202
- o = {:from=>source.empty? ? nil : source}
203
- o[:with] = (opts[:with] || []) + ctes if ctes
260
+ o = {:from=>source.empty? ? nil : source.freeze}
261
+ o[:with] = ((opts[:with] || EMPTY_ARRAY) + ctes).freeze if ctes
204
262
  o[:num_dataset_sources] = table_alias_num if table_alias_num > 0
205
263
  clone(o)
206
264
  end
207
265
 
208
266
  # Returns a dataset selecting from the current dataset.
209
- # Supplying the :alias option controls the alias of the result.
267
+ # Options:
268
+ # :alias :: Controls the alias of the table
269
+ # :column_aliases :: Also aliases columns, using derived column lists.
270
+ # Only used in conjunction with :alias.
210
271
  #
211
272
  # ds = DB[:items].order(:name).select(:id, :name)
212
273
  # # SELECT id,name FROM items ORDER BY name
@@ -214,21 +275,29 @@ module Sequel
214
275
  # ds.from_self
215
276
  # # SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1
216
277
  #
217
- # ds.from_self(:alias=>:foo)
278
+ # ds.from_self(alias: :foo)
218
279
  # # SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo
219
280
  #
220
- # ds.from_self(:alias=>:foo, :column_aliases=>[:c1, :c2])
281
+ # ds.from_self(alias: :foo, column_aliases: [:c1, :c2])
221
282
  # # SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo(c1, c2)
222
283
  def from_self(opts=OPTS)
223
284
  fs = {}
224
- @opts.keys.each{|k| fs[k] = nil unless NON_SQL_OPTIONS.include?(k)}
225
- clone(fs).from(opts[:alias] ? as(opts[:alias], opts[:column_aliases]) : self)
285
+ @opts.keys.each{|k| fs[k] = nil unless non_sql_option?(k)}
286
+ pr = proc do
287
+ c = clone(fs).from(opts[:alias] ? as(opts[:alias], opts[:column_aliases]) : self)
288
+ if cols = _columns
289
+ c.send(:columns=, cols)
290
+ end
291
+ c
292
+ end
293
+
294
+ opts.empty? ? cached_dataset(:_from_self_ds, &pr) : pr.call
226
295
  end
227
296
 
228
297
  # Match any of the columns to any of the patterns. The terms can be
229
- # strings (which use LIKE) or regular expressions (which are only
230
- # supported on MySQL and PostgreSQL). Note that the total number of
231
- # pattern matches will be Array(columns).length * Array(terms).length,
298
+ # strings (which use LIKE) or regular expressions if the database supports that.
299
+ # Note that the total number of pattern matches will be
300
+ # Array(columns).length * Array(terms).length,
232
301
  # which could cause performance issues.
233
302
  #
234
303
  # Options (all are boolean):
@@ -249,28 +318,29 @@ module Sequel
249
318
  # # SELECT * FROM items WHERE ((a LIKE '%test%' ESCAPE '\') OR (a LIKE 'foo' ESCAPE '\')
250
319
  # # OR (b LIKE '%test%' ESCAPE '\') OR (b LIKE 'foo' ESCAPE '\'))
251
320
  #
252
- # dataset.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true)
321
+ # dataset.grep([:a, :b], %w'%foo% %bar%', all_patterns: true)
253
322
  # # SELECT * FROM a WHERE (((a LIKE '%foo%' ESCAPE '\') OR (b LIKE '%foo%' ESCAPE '\'))
254
323
  # # AND ((a LIKE '%bar%' ESCAPE '\') OR (b LIKE '%bar%' ESCAPE '\')))
255
324
  #
256
- # dataset.grep([:a, :b], %w'%foo% %bar%', :all_columns=>true)
325
+ # dataset.grep([:a, :b], %w'%foo% %bar%', all_columns: true)
257
326
  # # SELECT * FROM a WHERE (((a LIKE '%foo%' ESCAPE '\') OR (a LIKE '%bar%' ESCAPE '\'))
258
327
  # # AND ((b LIKE '%foo%' ESCAPE '\') OR (b LIKE '%bar%' ESCAPE '\')))
259
328
  #
260
- # dataset.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true, :all_columns=>true)
329
+ # dataset.grep([:a, :b], %w'%foo% %bar%', all_patterns: true, all_columns: true)
261
330
  # # SELECT * FROM a WHERE ((a LIKE '%foo%' ESCAPE '\') AND (b LIKE '%foo%' ESCAPE '\')
262
331
  # # AND (a LIKE '%bar%' ESCAPE '\') AND (b LIKE '%bar%' ESCAPE '\'))
263
332
  def grep(columns, patterns, opts=OPTS)
333
+ column_op = opts[:all_columns] ? :AND : :OR
264
334
  if opts[:all_patterns]
265
335
  conds = Array(patterns).map do |pat|
266
- SQL::BooleanExpression.new(opts[:all_columns] ? :AND : :OR, *Array(columns).map{|c| SQL::StringExpression.like(c, pat, opts)})
336
+ SQL::BooleanExpression.new(column_op, *Array(columns).map{|c| SQL::StringExpression.like(c, pat, opts)})
267
337
  end
268
- where(SQL::BooleanExpression.new(opts[:all_patterns] ? :AND : :OR, *conds))
338
+ where(SQL::BooleanExpression.new(:AND, *conds))
269
339
  else
270
340
  conds = Array(columns).map do |c|
271
341
  SQL::BooleanExpression.new(:OR, *Array(patterns).map{|pat| SQL::StringExpression.like(c, pat, opts)})
272
342
  end
273
- where(SQL::BooleanExpression.new(opts[:all_columns] ? :AND : :OR, *conds))
343
+ where(SQL::BooleanExpression.new(column_op, *conds))
274
344
  end
275
345
  end
276
346
 
@@ -283,7 +353,7 @@ module Sequel
283
353
  # DB[:items].group{[a, sum(b)]} # SELECT * FROM items GROUP BY a, sum(b)
284
354
  def group(*columns, &block)
285
355
  virtual_row_columns(columns, block)
286
- clone(:group => (columns.compact.empty? ? nil : columns))
356
+ clone(:group => (columns.compact.empty? ? nil : columns.freeze))
287
357
  end
288
358
 
289
359
  # Alias of group
@@ -305,15 +375,15 @@ module Sequel
305
375
  # # SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name
306
376
  # # => [{:first_name=>'a', :last_name=>'b', :count=>1}, ...]
307
377
  #
308
- # DB[:items].group_and_count(:first_name___name).all
378
+ # DB[:items].group_and_count(Sequel[:first_name].as(:name)).all
309
379
  # # SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
310
380
  # # => [{:name=>'a', :count=>1}, ...]
311
381
  #
312
- # DB[:items].group_and_count{substr(first_name, 1, 1).as(initial)}.all
382
+ # DB[:items].group_and_count{substr(:first_name, 1, 1).as(:initial)}.all
313
383
  # # SELECT substr(first_name, 1, 1) AS initial, count(*) AS count FROM items GROUP BY substr(first_name, 1, 1)
314
384
  # # => [{:initial=>'a', :count=>1}, ...]
315
385
  def group_and_count(*columns, &block)
316
- select_group(*columns, &block).select_more(COUNT_OF_ALL_AS_COUNT)
386
+ select_group(*columns, &block).select_append(COUNT_OF_ALL_AS_COUNT)
317
387
  end
318
388
 
319
389
  # Returns a copy of the dataset with the given columns added to the list of
@@ -347,10 +417,10 @@ module Sequel
347
417
 
348
418
  # Returns a copy of the dataset with the HAVING conditions changed. See #where for argument types.
349
419
  #
350
- # DB[:items].group(:sum).having(:sum=>10)
420
+ # DB[:items].group(:sum).having(sum: 10)
351
421
  # # SELECT * FROM items GROUP BY sum HAVING (sum = 10)
352
422
  def having(*cond, &block)
353
- _filter(:having, *cond, &block)
423
+ add_filter(:having, cond, &block)
354
424
  end
355
425
 
356
426
  # Adds an INTERSECT clause using a second dataset object.
@@ -365,10 +435,10 @@ module Sequel
365
435
  # DB[:items].intersect(DB[:other_items])
366
436
  # # SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1
367
437
  #
368
- # DB[:items].intersect(DB[:other_items], :all=>true, :from_self=>false)
438
+ # DB[:items].intersect(DB[:other_items], all: true, from_self: false)
369
439
  # # SELECT * FROM items INTERSECT ALL SELECT * FROM other_items
370
440
  #
371
- # DB[:items].intersect(DB[:other_items], :alias=>:i)
441
+ # DB[:items].intersect(DB[:other_items], alias: :i)
372
442
  # # SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS i
373
443
  def intersect(dataset, opts=OPTS)
374
444
  raise(InvalidOperation, "INTERSECT not supported") unless supports_intersect_except?
@@ -379,20 +449,25 @@ module Sequel
379
449
  # Inverts the current WHERE and HAVING clauses. If there is neither a
380
450
  # WHERE or HAVING clause, adds a WHERE clause that is always false.
381
451
  #
382
- # DB[:items].where(:category => 'software').invert
452
+ # DB[:items].where(category: 'software').invert
383
453
  # # SELECT * FROM items WHERE (category != 'software')
384
454
  #
385
- # DB[:items].where(:category => 'software', :id=>3).invert
455
+ # DB[:items].where(category: 'software', id: 3).invert
386
456
  # # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
457
+ #
458
+ # See documentation for exclude for how inversion is handled in regards
459
+ # to SQL 3-valued boolean logic.
387
460
  def invert
388
- having, where = @opts.values_at(:having, :where)
389
- if having.nil? && where.nil?
390
- where(false)
391
- else
392
- o = {}
393
- o[:having] = SQL::BooleanExpression.invert(having) if having
394
- o[:where] = SQL::BooleanExpression.invert(where) if where
395
- clone(o)
461
+ cached_dataset(:_invert_ds) do
462
+ having, where = @opts.values_at(:having, :where)
463
+ if having.nil? && where.nil?
464
+ where(false)
465
+ else
466
+ o = {}
467
+ o[:having] = SQL::BooleanExpression.invert(having) if having
468
+ o[:where] = SQL::BooleanExpression.invert(where) if where
469
+ clone(o)
470
+ end
396
471
  end
397
472
  end
398
473
 
@@ -433,6 +508,7 @@ module Sequel
433
508
  # argument.
434
509
  # :implicit_qualifier :: The name to use for qualifying implicit conditions. By default,
435
510
  # the last joined or primary table is used.
511
+ # :join_using :: Force the using of JOIN USING, even if +expr+ is not an array of symbols.
436
512
  # :reset_implicit_qualifier :: Can set to false to ignore this join when future joins determine qualifier
437
513
  # for implicit conditions.
438
514
  # :qualify :: Can be set to false to not do any implicit qualification. Can be set
@@ -449,10 +525,10 @@ module Sequel
449
525
  # DB[:a].join_table(:cross, :b)
450
526
  # # SELECT * FROM a CROSS JOIN b
451
527
  #
452
- # DB[:a].join_table(:inner, DB[:b], :c=>d)
528
+ # DB[:a].join_table(:inner, DB[:b], c: d)
453
529
  # # SELECT * FROM a INNER JOIN (SELECT * FROM b) AS t1 ON (t1.c = a.d)
454
530
  #
455
- # DB[:a].join_table(:left, :b___c, [:d])
531
+ # DB[:a].join_table(:left, Sequel[:b].as(:c), [:d])
456
532
  # # SELECT * FROM a LEFT JOIN b AS c USING (d)
457
533
  #
458
534
  # DB[:a].natural_join(:b).join_table(:inner, :c) do |ta, jta, js|
@@ -466,7 +542,7 @@ module Sequel
466
542
  return s.join_table(type, ds, expr, options, &block)
467
543
  end
468
544
 
469
- using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
545
+ using_join = options[:join_using] || (expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)})
470
546
  if using_join && !supports_join_using?
471
547
  h = {}
472
548
  expr.each{|e| h[e] = e}
@@ -474,8 +550,6 @@ module Sequel
474
550
  end
475
551
 
476
552
  table_alias = options[:table_alias]
477
- last_alias = options[:implicit_qualifier]
478
- qualify_type = options[:qualify]
479
553
 
480
554
  if table.is_a?(SQL::AliasedExpression)
481
555
  table_expr = if table_alias
@@ -505,16 +579,17 @@ module Sequel
505
579
  raise(Sequel::Error, "can't use a block if providing an array of symbols as expr") if block
506
580
  SQL::JoinUsingClause.new(expr, type, table_expr)
507
581
  else
508
- last_alias ||= @opts[:last_joined_table] || first_source_alias
582
+ last_alias = options[:implicit_qualifier] || @opts[:last_joined_table] || first_source_alias
583
+ qualify_type = options[:qualify]
509
584
  if Sequel.condition_specifier?(expr)
510
- expr = expr.collect do |k, v|
585
+ expr = expr.map do |k, v|
511
586
  qualify_type = default_join_table_qualification if qualify_type.nil?
512
587
  case qualify_type
513
588
  when false
514
589
  nil # Do no qualification
515
590
  when :deep
516
- k = Sequel::Qualifier.new(self, table_name).transform(k)
517
- v = Sequel::Qualifier.new(self, last_alias).transform(v)
591
+ k = Sequel::Qualifier.new(table_name).transform(k)
592
+ v = Sequel::Qualifier.new(last_alias).transform(v)
518
593
  else
519
594
  k = qualified_column_name(k, table_name) if k.is_a?(Symbol)
520
595
  v = qualified_column_name(v, last_alias) if v.is_a?(Symbol)
@@ -524,13 +599,13 @@ module Sequel
524
599
  expr = SQL::BooleanExpression.from_value_pairs(expr)
525
600
  end
526
601
  if block
527
- expr2 = yield(table_name, last_alias, @opts[:join] || [])
602
+ expr2 = yield(table_name, last_alias, @opts[:join] || EMPTY_ARRAY)
528
603
  expr = expr ? SQL::BooleanExpression.new(:AND, expr, expr2) : expr2
529
604
  end
530
605
  SQL::JoinOnClause.new(expr, type, table_expr)
531
606
  end
532
607
 
533
- opts = {:join => (@opts[:join] || []) + [join]}
608
+ opts = {:join => ((@opts[:join] || EMPTY_ARRAY) + [join]).freeze}
534
609
  opts[:last_joined_table] = table_name unless options[:reset_implicit_qualifier] == false
535
610
  opts[:num_dataset_sources] = table_alias_num if table_alias_num
536
611
  clone(opts)
@@ -542,7 +617,7 @@ module Sequel
542
617
  UNCONDITIONED_JOIN_TYPES.each do |jtype|
543
618
  class_eval(<<-END, __FILE__, __LINE__+1)
544
619
  def #{jtype}_join(table, opts=Sequel::OPTS)
545
- raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if block_given?
620
+ raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if defined?(yield)
546
621
  raise(Sequel::Error, '#{jtype}_join 2nd argument should be an options hash, not conditions') unless opts.is_a?(Hash)
547
622
  join_table(:#{jtype}, table, nil, opts)
548
623
  end
@@ -553,10 +628,10 @@ module Sequel
553
628
  # or JOIN clauses, it will surround the subquery with LATERAL to enable it
554
629
  # to deal with previous tables in the query:
555
630
  #
556
- # DB.from(:a, DB[:b].where(:a__c=>:b__d).lateral)
631
+ # DB.from(:a, DB[:b].where(Sequel[:a][:c]=>Sequel[:b][:d]).lateral)
557
632
  # # SELECT * FROM a, LATERAL (SELECT * FROM b WHERE (a.c = b.d))
558
633
  def lateral
559
- clone(:lateral=>true)
634
+ cached_dataset(:_lateral_ds){clone(:lateral=>true)}
560
635
  end
561
636
 
562
637
  # If given an integer, the dataset will contain only the first l results.
@@ -603,20 +678,79 @@ module Sequel
603
678
  clone(:lock => style)
604
679
  end
605
680
 
681
+ # Return a dataset with a WHEN MATCHED THEN DELETE clause added to the
682
+ # MERGE statement. If a block is passed, treat it as a virtual row and
683
+ # use it as additional conditions for the match.
684
+ #
685
+ # merge_delete
686
+ # # WHEN MATCHED THEN DELETE
687
+ #
688
+ # merge_delete{a > 30}
689
+ # # WHEN MATCHED AND (a > 30) THEN DELETE
690
+ def merge_delete(&block)
691
+ _merge_when(:type=>:delete, &block)
692
+ end
693
+
694
+ # Return a dataset with a WHEN NOT MATCHED THEN INSERT clause added to the
695
+ # MERGE statement. If a block is passed, treat it as a virtual row and
696
+ # use it as additional conditions for the match.
697
+ #
698
+ # The arguments provided can be any arguments that would be accepted by
699
+ # #insert.
700
+ #
701
+ # merge_insert(i1: :i2, a: Sequel[:b]+11)
702
+ # # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
703
+ #
704
+ # merge_insert(:i2, Sequel[:b]+11){a > 30}
705
+ # # WHEN NOT MATCHED AND (a > 30) THEN INSERT VALUES (i2, (b + 11))
706
+ def merge_insert(*values, &block)
707
+ _merge_when(:type=>:insert, :values=>values, &block)
708
+ end
709
+
710
+ # Return a dataset with a WHEN MATCHED THEN UPDATE clause added to the
711
+ # MERGE statement. If a block is passed, treat it as a virtual row and
712
+ # use it as additional conditions for the match.
713
+ #
714
+ # merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
715
+ # # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
716
+ #
717
+ # merge_update(i1: :i2){a > 30}
718
+ # # WHEN MATCHED AND (a > 30) THEN UPDATE SET i1 = i2
719
+ def merge_update(values, &block)
720
+ _merge_when(:type=>:update, :values=>values, &block)
721
+ end
722
+
723
+ # Return a dataset with the source and join condition to use for the MERGE statement.
724
+ #
725
+ # merge_using(:m2, i1: :i2)
726
+ # # USING m2 ON (i1 = i2)
727
+ def merge_using(source, join_condition)
728
+ clone(:merge_using => [source, join_condition].freeze)
729
+ end
730
+
606
731
  # Returns a cloned dataset without a row_proc.
607
732
  #
608
- # ds = DB[:items]
609
- # ds.row_proc = proc(&:invert)
733
+ # ds = DB[:items].with_row_proc(:invert.to_proc)
610
734
  # ds.all # => [{2=>:id}]
611
735
  # ds.naked.all # => [{:id=>2}]
612
736
  def naked
613
- ds = clone
614
- ds.row_proc = nil
615
- ds
737
+ cached_dataset(:_naked_ds){with_row_proc(nil)}
738
+ end
739
+
740
+ # Returns a copy of the dataset that will raise a DatabaseLockTimeout instead
741
+ # of waiting for rows that are locked by another transaction
742
+ #
743
+ # DB[:items].for_update.nowait
744
+ # # SELECT * FROM items FOR UPDATE NOWAIT
745
+ def nowait
746
+ cached_dataset(:_nowait_ds) do
747
+ raise(Error, 'This dataset does not support raises errors instead of waiting for locked rows') unless supports_nowait?
748
+ clone(:nowait=>true)
749
+ end
616
750
  end
617
751
 
618
752
  # Returns a copy of the dataset with a specified order. Can be safely combined with limit.
619
- # If you call limit with an offset, it will override override the offset if you've called
753
+ # If you call limit with an offset, it will override the offset if you've called
620
754
  # offset first.
621
755
  #
622
756
  # DB[:items].offset(10) # SELECT * FROM items OFFSET 10
@@ -628,17 +762,17 @@ module Sequel
628
762
  clone(:offset => o)
629
763
  end
630
764
 
631
- # Adds an alternate filter to an existing filter using OR. If no filter
632
- # exists an +Error+ is raised.
765
+ # Adds an alternate filter to an existing WHERE clause using OR. If there
766
+ # is no WHERE clause, then the default is WHERE true, and OR would be redundant,
767
+ # so return the dataset in that case.
633
768
  #
634
769
  # DB[:items].where(:a).or(:b) # SELECT * FROM items WHERE a OR b
770
+ # DB[:items].or(:b) # SELECT * FROM items
635
771
  def or(*cond, &block)
636
- cond = cond.first if cond.size == 1
637
- v = @opts[:where]
638
- if v.nil? || (cond.respond_to?(:empty?) && cond.empty? && !block)
639
- clone
772
+ if @opts[:where].nil?
773
+ self
640
774
  else
641
- clone(:where => SQL::BooleanExpression.new(:OR, v, filter_expr(cond, &block)))
775
+ add_filter(:where, cond, false, :OR, &block)
642
776
  end
643
777
  end
644
778
 
@@ -651,19 +785,24 @@ module Sequel
651
785
  # DB[:items].order(:name) # SELECT * FROM items ORDER BY name
652
786
  # DB[:items].order(:a, :b) # SELECT * FROM items ORDER BY a, b
653
787
  # DB[:items].order(Sequel.lit('a + b')) # SELECT * FROM items ORDER BY a + b
654
- # DB[:items].order(:a + :b) # SELECT * FROM items ORDER BY (a + b)
788
+ # DB[:items].order(Sequel[:a] + :b) # SELECT * FROM items ORDER BY (a + b)
655
789
  # DB[:items].order(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name DESC
656
790
  # DB[:items].order(Sequel.asc(:name, :nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
657
791
  # DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
658
792
  # DB[:items].order(nil) # SELECT * FROM items
659
793
  def order(*columns, &block)
660
794
  virtual_row_columns(columns, block)
661
- clone(:order => (columns.compact.empty?) ? nil : columns)
795
+ clone(:order => (columns.compact.empty?) ? nil : columns.freeze)
662
796
  end
663
797
 
664
- # Alias of order_more, for naming consistency with order_prepend.
798
+ # Returns a copy of the dataset with the order columns added
799
+ # to the end of the existing order.
800
+ #
801
+ # DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
802
+ # DB[:items].order(:a).order_append(:b) # SELECT * FROM items ORDER BY a, b
665
803
  def order_append(*columns, &block)
666
- order_more(*columns, &block)
804
+ columns = @opts[:order] + columns if @opts[:order]
805
+ order(*columns, &block)
667
806
  end
668
807
 
669
808
  # Alias of order
@@ -671,14 +810,9 @@ module Sequel
671
810
  order(*columns, &block)
672
811
  end
673
812
 
674
- # Returns a copy of the dataset with the order columns added
675
- # to the end of the existing order.
676
- #
677
- # DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
678
- # DB[:items].order(:a).order_more(:b) # SELECT * FROM items ORDER BY a, b
813
+ # Alias of order_append.
679
814
  def order_more(*columns, &block)
680
- columns = @opts[:order] + columns if @opts[:order]
681
- order(*columns, &block)
815
+ order_append(*columns, &block)
682
816
  end
683
817
 
684
818
  # Returns a copy of the dataset with the order columns added
@@ -688,25 +822,30 @@ module Sequel
688
822
  # DB[:items].order(:a).order_prepend(:b) # SELECT * FROM items ORDER BY b, a
689
823
  def order_prepend(*columns, &block)
690
824
  ds = order(*columns, &block)
691
- @opts[:order] ? ds.order_more(*@opts[:order]) : ds
825
+ @opts[:order] ? ds.order_append(*@opts[:order]) : ds
692
826
  end
693
827
 
694
828
  # Qualify to the given table, or first source if no table is given.
695
829
  #
696
- # DB[:items].where(:id=>1).qualify
830
+ # DB[:items].where(id: 1).qualify
697
831
  # # SELECT items.* FROM items WHERE (items.id = 1)
698
832
  #
699
- # DB[:items].where(:id=>1).qualify(:i)
833
+ # DB[:items].where(id: 1).qualify(:i)
700
834
  # # SELECT i.* FROM items WHERE (i.id = 1)
701
- def qualify(table=first_source)
835
+ def qualify(table=(cache=true; first_source))
702
836
  o = @opts
703
- return clone if o[:sql]
704
- h = {}
705
- (o.keys & QUALIFY_KEYS).each do |k|
706
- h[k] = qualified_expression(o[k], table)
837
+ return self if o[:sql]
838
+
839
+ pr = proc do
840
+ h = {}
841
+ (o.keys & QUALIFY_KEYS).each do |k|
842
+ h[k] = qualified_expression(o[k], table)
843
+ end
844
+ h[:select] = [SQL::ColumnAll.new(table)].freeze if !o[:select] || o[:select].empty?
845
+ clone(h)
707
846
  end
708
- h[:select] = [SQL::ColumnAll.new(table)] if !o[:select] || o[:select].empty?
709
- clone(h)
847
+
848
+ cache ? cached_dataset(:_qualify_ds, &pr) : pr.call
710
849
  end
711
850
 
712
851
  # Modify the RETURNING clause, only supported on a few databases. If returning
@@ -717,9 +856,26 @@ module Sequel
717
856
  # DB[:items].returning # RETURNING *
718
857
  # DB[:items].returning(nil) # RETURNING NULL
719
858
  # DB[:items].returning(:id, :name) # RETURNING id, name
859
+ #
860
+ # DB[:items].returning.insert(:a=>1) do |hash|
861
+ # # hash for each row inserted, with values for all columns
862
+ # end
863
+ # DB[:items].returning.update(:a=>1) do |hash|
864
+ # # hash for each row updated, with values for all columns
865
+ # end
866
+ # DB[:items].returning.delete(:a=>1) do |hash|
867
+ # # hash for each row deleted, with values for all columns
868
+ # end
720
869
  def returning(*values)
721
- raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
722
- clone(:returning=>values)
870
+ if values.empty?
871
+ cached_dataset(:_returning_ds) do
872
+ raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
873
+ clone(:returning=>EMPTY_ARRAY)
874
+ end
875
+ else
876
+ raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
877
+ clone(:returning=>values.freeze)
878
+ end
723
879
  end
724
880
 
725
881
  # Returns a copy of the dataset with the order reversed. If no order is
@@ -730,8 +886,12 @@ module Sequel
730
886
  # DB[:items].order(:id).reverse # SELECT * FROM items ORDER BY id DESC
731
887
  # DB[:items].order(:id).reverse(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name ASC
732
888
  def reverse(*order, &block)
733
- virtual_row_columns(order, block)
734
- order(*invert_order(order.empty? ? @opts[:order] : order))
889
+ if order.empty? && !block
890
+ cached_dataset(:_reverse_ds){order(*invert_order(@opts[:order]))}
891
+ else
892
+ virtual_row_columns(order, block)
893
+ order(*invert_order(order.empty? ? @opts[:order] : order.freeze))
894
+ end
735
895
  end
736
896
 
737
897
  # Alias of +reverse+
@@ -748,7 +908,7 @@ module Sequel
748
908
  # DB[:items].select{[a, sum(b)]} # SELECT a, sum(b) FROM items
749
909
  def select(*columns, &block)
750
910
  virtual_row_columns(columns, block)
751
- clone(:select => columns)
911
+ clone(:select => columns.freeze)
752
912
  end
753
913
 
754
914
  # Returns a copy of the dataset selecting the wildcard if no arguments
@@ -760,9 +920,9 @@ module Sequel
760
920
  # DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
761
921
  def select_all(*tables)
762
922
  if tables.empty?
763
- clone(:select => nil)
923
+ cached_dataset(:_select_all_ds){clone(:select => nil)}
764
924
  else
765
- select(*tables.map{|t| i, a = split_alias(t); a || i}.map{|t| SQL::ColumnAll.new(t)})
925
+ select(*tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze)
766
926
  end
767
927
  end
768
928
 
@@ -777,7 +937,7 @@ module Sequel
777
937
  cur_sel = @opts[:select]
778
938
  if !cur_sel || cur_sel.empty?
779
939
  unless supports_select_all_and_column?
780
- return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_more(*columns, &block)
940
+ return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_append(*columns, &block)
781
941
  end
782
942
  cur_sel = [WILDCARD]
783
943
  end
@@ -791,7 +951,7 @@ module Sequel
791
951
  # DB[:items].select_group(:a, :b)
792
952
  # # SELECT a, b FROM items GROUP BY a, b
793
953
  #
794
- # DB[:items].select_group(:c___a){f(c2)}
954
+ # DB[:items].select_group(Sequel[:c].as(:a)){f(c2)}
795
955
  # # SELECT c AS a, f(c2) FROM items GROUP BY c, f(c2)
796
956
  def select_group(*columns, &block)
797
957
  virtual_row_columns(columns, block)
@@ -827,42 +987,35 @@ module Sequel
827
987
  end
828
988
  end
829
989
 
830
- # Skip locked rows when returning results from this dataset.
831
- def skip_locked
832
- raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
833
- clone(:skip_locked=>true)
990
+ # Specify that the check for limits/offsets when updating/deleting be skipped for the dataset.
991
+ def skip_limit_check
992
+ cached_dataset(:_skip_limit_check_ds) do
993
+ clone(:skip_limit_check=>true)
994
+ end
834
995
  end
835
996
 
836
- # Unbind bound variables from this dataset's filter and return an array of two
837
- # objects. The first object is a modified dataset where the filter has been
838
- # replaced with one that uses bound variable placeholders. The second object
839
- # is the hash of unbound variables. You can then prepare and execute (or just
840
- # call) the dataset with the bound variables to get results.
841
- #
842
- # ds, bv = DB[:items].where(:a=>1).unbind
843
- # ds # SELECT * FROM items WHERE (a = $a)
844
- # bv # {:a => 1}
845
- # ds.call(:select, bv)
846
- def unbind
847
- u = Unbinder.new
848
- ds = clone(:where=>u.transform(opts[:where]), :join=>u.transform(opts[:join]))
849
- [ds, u.binds]
997
+ # Skip locked rows when returning results from this dataset.
998
+ def skip_locked
999
+ cached_dataset(:_skip_locked_ds) do
1000
+ raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
1001
+ clone(:skip_locked=>true)
1002
+ end
850
1003
  end
851
1004
 
852
1005
  # Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
853
1006
  #
854
- # DB[:items].group(:a).having(:a=>1).where(:b).unfiltered
1007
+ # DB[:items].group(:a).having(a: 1).where(:b).unfiltered
855
1008
  # # SELECT * FROM items GROUP BY a
856
1009
  def unfiltered
857
- clone(:where => nil, :having => nil)
1010
+ cached_dataset(:_unfiltered_ds){clone(:where => nil, :having => nil)}
858
1011
  end
859
1012
 
860
1013
  # Returns a copy of the dataset with no grouping (GROUP or HAVING clause) applied.
861
1014
  #
862
- # DB[:items].group(:a).having(:a=>1).where(:b).ungrouped
1015
+ # DB[:items].group(:a).having(a: 1).where(:b).ungrouped
863
1016
  # # SELECT * FROM items WHERE b
864
1017
  def ungrouped
865
- clone(:group => nil, :having => nil)
1018
+ cached_dataset(:_ungrouped_ds){clone(:group => nil, :having => nil)}
866
1019
  end
867
1020
 
868
1021
  # Adds a UNION clause using a second dataset object.
@@ -876,10 +1029,10 @@ module Sequel
876
1029
  # DB[:items].union(DB[:other_items])
877
1030
  # # SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS t1
878
1031
  #
879
- # DB[:items].union(DB[:other_items], :all=>true, :from_self=>false)
1032
+ # DB[:items].union(DB[:other_items], all: true, from_self: false)
880
1033
  # # SELECT * FROM items UNION ALL SELECT * FROM other_items
881
1034
  #
882
- # DB[:items].union(DB[:other_items], :alias=>:i)
1035
+ # DB[:items].union(DB[:other_items], alias: :i)
883
1036
  # # SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS i
884
1037
  def union(dataset, opts=OPTS)
885
1038
  compound_clone(:union, dataset, opts)
@@ -889,32 +1042,24 @@ module Sequel
889
1042
  #
890
1043
  # DB[:items].limit(10, 20).unlimited # SELECT * FROM items
891
1044
  def unlimited
892
- clone(:limit=>nil, :offset=>nil)
1045
+ cached_dataset(:_unlimited_ds){clone(:limit=>nil, :offset=>nil)}
893
1046
  end
894
1047
 
895
1048
  # Returns a copy of the dataset with no order.
896
1049
  #
897
1050
  # DB[:items].order(:a).unordered # SELECT * FROM items
898
1051
  def unordered
899
- order(nil)
1052
+ cached_dataset(:_unordered_ds){clone(:order=>nil)}
900
1053
  end
901
1054
 
902
1055
  # Returns a copy of the dataset with the given WHERE conditions imposed upon it.
903
1056
  #
904
1057
  # Accepts the following argument types:
905
1058
  #
906
- # Hash :: list of equality/inclusion expressions
907
- # Array :: depends:
908
- # * If first member is a string, assumes the rest of the arguments
909
- # are parameters and interpolates them into the string.
910
- # * If all members are arrays of length two, treats the same way
911
- # as a hash, except it allows for duplicate keys to be
912
- # specified.
913
- # * Otherwise, treats each argument as a separate condition.
914
- # String :: taken literally
1059
+ # Hash, Array of pairs :: list of equality/inclusion expressions
915
1060
  # Symbol :: taken as a boolean column argument (e.g. WHERE active)
916
- # Sequel::SQL::BooleanExpression :: an existing condition expression,
917
- # probably created using the Sequel expression filter DSL.
1061
+ # Sequel::SQL::BooleanExpression, Sequel::LiteralString :: an existing condition expression, probably created
1062
+ # using the Sequel expression filter DSL.
918
1063
  #
919
1064
  # where also accepts a block, which should return one of the above argument
920
1065
  # types, and is treated the same way. This block yields a virtual row object,
@@ -925,16 +1070,16 @@ module Sequel
925
1070
  #
926
1071
  # Examples:
927
1072
  #
928
- # DB[:items].where(:id => 3)
1073
+ # DB[:items].where(id: 3)
929
1074
  # # SELECT * FROM items WHERE (id = 3)
930
1075
  #
931
- # DB[:items].where('price < ?', 100)
1076
+ # DB[:items].where(Sequel.lit('price < ?', 100))
932
1077
  # # SELECT * FROM items WHERE price < 100
933
1078
  #
934
1079
  # DB[:items].where([[:id, [1,2,3]], [:id, 0..10]])
935
1080
  # # SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
936
1081
  #
937
- # DB[:items].where('price < 100')
1082
+ # DB[:items].where(Sequel.lit('price < 100'))
938
1083
  # # SELECT * FROM items WHERE price < 100
939
1084
  #
940
1085
  # DB[:items].where(:active)
@@ -945,21 +1090,34 @@ module Sequel
945
1090
  #
946
1091
  # Multiple where calls can be chained for scoping:
947
1092
  #
948
- # software = dataset.where(:category => 'software').where{price < 100}
1093
+ # software = dataset.where(category: 'software').where{price < 100}
949
1094
  # # SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
950
1095
  #
951
1096
  # See the {"Dataset Filtering" guide}[rdoc-ref:doc/dataset_filtering.rdoc] for more examples and details.
952
1097
  def where(*cond, &block)
953
- _filter(:where, *cond, &block)
1098
+ add_filter(:where, cond, &block)
954
1099
  end
955
1100
 
1101
+ # Return a clone of the dataset with an addition named window that can be
1102
+ # referenced in window functions. See Sequel::SQL::Window for a list of
1103
+ # options that can be passed in. Example:
1104
+ #
1105
+ # DB[:items].window(:w, :partition=>:c1, :order=>:c2)
1106
+ # # SELECT * FROM items WINDOW w AS (PARTITION BY c1 ORDER BY c2)
1107
+ def window(name, opts)
1108
+ clone(:window=>((@opts[:window]||EMPTY_ARRAY) + [[name, SQL::Window.new(opts)].freeze]).freeze)
1109
+ end
1110
+
956
1111
  # Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
957
1112
  # A common table expression acts as an inline view for the query.
1113
+ #
958
1114
  # Options:
959
1115
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
960
1116
  # :recursive :: Specify that this is a recursive CTE
1117
+ # :materialized :: Set to false to force inlining of the CTE, or true to force not inlining
1118
+ # the CTE (PostgreSQL 12+/SQLite 3.35+).
961
1119
  #
962
- # DB[:items].with(:items, DB[:syx].where(:name.like('A%')))
1120
+ # DB[:items].with(:items, DB[:syx].where(Sequel[:name].like('A%')))
963
1121
  # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
964
1122
  def with(name, dataset, opts=OPTS)
965
1123
  raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
@@ -967,28 +1125,68 @@ module Sequel
967
1125
  s, ds = hoist_cte(dataset)
968
1126
  s.with(name, ds, opts)
969
1127
  else
970
- clone(:with=>(@opts[:with]||[]) + [Hash[opts].merge!(:name=>name, :dataset=>dataset)])
1128
+ clone(:with=>((@opts[:with]||EMPTY_ARRAY) + [Hash[opts].merge!(:name=>name, :dataset=>dataset)]).freeze)
971
1129
  end
972
1130
  end
973
1131
 
974
1132
  # Add a recursive common table expression (CTE) with the given name, a dataset that
975
1133
  # defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
976
- # of the CTE. Options:
1134
+ # of the CTE.
1135
+ #
1136
+ # Options:
977
1137
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
978
1138
  # :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
979
1139
  #
1140
+ # PostgreSQL 14+ Options:
1141
+ # :cycle :: Stop recursive searching when a cycle is detected. Includes two columns in the
1142
+ # result of the CTE, a cycle column indicating whether a cycle was detected for
1143
+ # the current row, and a path column for the path traversed to get to the current
1144
+ # row. If given, must be a hash with the following keys:
1145
+ # :columns :: (required) The column or array of columns to use to detect a cycle.
1146
+ # If the value of these columns match columns already traversed, then
1147
+ # a cycle is detected, and recursive searching will not traverse beyond
1148
+ # the cycle (the CTE will include the row where the cycle was detected).
1149
+ # :cycle_column :: The name of the cycle column in the output, defaults to :is_cycle.
1150
+ # :cycle_value :: The value of the cycle column in the output if the current row was
1151
+ # detected as a cycle, defaults to true.
1152
+ # :noncycle_value :: The value of the cycle column in the output if the current row
1153
+ # was not detected as a cycle, defaults to false. Only respected
1154
+ # if :cycle_value is given.
1155
+ # :path_column :: The name of the path column in the output, defaults to :path.
1156
+ # :search :: Include an order column in the result of the CTE that allows for breadth or
1157
+ # depth first searching. If given, must be a hash with the following keys:
1158
+ # :by :: (required) The column or array of columns to search by.
1159
+ # :order_column :: The name of the order column in the output, defaults to :ordercol.
1160
+ # :type :: Set to :breadth to use breadth-first searching (depth-first searching
1161
+ # is the default).
1162
+ #
980
1163
  # DB[:t].with_recursive(:t,
981
- # DB[:i1].select(:id, :parent_id).where(:parent_id=>nil),
982
- # DB[:i1].join(:t, :id=>:parent_id).select(:i1__id, :i1__parent_id),
1164
+ # DB[:i1].select(:id, :parent_id).where(parent_id: nil),
1165
+ # DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
983
1166
  # :args=>[:id, :parent_id])
984
1167
  #
985
- # # WITH RECURSIVE "t"("id", "parent_id") AS (
986
- # # SELECT "id", "parent_id" FROM "i1" WHERE ("parent_id" IS NULL)
1168
+ # # WITH RECURSIVE t(id, parent_id) AS (
1169
+ # # SELECT id, parent_id FROM i1 WHERE (parent_id IS NULL)
987
1170
  # # UNION ALL
988
- # # SELECT "i1"."id", "i1"."parent_id" FROM "i1" INNER JOIN "t" ON ("t"."id" = "i1"."parent_id")
989
- # # ) SELECT * FROM "t"
1171
+ # # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
1172
+ # # ) SELECT * FROM t
1173
+ #
1174
+ # DB[:t].with_recursive(:t,
1175
+ # DB[:i1].where(parent_id: nil),
1176
+ # DB[:i1].join(:t, id: :parent_id).select_all(:i1),
1177
+ # search: {by: :id, type: :breadth},
1178
+ # cycle: {columns: :id, cycle_value: 1, noncycle_value: 2})
1179
+ #
1180
+ # # WITH RECURSIVE t AS (
1181
+ # # SELECT * FROM i1 WHERE (parent_id IS NULL)
1182
+ # # UNION ALL
1183
+ # # (SELECT i1.* FROM i1 INNER JOIN t ON (t.id = i1.parent_id))
1184
+ # # )
1185
+ # # SEARCH BREADTH FIRST BY id SET ordercol
1186
+ # # CYCLE id SET is_cycle TO 1 DEFAULT 2 USING path
1187
+ # # SELECT * FROM t
990
1188
  def with_recursive(name, nonrecursive, recursive, opts=OPTS)
991
- raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
1189
+ raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
992
1190
  if hoist_cte?(nonrecursive)
993
1191
  s, ds = hoist_cte(nonrecursive)
994
1192
  s.with_recursive(name, ds, recursive, opts)
@@ -996,10 +1194,42 @@ module Sequel
996
1194
  s, ds = hoist_cte(recursive)
997
1195
  s.with_recursive(name, nonrecursive, ds, opts)
998
1196
  else
999
- clone(:with=>(@opts[:with]||[]) + [Hash[opts].merge!(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
1197
+ clone(:with=>((@opts[:with]||EMPTY_ARRAY) + [Hash[opts].merge!(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))]).freeze)
1000
1198
  end
1001
1199
  end
1002
1200
 
1201
+ if TRUE_FREEZE
1202
+ # Return a clone of the dataset extended with the given modules.
1203
+ # Note that like Object#extend, when multiple modules are provided
1204
+ # as arguments the cloned dataset is extended with the modules in reverse
1205
+ # order. If a block is provided, a DatasetModule is created using the block and
1206
+ # the clone is extended with that module after any modules given as arguments.
1207
+ def with_extend(*mods, &block)
1208
+ c = _clone(:freeze=>false)
1209
+ c.extend(*mods) unless mods.empty?
1210
+ c.extend(DatasetModule.new(&block)) if block
1211
+ c.freeze
1212
+ end
1213
+ else
1214
+ # :nocov:
1215
+ def with_extend(*mods, &block) # :nodoc:
1216
+ c = clone
1217
+ c.extend(*mods) unless mods.empty?
1218
+ c.extend(DatasetModule.new(&block)) if block
1219
+ c
1220
+ end
1221
+ # :nocov:
1222
+ end
1223
+
1224
+ # Returns a cloned dataset with the given row_proc.
1225
+ #
1226
+ # ds = DB[:items]
1227
+ # ds.all # => [{:id=>2}]
1228
+ # ds.with_row_proc(:invert.to_proc).all # => [{2=>:id}]
1229
+ def with_row_proc(callable)
1230
+ clone(:row_proc=>callable)
1231
+ end
1232
+
1003
1233
  # Returns a copy of the dataset with the static SQL used. This is useful if you want
1004
1234
  # to keep the same row_proc/graph, but change the SQL used to custom SQL.
1005
1235
  #
@@ -1012,9 +1242,27 @@ module Sequel
1012
1242
  # You can also provide a method name and arguments to call to get the SQL:
1013
1243
  #
1014
1244
  # DB[:items].with_sql(:insert_sql, :b=>1) # INSERT INTO items (b) VALUES (1)
1245
+ #
1246
+ # Note that datasets that specify custom SQL using this method will generally
1247
+ # ignore future dataset methods that modify the SQL used, as specifying custom SQL
1248
+ # overrides Sequel's SQL generator. You should probably limit yourself to the following
1249
+ # dataset methods when using this method, or use the implicit_subquery extension:
1250
+ #
1251
+ # * each
1252
+ # * all
1253
+ # * single_record (if only one record could be returned)
1254
+ # * single_value (if only one record could be returned, and a single column is selected)
1255
+ # * map
1256
+ # * as_hash
1257
+ # * to_hash
1258
+ # * to_hash_groups
1259
+ # * delete (if a DELETE statement)
1260
+ # * update (if an UPDATE statement, with no arguments)
1261
+ # * insert (if an INSERT statement, with no arguments)
1262
+ # * truncate (if a TRUNCATE statement, with no arguments)
1015
1263
  def with_sql(sql, *args)
1016
1264
  if sql.is_a?(Symbol)
1017
- sql = send(sql, *args)
1265
+ sql = public_send(sql, *args)
1018
1266
  else
1019
1267
  sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
1020
1268
  end
@@ -1025,26 +1273,33 @@ module Sequel
1025
1273
 
1026
1274
  # Add the dataset to the list of compounds
1027
1275
  def compound_clone(type, dataset, opts)
1028
- if hoist_cte?(dataset)
1276
+ if dataset.is_a?(Dataset) && dataset.opts[:with] && !supports_cte_in_compounds?
1029
1277
  s, ds = hoist_cte(dataset)
1030
1278
  return s.compound_clone(type, ds, opts)
1031
1279
  end
1032
- ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map(&:dup) + [[type, dataset.compound_from_self, opts[:all]]])
1280
+ ds = compound_from_self.clone(:compounds=>(Array(@opts[:compounds]).map(&:dup) + [[type, dataset.compound_from_self, opts[:all]].freeze]).freeze)
1033
1281
  opts[:from_self] == false ? ds : ds.from_self(opts)
1034
1282
  end
1035
1283
 
1036
1284
  # Return true if the dataset has a non-nil value for any key in opts.
1037
1285
  def options_overlap(opts)
1038
- !(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
1286
+ !(@opts.map{|k,v| k unless v.nil?}.compact & opts).empty?
1039
1287
  end
1040
1288
 
1289
+ # From types allowed to be considered a simple_select_all
1290
+ SIMPLE_SELECT_ALL_ALLOWED_FROM = [Symbol, SQL::Identifier, SQL::QualifiedIdentifier].freeze
1291
+
1041
1292
  # Whether this dataset is a simple select from an underlying table, such as:
1042
1293
  #
1043
1294
  # SELECT * FROM table
1044
1295
  # SELECT table.* FROM table
1045
1296
  def simple_select_all?
1046
- o = @opts.reject{|k,v| v.nil? || NON_SQL_OPTIONS.include?(k)}
1047
- if (f = o[:from]) && f.length == 1 && (f.first.is_a?(Symbol) || f.first.is_a?(SQL::AliasedExpression))
1297
+ return false unless (f = @opts[:from]) && f.length == 1
1298
+ o = @opts.reject{|k,v| v.nil? || non_sql_option?(k)}
1299
+ from = f.first
1300
+ from = from.expression if from.is_a?(SQL::AliasedExpression)
1301
+
1302
+ if SIMPLE_SELECT_ALL_ALLOWED_FROM.any?{|x| from.is_a?(x)}
1048
1303
  case o.length
1049
1304
  when 1
1050
1305
  true
@@ -1060,23 +1315,73 @@ module Sequel
1060
1315
 
1061
1316
  private
1062
1317
 
1063
- # Internal filtering method so it works on either the WHERE or HAVING clauses, with or
1064
- # without inversion.
1065
- def _filter_or_exclude(invert, clause, *cond, &block)
1066
- cond = cond.first if cond.size == 1
1067
- if cond.respond_to?(:empty?) && cond.empty? && !block
1068
- clone
1318
+ # Load the extensions into the receiver, without checking if the receiver is frozen.
1319
+ def _extension!(exts)
1320
+ Sequel.extension(*exts)
1321
+ exts.each do |ext|
1322
+ if pr = Sequel.synchronize{EXTENSIONS[ext]}
1323
+ pr.call(self)
1324
+ else
1325
+ raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
1326
+ end
1327
+ end
1328
+ self
1329
+ end
1330
+
1331
+ # If invert is true, invert the condition.
1332
+ def _invert_filter(cond, invert)
1333
+ if invert
1334
+ SQL::BooleanExpression.invert(cond)
1069
1335
  else
1070
- cond = filter_expr(cond, &block)
1071
- cond = SQL::BooleanExpression.invert(cond) if invert
1072
- cond = SQL::BooleanExpression.new(:AND, @opts[clause], cond) if @opts[clause]
1073
- clone(clause => cond)
1336
+ cond
1337
+ end
1338
+ end
1339
+
1340
+ # Append to the current MERGE WHEN clauses.
1341
+ # Mutates the hash to add the conditions, if a virtual row block is passed.
1342
+ def _merge_when(hash, &block)
1343
+ hash[:conditions] = Sequel.virtual_row(&block) if block
1344
+
1345
+ if merge_when = @opts[:merge_when]
1346
+ clone(:merge_when => (merge_when.dup << hash.freeze).freeze)
1347
+ else
1348
+ clone(:merge_when => [hash.freeze].freeze)
1074
1349
  end
1075
1350
  end
1076
1351
 
1077
- # Internal filter method so it works on either the having or where clauses.
1078
- def _filter(clause, *cond, &block)
1079
- _filter_or_exclude(false, clause, *cond, &block)
1352
+ # Add the given filter condition. Arguments:
1353
+ # clause :: Symbol or which SQL clause to effect, should be :where or :having
1354
+ # cond :: The filter condition to add
1355
+ # invert :: Whether the condition should be inverted (true or false)
1356
+ # combine :: How to combine the condition with an existing condition, should be :AND or :OR
1357
+ def add_filter(clause, cond, invert=false, combine=:AND, &block)
1358
+ if cond == EMPTY_ARRAY && !block
1359
+ raise Error, "must provide an argument to a filtering method if not passing a block"
1360
+ end
1361
+
1362
+ cond = cond.first if cond.size == 1
1363
+
1364
+ empty = cond == OPTS || cond == EMPTY_ARRAY
1365
+
1366
+ if empty && !block
1367
+ self
1368
+ else
1369
+ if cond == nil
1370
+ cond = Sequel::NULL
1371
+ end
1372
+ if empty && block
1373
+ cond = nil
1374
+ end
1375
+
1376
+ cond = _invert_filter(filter_expr(cond, &block), invert)
1377
+ cond = SQL::BooleanExpression.new(combine, @opts[clause], cond) if @opts[clause]
1378
+
1379
+ if cond.nil?
1380
+ cond = Sequel::NULL
1381
+ end
1382
+
1383
+ clone(clause => cond)
1384
+ end
1080
1385
  end
1081
1386
 
1082
1387
  # The default :qualify option to use for join tables if one is not specified.
@@ -1086,29 +1391,27 @@ module Sequel
1086
1391
 
1087
1392
  # SQL expression object based on the expr type. See +where+.
1088
1393
  def filter_expr(expr = nil, &block)
1089
- expr = nil if expr == []
1394
+ expr = nil if expr == EMPTY_ARRAY
1090
1395
 
1091
- if expr && block
1092
- return SQL::BooleanExpression.new(:AND, filter_expr(expr), filter_expr(block))
1093
- elsif block
1094
- expr = block
1396
+ if block
1397
+ cond = filter_expr(Sequel.virtual_row(&block))
1398
+ cond = SQL::BooleanExpression.new(:AND, filter_expr(expr), cond) if expr
1399
+ return cond
1095
1400
  end
1096
1401
 
1097
1402
  case expr
1098
1403
  when Hash
1099
1404
  SQL::BooleanExpression.from_value_pairs(expr)
1100
1405
  when Array
1101
- if (sexpr = expr.at(0)).is_a?(String)
1102
- SQL::PlaceholderLiteralString.new(sexpr, expr[1..-1], true)
1103
- elsif Sequel.condition_specifier?(expr)
1406
+ if Sequel.condition_specifier?(expr)
1104
1407
  SQL::BooleanExpression.from_value_pairs(expr)
1105
1408
  else
1106
- SQL::BooleanExpression.new(:AND, *expr.map{|x| filter_expr(x)})
1409
+ raise Error, "Invalid filter expression: #{expr.inspect}"
1107
1410
  end
1108
- when Proc
1109
- filter_expr(Sequel.virtual_row(&expr))
1110
- when Numeric, SQL::NumericExpression, SQL::StringExpression
1111
- raise(Error, "Invalid filter expression: #{expr.inspect}")
1411
+ when LiteralString
1412
+ LiteralString.new("(#{expr})")
1413
+ when Numeric, SQL::NumericExpression, SQL::StringExpression, Proc, String
1414
+ raise Error, "Invalid filter expression: #{expr.inspect}"
1112
1415
  when TrueClass, FalseClass
1113
1416
  if supports_where_true?
1114
1417
  SQL::BooleanExpression.new(:NOOP, expr)
@@ -1117,10 +1420,10 @@ module Sequel
1117
1420
  else
1118
1421
  SQL::Constants::SQLFALSE
1119
1422
  end
1120
- when String
1121
- LiteralString.new("(#{expr})")
1122
1423
  when PlaceholderLiteralizer::Argument
1123
1424
  expr.transform{|v| filter_expr(v)}
1425
+ when SQL::PlaceholderLiteralString
1426
+ expr.with_parens
1124
1427
  else
1125
1428
  expr
1126
1429
  end
@@ -1130,7 +1433,7 @@ module Sequel
1130
1433
  # clause from the given dataset added to it, and the second a clone of
1131
1434
  # the given dataset with the WITH clause removed.
1132
1435
  def hoist_cte(ds)
1133
- [clone(:with => (opts[:with] || []) + ds.opts[:with]), ds.clone(:with => nil)]
1436
+ [clone(:with => ((opts[:with] || EMPTY_ARRAY) + ds.opts[:with]).freeze), ds.clone(:with => nil)]
1134
1437
  end
1135
1438
 
1136
1439
  # Whether CTEs need to be hoisted from the given ds into the current ds.
@@ -1144,7 +1447,7 @@ module Sequel
1144
1447
  # DB[:items].invert_order([Sequel.desc(:id)]]) #=> [Sequel.asc(:id)]
1145
1448
  # DB[:items].invert_order([:category, Sequel.desc(:price)]) #=> [Sequel.desc(:category), Sequel.asc(:price)]
1146
1449
  def invert_order(order)
1147
- return nil unless order
1450
+ return unless order
1148
1451
  order.map do |f|
1149
1452
  case f
1150
1453
  when SQL::OrderedExpression
@@ -1161,6 +1464,11 @@ module Sequel
1161
1464
  server?(:default)
1162
1465
  end
1163
1466
 
1467
+ # Whether the given option key does not affect the generated SQL.
1468
+ def non_sql_option?(key)
1469
+ NON_SQL_OPTIONS.include?(key)
1470
+ end
1471
+
1164
1472
  # Treat the +block+ as a virtual_row block if not +nil+ and
1165
1473
  # add the resulting columns to the +columns+ array (modifies +columns+).
1166
1474
  def virtual_row_columns(columns, block)