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/doc/testing.rdoc ADDED
@@ -0,0 +1,169 @@
1
+ = Testing with Sequel
2
+
3
+ Whether or not you use Sequel in your application, you are usually going to want to have tests that ensure that your code works. When you are using Sequel, it's helpful to integrate it into your testing framework, and it's generally best to run each test in its own transaction if possible. That keeps all tests isolated from each other, and it's simple as it handles all of the cleanup for you. Sequel doesn't ship with helpers for common libraries, as the exact code you need is often application-specific, but this page offers some examples that you can either use directly or build on.
4
+
5
+ == Transactional tests
6
+
7
+ These run each test in its own transaction, the recommended way to test.
8
+
9
+ Make sure you are using Sequel 3.29.0 or above when using these examples, as older versions don't support the <tt>:rollback=>:always</tt> option.
10
+
11
+ === RSpec 1
12
+
13
+ class Spec::Example::ExampleGroup
14
+ def execute(*args, &block)
15
+ result = nil
16
+ Sequel::Model.db.transaction(:rollback=>:always){result = super(*args, &block)}
17
+ result
18
+ end
19
+ end
20
+
21
+ === RSpec 2, <2.8
22
+
23
+ class RSpec::Core::ExampleGroup
24
+ # Setting an around filter globally doesn't appear to work in <2.8,
25
+ # so set one up for each subclass.
26
+ def self.inherited(subclass)
27
+ super
28
+ subclass.around do |example|
29
+ Sequel::Model.db.transaction(:rollback=>:always){example.call}
30
+ end
31
+ end
32
+ end
33
+
34
+ === RSpec 2, >=2.8
35
+
36
+ # Global around filters should work
37
+ RSpec.configure do |c|
38
+ c.around(:each) do |example|
39
+ DB.transaction(:rollback=>:always){example.run}
40
+ end
41
+ end
42
+
43
+ === Test::Unit
44
+
45
+ # Must use this class as the base class for your tests
46
+ class SequelTestCase < Test::Unit::TestCase
47
+ def run(*args, &block)
48
+ result = nil
49
+ Sequel::Model.db.transaction(:rollback=>:always){result = super}
50
+ result
51
+ end
52
+ end
53
+
54
+ # Or you could override the base implementation like this
55
+ class Test::Unit::TestCase
56
+ alias_method :_original_run, :run
57
+
58
+ def run(*args, &block)
59
+ result = nil
60
+ Sequel::Model.db.transaction(:rollback => :always) do
61
+ result = _original_run(*args, &block)
62
+ end
63
+ result
64
+ end
65
+ end
66
+
67
+ === MiniTest::Unit
68
+
69
+ # Add a subclass
70
+ # Must use this class as the base class for your tests
71
+ class SequelTestCase < MiniTest::Unit::TestCase
72
+ def run(*args, &block)
73
+ result = nil
74
+ Sequel::Model.db.transaction(:rollback=>:always){result = super}
75
+ result
76
+ end
77
+ end
78
+
79
+ # Or you could override the base implementation like this
80
+ class MiniTest::Unit::TestCase
81
+ alias_method :_original_run, :run
82
+
83
+ def run(*args, &block)
84
+ result = nil
85
+ Sequel::Model.db.transaction(:rollback => :always) do
86
+ result = _original_run(*args, &block)
87
+ end
88
+ result
89
+ end
90
+ end
91
+
92
+ == Transactional testing with multiple databases
93
+
94
+ You can use the Sequel.transaction method to run a transaction on multiple databases, rolling all of them back. Instead of:
95
+
96
+ Sequel::Model.db.transaction(:rollback=>:always)
97
+
98
+ Use Sequel.transaction with an array of databases:
99
+
100
+ Sequel.transaction([DB1, DB2, DB3], :rollback=>:always)
101
+
102
+ == Nontransactional tests
103
+
104
+ In some cases, it is not possible to use transactions. For example, if you are testing a web application that is running in a separate process, you don't have access to that process's database connections, so you can't run your examples in transactions. In that case, the best way to handle things is to cleanup after each test by deleting or truncating the database tables used in the test.
105
+
106
+ The order in which you delete/truncate the tables is important if you are using referential integrity in your database (which you probably should be doing). If you are using referential integrity, you need to make sure to delete in tables referencing other tables before the tables that are being referenced. For example, if you have an +albums+ table with an +artist_id+ field referencing the +artists+ table, you want to delete/truncate the +albums+ table before the +artists+ table. Note that if you have cyclic references in your database, you will probably need to write your own custom cleaning code.
107
+
108
+ === RSpec
109
+
110
+ class Spec::Example::ExampleGroup
111
+ after do
112
+ [:table1, :table2].each{|x| Sequel::Model.db.from(x).truncate}
113
+ # or
114
+ [:table1, :table2].each{|x| Sequel::Model.db.from(x).delete}
115
+ end
116
+ end
117
+
118
+ === Test::Unit
119
+
120
+ # Must use this class as the base class for your tests
121
+ class SequelTestCase < Test::Unit::TestCase
122
+ def teardown
123
+ [:table1, :table2].each{|x| Sequel::Model.db.from(x).truncate}
124
+ # or
125
+ [:table1, :table2].each{|x| Sequel::Model.db.from(x).delete}
126
+ end
127
+ end
128
+
129
+ = Testing Sequel Itself
130
+
131
+ Sequel has multiple separate test suites. All test suites run under either RSpec 1 or RSpec 2.
132
+
133
+ == rake spec
134
+
135
+ The +spec+ rake task (which is also the default rake task) runs Sequel's core and model specs. These specs use a mocked database connection, and test for specific SQL used and for generally correct behavior.
136
+
137
+ == rake spec_plugin
138
+
139
+ The +spec_plugin+ rake task runs the specs for the plugins and extensions that ship with Sequel. These also use a mocked database connection, and operate very similarly to the general Sequel core and model specs.
140
+
141
+ == rake spec_<i>adapter</i> (e.g. rake spec_postgres)
142
+
143
+ The <tt>spec_<i>adapter</i></tt> specs run against a real database connection with nothing mocked, and test for correct results. They are slower than the standard specs, but they will catch errors that are mocked out by the default specs, as well as show issues that only occur on a certain database, adapter, or a combination of the two.
144
+
145
+ These specs are broken down into two parts. For each database, there are specific specs that only apply to that database, and these are called the adapter specs. There are also shared specs that apply to all (or almost all) databases, these are called the integration specs.
146
+
147
+ == Environment variables
148
+
149
+ Sequel often uses environment variables when testing to specify either the database to be tested or specify how testing should be done. You can also specify the databases to test by copying spec/spec_config.rb.example to spec/spec_config.rb and modifying it. See that file for details. It may be necessary to use spec_config.rb as opposed to an environment variable if your database connection cannot be specified by a connection string.
150
+
151
+ === Connection Strings
152
+
153
+ The following environment variables specify Database connection URL strings:
154
+
155
+ * SEQUEL_INTEGRATION_URL: integration specs
156
+ * SEQUEL_FB_SPEC_DB: firebird adapter specs
157
+ * SEQUEL_MSSQL_SPEC_DB: mssql adapter specs
158
+ * SEQUEL_PG_SPEC_DB: postgres adapter specs
159
+ * SEQUEL_SQLITE_SPEC_DB: sqlite adapter specs
160
+ * SEQUEL_MY_SPEC_DB: mysql adapter specs
161
+ * SEQUEL_DB2_SPEC_DB: db2 adapter specs
162
+
163
+ === Other
164
+
165
+ * SEQUEL_MSSQL_SPEC_REQUIRE: Separate file to require when running mssql adapter specs
166
+ * SEQUEL_DB2_SPEC_REQUIRE: Separate file to require when running db2 adapter specs
167
+ * SEQUEL_COLUMNS_INTROSPECTION: Whether to run the specs with the columns_introspection extension loaded by default
168
+ * SEQUEL_NO_PENDING: Don't mark any specs as pending, try running all specs
169
+ * SKIPPED_TEST_WARN: Warn when skipping any tests because libraries aren't available
@@ -0,0 +1,17 @@
1
+ = Thread Safety
2
+
3
+ Most Sequel usage (and all common Sequel usage) is thread safe by default. Specifically, multiple threads can operate on Database instances, Dataset instances, and Model classes concurrently without problems. In general, Database instance and Model classes are not modified after application startup, and modifying Dataset instances returns modified copies of the dataset instead of mutating it.
4
+
5
+ == Connection Pool
6
+
7
+ In order to allow multiple threads to operate on the same database at the same time, Sequel uses a connection pool. The connection pool is designed so that a thread uses a connection for the minimum amount of time, returning the connection to the pool as soon as it is done using the connection. If a thread requests a connection and the pool does not have an available connection, a new connection will be created. If the maximum number of connections in the pool has already been reached, the thread will block (actually busy-wait) until a connection is available or the the connection pool timeout has elapsed (in which case a PoolTimeout error will be raised).
8
+
9
+ == Exceptions
10
+
11
+ This is a small list of things that are specifically non thread-safe. This is not an exhaustive list, there may be cases not mentioned here.
12
+
13
+ 1) Model instances: Model instances are not thread-safe unless they are frozen first. Multiple threads should not operate on an unfrozen model instance concurrently.
14
+
15
+ 2) Model class modifications: Model class modifications, such as adding associations and loading plugins, are not designed to be thread safe. You should not modify a class in one thread if any other thread can concurrently access it. Model subclassing is designed to be thread-safe, so you create a model subclass in a thread and modify it safely.
16
+
17
+ 3) Dataset mutation methods: Dataset mutation methods are not thread safe, you should not call them on datasets that could be accessed by other threads. It is safe to clone the dataset first inside a thread and call mutation methods on the cloned dataset.
@@ -0,0 +1,137 @@
1
+ = Database Transactions
2
+
3
+ Sequel uses autocommit mode by default for all of its database adapters, so in general in Sequel if you want to use database transactions, you need to be explicit about it. There are a few cases where transactions are used implicitly by default:
4
+
5
+ * Dataset#import to insert many records at once
6
+ * Model#save
7
+ * Model#destroy
8
+ * Migrations if the database supports transactional schema
9
+ * A few model plugins
10
+
11
+ Everywhere else, it is up to use to use a database transaction if you want to.
12
+
13
+ == Basic Transaction Usage
14
+
15
+ In Sequel, the <tt>Database#transaction</tt> method should be called if you want to use a database transaction. This method must be called with a block. If the block does not raise an exception, the transaction is committed:
16
+
17
+ DB.transaction do # BEGIN
18
+ DB[:foo].insert(1) # INSERT
19
+ end # COMMIT
20
+
21
+ If the block raises a Sequel::Rollback exception, the transaction is rolled back, but no exception is raised outside the block:
22
+
23
+ DB.transaction do # BEGIN
24
+ raise Sequel::Rollback
25
+ end # ROLLBACK
26
+ # no exception raised
27
+
28
+ If any other exception is raised, the transaction is rolled back, and the exception is raised outside the block:
29
+
30
+ # ArgumentError raised
31
+ DB.transaction do # BEGIN
32
+ raise ArgumentError
33
+ end # ROLLBACK
34
+ # ArgumentError raised
35
+
36
+ If you want Sequel::Rollback exceptions to be reraised, use the <tt>:rollback => :reraise</tt> option:
37
+
38
+ DB.transaction(:rollback => :reraise) do # BEGIN
39
+ raise Sequel::Rollback
40
+ end # ROLLBACK
41
+ # Sequel::Rollback raised
42
+
43
+ If you always want to rollback (useful for testing), use the <tt>:rollback => :always</tt> option:
44
+
45
+ DB.transaction(:rollback => :always) do # BEGIN
46
+ DB[:foo].insert(1) # INSERT
47
+ end # ROLLBACK
48
+
49
+ If you want to check whether you are currently in a transaction, use the Database#in_transaction? method:
50
+
51
+ DB.in_transaction? # false
52
+ DB.transaction do
53
+ DB.in_transaction? # true
54
+ end
55
+
56
+ == Transaction Hooks
57
+
58
+ You can add hooks to an in progress transaction that are called after the transaction commits or rolls back:
59
+
60
+ x = nil
61
+ DB.transaction do
62
+ DB.after_commit{x = 1}
63
+ DB.after_rollback{x = 2}
64
+ x # nil
65
+ end
66
+ x # 1
67
+
68
+ x = nil
69
+ DB.transaction do
70
+ DB.after_commit{x = 1}
71
+ DB.after_rollback{x = 2}
72
+ raise Sequel::Rollback
73
+ end
74
+ x # 2
75
+
76
+ == Nested Transaction Calls / Savepoints
77
+
78
+ You can nest calls to transaction, which by default just reuses the existing transaction:
79
+
80
+ DB.transaction do # BEGIN
81
+ DB.transaction do
82
+ DB[:foo].insert(1) # INSERT
83
+ end
84
+ end # COMMIT
85
+
86
+ You can use the <tt>:savepoint => true</tt> option in the inner transaction to explicitly use a savepoint (if the database supports it):
87
+
88
+ DB.transaction do # BEGIN
89
+ DB.transaction(:savepoint => true) do # SAVEPOINT
90
+ DB[:foo].insert(1) # INSERT
91
+ end # RELEASE SAVEPOINT
92
+ end # COMMIT
93
+
94
+ If a Sequel::Rollback exception is raised inside the savepoint block, it will only rollback to the savepoint:
95
+
96
+ DB.transaction do # BEGIN
97
+ DB.transaction(:savepoint => true) do # SAVEPOINT
98
+ raise Sequel::Rollback
99
+ end # ROLLBACK TO SAVEPOINT
100
+ # no exception raised
101
+ end # COMMIT
102
+
103
+ Other exceptions, unless rescued inside the outer transaction block, will rollback the savepoint and the outer transactions, since they are reraised by the transaction code:
104
+
105
+ DB.transaction do # BEGIN
106
+ DB.transaction(:savepoint => true) do # SAVEPOINT
107
+ raise ArgumentError
108
+ end # ROLLBACK TO SAVEPOINT
109
+ end # ROLLBACK
110
+ # ArgumentError raised
111
+
112
+ == Prepared Transactions / Two-Phase Commit
113
+
114
+ Sequel supports database prepared transactions on PostgreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
115
+
116
+ To use prepared transactions in Sequel, you provide a string as the value of the :prepare option:
117
+
118
+ DB.transaction(:prepare => 'foo') do # BEGIN
119
+ DB[:foo].insert(1) # INSERT
120
+ end # PREPARE TRANSACTION 'foo'
121
+
122
+ Later, you can commit the prepared transaction:
123
+
124
+ DB.commit_prepared_transaction('foo')
125
+
126
+ or roll the prepared transaction back:
127
+
128
+ DB.rollback_prepared_transaction('foo')
129
+
130
+ == Transaction Isolation Levels
131
+
132
+ The SQL standard supports 4 isolation levels: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, and SERIALIZABLE. Not all databases implement the levels as specified in the standard (or implement the levels at all), but on PostgreSQL, MySQL, and Microsoft SQL Server, you can specify which transaction isolation level you want to use via the :isolation option to <tt>Database#transaction</tt>. The isolation level is specified as one of the following symbols: :uncommitted, :committed, :repeatable, and :serializable. Using this option make Sequel use the correct transaction isolation syntax for your database:
133
+
134
+ DB.transaction(:isolation => :serializable) do # BEGIN
135
+ # SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
136
+ DB[:foo].insert(1) # INSERT
137
+ end # COMMIT
data/doc/validations.rdoc CHANGED
@@ -284,7 +284,7 @@ You can mix and match the two approaches. For example, if all albums should hav
284
284
 
