sequel 3.21.0 → 3.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -0,0 +1,182 @@
1
+ # The pg_row_ops extension adds support to Sequel's DSL to make
2
+ # it easier to deal with PostgreSQL row-valued/composite types.
3
+ #
4
+ # To load the extension:
5
+ #
6
+ # Sequel.extension :pg_row_ops
7
+ #
8
+ # The most common usage is passing an expression to Sequel.pg_row_op:
9
+ #
10
+ # r = Sequel.pg_row_op(:row_column)
11
+ #
12
+ # If you have also loaded the pg_row extension, you can use
13
+ # Sequel.pg_row as well:
14
+ #
15
+ # r = Sequel.pg_row(:row_column)
16
+ #
17
+ # Also, on most Sequel expression objects, you can call the pg_row
18
+ # method:
19
+ #
20
+ # r = Sequel.expr(:row_column).pg_row
21
+ #
22
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
23
+ # you can also call Symbol#pg_row:
24
+ #
25
+ # r = :row_column.pg_row
26
+ #
27
+ # There's only fairly basic support currently. You can use the [] method to access
28
+ # a member of the composite type:
29
+ #
30
+ # r[:a] # (row_column).a
31
+ #
32
+ # This can be chained:
33
+ #
34
+ # r[:a][:b] # ((row_column).a).b
35
+ #
36
+ # If you've loaded the pg_array_ops extension, you there is also support for composite
37
+ # types that include arrays, or arrays of composite types:
38
+ #
39
+ # r[1][:a] # (row_column[1]).a
40
+ # r[:a][1] # (row_column).a[1]
41
+ #
42
+ # The only other support is the splat method:
43
+ #
44
+ # r.splat # (row_column.*)
45
+ #
46
+ # The splat method is necessary if you are trying to reference a table's type when the
47
+ # table has the same name as one of it's columns. For example:
48
+ #
49
+ # DB.create_table(:a){Integer :a; Integer :b}
50
+ #
51
+ # Let's say you want to reference the composite type for the table:
52
+ #
53
+ # a = Sequel.pg_row_op(:a)
54
+ # DB[:a].select(a[:b]) # SELECT (a).b FROM a
55
+ #
56
+ # Unfortunately, that doesn't work, as it references the integer column, not the table.
57
+ # The splat method works around this:
58
+ #
59
+ # DB[:a].select(a.splat[:b]) # SELECT (a.*).b FROM a
60
+ #
61
+ # Splat also takes an argument which is used for casting. This is necessary if you
62
+ # want to return the composite type itself, instead of the columns in the composite
63
+ # type. For example:
64
+ #
65
+ # DB[:a].select(a.splat).first # SELECT (a.*) FROM a
66
+ # # => {:a=>1, :b=>2}
67
+ #
68
+ # By casting the expression, you can get a composite type returned:
69
+ #
70
+ # DB[:a].select(a.splat).first # SELECT (a.*)::a FROM a
71
+ # # => {:a=>"(1,2)"} # or {:a=>{:a=>1, :b=>2}} if the "a" type has been registered
72
+ # # with the the pg_row extension
73
+ #
74
+ # This feature is mostly useful for a different way to graph tables:
75
+ #
76
+ # DB[:a].join(:b, :id=>:b_id).select(Sequel.pg_row_op(:a).splat(:a),
77
+ # Sequel.pg_row_op(:b).splat(:b))
78
+ # # SELECT (a.*)::a, (b.*)::b FROM a INNER JOIN b ON (b.id = a.b_id)
79
+ # # => {:a=>{:id=>1, :b_id=>2}, :b=>{:id=>2}}
80
+
81
+ module Sequel
82
+ module Postgres
83
+ # This class represents a composite type expression reference.
84
+ class PGRowOp < SQL::PlaceholderLiteralString
85
+ OPEN = '('.freeze
86
+ CLOSE_DOT = ').'.freeze
87
+ CLOSE_STAR = '.*)'.freeze
88
+ CLOSE_STAR_CAST = '.*)::'.freeze
89
+ EMPTY = "".freeze
90
+ ROW = [OPEN, CLOSE_STAR].freeze
91
+ ROW_CAST = [OPEN, CLOSE_STAR_CAST].freeze
92
+ QUALIFY = [OPEN, CLOSE_DOT].freeze
93
+ WRAP = [EMPTY].freeze
94
+
95
+ # Wrap the expression in a PGRowOp, without changing the
96
+ # SQL it would use.
97
+ def self.wrap(expr)
98
+ PGRowOp.new(WRAP, [expr])
99
+ end
100
+
101
+ # Access a member of the composite type if given a
102
+ # symbol or an SQL::Identifier. For all other access,
103
+ # assuming the pg_array_ops extension is loaded and
104
+ # that it represents an array access. In either
105
+ # case, return a PgRowOp so that access can be cascaded.
106
+ def [](member)
107
+ case member
108
+ when Symbol, SQL::Identifier
109
+ PGRowOp.new(QUALIFY, [self, member])
110
+ else
111
+ PGRowOp.wrap(Sequel.pg_array_op(self)[member])
112
+ end
113
+ end
114
+
115
+ # Use the (identifier).* syntax to reference the members
116
+ # of the composite type as separate columns. Generally
117
+ # used when you want to expand the columns of a composite
118
+ # type to be separate columns in the result set.
119
+ #
120
+ # Sequel.pg_row_op(:a).* # (a).*
121
+ # Sequel.pg_row_op(:a)[:b].* # ((a).b).*
122
+ def *(ce=(arg=false;nil))
123
+ if arg == false
124
+ Sequel::SQL::ColumnAll.new([self])
125
+ else
126
+ super(ce)
127
+ end
128
+ end
129
+
130
+ # Use the (identifier.*) syntax to indicate that this
131
+ # expression represents the composite type of one
132
+ # of the tables being referenced, if it has the same
133
+ # name as one of the columns. If the cast_to argument
134
+ # is given, also cast the expression to that type
135
+ # (which should be a symbol representing the composite type).
136
+ # This is used if you want to return whole table row as a
137
+ # composite type.
138
+ #
139
+ # Sequel.pg_row_op(:a).splat[:b] # (a.*).b
140
+ # Sequel.pg_row_op(:a).splat(:a) # (a.*)::a
141
+ def splat(cast_to=nil)
142
+ if args.length > 1
143
+ raise Error, 'cannot splat a PGRowOp with multiple arguments'
144
+ end
145
+
146
+ if cast_to
147
+ PGRowOp.new(ROW_CAST, args + [cast_to])
148
+ else
149
+ PGRowOp.new(ROW, args)
150
+ end
151
+ end
152
+
153
+ module ExpressionMethods
154
+ # Return a PGRowOp wrapping the receiver.
155
+ def pg_row
156
+ Sequel.pg_row_op(self)
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ module SQL::Builders
163
+ # Return a PGRowOp wrapping the given expression.
164
+ def pg_row_op(expr)
165
+ Postgres::PGRowOp.wrap(expr)
166
+ end
167
+ end
168
+
169
+ class SQL::GenericExpression
170
+ include Sequel::Postgres::PGRowOp::ExpressionMethods
171
+ end
172
+
173
+ class LiteralString
174
+ include Sequel::Postgres::PGRowOp::ExpressionMethods
175
+ end
176
+ end
177
+
178
+ if Sequel.core_extensions?
179
+ class Symbol
180
+ include Sequel::Postgres::PGRowOp::ExpressionMethods
181
+ end
182
+ end
@@ -0,0 +1,317 @@
1
+ # This extension adds a statement cache to Sequel's postgres adapter,
2
+ # with the ability to automatically prepare statements that are
3
+ # executed repeatedly. When combined with the pg_auto_parameterize
4
+ # extension, it can take Sequel code such as:
5
+ #
6
+ # DB.extension :pg_auto_parameterize, :pg_statement_cache
7
+ # DB[:table].filter(:a=>1)
8
+ # DB[:table].filter(:a=>2)
9
+ # DB[:table].filter(:a=>3)
10
+ #
11
+ # And use the same prepared statement to execute the queries.
12
+ #
13
+ # The backbone of this extension is a modified LRU cache. It considers
14
+ # both the last executed time and the number of executions when
15
+ # determining which queries to keep in the cache. It only cleans the
16
+ # cache when a high water mark has been passed, and removes queries
17
+ # until it reaches the low water mark, in order to avoid thrashing when
18
+ # you are using more than the maximum number of queries. To avoid
19
+ # preparing queries when it isn't necessary, it does not prepare them
20
+ # on the server side unless they are being executed more than once.
21
+ # The cache is very tunable, allowing you to set the high and low
22
+ # water marks, the number of executions before preparing the query,
23
+ # and even use a custom callback for determine which queries to keep
24
+ # in the cache.
25
+ #
26
+ # Note that automatically preparing statements does have some issues.
27
+ # Most notably, if you change the result type that the query returns,
28
+ # PostgreSQL will raise an error. This can happen if you have
29
+ # prepared a statement that selects all columns from a table, and then
30
+ # you add or remove a column from that table. This extension does
31
+ # attempt to check that case and clear the statement caches if you use
32
+ # alter_table from within Sequel, but it cannot fix the case when such
33
+ # a change is made externally.
34
+ #
35
+ # This extension only works when the pg driver is used as the backend
36
+ # for the postgres adapter.
37
+
38
+ module Sequel
39
+ module Postgres
40
+ module StatementCache
41
+ # A simple structure used for the values in the StatementCache's hash.
42
+ # It does not hold the related SQL, since that is used as the key for
43
+ # the StatementCache's hash.
44
+ class Statement
45
+ # The last time this statement was seen by the cache, persumably the
46
+ # last time it was executed.
47
+ attr_accessor :last_seen
48
+
49
+ # The total number of executions since the statement entered the cache.
50
+ attr_accessor :num_executes
51
+
52
+ # The id related to the statement, used as part of the prepared statement
53
+ # name.
54
+ attr_reader :cache_id
55
+
56
+ # Used when adding entries to the cache, just sets their id. Uses
57
+ # 0 for num_executes since that is incremented elsewhere. Does not
58
+ # set last_seen since that is set elsewhere to reduce branching.
59
+ def initialize(cache_id)
60
+ @num_executes = 0
61
+ @cache_id = cache_id
62
+ end
63
+
64
+ # The name to use for the server side prepared statement. Note that this
65
+ # statement might not actually be prepared.
66
+ def name
67
+ "sequel_pgap_#{cache_id}"
68
+ end
69
+ end
70
+
71
+ # The backbone of the block, a modified LRU (least recently used) cache
72
+ # mapping SQL query strings to Statement objects.
73
+ class StatementCache
74
+ include Enumerable
75
+
76
+ # Set the options for the statement cache. These are generally set at
77
+ # the database level using the :statement_cache_opts Database option.
78
+ #
79
+ # :max_size :: The maximum size (high water mark) for the cache. If
80
+ # an entry is added when the current size of the cache is
81
+ # equal to the maximum size, the cache is cleaned up to
82
+ # reduce the number of entries to the :min_size. Defaults
83
+ # to 1000.
84
+ # :min_size :: The minimum size (low water mark) for the cache. On
85
+ # cleanup, the size of the cache is reduced to this
86
+ # number. Note that there could be fewer than this
87
+ # number of entries in the cache. Defaults to :max_size/2.
88
+ # :prepare_after :: The number of executions to wait for before preparing
89
+ # the query server-side. If set to 1, prepares all executed
90
+ # queries server-side. If set to 5, does not attempt to
91
+ # prepare the query until the 5th execution. Defaults to 2.
92
+ # :sorter :: A callable object that takes two arguments, the current time
93
+ # and the related Statement instance, and should return some
94
+ # Comparable (usually a numeric) such that the lowest values
95
+ # returned are the first to be removed when it comes time to
96
+ # clean the pool. The default is basically:
97
+ #
98
+ # lambda{|t, stmt| (stmt.last_seen - t)/stmt.num_executes}
99
+ #
100
+ # so that it doesn't remove statements that have been executed
101
+ # many times just because many less-frequently executed statements
102
+ # have been executed recently.
103
+ #
104
+ # The block passed is called with the Statement object's name, only for
105
+ # statements that have been prepared, and should be used to deallocate the
106
+ # statements.
107
+ def initialize(opts={}, &block)
108
+ @cleanup_proc = block
109
+ @prepare_after = opts.fetch(:prepare_after, 2)
110
+ @max_size = opts.fetch(:max_size, 1000)
111
+ @min_size = opts.fetch(:min_size, @max_size/2)
112
+ @sorter = opts.fetch(:sorter){method(:default_sorter)}
113
+ @ids = (1..@max_size).to_a.reverse
114
+ @hash = {}
115
+ #
116
+ # We add one so that when we clean the cache, the entry
117
+ # about to be added brings us to the min_size.
118
+ @size_diff = @max_size - @min_size + 1
119
+ end
120
+
121
+ # Completely clear the statement cache, deallocating on
122
+ # the server side all statements that have been prepared.
123
+ def clear
124
+ @hash.keys.each{|k| remove(k)}
125
+ end
126
+
127
+ # Yield each SQL string and Statement instance in the cache
128
+ # to the block.
129
+ def each(&block)
130
+ @hash.each(&block)
131
+ end
132
+
133
+ # Get the related statement name from the cache. If the
134
+ # entry is already in the cache, just bump it's last seen
135
+ # time and the number of executions. Otherwise, add it
136
+ # to the cache. If the cache is already full, clean it up
137
+ # before adding it.
138
+ #
139
+ # If the num of executions has passed the threshhold, yield
140
+ # the statement name to the block, which should be used to
141
+ # prepare the statement on the server side.
142
+ #
143
+ # This method should return the prepared statment name if
144
+ # the statement has been prepared, and nil if the query
145
+ # has not been prepared and the statement should be executed
146
+ # normally.
147
+ def fetch(sql)
148
+ unless stmt = @hash[sql]
149
+ # Get the next id from the id pool.
150
+ unless id = @ids.pop
151
+ # No id left, cache must be full, so cleanup and then
152
+ # get the next id from the id pool.
153
+ cleanup
154
+ id = @ids.pop
155
+ end
156
+ @hash[sql] = stmt = Statement.new(id)
157
+ end
158
+
159
+ stmt.last_seen = Time.now
160
+ stmt.num_executes += 1
161
+
162
+ if stmt.num_executes >= @prepare_after
163
+ if stmt.num_executes == @prepare_after
164
+ begin
165
+ yield(stmt.name)
166
+ rescue PGError
167
+ # An error occurred while preparing the statement,
168
+ # execute it normally (which will probably raise
169
+ # the error again elsewhere), but decrement the
170
+ # number of executions so we don't think we've
171
+ # prepared the statement when we haven't.
172
+ stmt.num_executes -= 1
173
+ return nil
174
+ end
175
+ end
176
+ stmt.name
177
+ end
178
+ end
179
+
180
+ # The current size of the statement cache.
181
+ def size
182
+ @hash.length
183
+ end
184
+
185
+ private
186
+
187
+ # Sort by time since last execution and number of executions.
188
+ # We don't want to throw stuff out of the
189
+ # cache if it has been executed a lot,
190
+ # but a bunch of queries that were
191
+ # executed only once came in more recently.
192
+ def default_sorter(t, stmt)
193
+ (stmt.last_seen - t)/stmt.num_executes
194
+ end
195
+
196
+ # After sorting the cache appropriately (so that the least important
197
+ # items are first), reduce the number of entries in the cache to
198
+ # the low water mark by removing the related query. Should only be
199
+ # called when the cache is full.
200
+ def cleanup
201
+ t = Time.now
202
+ @hash.sort_by{|k,v| @sorter.call(t, v)}.first(@size_diff).each{|sql, stmt| remove(sql)}
203
+ end
204
+
205
+ # Remove the query from the cache. If it has been prepared,
206
+ # call the cleanup_proc to deallocate the statement.
207
+ def remove(sql)
208
+ stmt = @hash.delete(sql)
209
+ if stmt.num_executes >= @prepare_after
210
+ @cleanup_proc.call(stmt.name)
211
+ end
212
+
213
+ # Return id to the pool of ids
214
+ @ids.push(stmt.cache_id)
215
+ end
216
+ end
217
+
218
+ module AdapterMethods
219
+ # A regular expression for the types of queries to cache. Any queries not
220
+ # matching this regular expression are not cached.
221
+ DML_RE = /\A(WITH|SELECT|INSERT|UPDATE|DELETE) /
222
+
223
+ # The StatementCache instance for this connection. Note that
224
+ # each connection has a separate StatementCache, because prepared
225
+ # statements are connection-specific.
226
+ attr_reader :statement_cache
227
+
228
+ # Set the statement_cache for the connection, using the database's
229
+ # :statement_cache_opts option.
230
+ def self.extended(c)
231
+ c.instance_variable_set(:@statement_cache, StatementCache.new(c.sequel_db.opts[:statement_cache_opts] || {}){|name| c.deallocate(name)})
232
+ end
233
+
234
+ # pg seems to already use the db method (but not the @db instance variable),
235
+ # so use the sequel_db method to access the related Sequel::Database object.
236
+ def sequel_db
237
+ @db
238
+ end
239
+
240
+ # Deallocate on the server the prepared statement with the given name.
241
+ def deallocate(name)
242
+ begin
243
+ execute("DEALLOCATE #{name}")
244
+ rescue PGError
245
+ # table probably got removed, just ignore it
246
+ end
247
+ end
248
+
249
+ private
250
+
251
+ # If the sql query string is one we should cache, cache it. If the query already
252
+ # has a related prepared statement with it, execute the prepared statement instead
253
+ # of executing the query normally.
254
+ def execute_query(sql, args=nil)
255
+ if sql =~ DML_RE
256
+ if name = statement_cache.fetch(sql){|stmt_name| sequel_db.log_yield("PREPARE #{stmt_name} AS #{sql}"){prepare(stmt_name, sql)}}
257
+ if args
258
+ sequel_db.log_yield("EXECUTE #{name} (#{sql})", args){exec_prepared(name, args)}
259
+ else
260
+ sequel_db.log_yield("EXECUTE #{name} (#{sql})"){exec_prepared(name)}
261
+ end
262
+ else
263
+ super
264
+ end
265
+ else
266
+ super
267
+ end
268
+ end
269
+ end
270
+
271
+ module DatabaseMethods
272
+ # Setup the after_connect proc for the connection pool to make
273
+ # sure the connection object is extended with the appropriate
274
+ # module. This disconnects any existing connections to ensure
275
+ # that all connections in the pool have been extended appropriately.
276
+ def self.extended(db)
277
+ # Respect existing after_connect proc if one is present
278
+ pr = db.opts[:after_connect]
279
+
280
+ # Set the after_connect proc to extend the adapter with
281
+ # the statement cache support.
282
+ db.pool.after_connect = db.opts[:after_connect] = proc do |c|
283
+ pr.call(c) if pr
284
+ c.extend(AdapterMethods)
285
+ end
286
+
287
+ # Disconnect to make sure all connections get set up with
288
+ # statement cache.
289
+ db.disconnect
290
+ end
291
+
292
+ # Clear statement caches for all connections when altering tables.
293
+ def alter_table(*)
294
+ clear_statement_caches
295
+ super
296
+ end
297
+
298
+ # Clear statement caches for all connections when dropping tables.
299
+ def drop_table(*)
300
+ clear_statement_caches
301
+ super
302
+ end
303
+
304
+ private
305
+
306
+ # Clear the statement cache for all connections. Note that for
307
+ # the threaded pools, this will not affect connections currently
308
+ # allocated to other threads.
309
+ def clear_statement_caches
310
+ pool.all_connections{|c| c.statement_cache.clear}
311
+ end
312
+ end
313
+ end
314
+ end
315
+
316
+ Database.register_extension(:pg_statement_cache, Postgres::StatementCache::DatabaseMethods)
317
+ end
@@ -1,82 +1,27 @@
1
1
  # The pretty_table extension adds Sequel::Dataset#print and the
