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
data/lib/sequel/core.rb CHANGED
@@ -17,20 +17,26 @@
17
17
  #
18
18
  # Sequel.sqlite('blog.db'){|db| puts db[:users].count}
19
19
  #
20
- # You can set the +SEQUEL_NO_CORE_EXTENSIONS+ constant or environment variable to have
21
- # Sequel not extend the core classes.
20
+ # Sequel currently adds methods to the Array, Hash, String and Symbol classes by
21
+ # default. You can either require 'sequel/no_core_ext' or set the
22
+ # +SEQUEL_NO_CORE_EXTENSIONS+ constant or environment variable before requiring
23
+ # sequel to have # Sequel not add methods to those classes.
22
24
  #
23
25
  # For a more expanded introduction, see the {README}[link:files/README_rdoc.html].
24
26
  # For a quicker introduction, see the {cheat sheet}[link:files/doc/cheat_sheet_rdoc.html].
25
27
  module Sequel
26
28
  @convert_two_digit_years = true
27
29
  @datetime_class = Time
30
+ @empty_array_handle_nulls = true
28
31
  @virtual_row_instance_eval = true
29
32
  @require_thread = nil
30
33
 
31
34
  # Mutex used to protect file loading/requireing
32
35
  @require_mutex = Mutex.new
33
36
 
37
+ # Whether Sequel is being run in single threaded mode
38
+ @single_threaded = false
39
+
34
40
  class << self
35
41
  # Sequel converts two digit years in <tt>Date</tt>s and <tt>DateTime</tt>s by default,
36
42
  # so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
@@ -54,6 +60,27 @@ module Sequel
54
60
  # days on +DateTime+).
55
61
  attr_accessor :datetime_class
56
62
 
63
+ # Sets whether or not to attempt to handle NULL values correctly when given
64
+ # an empty array. By default:
65
+ #
66
+ # DB[:a].filter(:b=>[])
67
+ # # SELECT * FROM a WHERE (b != b)
68
+ # DB[:a].exclude(:b=>[])
69
+ # # SELECT * FROM a WHERE (b = b)
70
+ #
71
+ # However, some databases (e.g. MySQL) will perform very poorly
72
+ # with this type of query. You can set this to false to get the
73
+ # following behavior:
74
+ #
75
+ # DB[:a].filter(:b=>[])
76
+ # # SELECT * FROM a WHERE 1 = 0
77
+ # DB[:a].exclude(:b=>[])
78
+ # # SELECT * FROM a WHERE 1 = 1
79
+ #
80
+ # This may not handle NULLs correctly, but can be much faster on
81
+ # some databases.
82
+ attr_accessor :empty_array_handle_nulls
83
+
57
84
  # For backwards compatibility, has no effect.
58
85
  attr_accessor :virtual_row_instance_eval
59
86
 
@@ -64,6 +91,7 @@ module Sequel
64
91
 
65
92
  # Make thread safe requiring reentrant to prevent deadlocks.
66
93
  def check_requiring_thread
94
+ return yield if @single_threaded
67
95
  t = Thread.current
68
96
  return(yield) if @require_thread == t
69
97
  @require_mutex.synchronize do
@@ -118,6 +146,12 @@ module Sequel
118
146
  def self.connect(*args, &block)
119
147
  Database.connect(*args, &block)
120
148
  end
149
+
150
+ # Assume the core extensions are not loaded by default, if the core_extensions
151
+ # extension is loaded, this will be overridden.
152
+ def self.core_extensions?
153
+ false
154
+ end
121
155
 
122
156
  # Convert the +exception+ to the given class. The given class should be
123
157
  # <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
@@ -182,25 +216,62 @@ module Sequel
182
216
  def self.quote_identifiers=(value)
183
217
  Database.quote_identifiers = value
184
218
  end
219
+
220
+ # Convert each item in the array to the correct type, handling multi-dimensional
221
+ # arrays. For each element in the array or subarrays, call the converter,
222
+ # unless the value is nil.
223
+ def self.recursive_map(array, converter)
224
+ array.map do |i|
225
+ if i.is_a?(Array)
226
+ recursive_map(i, converter)
227
+ elsif i
228
+ converter.call(i)
229
+ end
230
+ end
231
+ end
185
232
 
186
233
  # Require all given +files+ which should be in the same or a subdirectory of
187
234
  # this file. If a +subdir+ is given, assume all +files+ are in that subdir.
188
235
  # This is used to ensure that the files loaded are from the same version of
189
236
  # Sequel as this file.
190
237
  def self.require(files, subdir=nil)