285
285
  +validates_unique+ also accepts a block to scope the uniqueness constraint. For example, if you want to ensure that all active albums have a unique name, but inactive albums can duplicate the name:
286
286
 
287
- validates_unique(:name){|ds| ds.filter(:active)}
287
+ validates_unique(:name){|ds| ds.where(:active)}
288
288
 
289
289
  If you provide a block, it is called with the dataset to use for the uniqueness check, which you can then filter to scope the uniqueness validation to a subset of the model's dataset.
290
290
 
@@ -1,6 +1,6 @@
1
1
  = Virtual Row Blocks
2
2
 
3
- Dataset methods filter, order, and select all take blocks that are referred to as
3
+ Dataset methods where, order, and select all take blocks that are referred to as
4
4
  virtual row blocks. Many other dataset methods pass the blocks
5
5
  they are given into one of those three methods, so there are actually
6
6
  many Sequel::Dataset methods that take virtual row blocks.
@@ -11,7 +11,7 @@ Virtual Rows were created to work around the issue that some parts of
11
11
  Sequel's standard DSL could not be used on ruby 1.9. For example, the
12
12
  following Sequel code works on ruby 1.8, but not ruby 1.9:
13
13
 
14
- dataset.filter(:a > :b[:c])
14
+ dataset.where(:a > :b[:c])
15
15
  # WHERE a > b(c)