2
2
  # Sequel::PrettyTable class for creating nice-looking plain-text
3
- # tables.
3
+ # tables. Example:
4
+ #
5
+ # +--+-------+
6
+ # |id|name |
7
+ # |--+-------|
8
+ # |1 |fasdfas|
9
+ # |2 |test |
10
+ # +--+-------+
11
+ #
12
+ # To load the extension:
13
+ #
14
+ # Sequel.extension :pretty_table
4
15
 
5
16
  module Sequel
17
+ extension :_pretty_table
18
+
6
19
  class Dataset
7
20
  # Pretty prints the records in the dataset as plain-text table.
8
21
  def print(*cols)
9
- Sequel::PrettyTable.print(naked.all, cols.empty? ? columns : cols)
22
+ ds = naked
23
+ rows = ds.all
24
+ Sequel::PrettyTable.print(rows, cols.empty? ? ds.columns : cols)
10
25
  end
11
26
  end
12
-
13
- module PrettyTable
14
- # Prints nice-looking plain-text tables via puts
15
- #
16
- # +--+-------+
17
- # |id|name |
18
- # |--+-------|
19
- # |1 |fasdfas|
20
- # |2 |test |
21
- # +--+-------+
22
- def self.print(records, columns = nil) # records is an array of hashes
23
- columns ||= records.first.keys.sort_by{|x|x.to_s}
24
- sizes = column_sizes(records, columns)
25
- sep_line = separator_line(columns, sizes)
26
-
27
- puts sep_line
28
- puts header_line(columns, sizes)
29
- puts sep_line
30
- records.each {|r| puts data_line(columns, sizes, r)}
31
- puts sep_line
32
- end
33
-
34
- ### Private Module Methods ###
35
-
36
- # Hash of the maximum size of the value for each column
37
- def self.column_sizes(records, columns) # :nodoc:
38
- sizes = Hash.new {0}
39
- columns.each do |c|
40
- s = c.to_s.size
41
- sizes[c.to_sym] = s if s > sizes[c.to_sym]
42
- end
43
- records.each do |r|
44
- columns.each do |c|
45
- s = r[c].to_s.size
46
- sizes[c.to_sym] = s if s > sizes[c.to_sym]
47
- end
48
- end
49
- sizes
50
- end
51
-
52
- # String for each data line
53
- def self.data_line(columns, sizes, record) # :nodoc:
54
- '|' << columns.map {|c| format_cell(sizes[c], record[c])}.join('|') << '|'
55
- end
56
-
57
- # Format the value so it takes up exactly size characters
58
- def self.format_cell(size, v) # :nodoc:
59
- case v
60
- when Bignum, Fixnum
61
- "%#{size}d" % v
62
- when Float
63
- "%#{size}g" % v
64
- else
65
- "%-#{size}s" % v.to_s
66
- end
67
- end
68
-
69
- # String for header line
70
- def self.header_line(columns, sizes) # :nodoc:
71
- '|' << columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') << '|'
72
- end
73
-
74
- # String for separtor line
75
- def self.separator_line(columns, sizes) # :nodoc:
76
- '+' << columns.map {|c| '-' * sizes[c]}.join('+') << '+'
77
- end
78
-
79
- private_class_method :column_sizes, :data_line, :format_cell, :header_line, :separator_line
80
- end
81
27
  end
