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,513 @@
1
+ # The pg_range extension adds support for the PostgreSQL 9.2+ range
2
+ # types to Sequel. PostgreSQL range types are similar to ruby's
3
+ # Range class, representating an array of values. However, they
4
+ # are more flexible than ruby's ranges, allowing exclusive beginnings
5
+ # and endings (ruby's range only allows exclusive endings), and
6
+ # unbounded beginnings and endings (which ruby's range does not
7
+ # support).
8
+ #
9
+ # This extension integrates with Sequel's native postgres adapter, so
10
+ # that when range type values are retrieved, they are parsed and returned
11
+ # as instances of Sequel::Postgres::PGRange. PGRange mostly acts
12
+ # like a Range, but it's not a Range as not all PostgreSQL range
13
+ # type values would be valid ruby ranges. If the range type value
14
+ # you are using is a valid ruby range, you can call PGRange#to_range
15
+ # to get a Range. However, if you call PGRange#to_range on a range
16
+ # type value uses features that ruby's Range does not support, an
17
+ # exception will be raised.
18
+ #
19
+ # In addition to the parser, this extension comes with literalizers
20
+ # for both PGRange and Range that use the standard Sequel literalization
21
+ # callbacks, so they work on all adapters.
22
+ #
23
+ # To turn an existing Range into a PGRange, use Sequel.pg_range:
24
+ #
25
+ # Sequel.pg_range(range)
26
+ #
27
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
28
+ # you can also use Range#pg_range:
29
+ #
30
+ # range.pg_range
31
+ #
32
+ # You may want to specify a specific range type:
33
+ #
34
+ # Sequel.pg_range(range, :daterange)
35
+ # range.pg_range(:daterange)
36
+ #
37
+ # If you specify the range database type, Sequel will automatically cast
38
+ # the value to that type when literalizing.
39
+ #
40
+ # If you would like to use range columns in your model objects, you
41
+ # probably want to modify the schema parsing/typecasting so that it
42
+ # recognizes and correctly handles the range type columns, which you can
43
+ # do by:
44
+ #
45
+ # DB.extension :pg_range
46
+ #
47
+ # If you are not using the native postgres adapter, you probably
48
+ # also want to use the typecast_on_load plugin in the model, and
49
+ # set it to typecast the range type column(s) on load.
50
+ #
51
+ # This extension integrates with the pg_array extension. If you plan
52
+ # to use arrays of range types, load the pg_array extension before the
53
+ # pg_range extension:
54
+ #
55
+ # DB.extension :pg_array, :pg_range
56
+
57
+ Sequel.require 'adapters/utils/pg_types'
58
+
59
+ module Sequel
60
+ module Postgres
61
+ class PGRange
62
+ include Sequel::SQL::AliasMethods
63
+
64
+ # Map of string database type names to type symbols (e.g. 'int4range' => :int4range),
65
+ # used in the schema parsing.
66
+ RANGE_TYPES = {}
67
+
68
+ EMPTY = 'empty'.freeze
69
+ EMPTY_STRING = ''.freeze
70
+ QUOTED_EMPTY_STRING = '""'.freeze
71
+ OPEN_PAREN = "(".freeze
72
+ CLOSE_PAREN = ")".freeze
73
+ OPEN_BRACKET = "[".freeze
74
+ CLOSE_BRACKET = "]".freeze
75
+ ESCAPE_RE = /("|,|\\|\[|\]|\(|\))/.freeze
76
+ ESCAPE_REPLACE = '\\\\\1'.freeze
77
+ CAST = '::'.freeze
78
+
79
+ # Registers a range type that the extension should handle. Makes a Database instance that
80
+ # has been extended with DatabaseMethods recognize the range type given and set up the
81
+ # appropriate typecasting. Also sets up automatic typecasting for the native postgres
82
+ # adapter, so that on retrieval, the values are automatically converted to PGRange instances.
83
+ # The db_type argument should be the name of the range type. Accepts the following options:
84
+ #
85
+ # :converter :: A callable object (e.g. Proc), that is called with the start or end of the range
86
+ # (usually a string), and should return the appropriate typecasted object.
87
+ # :oid :: The PostgreSQL OID for the range type. This is used by the Sequel postgres adapter
88
+ # to set up automatic type conversion on retrieval from the database.
89
+ # :subtype_oid :: Should be the PostgreSQL OID for the range's subtype. If given,
90
+ # automatically sets the :converter option by looking for scalar conversion
91
+ # proc.
92
+ #
93
+ # If a block is given, it is treated as the :converter option.
94
+ def self.register(db_type, opts={}, &block)
95
+ db_type = db_type.to_s.dup.freeze
96
+
97
+ if converter = opts[:converter]
98
+ raise Error, "can't provide both a block and :converter option to register" if block
99
+ else
100
+ converter = block
101
+ end
102
+
103
+ if soid = opts[:subtype_oid]
104
+ raise Error, "can't provide both a converter and :scalar_oid option to register" if converter
105
+ raise Error, "no conversion proc for :scalar_oid=>#{soid.inspect} in PG_TYPES" unless converter = PG_TYPES[soid]
106
+ end
107
+
108
+ parser = Parser.new(db_type, converter)
109
+
110
+ RANGE_TYPES[db_type] = db_type.to_sym
111
+
112
+ DatabaseMethods.define_range_typecast_method(db_type, parser)
113
+
114
+ if oid = opts[:oid]
115
+ Sequel::Postgres::PG_TYPES[oid] = parser
116
+ end
117
+
118
+ nil
119
+ end
120
+
121
+ # Creates callable objects that convert strings into PGRange instances.
122
+ class Parser
123
+ # Regexp that parses the full range of PostgreSQL range type output,
124
+ # except for empty ranges.
125
+ PARSER = /\A(\[|\()("((?:\\"|[^"])*)"|[^"]*),("((?:\\"|[^"])*)"|[^"]*)(\]|\))\z/o
126
+
127
+ REPLACE_RE = /\\(.)/.freeze
128
+ REPLACE_WITH = '\1'.freeze
129
+
130
+ # The database range type for this parser (e.g. 'int4range'),
131
+ # automatically setting the db_type for the returned PGRange instances.
132
+ attr_reader :db_type
133
+
134
+ # A callable object to convert the beginning and ending of the range into
135
+ # the appropriate ruby type.
136
+ attr_reader :converter
137
+
138
+ # Set the db_type and converter on initialization.
139
+ def initialize(db_type, converter=nil)
140
+ @db_type = db_type.to_s.dup.freeze if db_type
141
+ @converter = converter
142
+ end
143
+
144
+ # Parse the range type input string into a PGRange value.
145
+ def call(string)
146
+ if string == EMPTY
147
+ return PGRange.empty(db_type)
148
+ end
149
+
150
+ raise(InvalidValue, "invalid or unhandled range format: #{string.inspect}") unless matches = PARSER.match(string)
151
+
152
+ exclude_begin = matches[1] == '('
153
+ exclude_end = matches[6] == ')'
154
+
155
+ # If the input is quoted, it needs to be unescaped. Also, quoted input isn't
156
+ # checked for emptiness, since the empty quoted string is considered an
157
+ # element that happens to be the empty string, while an unquoted empty string
158
+ # is considered unbounded.
159
+ #
160
+ # While PostgreSQL allows pure escaping for input (without quoting), it appears
161
+ # to always use the quoted output form when characters need to be escaped, so
162
+ # there isn't a need to unescape unquoted output.
163
+ if beg = matches[3]
164
+ beg.gsub!(REPLACE_RE, REPLACE_WITH)
165
+ else
166
+ beg = matches[2] unless matches[2].empty?
167
+ end
168
+ if en = matches[5]
169
+ en.gsub!(REPLACE_RE, REPLACE_WITH)
170
+ else
171
+ en = matches[4] unless matches[4].empty?
172
+ end
173
+
174
+ if c = converter
175
+ beg = c.call(beg) if beg
176
+ en = c.call(en) if en
177
+ end
178
+
179
+ PGRange.new(beg, en, :exclude_begin=>exclude_begin, :exclude_end=>exclude_end, :db_type=>db_type)
180
+ end
181
+ end
182
+
183
+ module DatabaseMethods
184
+ # Reset the conversion procs if using the native postgres adapter,
185
+ # and extend the datasets to correctly literalize ruby Range values.
186
+ def self.extended(db)
187
+ db.extend_datasets(DatasetMethods)
188
+ end
189
+
190
+ # Define a private range typecasting method for the given type that uses
191
+ # the parser argument to do the type conversion.
192
+ def self.define_range_typecast_method(type, parser)
193
+ meth = :"typecast_value_#{type}"
194
+ define_method(meth){|v| typecast_value_pg_range(v, parser)}
195
+ private meth
196
+ end
197
+
198
+ # Handle Range and PGRange values in bound variables
199
+ def bound_variable_arg(arg, conn)
200
+ case arg
201
+ when PGRange
202
+ arg.unquoted_literal(schema_utility_dataset)
203
+ when Range
204
+ PGRange.from_range(arg).unquoted_literal(schema_utility_dataset)
205
+ else
206
+ super
207
+ end
208
+ end
209
+
210
+ private
211
+
212
+ # Handle arrays of range types in bound variables.
213
+ def bound_variable_array(a)
214
+ case a
215
+ when PGRange, Range
216
+ "\"#{bound_variable_arg(a, nil)}\""
217
+ else
218
+ super
219
+ end
220
+ end
221
+
222
+ # Manually override the typecasting for tsrange and tstzrange types so that
223
+ # they use the database's timezone instead of the global Sequel
224
+ # timezone.
225
+ def get_conversion_procs
226
+ procs = super
227
+
228
+ procs[3908] = Parser.new("tsrange", procs[1114])
229
+ procs[3910] = Parser.new("tstzrange", procs[1184])
230
+ if defined?(PGArray::Creator)
231
+ procs[3909] = PGArray::Creator.new("tsrange", procs[3908])
232
+ procs[3911] = PGArray::Creator.new("tstzrange", procs[3910])
233
+ end
234
+
235
+ procs
236
+ end
237
+
238
+ # Recognize the registered database range types.
239
+ def schema_column_type(db_type)
240
+ if type = RANGE_TYPES[db_type]
241
+ type
242
+ else
243
+ super
244
+ end
245
+ end
246
+
247
+ # Typecast value correctly to a PGRange. If already an
248
+ # PGRange instance with the same db_type, return as is.
249
+ # If a PGRange with a different subtype, return a new
250
+ # PGRange with the same values and the expected subtype.
251
+ # If a Range object, create a PGRange with the given
252
+ # db_type. If a string, assume it is in PostgreSQL
253
+ # output format and parse it using the parser.
254
+ def typecast_value_pg_range(value, parser)
255
+ case value
256
+ when PGRange
257
+ if value.db_type.to_s == parser.db_type
258
+ value
259
+ elsif value.empty?
260
+ PGRange.empty(parser.db_type)
261
+ else
262
+ PGRange.new(value.begin, value.end, :exclude_begin=>value.exclude_begin?, :exclude_end=>value.exclude_end?, :db_type=>parser.db_type)
263
+ end
264
+ when Range
265
+ PGRange.from_range(value, parser.db_type)
266
+ when String
267
+ parser.call(value)
268
+ else
269
+ raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}"
270
+ end
271
+ end
272
+ end
273
+
274
+ module DatasetMethods
275
+ # Handle literalization of ruby Range objects, treating them as
276
+ # PostgreSQL ranges.
277
+ def literal_other_append(sql, v)
278
+ case v
279
+ when Range
280
+ super(sql, Sequel::Postgres::PGRange.from_range(v))
281
+ else
282
+ super
283
+ end
284
+ end
285
+ end
286
+
287
+ include Enumerable
288
+
289
+ # The beginning of the range. If nil, the range has an unbounded beginning.
290
+ attr_reader :begin
291
+
292
+ # The end of the range. If nil, the range has an unbounded ending.
293
+ attr_reader :end
294
+
295
+ # The PostgreSQL database type for the range (e.g. 'int4range').
296
+ attr_reader :db_type
297
+
298
+ # Create a new PGRange instance using the beginning and ending of the ruby Range,
299
+ # with the given db_type.
300
+ def self.from_range(range, db_type=nil)
301
+ new(range.begin, range.end, :exclude_end=>range.exclude_end?, :db_type=>db_type)
302
+ end
303
+
304
+ # Create an empty PGRange with the given database type.
305
+ def self.empty(db_type=nil)
306
+ new(nil, nil, :empty=>true, :db_type=>db_type)
307
+ end
308
+
309
+ # Initialize a new PGRange instance. Accepts the following options:
310
+ #
311
+ # :db_type :: The PostgreSQL database type for the range.
312
+ # :empty :: Whether the range is empty (has no points)
313
+ # :exclude_begin :: Whether the beginning element is excluded from the range.
314
+ # :exclude_end :: Whether the ending element is excluded from the range.
315
+ def initialize(beg, en, opts={})
316
+ @begin = beg
317
+ @end = en
318
+ @empty = !!opts[:empty]
319
+ @exclude_begin = !!opts[:exclude_begin]
320
+ @exclude_end = !!opts[:exclude_end]
321
+ @db_type = opts[:db_type]
322
+ if @empty
323
+ raise(Error, 'cannot have an empty range with either a beginning or ending') unless @begin.nil? && @end.nil? && opts[:exclude_begin].nil? && opts[:exclude_end].nil?
324
+ end
325
+ end
326
+
327
+ # Delegate to the ruby range object so that the object mostly acts like a range.
328
+ range_methods = %w'each last first step'
329
+ range_methods << 'cover?' if RUBY_VERSION >= '1.9'
330
+ range_methods.each do |m|
331
+ class_eval("def #{m}(*a, &block) to_range.#{m}(*a, &block) end", __FILE__, __LINE__)
332
+ end
333
+
334
+ # Consider the receiver equal to other PGRange instances with the
335
+ # same beginning, ending, exclusions, and database type. Also consider
336
+ # it equal to Range instances if this PGRange can be converted to a
337
+ # a Range and those ranges are equal.
338
+ def eql?(other)
339
+ case other
340
+ when PGRange
341
+ if db_type == other.db_type
342
+ if empty?
343
+ other.empty?
344
+ elsif other.empty?
345
+ false
346
+ else
347
+ [:@begin, :@end, :@exclude_begin, :@exclude_end].all?{|v| instance_variable_get(v) == other.instance_variable_get(v)}
348
+ end
349
+ else
350
+ false
351
+ end
352
+ when Range
353
+ if valid_ruby_range?
354
+ to_range.eql?(other)
355
+ else
356
+ false
357
+ end
358
+ else
359
+ false
360
+ end
361
+ end
362
+ alias == eql?
363
+
364
+ # Allow PGRange values in case statements, where they return true if they
365
+ # are equal to each other using eql?, or if this PGRange can be converted
366
+ # to a Range, delegating to that range.
367
+ def ===(other)
368
+ if eql?(other)
369
+ true
370
+ else
371
+ if valid_ruby_range?
372
+ to_range === other
373
+ else
374
+ false
375
+ end
376
+ end
377
+ end
378
+
379
+ # Whether this range is empty (has no points).
380
+ def empty?
381
+ @empty
382
+ end
383
+
384
+ # Whether the beginning element is excluded from the range.
385
+ def exclude_begin?
386
+ @exclude_begin
387
+ end
388
+
389
+ # Whether the ending element is excluded from the range.
390
+ def exclude_end?
391
+ @exclude_end
392
+ end
393
+
394
+ # Append a literalize version of the receiver to the sql.
395
+ def sql_literal_append(ds, sql)
396
+ ds.literal_append(sql, unquoted_literal(ds))
397
+ if s = @db_type
398
+ sql << CAST << s.to_s
399
+ end
400
+ end
401
+
402
+ # Return a ruby Range object for this instance, if one can be created.
403
+ def to_range
404
+ return @range if @range
405
+ raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
406
+ raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
407
+ raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") unless self.begin
408
+ raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") unless self.end
409
+ @range = Range.new(self.begin, self.end, exclude_end?)
410
+ end
411
+
412
+ # Whether or not this PGRange is a valid ruby range. In order to be a valid ruby range,
413
+ # it must have a beginning and an ending (no unbounded ranges), and it cannot exclude
414
+ # the beginning element.
415
+ def valid_ruby_range?
416
+ !(empty? || exclude_begin? || !self.begin || !self.end)
417
+ end
418
+
419
+ # Whether the beginning of the range is unbounded.
420
+ def unbounded_begin?
421
+ self.begin.nil? && !empty?
422
+ end
423
+
424
+ # Whether the end of the range is unbounded.
425
+ def unbounded_end?
426
+ self.end.nil? && !empty?
427
+ end
428
+
429
+ # Return a string containing the unescaped version of the range.
430
+ # Separated out for use by the bound argument code.
431
+ def unquoted_literal(ds)
432
+ if empty?
433
+ EMPTY
434
+ else
435
+ "#{exclude_begin? ? OPEN_PAREN : OPEN_BRACKET}#{escape_value(self.begin, ds)},#{escape_value(self.end, ds)}#{exclude_end? ? CLOSE_PAREN : CLOSE_BRACKET}"
436
+ end
437
+ end
438
+
439
+ private
440
+
441
+ # Escape common range types. Instead of quoting, just backslash escape all
442
+ # special characters.
443
+ def escape_value(k, ds)
444
+ case k
445
+ when nil
446
+ EMPTY_STRING
447
+ when Date, Time
448
+ ds.literal(k)[1...-1]
449
+ when Integer, Float
450
+ k.to_s
451
+ when BigDecimal
452
+ k.to_s('F')
453
+ when LiteralString
454
+ k
455
+ when String
456
+ if k.empty?
457
+ QUOTED_EMPTY_STRING
458
+ else
459
+ k.gsub(ESCAPE_RE, ESCAPE_REPLACE)
460
+ end
461
+ else
462
+ ds.literal(k).gsub(ESCAPE_RE, ESCAPE_REPLACE)
463
+ end
464
+ end
465
+ end
466
+
467
+ PGRange.register('int4range', :oid=>3904, :subtype_oid=>23)
468
+ PGRange.register('numrange', :oid=>3906, :subtype_oid=>1700)
469
+ PGRange.register('tsrange', :oid=>3908, :subtype_oid=>1114)
470
+ PGRange.register('tstzrange', :oid=>3910, :subtype_oid=>1184)
471
+ PGRange.register('daterange', :oid=>3912, :subtype_oid=>1082)
472
+ PGRange.register('int8range', :oid=>3926, :subtype_oid=>20)
473
+ if defined?(PGArray) && PGArray.respond_to?(:register)
474
+ PGArray.register('int4range', :oid=>3905, :scalar_oid=>3904, :scalar_typecast=>:int4range)
475
+ PGArray.register('numrange', :oid=>3907, :scalar_oid=>3906, :scalar_typecast=>:numrange)
476
+ PGArray.register('tsrange', :oid=>3909, :scalar_oid=>3908, :scalar_typecast=>:tsrange)
477
+ PGArray.register('tstzrange', :oid=>3911, :scalar_oid=>3910, :scalar_typecast=>:tstzrange)
478
+ PGArray.register('daterange', :oid=>3913, :scalar_oid=>3912, :scalar_typecast=>:daterange)
479
+ PGArray.register('int8range', :oid=>3927, :scalar_oid=>3926, :scalar_typecast=>:int8range)
480
+ end
481
+ end
482
+
483
+ module SQL::Builders
484
+ # Convert the object to a Postgres::PGRange.
485
+ def pg_range(v, db_type=nil)
486
+ case v
487
+ when Postgres::PGRange
488
+ if db_type.nil? || v.db_type == db_type
489
+ v
490
+ else
491
+ Postgres::PGRange.new(v.begin, v.end, :exclude_begin=>v.exclude_begin?, :exclude_end=>v.exclude_end?, :db_type=>db_type)
492
+ end
493
+ when Range
494
+ Postgres::PGRange.from_range(v, db_type)
495
+ else
496
+ # May not be defined unless the pg_range_ops extension is used
497
+ pg_range_op(v)
498
+ end
499
+ end
500
+ end
501
+
502
+ Database.register_extension(:pg_range, Postgres::PGRange::DatabaseMethods)
503
+ end
504
+
505
+ if Sequel.core_extensions?
506
+ class Range
507
+ # Create a new PGRange using the receiver as the input range,
508
+ # with the given database type.
509
+ def pg_range(db_type=nil)
510
+ Sequel::Postgres::PGRange.from_range(self, db_type)
511
+ end
512
+ end
513
+ end
@@ -0,0 +1,150 @@
1
+ # The pg_range_ops extension adds support to Sequel's DSL to make
2
+ # it easier to call PostgreSQL range functions and operators.
3
+ #
4
+ # To load the extension:
5
+ #
6
+ # Sequel.extension :pg_range_ops
7
+ #
8
+ # The most common usage is passing an expression to Sequel.pg_range_op:
9
+ #
10
+ # r = Sequel.pg_range_op(:range)
11
+ #
12
+ # If you have also loaded the pg_range extension, you can use
13
+ # Sequel.pg_range as well:
14
+ #
15
+ # r = Sequel.pg_range(:range)
16
+ #
17
+ # Also, on most Sequel expression objects, you can call the pg_range
18
+ # method:
19
+ #
20
+ # r = Sequel.expr(:range).pg_range
21
+ #
22
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
23
+ # you can also call Symbol#pg_range:
24
+ #
25
+ # r = :range.pg_range
26
+ #
27
+ # This creates a Sequel::Postgres::RangeOp object that can be used
28
+ # for easier querying:
29
+ #
30
+ # r.contains(:other) # range @> other
31
+ # r.contained_by(:other) # range <@ other
32
+ # r.overlaps(:other) # range && other
33
+ # r.left_of(:other) # range << other
34
+ # r.right_of(:other) # range >> other
35
+ # r.starts_before(:other) # range &< other
36
+ # r.ends_after(:other) # range &> other
37
+ # r.adjacent_to(:other) # range -|- other
38
+ #
39
+ # r.lower # lower(range)
40
+ # r.upper # upper(range)
41
+ # r.isempty # isempty(range)
42
+ # r.lower_inc # lower_inc(range)
43
+ # r.upper_inc # upper_inc(range)
44
+ # r.lower_inf # lower_inf(range)
45
+ # r.upper_inf # upper_inf(range)
46
+ #
47
+ # See the PostgreSQL range function and operator documentation for more
48
+ # details on what these functions and operators do.
49
+ #
50
+ # If you are also using the pg_range extension, you should load it before
51
+ # loading this extension. Doing so will allow you to use PGArray#op to get
52
+ # an RangeOp, allowing you to perform range operations on range literals.
53
+
54
+ module Sequel
55
+ module Postgres
56
+ # The RangeOp class is a simple container for a single object that
57
+ # defines methods that yield Sequel expression objects representing
58
+ # PostgreSQL range operators and functions.
59
+ #
60
+ # Most methods in this class are defined via metaprogramming, see
61
+ # the pg_range_ops extension documentation for details on the API.
62
+ class RangeOp < Sequel::SQL::Wrapper
63
+ OPERATORS = {
64
+ :contains => ["(".freeze, " @> ".freeze, ")".freeze].freeze,
65
+ :contained_by => ["(".freeze, " <@ ".freeze, ")".freeze].freeze,
66
+ :left_of => ["(".freeze, " << ".freeze, ")".freeze].freeze,
67
+ :right_of => ["(".freeze, " >> ".freeze, ")".freeze].freeze,
68
+ :starts_before => ["(".freeze, " &< ".freeze, ")".freeze].freeze,
69
+ :ends_after => ["(".freeze, " &> ".freeze, ")".freeze].freeze,
70
+ :adjacent_to => ["(".freeze, " -|- ".freeze, ")".freeze].freeze,
71
+ :overlaps => ["(".freeze, " && ".freeze, ")".freeze].freeze,
72
+ }
73
+ FUNCTIONS = %w'lower upper isempty lower_inc upper_inc lower_inf upper_inf'
74
+
75
+ FUNCTIONS.each do |f|
76
+ class_eval("def #{f}; function(:#{f}) end", __FILE__, __LINE__)
77
+ end
78
+ OPERATORS.keys.each do |f|
79
+ class_eval("def #{f}(v); operator(:#{f}, v) end", __FILE__, __LINE__)
80
+ end
81
+
82
+ # These operators are already supported by the wrapper, but for ranges they
83
+ # return ranges, so wrap the results in another RangeOp.
84
+ %w'+ * -'.each do |f|
85
+ class_eval("def #{f}(v); RangeOp.new(super) end", __FILE__, __LINE__)
86
+ end
87
+
88
+ # Return the receiver.
89
+ def pg_range
90
+ self
91
+ end
92
+
93
+ private
94
+
95
+ # Create a boolen expression for the given type and argument.
96
+ def operator(type, other)
97
+ Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(OPERATORS[type], [value, other]))
98
+ end
99
+
100
+ # Return a function called with the receiver.
101
+ def function(name)
102
+ Sequel::SQL::Function.new(name, self)
103
+ end
104
+ end
105
+
106
+ module RangeOpMethods
107
+ # Wrap the receiver in an RangeOp so you can easily use the PostgreSQL
108
+ # range functions and operators with it.
109
+ def pg_range
110
+ RangeOp.new(self)
111
+ end
112
+ end
113
+
114
+ if defined?(PGRange)
115
+ class PGRange
116
+ # Wrap the PGRange instance in an RangeOp, allowing you to easily use
117
+ # the PostgreSQL range functions and operators with literal ranges.
118
+ def op
119
+ RangeOp.new(self)
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ module SQL::Builders
126
+ # Return the expression wrapped in the Postgres::RangeOp.
127
+ def pg_range_op(v)
128
+ case v
129
+ when Postgres::RangeOp
130
+ v
131
+ else
132
+ Postgres::RangeOp.new(v)
133
+ end
134
+ end
135
+ end
136
+
137
+ class SQL::GenericExpression
138
+ include Sequel::Postgres::RangeOpMethods
139
+ end
140
+
141
+ class LiteralString
142
+ include Sequel::Postgres::RangeOpMethods
143
+ end
144
+ end
145
+
146
+ if Sequel.core_extensions?
147
+ class Symbol
148
+ include Sequel::Postgres::RangeOpMethods
149
+ end
150
+ end