16
16
 
17
17
  This code does not work on ruby 1.9 for two reasons. First, Symbol#>
@@ -20,30 +20,15 @@ does not override it to return an SQL inequality expression. Second, Symbol#[]
20
20
  is already defined on ruby 1.9, so Sequel does not override it to return an
21
21
  SQL function expression.
22
22
 
23
- Prior to the introduction of virtual rows, the way to handle this was
24
- to use the methods that work on both ruby 1.8 and ruby 1.9:
23
+ It's possible to use Sequel's DSL to represent such expressions, but it is a
24
+ little verbose:
25
25
 
26
- dataset.filter(:a.identifier > :b.sql_function(:c))
26
+ dataset.where(Sequel.expr(:a) > Sequel.function(:b, :c))
27
27
  # WHERE a > b(c)
28
28
 
29
- However, that code is a little verbose. The virtual row DSL makes such code
30
- more concise:
29
+ The virtual row DSL makes such code more concise:
31
30
 
32
- dataset.filter{a > b(c)}
33
-
34
- Another use of virtual rows is when you turn off Sequel's core extensions off
35
- using the SEQUEL_NO_CORE_EXTENSIONS constant or environment variable. With
36
- the core extensions turned off, much of the standard Sequel DSL is not
37
- available. For example, you are no longer able to do:
38
-
39
- dataset.filter(:a & (:b | ~:c))
40
- # WHERE a AND (b OR NOT c)
41
-
42
- Because Symbol#&, Symbol#| and Symbol#~ are not defined when the core
43
- extensions are turned off. However, with virtual rows allow almost the same
44
- syntax even without the core extensions:
45
-
46
- dataset.filter{a & (b | ~c)}
31
+ dataset.where{a > b(c)}
47
32
 