191
- Array(files).each{|f| super("#{File.dirname(__FILE__)}/#{"#{subdir}/" if subdir}#{f}")}
238
+ Array(files).each{|f| super("#{File.dirname(__FILE__).untaint}/#{"#{subdir}/" if subdir}#{f}")}
192
239
  end
193
240
 
194
- # Set whether to set the single threaded mode for all databases by default. By default,
241
+ # Set whether Sequel is being used in single threaded mode. By default,
195
242
  # Sequel uses a thread-safe connection pool, which isn't as fast as the
196
- # single threaded connection pool. If your program will only have one thread,
197
- # and speed is a priority, you may want to set this to true:
243
+ # single threaded connection pool, and also has some additional thread
244
+ # safety checks. If your program will only have one thread,
245
+ # and speed is a priority, you should set this to true:
198
246
  #
199
247
  # Sequel.single_threaded = true
200
248
  def self.single_threaded=(value)
249
+ @single_threaded = value
201
250
  Database.single_threaded = value
202
251
  end
203
252
 
253
+ COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
254
+ COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
255
+ COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
256
+
257
+ # Splits the symbol into three parts. Each part will
258
+ # either be a string or nil.
259
+ #
260
+ # For columns, these parts are the table, column, and alias.
261
+ # For tables, these parts are the schema, table, and alias.
262
+ def self.split_symbol(sym)
263
+ case s = sym.to_s
264
+ when COLUMN_REF_RE1
265
+ [$1, $2, $3]
266
+ when COLUMN_REF_RE2
267
+ [nil, $1, $2]
268
+ when COLUMN_REF_RE3
269
+ [$1, $2, nil]
270
+ else
271
+ [nil, s, nil]
272
+ end
273
+ end
274
+
204
275
  # Converts the given +string+ into a +Date+ object.
205
276
  #
206
277
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
@@ -228,17 +299,76 @@ module Sequel
228
299
  end
229
300
  end
230
301
 
231
- # Converts the given +string+ into a +Time+ object.
302
+ # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
232
303
  #
233
- # Sequel.string_to_datetime('10:20:30') # Time.parse('10:20:30')
304
+ # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
305
+ # DB.literal(v) # => '10:20:30'
234
306
  def self.string_to_time(string)
235
307
  begin
236
- Time.parse(string)
308
+ SQLTime.parse(string)
237
309
  rescue => e
238
310
  raise convert_exception_class(e, InvalidValue)
239
311
  end
240
312
  end
241
313
 
314
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE != 'ruby'
315
+ # Mutex used to protect mutable data structures
316
+ @data_mutex = Mutex.new
317
+
318
+ # Unless in single threaded mode, protects access to any mutable
319
+ # global data structure in Sequel.
320
+ # Uses a non-reentrant mutex, so calling code should be careful.
321
+ def self.synchronize(&block)
322
+ @single_threaded ? yield : @data_mutex.synchronize(&block)
323
+ end
324
+ else
325
+ # Yield directly to the block. You don't need to synchronize
326
+ # access on MRI because the GVL makes certain methods atomic.
327
+ def self.synchronize(&block)
328
+ yield
329
+ end
330
+ end
331
+
332
+ # Uses a transaction on all given databases with the given options. This:
333
+ #
334
+ # Sequel.transaction([DB1, DB2, DB3]){...}
335
+ #
336
+ # is equivalent to:
337
+ #
338
+ # DB1.transaction do
339
+ # DB2.transaction do
340
+ # DB3.transaction do
341
+ # ...
342
+ # end
343
+ # end
344
+ # end
345
+ #
346
+ # except that if Sequel::Rollback is raised by the block, the transaction is
347
+ # rolled back on all databases instead of just the last one.
348
+ #
349
+ # Note that this method cannot guarantee that all databases will commit or
350
+ # rollback. For example, if DB3 commits but attempting to commit on DB2
351
+ # fails (maybe because foreign key checks are deferred), there is no way
352
+ # to uncommit the changes on DB3. For that kind of support, you need to
353
+ # have two-phase commit/prepared transactions (which Sequel supports on
354
+ # some databases).
355
+ def self.transaction(dbs, opts={}, &block)
356
+ unless opts[:rollback]
357
+ rescue_rollback = true
358
+ opts = opts.merge(:rollback=>:reraise)
359
+ end
360
+ pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
361
+ if rescue_rollback
362
+ begin
363
+ pr.call
364
+ rescue Sequel::Rollback => e
365
+ nil
366
+ end
367
+ else
368
+ pr.call
369
+ end
370
+ end
371
+
242
372
  # Same as Sequel.require, but wrapped in a mutex in order to be thread safe.
243
373
  def self.ts_require(*args)
244
374
  check_requiring_thread{require(*args)}
@@ -292,8 +422,10 @@ module Sequel
292
422
 
293
423
  private_class_method :adapter_method, :def_adapter_method
294
424
 
295
- require(%w"metaprogramming sql connection_pool exceptions dataset database timezones version")
296
- require('core_sql') if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
425
+ require(%w"metaprogramming sql connection_pool exceptions dataset database timezones ast_transformer version")
426
+ if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
427
+ extension(:core_extensions)
428
+ end
297
429
 
298
430
  # Add the database adapter class methods to Sequel via metaprogramming
299
431
  def_adapter_method(*Database::ADAPTERS)
@@ -1,12 +1,12 @@
1
1
  module Sequel
2
2
  class Database
3
3
  # ---------------------
4
- # :section: Methods relating to adapters, connecting, disconnecting, and sharding
4
+ # :section: 4 - Methods relating to adapters, connecting, disconnecting, and sharding
5
5
  # This methods involve the Database's connection pool.
6
6
  # ---------------------
7
7
 
8
8
  # Array of supported database adapters
9
- ADAPTERS = %w'ado amalgalite db2 dbi do firebird informix jdbc mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
9
+ ADAPTERS = %w'ado amalgalite cubrid db2 dbi do firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
10
10
 
11
11
  # Whether to use the single threaded connection pool by default
12
12
  @@single_threaded = false
@@ -46,6 +46,7 @@ module Sequel
46
46
  when String
47
47
  if match = /\A(jdbc|do):/o.match(conn_string)
48
48
  c = adapter_class(match[1].to_sym)
49
+ opts = opts.merge(:orig_opts=>opts.dup)
49
50
  opts = {:uri=>conn_string}.merge(opts)
50
51
  else
51
52
  uri = URI.parse(conn_string)
@@ -54,12 +55,15 @@ module Sequel
54
55
  c = adapter_class(scheme)
55
56
  uri_options = c.send(:uri_to_options, uri)
56
57
  uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
57
- uri_options.entries.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
58
+ uri_options.to_a.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
59
+ opts = opts.merge(:orig_opts=>opts.dup)
60
+ opts[:uri] = conn_string
58
61
  opts = uri_options.merge(opts)
59
62
  opts[:adapter] = scheme
60
63
  end
61
64
  when Hash
62
65
  opts = conn_string.merge(opts)
66
+ opts = opts.merge(:orig_opts=>opts.dup)
63
67
  c = adapter_class(opts[:adapter_class] || opts[:adapter] || opts['adapter'])
64
68
  else
65
69
  raise Error, "Sequel::Database.connect takes either a Hash or a String, given: #{conn_string.inspect}"
@@ -77,7 +81,7 @@ module Sequel
77
81
  ensure
78
82
  if block_given?
79
83
  db.disconnect if db
80
- ::Sequel::DATABASES.delete(db)
84
+ Sequel.synchronize{::Sequel::DATABASES.delete(db)}
81
85
  end
82
86
  end
83
87
  block_given? ? result : db
@@ -131,10 +135,12 @@ module Sequel
131
135
  #
132
136
  # DB.add_servers(:f=>{:host=>"hash_host_f"})
133
137
  def add_servers(servers)
134
- @opts[:servers] = @opts[:servers] ? @opts[:servers].merge(servers) : servers
135
- @pool.add_servers(servers.keys)
138
+ if h = @opts[:servers]
139
+ Sequel.synchronize{h.merge!(servers)}
140
+ @pool.add_servers(servers.keys)
141
+ end
136
142
  end
137
-
143
+
138
144
  # Connects to the database. This method should be overridden by descendants.
139
145
  def connect(server)
140
146
  raise NotImplemented, "#connect should be overridden by adapters"
@@ -155,8 +161,8 @@ module Sequel
155
161
 
156
162
  # Disconnects all available connections from the connection pool. Any
157
163
  # connections currently in use will not be disconnected. Options:
158
- # * :servers - Should be a symbol specifing the server to disconnect from,
159
- # or an array of symbols to specify multiple servers.
164
+ # :servers :: Should be a symbol specifing the server to disconnect from,
165
+ # or an array of symbols to specify multiple servers.
160
166
  #
161
167
  # Example:
162
168
  #
@@ -167,6 +173,13 @@ module Sequel
167
173
  pool.disconnect(opts)
168
174
  end
169
175
 
176
+ # Should only be called by the connection pool code to disconnect a connection.
177
+ # By default, calls the close method on the connection object, since most
178
+ # adapters use that, but should be overwritten on other adapters.
179
+ def disconnect_connection(conn)
180
+ conn.close
181
+ end
182
+
170
183
  # Yield a new Database instance for every server in the connection pool.
171
184
  # Intended for use in sharded environments where there is a need to make schema
172
185
  # modifications (DDL queries) on each shard.
@@ -187,11 +200,8 @@ module Sequel
187
200
  #
188
201
  # DB.remove_servers(:f1, :f2)
189
202
  def remove_servers(*servers)
190
- if @opts[:servers] && !@opts[:servers].empty?
191
- servs = @opts[:servers].dup
192
- servers.flatten!
193
- servers.each{|s| servs.delete(s)}
194
- @opts[:servers] = servs
203
+ if h = @opts[:servers]
204
+ servers.flatten.each{|s| Sequel.synchronize{h.delete(s)}}
195
205
  @pool.remove_servers(servers)
196
206
  end
197
207
  end
@@ -209,21 +219,26 @@ module Sequel
209
219
  @single_threaded
210
220
  end
211
221
 
212
- # Acquires a database connection, yielding it to the passed block. This is
213
- # useful if you want to make sure the same connection is used for all
214
- # database queries in the block. It is also useful if you want to gain
215
- # direct access to the underlying connection object if you need to do
216
- # something Sequel does not natively support.
217
- #
218
- # If a server option is given, acquires a connection for that specific
219
- # server, instead of the :default server.
220
- #
221
- #
222
- # DB.synchronize do |conn|
223
- # ...
224
- # end
225
- def synchronize(server=nil, &block)
226
- @pool.hold(server || :default, &block)
222
+ if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
223
+ # Acquires a database connection, yielding it to the passed block. This is
224
+ # useful if you want to make sure the same connection is used for all
225
+ # database queries in the block. It is also useful if you want to gain
226
+ # direct access to the underlying connection object if you need to do
227
+ # something Sequel does not natively support.
228
+ #
229
+ # If a server option is given, acquires a connection for that specific
230
+ # server, instead of the :default server.
231
+ #
232
+ # DB.synchronize do |conn|
233
+ # ...
234
+ # end
235
+ def synchronize(server=nil)
236
+ @pool.hold(server || :default){|conn| yield conn}
237
+ end
238
+ else
239
+ def synchronize(server=nil, &block)
240
+ @pool.hold(server || :default, &block)
241
+ end
227
242
  end
228
243
 
229
244
  # Attempts to acquire a database connection. Returns true if successful.
@@ -235,6 +250,21 @@ module Sequel
235
250
  true
236
251
  end
237
252
 
253
+ # Check whether the given connection is currently valid, by
254
+ # running a query against it. If the query fails, the
255
+ # connection should probably be removed from the connection
256
+ # pool.
257
+ def valid_connection?(conn)
258
+ sql = valid_connection_sql
259
+ begin
260
+ log_connection_execute(conn, sql)
261
+ rescue Sequel::DatabaseError, *database_error_classes
262
+ false
263
+ else
264
+ true
265
+ end
266
+ end
267
+
238
268
  private
239
269
 
240
270
  # The default options for the connection pool.
@@ -255,11 +285,18 @@ module Sequel
255
285
  else
256
286
  raise Error, 'Server opts should be a hash or proc'
257
287
  end
288
+ elsif server.is_a?(Hash)
289
+ @opts.merge(server)
258
290
  else
259
291
  @opts.dup
260
292
  end
261
293
  opts.delete(:servers)
262
294
  opts
263
295
  end
296
+
297
+ # The SQL query to issue to check if a connection is valid.
298
+ def valid_connection_sql
299
+ @valid_connection_sql ||= select(nil).sql
300
+ end
264
301
  end
265
302
  end
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  class Database
3
3
  # ---------------------
4
- # :section: Methods that create datasets
4
+ # :section: 3 - Methods that create datasets
5
5
  # These methods all return instances of this database's dataset class.
6
6
  # ---------------------
7
7
 
@@ -24,10 +24,10 @@ module Sequel
24
24
  #
25
25
  # DB.dataset # SELECT *
26
26
  # DB.dataset.from(:items) # SELECT * FROM items
27
- def dataset
28
- ds = Sequel::Dataset.new(self)
27
+ def dataset(opts=nil)
28
+ @dataset_class.new(self, opts)
29
29
  end
30
-
30
+
31
31
  # Fetches records for an arbitrary SQL statement. If a block is given,
32
32
  # it is used to iterate over the records:
33
33
  #
@@ -1,10 +1,13 @@
1
1
  module Sequel
2
2
  class Database
3
3
  # ---------------------
4
- # :section: Methods that set defaults for created datasets
4
+ # :section: 5 - Methods that set defaults for created datasets
5
5
  # This methods change the default behavior of this database's datasets.
6
6
  # ---------------------
7
7
 
8
+ # The default class to use for datasets
9
+ DatasetClass = Sequel::Dataset
10
+
8
11
  # The identifier input method to use by default
9
12
  @@identifier_input_method = nil
10
13
 
@@ -42,9 +45,62 @@ module Sequel
42
45
  @@quote_identifiers = value
43
46
  end
44
47
 
45
- # The default schema to use, generally should be nil.
48
+ # The class to use for creating datasets. Should respond to
49
+ # new with the Database argument as the first argument, and
50
+ # an optional options hash.
51
+ attr_reader :dataset_class
52
+
53
+ # The default schema to use, generally should be nil. This sets
54
+ # the default schema used for some schema modification and
55
+ # introspection queries, but does not effect most dataset code.
46
56
  attr_accessor :default_schema
47
57
 
58
+ # If the database has any dataset modules associated with it,
59
+ # use a subclass of the given class that includes the modules
60
+ # as the dataset class.
61
+ def dataset_class=(c)
62
+ unless @dataset_modules.empty?
63
+ c = Class.new(c)
64
+ @dataset_modules.each{|m| c.send(:include, m)}
65
+ end
66
+ @dataset_class = c
67
+ end
68
+
69
+ # Equivalent to extending all datasets produced by the database with a
70
+ # module. What it actually does is use a subclass of the current dataset_class
71
+ # as the new dataset_class, and include the module in the subclass.
72
+ # Instead of a module, you can provide a block that is used to create an
73
+ # anonymous module.
74
+ #
75
+ # This allows you to override any of the dataset methods even if they are
76
+ # defined directly on the dataset class that this Database object uses.
77
+ #
78
+ # Examples:
79
+ #
80
+ # # Introspec columns for all of DB's datasets
81
+ # DB.extend_datasets(Sequel::ColumnsIntrospection)
82
+ #
83
+ # # Trace all SELECT queries by printing the SQL and the full backtrace
84
+ # DB.extend_datasets do
85
+ # def fetch_rows(sql)
86
+ # puts sql
87
+ # puts caller
88
+ # super
89
+ # end
90
+ # end
91
+ def extend_datasets(mod=nil, &block)
92
+ raise(Error, "must provide either mod or block, not both") if mod && block
93
+ reset_schema_utility_dataset
94
+ mod = Module.new(&block) if block
95
+ if @dataset_modules.empty?
96
+ @dataset_modules = [mod]
97
+ @dataset_class = Class.new(@dataset_class)
98
+ else
99
+ @dataset_modules << mod
100
+ end
101
+ @dataset_class.send(:include, mod)
102
+ end
103
+
48
104
  # The method to call on identifiers going into the database
49
105
  def identifier_input_method
50
106
  case @identifier_input_method
@@ -109,6 +165,11 @@ module Sequel
109
165
 
110
166
  private
111
167
 
168
+ # The default dataset class to use for the database
169
+ def dataset_class_default
170
+ self.class.const_get(:DatasetClass)
171
+ end
172
+
112
173
  # The default value for default_schema.
113
174
  def default_schema_default
114
175
  nil
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  class Database
3
3
  # ---------------------
4
- # :section: Methods relating to logging
4
+ # :section: 6 - Methods relating to logging
5
5
  # This methods affect relating to the logging of executed SQL.
6
6
  # ---------------------
7
7
 
@@ -17,6 +17,11 @@ module Sequel
17
17
  # is :info, it can be set to :debug to log at DEBUG level.
18
18
  attr_accessor :sql_log_level
19
19
 
20
+ # Log a message at error level, with information about the exception.
21
+ def log_exception(exception, message)
22
+ log_each(:error, "#{exception.class}: #{exception.message.strip if exception.message}: #{message}")
23
+ end
24
+
20
25
  # Log a message at level info to all loggers.
21
26
  def log_info(message, args=nil)
22
27
  log_each(:info, args ? "#{message}; #{args.inspect}" : message)
@@ -31,7 +36,7 @@ module Sequel
31
36
  begin
32
37
  yield
33
38
  rescue => e
34
- log_each(:error, "#{e.class}: #{e.message.strip}: #{sql}")
39
+ log_exception(e, sql)
35
40
  raise
36
41
  ensure
37
42
  log_duration(Time.now - start, sql) unless e