sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -0,0 +1,259 @@
1
+ # The pg_hstore_ops extension adds support to Sequel's DSL to make
2
+ # it easier to call PostgreSQL hstore functions and operators. The
3
+ # most common usage is taking an object that represents an SQL
4
+ # expression (such as a :symbol), and calling #hstore on it:
5
+ #
6
+ # h = :hstore_column.hstore
7
+ #
8
+ # This creates a Sequel::Postgres::HStoreOp object that can be used
9
+ # for easier querying:
10
+ #
11
+ # h - 'a' # hstore_column - 'a'
12
+ # h['a'] # hstore_column -> 'a'
13
+ #
14
+ # h.concat(:other_hstore_column) # ||
15
+ # h.has_key?('a') # ?
16
+ # h.contain_all(:array_column) # ?&
17
+ # h.contain_any(:array_column) # ?|
18
+ # h.contains(:other_hstore_column) # @>
19
+ # h.contained_by(:other_hstore_column) # <@
20
+ #
21
+ # h.defined # defined(hstore_column)
22
+ # h.delete('a') # delete(hstore_column, 'a')
23
+ # h.each # each(hstore_column)
24
+ # h.keys # akeys(hstore_column)
25
+ # h.populate(:a) # populate_record(a, hstore_column)
26
+ # h.record_set(:a) # (a #= hstore_column)
27
+ # h.skeys # skeys(hstore_column)
28
+ # h.slice(:a) # slice(hstore_column, a)
29
+ # h.svals # svals(hstore_column)
30
+ # h.to_array # hstore_to_array(hstore_column)
31
+ # h.to_matrix # hstore_to_matrix(hstore_column)
32
+ # h.values # avals(hstore_column)
33
+ #
34
+ # See the PostgreSQL hstore function and operator documentation for more
35
+ # details on what these functions and operators do.
36
+ #
37
+ # If you are also using the pg_hstore extension, you should load it before
38
+ # loading this extension. Doing so will allow you to use HStore#op to get
39
+ # an HStoreOp, allowing you to perform hstore operations on hstore literals.
40
+
41
+ module Sequel
42
+ module Postgres
43
+ # The HStoreOp class is a simple container for a single object that
44
+ # defines methods that yield Sequel expression objects representing
45
+ # PostgreSQL hstore operators and functions.
46
+ #
47
+ # In the method documentation examples, assume that:
48
+ #
49
+ # hstore_op = :hstore.hstore
50
+ class HStoreOp < Sequel::SQL::Wrapper
51
+ CONCAT = ["(".freeze, " || ".freeze, ")".freeze].freeze
52
+ CONTAIN_ALL = ["(".freeze, " ?& ".freeze, ")".freeze].freeze
53
+ CONTAIN_ANY = ["(".freeze, " ?| ".freeze, ")".freeze].freeze
54
+ CONTAINS = ["(".freeze, " @> ".freeze, ")".freeze].freeze
55
+ CONTAINED_BY = ["(".freeze, " <@ ".freeze, ")".freeze].freeze
56
+ HAS_KEY = ["(".freeze, " ? ".freeze, ")".freeze].freeze
57
+ LOOKUP = ["(".freeze, " -> ".freeze, ")".freeze].freeze
58
+ RECORD_SET = ["(".freeze, " #= ".freeze, ")".freeze].freeze
59
+
60
+ # Delete entries from an hstore using the subtraction operator:
61
+ #
62
+ # hstore_op - 'a' # (hstore - 'a')
63
+ def -(other)
64
+ HStoreOp.new(super)
65
+ end
66
+
67
+ # Lookup the value for the given key in an hstore:
68
+ #
69
+ # hstore_op['a'] # (hstore -> 'a')
70
+ def [](key)
71
+ Sequel::SQL::StringExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, key]))
72
+ end
73
+
74
+ # Check if the receiver contains all of the keys in the given array:
75
+ #
76
+ # hstore_op.contain_all(:a) # (hstore ?& a)
77
+ def contain_all(other)
78
+ bool_op(CONTAIN_ALL, other)
79
+ end
80
+
81
+ # Check if the receiver contains any of the keys in the given array:
82
+ #
83
+ # hstore_op.contain_any(:a) # (hstore ?| a)
84
+ def contain_any(other)
85
+ bool_op(CONTAIN_ANY, other)
86
+ end
87
+
88
+ # Check if the receiver contains all entries in the other hstore:
89
+ #
90
+ # hstore_op.contains(:h) # (hstore @> h)
91
+ def contains(other)
92
+ bool_op(CONTAINS, other)
93
+ end
94
+
95
+ # Check if the other hstore contains all entries in the receiver:
96
+ #
97
+ # hstore_op.contained_by(:h) # (hstore <@ h)
98
+ def contained_by(other)
99
+ bool_op(CONTAINED_BY, other)
100
+ end
101
+
102
+ # Check if the receiver contains a non-NULL value for the given key:
103
+ #
104
+ # hstore_op.defined('a') # defined(hstore, 'a')
105
+ def defined(key)
106
+ Sequel::SQL::BooleanExpression.new(:NOOP, function(:defined, key))
107
+ end
108
+
109
+ # Delete the matching entries from the receiver:
110
+ #
111
+ # hstore_op.delete('a') # delete(hstore, 'a')
112
+ def delete(key)
113
+ HStoreOp.new(function(:delete, key))
114
+ end
115
+
116
+ # Transform the receiver into a set of keys and values:
117
+ #
118
+ # hstore_op.each # each(hstore)
119
+ def each
120
+ function(:each)
121
+ end
122
+
123
+ # Check if the receiver contains the given key:
124
+ #
125
+ # hstore_op.has_key?('a') # (hstore ? 'a')
126
+ def has_key?(key)
127
+ bool_op(HAS_KEY, key)
128
+ end
129
+ alias include? has_key?
130
+ alias key? has_key?
131
+ alias member? has_key?
132
+ alias exist? has_key?
133
+
134
+ # Return the receiver.
135
+ def hstore
136
+ self
137
+ end
138
+
139
+ # Return the keys as a PostgreSQL array:
140
+ #
141
+ # hstore_op.keys # akeys(hstore)
142
+ def keys
143
+ function(:akeys)
144
+ end
145
+ alias akeys keys
146
+
147
+ # Merge a given hstore into the receiver:
148
+ #
149
+ # hstore_op.merge(:a) # (hstore || a)
150
+ def merge(other)
151
+ HStoreOp.new(Sequel::SQL::PlaceholderLiteralString.new(CONCAT, [self, other]))
152
+ end
153
+ alias concat merge
154
+
155
+ # Create a new record populated with entries from the receiver:
156
+ #
157
+ # hstore_op.populate(:a) # populate_record(a, hstore)
158
+ def populate(record)
159
+ SQL::Function.new(:populate_record, record, self)
160
+ end
161
+
162
+ # Update the values in a record using entries in the receiver:
163
+ #
164
+ # hstore_op.record_set(:a) # (a #= hstore)
165
+ def record_set(record)
166
+ Sequel::SQL::PlaceholderLiteralString.new(RECORD_SET, [record, value])
167
+ end
168
+
169
+ # Return the keys as a PostgreSQL set:
170
+ #
171
+ # hstore_op.skeys # skeys(hstore)
172
+ def skeys
173
+ function(:skeys)
174
+ end
175
+
176
+ # Return an hstore with only the keys in the given array:
177
+ #
178
+ # hstore_op.slice(:a) # slice(hstore, a)
179
+ def slice(keys)
180
+ HStoreOp.new(function(:slice, keys))
181
+ end
182
+
183
+ # Return the values as a PostgreSQL set:
184
+ #
185
+ # hstore_op.svals # svals(hstore)
186
+ def svals
187
+ function(:svals)
188
+ end
189
+
190
+ # Return a flattened array of the receiver with alternating
191
+ # keys and values:
192
+ #
193
+ # hstore_op.to_array # hstore_to_array(hstore)
194
+ def to_array
195
+ function(:hstore_to_array)
196
+ end
197
+
198
+ # Return a nested array of the receiver, with arrays of
199
+ # 2 element (key/value) arrays:
200
+ #
201
+ # hstore_op.to_matrix # hstore_to_matrix(hstore)
202
+ def to_matrix
203
+ function(:hstore_to_matrix)
204
+ end
205
+
206
+ # Return the values as a PostgreSQL array:
207
+ #
208
+ # hstore_op.values # avals(hstore)
209
+ def values
210
+ function(:avals)
211
+ end
212
+ alias avals values
213
+
214
+ private
215
+
216
+ # Return a placeholder literal with the given str and args, wrapped
217
+ # in a boolean expression, used by operators that return booleans.
218
+ def bool_op(str, other)
219
+ Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(str, [value, other]))
220
+ end
221
+
222
+ # Return a function with the given name, and the receiver as the first
223
+ # argument, with any additional arguments given.
224
+ def function(name, *args)
225
+ SQL::Function.new(name, self, *args)
226
+ end
227
+ end
228
+
229
+ module HStoreOpMethods
230
+ # Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
231
+ # hstore functions and operators with it.
232
+ def hstore
233
+ HStoreOp.new(self)
234
+ end
235
+ end
236
+
237
+ if defined?(HStore)
238
+ class HStore
239
+ # Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
240
+ # hstore functions and operators with it.
241
+ def op
242
+ HStoreOp.new(self)
243
+ end
244
+ end
245
+ end
246
+ end
247
+
248
+ class SQL::GenericExpression
249
+ include Sequel::Postgres::HStoreOpMethods
250
+ end
251
+
252
+ class LiteralString
253
+ include Sequel::Postgres::HStoreOpMethods
254
+ end
255
+ end
256
+
257
+ class Symbol
258
+ include Sequel::Postgres::HStoreOpMethods
259
+ end
@@ -0,0 +1,89 @@
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.extend Sequel::Postgres::InetDatabaseMethods
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 does not add special support for the macaddr
18
+ # type. Ruby doesn't have a stdlib class that represents mac
19
+ # addresses, so these will still be returned as strings.
20
+
21
+ require 'ipaddr'
22
+
23
+ module Sequel
24
+ module Postgres
25
+ # Methods enabling Database object integration with the inet/cidr types.
26
+ module InetDatabaseMethods
27
+
28
+ # Reset the conversion procs when extending the Database object, so
29
+ # it will pick up the inet/cidr convertor. Also, extend the datasets
30
+ # with support for literalizing the IPAddr types.
31
+ def self.extended(db)
32
+ db.reset_conversion_procs if db.respond_to?(:reset_conversion_procs)
33
+ db.extend_datasets(InetDatasetMethods)
34
+ end
35
+
36
+ # Convert an IPAddr arg to a string. Probably not necessary, but done
37
+ # for safety.
38
+ def bound_variable_arg(arg, conn)
39
+ case arg
40
+ when IPAddr
41
+ "#{arg.to_s}/#{arg.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ # Make the column type detection recognize the inet and cidr types.
48
+ def schema_column_type(db_type)
49
+ case db_type
50
+ when 'inet', 'cidr'
51
+ :ipaddr
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ # Typecast the given value to an IPAddr object.
60
+ def typecast_value_ipaddr(value)
61
+ case value
62
+ when IPAddr
63
+ value
64
+ when String
65
+ IPAddr.new(value)
66
+ else
67
+ raise Sequel::InvalidValue, "invalid value for inet/cidr: #{value.inspect}"
68
+ end
69
+ end
70
+ end
71
+
72
+ module InetDatasetMethods
73
+ private
74
+
75
+ # Convert IPAddr value to a string and append a literal version
76
+ # of the string to the sql.
77
+ def literal_other_append(sql, value)
78
+ if value.is_a?(IPAddr)
79
+ literal_string_append(sql, "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}")
80
+ else
81
+ super
82
+ end
83
+ end
84
+ end
85
+
86
+ PG_TYPES = {} unless defined?(PG_TYPES)
87
+ PG_TYPES[869] = PG_TYPES[650] = IPAddr.method(:new)
88
+ end
89
+ end
@@ -0,0 +1,178 @@
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
+ #
22
+ # array.pg_json
23
+ # hash.pg_json
24
+ #
25
+ # So if you want to insert an array or hash into an json database column:
26
+ #
27
+ # DB[:table].insert(:column=>[1, 2, 3].pg_json)
28
+ # DB[:table].insert(:column=>{'a'=>1, 'b'=>2}.pg_json)
29
+ #
30
+ # If you would like to use PostgreSQL json columns in your model
31
+ # objects, you probably want to modify the schema parsing/typecasting
32
+ # so that it recognizes and correctly handles the json type, which
33
+ # you can do by:
34
+ #
35
+ # DB.extend Sequel::Postgres::JSONDatabaseMethods
36
+ #
37
+ # If you are not using the native postgres adapter, you probably
38
+ # also want to use the typecast_on_load plugin in the model, and
39
+ # set it to typecast the json column(s) on load.
40
+ #
41
+ # The extension is designed to be used with the json type natively
42
+ # supported by PostgreSQL 9.2+. There was also a PostgreSQL extension released
43
+ # that allowed the json type to be used on PostgreSQL 9.1. To make
44
+ # this extension support that type in the native adapter, do the
45
+ # following after loading this extension:
46
+ #
47
+ # Sequel::Postgres::PG_NAMED_TYPES = {} unless defined?(Sequel::Postgres::PG_NAMED_TYPES)
48
+ # Sequel::Postgres::PG_NAMED_TYPES[:json] = Sequel::Postgres::PG_TYPES[114]
49
+ #
50
+ # This extension requires both the json and delegate libraries.
51
+
52
+ require 'delegate'
53
+ require 'json'
54
+
55
+ module Sequel
56
+ module Postgres
57
+ CAST_JSON = '::json'.freeze
58
+
59
+ # Class representating PostgreSQL JSON column array values.
60
+ class JSONArray < DelegateClass(Array)
61
+ # Convert the array to a string using to_json, append a
62
+ # literalized version of the string to the sql, and explicitly
63
+ # cast the string to json.
64
+ def sql_literal_append(ds, sql)
65
+ ds.literal_append(sql, to_json)
66
+ sql << CAST_JSON
67
+ end
68
+ end
69
+
70
+ # Class representating PostgreSQL JSON column hash/object values.
71
+ class JSONHash < DelegateClass(Hash)
72
+ # Convert the array to a string using to_json, append a
73
+ # literalized version of the string to the sql, and explicitly
74
+ # cast the string to json.
75
+ def sql_literal_append(ds, sql)
76
+ ds.literal_append(sql, to_json)
77
+ sql << CAST_JSON
78
+ end
79
+
80
+ # Return the object being delegated to.
81
+ alias to_hash __getobj__
82
+ end
83
+
84
+ # Methods enabling Database object integration with the json type.
85
+ module JSONDatabaseMethods
86
+ # Parse the given string as json, returning either a JSONArray
87
+ # or JSONHash instance, and raising an error if the JSON
88
+ # parsing does not yield an array or hash.
89
+ def self.parse_json(s)
90
+ begin
91
+ value = JSON.parse(s)
92
+ rescue JSON::ParserError=>e
93
+ raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
94
+ end
95
+
96
+ case value
97
+ when Array
98
+ JSONArray.new(value)
99
+ when Hash
100
+ JSONHash.new(value)
101
+ else
102
+ raise Sequel::InvalidValue, "unhandled json value: #{value.inspect} (from #{s.inspect})"
103
+ end
104
+ end
105
+
106
+ # Reset the conversion procs when extending the Database object, so
107
+ # it will pick up the json convertor. This is only done for the native
108
+ # postgres adapter.
109
+ def self.extended(db)
110
+ db.reset_conversion_procs if db.respond_to?(:reset_conversion_procs)
111
+ end
112
+
113
+ # Handle JSONArray and JSONHash in bound variables
114
+ def bound_variable_arg(arg, conn)
115
+ case arg
116
+ when JSONArray, JSONHash
117
+ arg.to_json
118
+ else
119
+ super
120
+ end
121
+ end
122
+
123
+ # Make the column type detection recognize the json type.
124
+ def schema_column_type(db_type)
125
+ case db_type
126
+ when 'json'
127
+ :json
128
+ else
129
+ super
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ # Given a value to typecast to the json column
136
+ # * If given a JSONArray or JSONHash, just return the value
137
+ # * If given an Array, return a JSONArray
138
+ # * If given a Hash, return a JSONHash
139
+ # * If given a String, parse it as would be done during
140
+ # database retrieval.
141
+ def typecast_value_json(value)
142
+ case value
143
+ when JSONArray, JSONHash
144
+ value
145
+ when Array
146
+ JSONArray.new(value)
147
+ when Hash
148
+ JSONHash.new(value)
149
+ when String
150
+ JSONDatabaseMethods.parse_json(value)
151
+ else
152
+ raise Sequel::InvalidValue, "invalid value for json: #{value.inspect}"
153
+ end
154
+ end
155
+ end
156
+
157
+ PG_TYPES = {} unless defined?(PG_TYPES)
158
+ PG_TYPES[114] = JSONDatabaseMethods.method(:parse_json)
159
+ end
160
+ end
161
+
162
+ class Array
163
+ # Return a Sequel::Postgres::JSONArray proxy to the receiver.
164
+ # This is mostly useful as a short cut for creating JSONArray
165
+ # objects that didn't come from the database.
166
+ def pg_json
167
+ Sequel::Postgres::JSONArray.new(self)
168
+ end
169
+ end
170
+
171
+ class Hash
172
+ # Return a Sequel::Postgres::JSONHash proxy to the receiver.
173
+ # This is mostly useful as a short cut for creating JSONHash
174
+ # objects that didn't come from the database.
175
+ def pg_json
176
+ Sequel::Postgres::JSONHash.new(self)
177
+ end
178
+ end