48
33
  == Regular Procs vs Instance Evaled Procs
49
34
 
@@ -54,11 +39,11 @@ evaluated in the context of an instance of Sequel::SQL::VirtualRow.
54
39
 
55
40
  ds = DB[:items]
56
41
  # Regular proc
57
- ds.filter{|o| o.column > 1}
42
+ ds.where{|o| o.column > 1}
58
43
  # WHERE column > 1
59
44
 
60
45
  # Instance-evaled proc
61
- ds.filter{column > 1}
46
+ ds.where{column > 1}
62
47
  # WHERE column > 1
63
48
 
64
49
  If you aren't familiar with the difference between regular blocks and instance
@@ -75,21 +60,21 @@ inside the proc. If that doesn't make sense, maybe this example will help:
75
60
  b = 32
76
61
 
77
62
  # Regular proc
78
- ds.filter{|o| o.c > a - b}
63
+ ds.where{|o| o.c > a - b}
79
64
  # WHERE c > 10
80
65
 
81
66
  # Instance-evaled proc
82
- ds.filter{c > a - b}
67
+ ds.where{c > a - b}
83
68
  # WHERE c > (a - 32)
84
69
 
85
- There are two related differences here. First is the usage of "o.c" vs "c",
86
- and second is the difference between the the use of "a". In the regular proc,
87
- you couldn't call c without an explicit receiver in the proc, unless the self of the
88
- surrounding scope responded to it. For a, note how ruby calls the method on
70
+ There are two related differences here. First is the usage of <tt>o.c</tt> vs +c+,
71
+ and second is the difference between the the use of +a+. In the regular proc,
72
+ you couldn't call +c+ without an explicit receiver in the proc, unless the self of the
73
+ surrounding scope responded to it. For +a+, note how ruby calls the method on
89
74
  the receiver of the surrounding scope in the regular proc, which returns an integer,
