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,334 @@
1
+ # The pg_hstore extension adds support for the PostgreSQL hstore type
2
+ # to Sequel. hstore is an extension that ships with PostgreSQL, and
3
+ # the hstore type stores an arbitrary key-value table, where the keys
4
+ # are strings and the values are strings or NULL.
5
+ #
6
+ # This extension integrates with Sequel's native postgres adapter, so
7
+ # that when hstore fields are retrieved, they are parsed and returned
8
+ # as instances of Sequel::Postgres::HStore. HStore is
9
+ # a DelegateClass of Hash, so it mostly acts like a hash, but not
10
+ # completely (is_a?(Hash) is false). If you want the actual hash,
11
+ # you can call Hstore#to_hash. This is done so that Sequel does not
12
+ # treat a HStore like a Hash by default, which would cause issues.
13
+ #
14
+ # In addition to the parsers, this extension comes with literalizers
15
+ # for HStore using the standard Sequel literalization callbacks, so
16
+ # they work with on all adapters.
17
+ #
18
+ # To turn an existing Hash into an HStore, use Sequel.hstore:
19
+ #
20
+ # Sequel.hstore(hash)
21
+ #
22
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
23
+ # you can also use Hash#hstore:
24
+ #
25
+ # hash.hstore
26
+ #
27
+ # Since the hstore type only supports strings, non string keys and
28
+ # values are converted to strings
29
+ #
30
+ # {:foo=>1}.hstore.to_hash # {'foo'=>'1'}
31
+ # v = {}.hstore
32
+ # v[:foo] = 1
33
+ # v # {'foo'=>'1'}
34
+ #
35
+ # However, to make life easier, lookups by key are converted to
36
+ # strings (even when accessing the underlying hash directly):
37
+ #
38
+ # {'foo'=>'bar'}.hstore[:foo] # 'bar'
39
+ # {'foo'=>'bar'}.hstore.to_hash[:foo] # 'bar'
40
+ #
41
+ # HStore instances mostly just delegate to the underlying hash
42
+ # instance, so Hash methods that modify the receiver or returned
43
+ # modified copies of the receiver may not do string conversion.
44
+ # The following methods will handle string conversion, and more
45
+ # can be added later if desired:
46
+ #
47
+ # * \[\]
48
+ # * \[\]=
49
+ # * assoc (ruby 1.9 only)
50
+ # * delete
51
+ # * fetch
52
+ # * has_key?
53
+ # * has_value?
54
+ # * include?
55
+ # * key (ruby 1.9 only)
56
+ # * key?
57
+ # * member?
58
+ # * merge
59
+ # * merge!
60
+ # * rassoc (ruby 1.9 only)
61
+ # * replace
62
+ # * store
63
+ # * update
64
+ # * value?
65
+ #
66
+ # If you want to insert a hash into an hstore database column:
67
+ #
68
+ # DB[:table].insert(:column=>{'foo'=>'bar'}.hstore)
69
+ #
70
+ # If you would like to use hstore columns in your model objects, you
71
+ # probably want to modify the schema parsing/typecasting so that it
72
+ # recognizes and correctly handles the hstore columns, which you can
73
+ # do by:
74
+ #
75
+ # DB.extension :pg_hstore
76
+ #
77
+ # If you are not using the native postgres adapter, you probably
78
+ # also want to use the typecast_on_load plugin in the model, and
79
+ # set it to typecast the hstore column(s) on load.
80
+ #
81
+ # This extension requires the delegate and strscan libraries.
82
+
83
+ require 'delegate'
84
+ require 'strscan'
85
+
86
+ module Sequel
87
+ module Postgres
88
+ class HStore < DelegateClass(Hash)
89
+ include Sequel::SQL::AliasMethods
90
+
91
+ # Parser for PostgreSQL hstore output format.
92
+ class Parser < StringScanner
93
+ QUOTE_RE = /"/.freeze
94
+ KV_SEP_RE = /"\s*=>\s*/.freeze
95
+ NULL_RE = /NULL/.freeze
96
+ SEP_RE = /,\s*/.freeze
97
+ QUOTED_RE = /(\\"|[^"])*/.freeze
98
+ REPLACE_RE = /\\(.)/.freeze
99
+ REPLACE_WITH = '\1'.freeze
100
+
101
+ # Parse the output format that PostgreSQL uses for hstore
102
+ # columns. Note that this does not attempt to parse all
103
+ # input formats that PostgreSQL will accept. For instance,
104
+ # it expects all keys and non-NULL values to be quoted.
105
+ #
106
+ # Return the resulting hash of objects. This can be called
107
+ # multiple times, it will cache the parsed hash on the first
108
+ # call and use it for subsequent calls.
109
+ def parse
110
+ return @result if @result
111
+ hash = {}
112
+ while !eos?
113
+ skip(QUOTE_RE)
114
+ k = parse_quoted
115
+ skip(KV_SEP_RE)
116
+ if skip(QUOTE_RE)
117
+ v = parse_quoted
118
+ skip(QUOTE_RE)
119
+ else
120
+ scan(NULL_RE)
121
+ v = nil
122
+ end
123
+ skip(SEP_RE)
124
+ hash[k] = v
125
+ end
126
+ @result = hash
127
+ end
128
+
129
+ private
130
+
131
+ # Parse and unescape a quoted key/value.
132
+ def parse_quoted
133
+ scan(QUOTED_RE).gsub(REPLACE_RE, REPLACE_WITH)
134
+ end
135
+ end
136
+
137
+ module DatabaseMethods
138
+ # Handle hstores in bound variables
139
+ def bound_variable_arg(arg, conn)
140
+ case arg
141
+ when HStore
142
+ arg.unquoted_literal
143
+ when Hash
144
+ HStore.new(arg).unquoted_literal
145
+ else
146
+ super
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ # Recognize the hstore database type.
153
+ def schema_column_type(db_type)
154
+ db_type == 'hstore' ? :hstore : super
155
+ end
156
+
157
+ # Typecast value correctly to HStore. If already an
158
+ # HStore instance, return as is. If a hash, return
159
+ # an HStore version of it. If a string, assume it is
160
+ # in PostgreSQL output format and parse it using the
161
+ # parser.
162
+ def typecast_value_hstore(value)
163
+ case value
164
+ when HStore
165
+ value
166
+ when Hash
167
+ HStore.new(value)
168
+ else
169
+ raise Sequel::InvalidValue, "invalid value for hstore: #{value.inspect}"
170
+ end
171
+ end
172
+ end
173
+
174
+ # Default proc used for all underlying HStore hashes, so that even
175
+ # if you grab the underlying hash, it will still convert non-string
176
+ # keys to strings during lookup.
177
+ DEFAULT_PROC = lambda{|h, k| h[k.to_s] unless k.is_a?(String)}
178
+
179
+ QUOTE = '"'.freeze
180
+ COMMA = ",".freeze
181
+ KV_SEP = "=>".freeze
182
+ NULL = "NULL".freeze
183
+ ESCAPE_RE = /("|\\)/.freeze
184
+ ESCAPE_REPLACE = '\\\\\1'.freeze
185
+ HSTORE_CAST = '::hstore'.freeze
186
+
187
+ if RUBY_VERSION >= '1.9'
188
+ # Undef 1.9 marshal_{dump,load} methods in the delegate class,
189
+ # so that ruby 1.9 uses the old style _dump/_load methods defined
190
+ # in the delegate class, instead of the marshal_{dump,load} methods
191
+ # in the Hash class.
192
+ undef_method :marshal_load
193
+ undef_method :marshal_dump
194
+ end
195
+
196
+ # Use custom marshal loading, since underlying hash uses a default proc.
197
+ def self._load(args)
198
+ new(Hash[Marshal.load(args)])
199
+ end
200
+
201
+ # Parse the given string into an HStore, assuming the str is in PostgreSQL
202
+ # hstore output format.
203
+ def self.parse(str)
204
+ new(Parser.new(str).parse)
205
+ end
206
+
207
+ # Override methods that accept key argument to convert to string.
208
+ (%w'[] delete has_key? include? key? member?' + Array((%w'assoc' if RUBY_VERSION >= '1.9.0'))).each do |m|
209
+ class_eval("def #{m}(k) super(k.to_s) end", __FILE__, __LINE__)
210
+ end
211
+
212
+ # Override methods that accept value argument to convert to string unless nil.
213
+ (%w'has_value? value?' + Array((%w'key rassoc' if RUBY_VERSION >= '1.9.0'))).each do |m|
214
+ class_eval("def #{m}(v) super(convert_value(v)) end", __FILE__, __LINE__)
215
+ end
216
+
217
+ # Override methods that accept key and value arguments to convert to string appropriately.
218
+ %w'[]= store'.each do |m|
219
+ class_eval("def #{m}(k, v) super(k.to_s, convert_value(v)) end", __FILE__, __LINE__)
220
+ end
221
+
222
+ # Override methods that take hashes to convert the hashes to using strings for keys and
223
+ # values before using them.
224
+ %w'initialize merge! update replace'.each do |m|
225
+ class_eval("def #{m}(h, &block) super(convert_hash(h), &block) end", __FILE__, __LINE__)
226
+ end
227
+
228
+ # Use custom marshal dumping, since underlying hash uses a default proc.
229
+ def _dump(*)
230
+ Marshal.dump(to_a)
231
+ end
232
+
233
+ # Override to force the key argument to a string.
234
+ def fetch(key, *args, &block)
235
+ super(key.to_s, *args, &block)
236
+ end
237
+
238
+ # Convert the input hash to string keys and values before merging,
239
+ # and return a new HStore instance with the merged hash.
240
+ def merge(hash, &block)
241
+ self.class.new(super(convert_hash(hash), &block))
242
+ end
243
+
244
+ # Return the underlying hash used by this HStore instance.
245
+ alias to_hash __getobj__
246
+
247
+ # Append a literalize version of the hstore to the sql.
248
+ def sql_literal_append(ds, sql)
249
+ ds.literal_append(sql, unquoted_literal)
250
+ sql << HSTORE_CAST
251
+ end
252
+
253
+ # Return a string containing the unquoted, unstring-escaped
254
+ # literal version of the hstore. Separated out for use by
255
+ # the bound argument code.
256
+ def unquoted_literal
257
+ str = ''
258
+ comma = false
259
+ commas = COMMA
260
+ quote = QUOTE
261
+ kv_sep = KV_SEP
262
+ null = NULL
263
+ each do |k, v|
264
+ str << commas if comma
265
+ str << quote << escape_value(k) << quote
266
+ str << kv_sep
267
+ if v.nil?
268
+ str << null
269
+ else
270
+ str << quote << escape_value(v) << quote
271
+ end
272
+ comma = true
273
+ end
274
+ str
275
+ end
276
+
277
+ private
278
+
279
+ # Return a new hash based on the input hash with string
280
+ # keys and string or nil values.
281
+ def convert_hash(h)
282
+ hash = Hash.new(&DEFAULT_PROC)
283
+ h.each{|k,v| hash[k.to_s] = convert_value(v)}
284
+ hash
285
+ end
286
+
287
+ # Return value v as a string unless it is already nil.
288
+ def convert_value(v)
289
+ v.to_s unless v.nil?
290
+ end
291
+
292
+ # Escape key/value strings when literalizing to
293
+ # correctly handle backslash and quote characters.
294
+ def escape_value(k)
295
+ k.to_s.gsub(ESCAPE_RE, ESCAPE_REPLACE)
296
+ end
297
+ end
298
+
299
+ PG_NAMED_TYPES = {} unless defined?(PG_NAMED_TYPES)
300
+ # Associate the named types by default.
301
+ PG_NAMED_TYPES[:hstore] = HStore.method(:parse)
302
+ end
303
+
304
+ module SQL::Builders
305
+ # Return a Postgres::HStore proxy for the given hash.
306
+ def hstore(v)
307
+ case v
308
+ when Postgres::HStore
309
+ v
310
+ when Hash
311
+ Postgres::HStore.new(v)
312
+ else
313
+ # May not be defined unless the pg_hstore_ops extension is used
314
+ hstore_op(v)
315
+ end
316
+ end
317
+ end
318
+
319
+ Database.register_extension(:pg_hstore, Postgres::HStore::DatabaseMethods)
320
+ end
321
+
322
+ if Sequel.core_extensions?
323
+ class Hash
324
+ # Create a new HStore using the receiver as the input
325
+ # hash. Note that the HStore created will not use the
326
+ # receiver as the backing store, since it has to
327
+ # modify the hash. To get the new backing store, use:
328
+ #
329
+ # hash.hstore.to_hash
330
+ def hstore
331
+ Sequel::Postgres::HStore.new(self)
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,293 @@
1
+ # The pg_hstore_ops extension adds support to Sequel's DSL to make
2
+ # it easier to call PostgreSQL hstore functions and operators.
3
+ #
4
+ # To load the extension:
5
+ #
6
+ # Sequel.extension :pg_hstore_ops
7
+ #
8
+ # The most common usage is taking an object that represents an SQL
9
+ # expression (such as a :symbol), and calling Sequel.hstore_op with it:
10
+ #
11
+ # h = Sequel.hstore_op(:hstore_column)
12
+ #
13
+ # If you have also loaded the pg_hstore extension, you can use
14
+ # Sequel.hstore as well:
15
+ #
16
+ # h = Sequel.hstore(:hstore_column)
17
+ #
18
+ # Also, on most Sequel expression objects, you can call the hstore
19
+ # method:
20
+ #
21
+ # h = Sequel.expr(:hstore_column).hstore
22
+ #
23
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
24
+ # you can also call Symbol#hstore:
25
+ #
26
+ # h = :hstore_column.hstore
27
+ #
28
+ # This creates a Sequel::Postgres::HStoreOp object that can be used
29
+ # for easier querying:
30
+ #
31
+ # h - 'a' # hstore_column - 'a'
32
+ # h['a'] # hstore_column -> 'a'
33
+ #
34
+ # h.concat(:other_hstore_column) # ||
35
+ # h.has_key?('a') # ?
36
+ # h.contain_all(:array_column) # ?&
37
+ # h.contain_any(:array_column) # ?|
38
+ # h.contains(:other_hstore_column) # @>
39
+ # h.contained_by(:other_hstore_column) # <@
40
+ #
41
+ # h.defined # defined(hstore_column)
42
+ # h.delete('a') # delete(hstore_column, 'a')
43
+ # h.each # each(hstore_column)
44
+ # h.keys # akeys(hstore_column)
45
+ # h.populate(:a) # populate_record(a, hstore_column)
46
+ # h.record_set(:a) # (a #= hstore_column)
47
+ # h.skeys # skeys(hstore_column)
48
+ # h.slice(:a) # slice(hstore_column, a)
49
+ # h.svals # svals(hstore_column)
50
+ # h.to_array # hstore_to_array(hstore_column)
51
+ # h.to_matrix # hstore_to_matrix(hstore_column)
52
+ # h.values # avals(hstore_column)
53
+ #
54
+ # See the PostgreSQL hstore function and operator documentation for more
55
+ # details on what these functions and operators do.
56
+ #
57
+ # If you are also using the pg_hstore extension, you should load it before
58
+ # loading this extension. Doing so will allow you to use HStore#op to get
59
+ # an HStoreOp, allowing you to perform hstore operations on hstore literals.
60
+
61
+ module Sequel
62
+ module Postgres
63
+ # The HStoreOp class is a simple container for a single object that
64
+ # defines methods that yield Sequel expression objects representing
65
+ # PostgreSQL hstore operators and functions.
66
+ #
67
+ # In the method documentation examples, assume that:
68
+ #
69
+ # hstore_op = :hstore.hstore
70
+ class HStoreOp < Sequel::SQL::Wrapper
71
+ CONCAT = ["(".freeze, " || ".freeze, ")".freeze].freeze
72
+ CONTAIN_ALL = ["(".freeze, " ?& ".freeze, ")".freeze].freeze
73
+ CONTAIN_ANY = ["(".freeze, " ?| ".freeze, ")".freeze].freeze
74
+ CONTAINS = ["(".freeze, " @> ".freeze, ")".freeze].freeze
75
+ CONTAINED_BY = ["(".freeze, " <@ ".freeze, ")".freeze].freeze
76
+ HAS_KEY = ["(".freeze, " ? ".freeze, ")".freeze].freeze
77
+ LOOKUP = ["(".freeze, " -> ".freeze, ")".freeze].freeze
78
+ RECORD_SET = ["(".freeze, " #= ".freeze, ")".freeze].freeze
79
+
80
+ # Delete entries from an hstore using the subtraction operator:
81
+ #
82
+ # hstore_op - 'a' # (hstore - 'a')
83
+ def -(other)
84
+ HStoreOp.new(super)
85
+ end
86
+
87
+ # Lookup the value for the given key in an hstore:
88
+ #
89
+ # hstore_op['a'] # (hstore -> 'a')
90
+ def [](key)
91
+ Sequel::SQL::StringExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, key]))
92
+ end
93
+
94
+ # Check if the receiver contains all of the keys in the given array:
95
+ #
96
+ # hstore_op.contain_all(:a) # (hstore ?& a)
97
+ def contain_all(other)
98
+ bool_op(CONTAIN_ALL, other)
99
+ end
100
+
101
+ # Check if the receiver contains any of the keys in the given array:
102
+ #
103
+ # hstore_op.contain_any(:a) # (hstore ?| a)
104
+ def contain_any(other)
105
+ bool_op(CONTAIN_ANY, other)
106
+ end
107
+
108
+ # Check if the receiver contains all entries in the other hstore:
109
+ #
110
+ # hstore_op.contains(:h) # (hstore @> h)
111
+ def contains(other)
112
+ bool_op(CONTAINS, other)
113
+ end
114
+
115
+ # Check if the other hstore contains all entries in the receiver:
116
+ #
117
+ # hstore_op.contained_by(:h) # (hstore <@ h)
118
+ def contained_by(other)
119
+ bool_op(CONTAINED_BY, other)
120
+ end
121
+
122
+ # Check if the receiver contains a non-NULL value for the given key:
123
+ #
124
+ # hstore_op.defined('a') # defined(hstore, 'a')
125
+ def defined(key)
126
+ Sequel::SQL::BooleanExpression.new(:NOOP, function(:defined, key))
127
+ end
128
+
129
+ # Delete the matching entries from the receiver:
130
+ #
131
+ # hstore_op.delete('a') # delete(hstore, 'a')
132
+ def delete(key)
133
+ HStoreOp.new(function(:delete, key))
134
+ end
135
+
136
+ # Transform the receiver into a set of keys and values:
137
+ #
138
+ # hstore_op.each # each(hstore)
139
+ def each
140
+ function(:each)
141
+ end
142
+
143
+ # Check if the receiver contains the given key:
144
+ #
145
+ # hstore_op.has_key?('a') # (hstore ? 'a')
146
+ def has_key?(key)
147
+ bool_op(HAS_KEY, key)
148
+ end
149
+ alias include? has_key?
150
+ alias key? has_key?
151
+ alias member? has_key?
152
+ alias exist? has_key?
153
+
154
+ # Return the receiver.
155
+ def hstore
156
+ self
157
+ end
158
+
159
+ # Return the keys as a PostgreSQL array:
160
+ #
161
+ # hstore_op.keys # akeys(hstore)
162
+ def keys
163
+ function(:akeys)
164
+ end
165
+ alias akeys keys
166
+
167
+ # Merge a given hstore into the receiver:
168
+ #
169
+ # hstore_op.merge(:a) # (hstore || a)
170
+ def merge(other)
171
+ HStoreOp.new(Sequel::SQL::PlaceholderLiteralString.new(CONCAT, [self, other]))
172
+ end
173
+ alias concat merge
174
+
175
+ # Create a new record populated with entries from the receiver:
176
+ #
177
+ # hstore_op.populate(:a) # populate_record(a, hstore)
178
+ def populate(record)
179
+ SQL::Function.new(:populate_record, record, self)
180
+ end
181
+
182
+ # Update the values in a record using entries in the receiver:
183
+ #
184
+ # hstore_op.record_set(:a) # (a #= hstore)
185
+ def record_set(record)
186
+ Sequel::SQL::PlaceholderLiteralString.new(RECORD_SET, [record, value])
187
+ end
188
+
189
+ # Return the keys as a PostgreSQL set:
190
+ #
191
+ # hstore_op.skeys # skeys(hstore)
192
+ def skeys
193
+ function(:skeys)
194
+ end
195
+
196
+ # Return an hstore with only the keys in the given array:
197
+ #
198
+ # hstore_op.slice(:a) # slice(hstore, a)
199
+ def slice(keys)
200
+ HStoreOp.new(function(:slice, keys))
201
+ end
202
+
203
+ # Return the values as a PostgreSQL set:
204
+ #
205
+ # hstore_op.svals # svals(hstore)
206
+ def svals
207
+ function(:svals)
208
+ end
209
+
210
+ # Return a flattened array of the receiver with alternating
211
+ # keys and values:
212
+ #
213
+ # hstore_op.to_array # hstore_to_array(hstore)
214
+ def to_array
215
+ function(:hstore_to_array)
216
+ end
217
+
218
+ # Return a nested array of the receiver, with arrays of
219
+ # 2 element (key/value) arrays:
220
+ #
221
+ # hstore_op.to_matrix # hstore_to_matrix(hstore)
222
+ def to_matrix
223
+ function(:hstore_to_matrix)
224
+ end
225
+
226
+ # Return the values as a PostgreSQL array:
227
+ #
228
+ # hstore_op.values # avals(hstore)
229
+ def values
230
+ function(:avals)
231
+ end
232
+ alias avals values
233
+
234
+ private
235
+
236
+ # Return a placeholder literal with the given str and args, wrapped
237
+ # in a boolean expression, used by operators that return booleans.
238
+ def bool_op(str, other)
239
+ Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(str, [value, other]))
240
+ end
241
+
242
+ # Return a function with the given name, and the receiver as the first
243
+ # argument, with any additional arguments given.
244
+ def function(name, *args)
245
+ SQL::Function.new(name, self, *args)
246
+ end
247
+ end
248
+
249
+ module HStoreOpMethods
250
+ # Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
251
+ # hstore functions and operators with it.
252
+ def hstore
253
+ HStoreOp.new(self)
254
+ end
255
+ end
256
+
257
+ if defined?(HStore)
258
+ class HStore
259
+ # Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
260
+ # hstore functions and operators with it.
261
+ def op
262
+ HStoreOp.new(self)
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ module SQL::Builders
269
+ # Return the object wrapped in an Postgres::HStoreOp.
270
+ def hstore_op(v)
271
+ case v
272
+ when Postgres::HStoreOp
273
+ v
274
+ else
275
+ Postgres::HStoreOp.new(v)
276
+ end
277
+ end
278
+ end
279
+
280
+ class SQL::GenericExpression
281
+ include Sequel::Postgres::HStoreOpMethods
282
+ end
283
+
284
+ class LiteralString
285
+ include Sequel::Postgres::HStoreOpMethods
286
+ end
287
+ end
288
+
289
+ if Sequel.core_extensions?
290
+ class Symbol
291
+ include Sequel::Postgres::HStoreOpMethods
292
+ end
293
+ end