82
-
@@ -1,6 +1,10 @@
1
1
  # The query extension adds Sequel::Dataset#query which allows
2
2
  # a different way to construct queries instead of the usual
3
3
  # method chaining.
4
+ #
5
+ # To load the extension, do:
6
+ #
7
+ # Sequel.extension :query
4
8
 
5
9
  module Sequel
6
10
  class Database
@@ -16,13 +20,13 @@ module Sequel
16
20
  #
17
21
  # dataset = DB[:items].query do
18
22
  # select :x, :y, :z
19
- # filter{|o| (o.x > 1) & (o.y > 2)}
20
- # order :z.desc
23
+ # filter{(x > 1) & (y > 2)}
24
+ # reverse :z
21
25
  # end
22
26
  #
23
27
  # Which is the same as:
24
28
  #
25
- # dataset = DB[:items].select(:x, :y, :z).filter{|o| (o.x > 1) & (o.y > 2)}.order(:z.desc)
29
+ # dataset = DB[:items].select(:x, :y, :z).filter{(x > 1) & (y > 2)}.reverse(:z)
26
30
  #
27
31
  # Note that inside a call to query, you cannot call each, insert, update,
28
32
  # or delete (or any method that calls those), or Sequel will raise an
@@ -43,7 +47,7 @@ module Sequel
43
47
 
44
48
  # Merge the given options into the receiver's options and return the receiver
45
49
  # instead of cloning the receiver.
46
- def clone(opts = nil)
50
+ def clone(opts = {})
47
51
  @opts.merge!(opts)
48
52
  self
49
53
  end