90
- and does the substraction before Sequel gets access to it. In the instance evaled
91
- proc, calling a without a receiver calls the a method on the VirtualRow instance.
92
- For b, note that it operates the same in both cases, as it is a local variable.
75
+ and does the subtraction before Sequel gets access to it. In the instance evaled
76
+ proc, calling +a+ without a receiver calls the a method on the VirtualRow instance.
77
+ For +b+, note that it operates the same in both cases, as it is a local variable.
93
78
 
94
79
  Basically, the choice for whether to use a regular proc or an instance evaled proc is
95
80
  completely up to you. The same things can be accomplished with both.
@@ -105,7 +90,7 @@ variable, you can call it with () to differentiate the method call from the
105
90
  local variable access. This is mostly useful in instance_evaled procs:
106
91
 
107
92
  b = 32
108
- ds.filter{b() > b}
93
+ ds.where{b() > b}
109
94
  # WHERE b > 32
110
95
 
111
96
  == VirtualRow Methods
@@ -120,8 +105,8 @@ not qualified by any table. You get an SQL::Identifier if the method is called
120
105
  without a block or arguments, and doesn't have a double underscore in the method
121
106
  name:
122
107
 
123
- ds.filter{|o| o.column > 1}
124
- ds.filter{column > 1}
108
+ ds.where{|o| o.column > 1}
109
+ ds.where{column > 1}
125
110
  # WHERE column > 1
