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