sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -0,0 +1,191 @@
1
+ # The pg_interval extension adds support for PostgreSQL's interval type.
2
+ #
3
+ # This extension integrates with Sequel's native postgres adapter, so
4
+ # that when interval type values are retrieved, they are parsed and returned
5
+ # as instances of ActiveSupport::Duration.
6
+ #
7
+ # In addition to the parser, this extension adds literalizers for
8
+ # ActiveSupport::Duration that use the standard Sequel literalization
9
+ # callbacks, so they work on all adapters.
10
+ #
11
+ # If you would like to use interval columns in your model objects, you
12
+ # probably want to modify the typecasting so that it
13
+ # recognizes and correctly handles the interval columns, which you can
14
+ # do by:
15
+ #
16
+ # DB.extension :pg_interval
17
+ #
18
+ # If you are not using the native postgres adapter, you probably
19
+ # also want to use the typecast_on_load plugin in the model, and
20
+ # set it to typecast the interval type column(s) on load.
21
+ #
22
+ # This extension integrates with the pg_array extension. If you plan
23
+ # to use arrays of interval types, load the pg_array extension before the
24
+ # pg_interval extension:
25
+ #
26
+ # DB.extension :pg_array, :pg_interval
27
+ #
28
+ # The parser this extension uses requires that IntervalStyle for PostgreSQL
29
+ # is set to postgres (the default setting). If IntervalStyle is changed from
30
+ # the default setting, the parser will probably not work. The parser used is
31
+ # very simple, and is only designed to parse PostgreSQL's default output
32
+ # format, it is not designed to support all input formats that PostgreSQL
33
+ # supports.
34
+
35
+ require 'active_support/duration'
36
+ Sequel.require 'adapters/utils/pg_types'
37
+
38
+ module Sequel
39
+ module Postgres
40
+ module IntervalDatabaseMethods
41
+ EMPTY_INTERVAL = '0'.freeze
42
+ DURATION_UNITS = [:years, :months, :days, :minutes, :seconds].freeze
43
+
44
+ # Return an unquoted string version of the duration object suitable for
45
+ # use as a bound variable.
46
+ def self.literal_duration(duration)
47
+ h = Hash.new(0)
48
+ duration.parts.each{|unit, value| h[unit] += value}
49
+ s = ''
50
+
51
+ DURATION_UNITS.each do |unit|
52
+ if (v = h[unit]) != 0
53
+ s << "#{v.is_a?(Integer) ? v : sprintf('%0.6f', v)} #{unit} "
54
+ end
55
+ end
56
+
57
+ if s.empty?
58
+ EMPTY_INTERVAL
59
+ else
60
+ s
61
+ end
62
+ end
63
+
64
+ # Creates callable objects that convert strings into ActiveSupport::Duration instances.
65
+ class Parser
66
+ # Regexp that parses the full range of PostgreSQL interval type output.
67
+ PARSER = /\A([+-]?\d+ years?\s?)?([+-]?\d+ mons?\s?)?([+-]?\d+ days?\s?)?(?:(?:([+-])?(\d\d):(\d\d):(\d\d(\.\d+)?))|([+-]?\d+ hours?\s?)?([+-]?\d+ mins?\s?)?([+-]?\d+(\.\d+)? secs?\s?)?)?\z/o
68
+
69
+ # Parse the interval input string into an ActiveSupport::Duration instance.
70
+ def call(string)
71
+ raise(InvalidValue, "invalid or unhandled interval format: #{string.inspect}") unless matches = PARSER.match(string)
72
+
73
+ value = 0
74
+ parts = []
75
+
76
+ if v = matches[1]
77
+ v = v.to_i
78
+ value += 31557600 * v
79
+ parts << [:years, v]
80
+ end
81
+ if v = matches[2]
82
+ v = v.to_i
83
+ value += 2592000 * v
84
+ parts << [:months, v]
85
+ end
86
+ if v = matches[3]
87
+ v = v.to_i
88
+ value += 86400 * v
89
+ parts << [:days, v]
90
+ end
91
+ if matches[5]
92
+ seconds = matches[5].to_i * 3600 + matches[6].to_i * 60
93
+ seconds += matches[8] ? matches[7].to_f : matches[7].to_i
94
+ seconds *= -1 if matches[4] == '-'
95
+ value += seconds
96
+ parts << [:seconds, seconds]
97
+ elsif matches[9] || matches[10] || matches[11]
98
+ seconds = 0
99
+ if v = matches[9]
100
+ seconds += v.to_i * 3600
101
+ end
102
+ if v = matches[10]
103
+ seconds += v.to_i * 60
104
+ end
105
+ if v = matches[11]
106
+ seconds += matches[12] ? v.to_f : v.to_i
107
+ end
108
+ value += seconds
109
+ parts << [:seconds, seconds]
110
+ end
111
+
112
+ ActiveSupport::Duration.new(value, parts)
113
+ end
114
+ end
115
+
116
+ # Single instance of Parser used for parsing, to save on memory (since the parser has no state).
117
+ PARSER = Parser.new
118
+
119
+ # Reset the conversion procs if using the native postgres adapter,
120
+ # and extend the datasets to correctly literalize ActiveSupport::Duration values.
121
+ def self.extended(db)
122
+ db.extend_datasets(IntervalDatasetMethods)
123
+ end
124
+
125
+ # Handle ActiveSupport::Duration values in bound variables.
126
+ def bound_variable_arg(arg, conn)
127
+ case arg
128
+ when ActiveSupport::Duration
129
+ IntervalDatabaseMethods.literal_duration(arg)
130
+ else
131
+ super
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ # Handle arrays of interval types in bound variables.
138
+ def bound_variable_array(a)
139
+ case a
140
+ when ActiveSupport::Duration
141
+ "\"#{IntervalDatabaseMethods.literal_duration(a)}\""
142
+ else
143
+ super
144
+ end
145
+ end
146
+
147
+ # Typecast value correctly to an ActiveSupport::Duration instance.
148
+ # If already an ActiveSupport::Duration, return it.
149
+ # If a numeric argument is given, assume it represents a number
150
+ # of seconds, and create a new ActiveSupport::Duration instance
151
+ # representing that number of seconds.
152
+ # If a String, assume it is in PostgreSQL interval output format
153
+ # and attempt to parse it.
154
+ def typecast_value_interval(value)
155
+ case value
156
+ when ActiveSupport::Duration
157
+ value
158
+ when Numeric
159
+ ActiveSupport::Duration.new(value, [[:seconds, value]])
160
+ when String
161
+ PARSER.call(value)
162
+ else
163
+ raise Sequel::InvalidValue, "invalid value for interval type: #{value.inspect}"
164
+ end
165
+ end
166
+ end
167
+
168
+ module IntervalDatasetMethods
169
+ CAST_INTERVAL = '::interval'.freeze
170
+
171
+ # Handle literalization of ActiveSupport::Duration objects, treating them as
172
+ # PostgreSQL intervals.
173
+ def literal_other_append(sql, v)
174
+ case v
175
+ when ActiveSupport::Duration
176
+ literal_append(sql, IntervalDatabaseMethods.literal_duration(v))
177
+ sql << CAST_INTERVAL
178
+ else
179
+ super
180
+ end
181
+ end
182
+ end
183
+
184
+ PG_TYPES[1186] = Postgres::IntervalDatabaseMethods::PARSER
185
+ if defined?(PGArray) && PGArray.respond_to?(:register)
186
+ PGArray.register('interval', :oid=>1187, :scalar_oid=>1186)
187
+ end
188
+ end
189
+
190
+ Database.register_extension(:pg_interval, Postgres::IntervalDatabaseMethods)
191
+ end
@@ -0,0 +1,208 @@
1
+ # The pg_json extension adds support for Sequel to handle
2
+ # PostgreSQL's json type. It is slightly more strict than the
3
+ # PostgreSQL json type in that the object returned must be an
4
+ # array or object (PostgreSQL's json type considers plain numbers
5
+ # and strings as valid). This is because Sequel relies completely
6
+ # on the ruby JSON library for parsing, and ruby's JSON library
7
+ # does not accept the values.
8
+ #
9
+ # This extension integrates with Sequel's native postgres adapter, so
10
+ # that when json fields are retrieved, they are parsed and returned
11
+ # as instances of Sequel::Postgres::JSONArray or
12
+ # Sequel::Postgres::JSONHash. JSONArray and JSONHash are
13
+ # DelegateClasses of Array and Hash, so they mostly act the same, but
14
+ # not completely (json_array.is_a?(Array) is false). If you want
15
+ # the actual array for a JSONArray, call JSONArray#to_a. If you want
16
+ # the actual hash for a JSONHash, call JSONHash#to_hash.
17
+ # This is done so that Sequel does not treat JSONArray and JSONHash
18
+ # like Array and Hash by default, which would cause issues.
19
+ #
20
+ # To turn an existing Array or Hash into a JSONArray or JSONHash,
21
+ # use Sequel.pg_json:
22
+ #
23
+ # Sequel.pg_json(array)
24
+ # Sequel.pg_json(hash)
25
+ #
26
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
27
+ # you can also use Array#pg_json and Hash#pg_json:
28
+ #
29
+ # array.pg_json
30
+ # hash.pg_json
31
+ #
32
+ # So if you want to insert an array or hash into an json database column:
33
+ #
34
+ # DB[:table].insert(:column=>Sequel.pg_json([1, 2, 3]))
35
+ # DB[:table].insert(:column=>Sequel.pg_json({'a'=>1, 'b'=>2}))
36
+ #
37
+ # If you would like to use PostgreSQL json columns in your model
38
+ # objects, you probably want to modify the schema parsing/typecasting
39
+ # so that it recognizes and correctly handles the json type, which
40
+ # you can do by:
41
+ #
42
+ # DB.extension :pg_json
43
+ #
44
+ # If you are not using the native postgres adapter, you probably
45
+ # also want to use the typecast_on_load plugin in the model, and
46
+ # set it to typecast the json column(s) on load.
47
+ #
48
+ # This extension integrates with the pg_array extension. If you plan
49
+ # to use the json[] type, load the pg_array extension before the
50
+ # pg_json extension:
51
+ #
52
+ # DB.extension :pg_array, :pg_json
53
+ #
54
+ # This extension requires both the json and delegate libraries.
55
+
56
+ require 'delegate'
57
+ require 'json'
58
+ Sequel.require 'adapters/utils/pg_types'
59
+
60
+ module Sequel
61
+ module Postgres
62
+ CAST_JSON = '::json'.freeze
63
+
64
+ # Class representating PostgreSQL JSON column array values.
65
+ class JSONArray < DelegateClass(Array)
66
+ # Convert the array to a string using to_json, append a
67
+ # literalized version of the string to the sql, and explicitly
68
+ # cast the string to json.
69
+ def sql_literal_append(ds, sql)
70
+ ds.literal_append(sql, to_json)
71
+ sql << CAST_JSON
72
+ end
73
+ end
74
+
75
+ # Class representating PostgreSQL JSON column hash/object values.
76
+ class JSONHash < DelegateClass(Hash)
77
+ # Convert the array to a string using to_json, append a
78
+ # literalized version of the string to the sql, and explicitly
79
+ # cast the string to json.
80
+ def sql_literal_append(ds, sql)
81
+ ds.literal_append(sql, to_json)
82
+ sql << CAST_JSON
83
+ end
84
+
85
+ # Return the object being delegated to.
86
+ alias to_hash __getobj__
87
+ end
88
+
89
+ # Methods enabling Database object integration with the json type.
90
+ module JSONDatabaseMethods
91
+ # Parse the given string as json, returning either a JSONArray
92
+ # or JSONHash instance, and raising an error if the JSON
93
+ # parsing does not yield an array or hash.
94
+ def self.parse_json(s)
95
+ begin
96
+ value = JSON.parse(s)
97
+ rescue JSON::ParserError=>e
98
+ raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
99
+ end
100
+
101
+ case value
102
+ when Array
103
+ JSONArray.new(value)
104
+ when Hash
105
+ JSONHash.new(value)
106
+ else
107
+ raise Sequel::InvalidValue, "unhandled json value: #{value.inspect} (from #{s.inspect})"
108
+ end
109
+ end
110
+
111
+ # Handle JSONArray and JSONHash in bound variables
112
+ def bound_variable_arg(arg, conn)
113
+ case arg
114
+ when JSONArray, JSONHash
115
+ arg.to_json
116
+ else
117
+ super
118
+ end
119
+ end
120
+
121
+ # Make the column type detection recognize the json type.
122
+ def schema_column_type(db_type)
123
+ case db_type
124
+ when 'json'
125
+ :json
126
+ else
127
+ super
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ # Handle json[] types in bound variables.
134
+ def bound_variable_array(a)
135
+ case a
136
+ when JSONHash, JSONArray
137
+ "\"#{a.to_json.gsub('"', '\\"')}\""
138
+ else
139
+ super
140
+ end
141
+ end
142
+
143
+ # Given a value to typecast to the json column
144
+ # * If given a JSONArray or JSONHash, just return the value
145
+ # * If given an Array, return a JSONArray
146
+ # * If given a Hash, return a JSONHash
147
+ # * If given a String, parse it as would be done during
148
+ # database retrieval.
149
+ def typecast_value_json(value)
150
+ case value
151
+ when JSONArray, JSONHash
152
+ value
153
+ when Array
154
+ JSONArray.new(value)
155
+ when Hash
156
+ JSONHash.new(value)
157
+ when String
158
+ JSONDatabaseMethods.parse_json(value)
159
+ else
160
+ raise Sequel::InvalidValue, "invalid value for json: #{value.inspect}"
161
+ end
162
+ end
163
+ end
164
+
165
+ PG_TYPES[114] = JSONDatabaseMethods.method(:parse_json)
166
+ if defined?(PGArray) && PGArray.respond_to?(:register)
167
+ PGArray.register('json', :oid=>199, :scalar_oid=>114)
168
+ end
169
+ end
170
+
171
+ module SQL::Builders
172
+ # Wrap the array or hash in a Postgres::JSONArray or Postgres::JSONHash.
173
+ def pg_json(v)
174
+ case v
175
+ when Postgres::JSONArray, Postgres::JSONHash
176
+ v
177
+ when Array
178
+ Postgres::JSONArray.new(v)
179
+ when Hash
180
+ Postgres::JSONHash.new(v)
181
+ else
182
+ raise Error, "Sequel.pg_json requires a hash or array argument"
183
+ end
184
+ end
185
+ end
186
+
187
+ Database.register_extension(:pg_json, Postgres::JSONDatabaseMethods)
188
+ end
189
+
190
+ if Sequel.core_extensions?
191
+ class Array
192
+ # Return a Sequel::Postgres::JSONArray proxy to the receiver.
193
+ # This is mostly useful as a short cut for creating JSONArray
194
+ # objects that didn't come from the database.
195
+ def pg_json
196
+ Sequel::Postgres::JSONArray.new(self)
197
+ end
198
+ end
199
+
200
+ class Hash
201
+ # Return a Sequel::Postgres::JSONHash proxy to the receiver.
202
+ # This is mostly useful as a short cut for creating JSONHash
203
+ # objects that didn't come from the database.
204
+ def pg_json
205
+ Sequel::Postgres::JSONHash.new(self)
206
+ end
207
+ end
208
+ end