126
111
 
127
112
  == SQL::QualifiedIdentifiers - Qualified columns
@@ -131,8 +116,8 @@ are qualified to a specific table. You get an SQL::QualifiedIdentifier if
131
116
  the method is called without a block or arguments, and has a double underscore
132
117
  in the method name:
133
118
 
134
- ds.filter{|o| o.table__column > 1}
135
- ds.filter{table__column > 1}
119
+ ds.where{|o| o.table__column > 1}
120
+ ds.where{table__column > 1}
136
121
  # WHERE table.column > 1
137
122
 
138
123
  Using the double underscore for SQL::QualifiedIdentifiers was done to make
@@ -144,15 +129,15 @@ into a qualified column.
144
129
  SQL::Functions can be thought of as function calls in SQL. You get a simple
145
130
  function call if you call a method with arguments and without a block:
146
131
 
147
- ds.filter{|o| o.function(1) > 1}
148
- ds.filter{function(1) > 1}
132
+ ds.where{|o| o.function(1) > 1}
133
+ ds.where{function(1) > 1}
149
134
  # WHERE function(1) > 1
150
135
 
151
136
  To call a SQL function with multiple arguments, just use those arguments in
152
137
  your function call:
153
138
 
154
- ds.filter{|o| o.function(1, o.a) > 1}
155
- ds.filter{function(1, a) > 1}
139
+ ds.where{|o| o.function(1, o.a) > 1}
140
+ ds.where{function(1, a) > 1}
156
141
  # WHERE function(1, a) > 1
157
142
 
158
143
  If the SQL function does not accept any arguments, you need to provide an empty
@@ -191,7 +176,8 @@ the method call:
191
176
  SQL::WindowFunctions can be thought of as calls to SQL window functions. Not
192
177
  all databases support them, but they are very helpful for certain types of
193
178
  queries. To use them, you need to make :over the first argument of the method
194
- call, with an optional hash as the second argument: Here are some examples of use:
179
+ call, with an optional hash as the second argument, and provide an empty block
180
+ to the method. Here are some examples of use:
195
181
 
196
182
  ds.select{|o| o.rank(:over){}}
197
183
  ds.select{rank(:over){}}
@@ -205,6 +191,55 @@ call, with an optional hash as the second argument: Here are some examples of us
205
191
  ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}}
206
192
  # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3)
207
193
 
194
+ == Operators
195
+
196
+ VirtualRows use method_missing to handle almost all method calls. However, they
197
+ have special handling of some operator methods to make certain things easier. The
198
+ operators all use a prefix form.
199
+
200
+ === Math Operators
201
+
202
+ The standard +, -, *, and / mathematical operators are defined:
203
+
204
+ ds.select{|o| o.-(1, o.a).as(b)}
205
+ ds.select{self.-(1, a).as(b)}
206
+ # SELECT (1 - a) AS b
207
+
208
+ === Boolean Operators
209
+
210
+ The & and | methods are defined to use AND and OR:
211
+
212
+ ds.where{|o| o.&({:a=>:b}, :c)}
213
+ ds.where{self.&({:a=>:b}, :c)}
214
+ # WHERE ((a = b) AND c)
215
+
216
+ The ~ method is defined to do inversion:
217
+
218
+ ds.where{|o| o.~({:a=>1, :b=>2})}
219
+ ds.where{self.~({:a=>1, :b=>2})}
220
+ # WHERE ((a != 1) OR (b != 2))
221
+
222
+ === Inequality Operators
223
+
224
+ The standard >, <, >=, and <= inequality operators are defined:
225
+
226
+ ds.where{|o| o.>(1, :c)}
227
+ ds.where{self.>(1, :c)}
228
+ # WHERE (1 > c)
229
+
230
+ == Literal Strings
231
+
232
+ The backtick operator can be used inside an instance-evaled
233
+ virtual row block to create a literal string:
234
+
235
+ ds.where{a > `some SQL`}
236
+ # WHERE (a > some SQL)
237
+
238
+ You can use this on a regular virtual row block too, but it
239
+ doesn't look as nice:
240
+
241
+ ds.where{|o| o.>(:a, o.`('some SQL')}
242
+
208
243
  == Returning multiple values
209
244
 
210
245
  It's common when using select and order virtual row blocks to want to