sequel 5.39.0 → 5.72.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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +408 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +13 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +26 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.40.0.txt +40 -0
  17. data/doc/release_notes/5.41.0.txt +25 -0
  18. data/doc/release_notes/5.42.0.txt +136 -0
  19. data/doc/release_notes/5.43.0.txt +98 -0
  20. data/doc/release_notes/5.44.0.txt +32 -0
  21. data/doc/release_notes/5.45.0.txt +34 -0
  22. data/doc/release_notes/5.46.0.txt +87 -0
  23. data/doc/release_notes/5.47.0.txt +59 -0
  24. data/doc/release_notes/5.48.0.txt +14 -0
  25. data/doc/release_notes/5.49.0.txt +59 -0
  26. data/doc/release_notes/5.50.0.txt +78 -0
  27. data/doc/release_notes/5.51.0.txt +47 -0
  28. data/doc/release_notes/5.52.0.txt +87 -0
  29. data/doc/release_notes/5.53.0.txt +23 -0
  30. data/doc/release_notes/5.54.0.txt +27 -0
  31. data/doc/release_notes/5.55.0.txt +21 -0
  32. data/doc/release_notes/5.56.0.txt +51 -0
  33. data/doc/release_notes/5.57.0.txt +23 -0
  34. data/doc/release_notes/5.58.0.txt +31 -0
  35. data/doc/release_notes/5.59.0.txt +73 -0
  36. data/doc/release_notes/5.60.0.txt +22 -0
  37. data/doc/release_notes/5.61.0.txt +43 -0
  38. data/doc/release_notes/5.62.0.txt +132 -0
  39. data/doc/release_notes/5.63.0.txt +33 -0
  40. data/doc/release_notes/5.64.0.txt +50 -0
  41. data/doc/release_notes/5.65.0.txt +21 -0
  42. data/doc/release_notes/5.66.0.txt +24 -0
  43. data/doc/release_notes/5.67.0.txt +32 -0
  44. data/doc/release_notes/5.68.0.txt +61 -0
  45. data/doc/release_notes/5.69.0.txt +26 -0
  46. data/doc/release_notes/5.70.0.txt +35 -0
  47. data/doc/release_notes/5.71.0.txt +21 -0
  48. data/doc/release_notes/5.72.0.txt +33 -0
  49. data/doc/schema_modification.rdoc +1 -1
  50. data/doc/security.rdoc +9 -9
  51. data/doc/sharding.rdoc +3 -1
  52. data/doc/sql.rdoc +28 -16
  53. data/doc/testing.rdoc +22 -11
  54. data/doc/transactions.rdoc +6 -6
  55. data/doc/virtual_rows.rdoc +2 -2
  56. data/lib/sequel/adapters/ado/access.rb +1 -1
  57. data/lib/sequel/adapters/ado.rb +17 -17
  58. data/lib/sequel/adapters/amalgalite.rb +3 -5
  59. data/lib/sequel/adapters/ibmdb.rb +2 -2
  60. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  61. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  62. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  64. data/lib/sequel/adapters/jdbc.rb +16 -18
  65. data/lib/sequel/adapters/mysql.rb +92 -67
  66. data/lib/sequel/adapters/mysql2.rb +54 -49
  67. data/lib/sequel/adapters/odbc.rb +6 -2
  68. data/lib/sequel/adapters/oracle.rb +4 -3
  69. data/lib/sequel/adapters/postgres.rb +83 -40
  70. data/lib/sequel/adapters/shared/access.rb +11 -1
  71. data/lib/sequel/adapters/shared/db2.rb +30 -0
  72. data/lib/sequel/adapters/shared/mssql.rb +90 -9
  73. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  74. data/lib/sequel/adapters/shared/oracle.rb +82 -1
  75. data/lib/sequel/adapters/shared/postgres.rb +496 -178
  76. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  77. data/lib/sequel/adapters/shared/sqlite.rb +116 -11
  78. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  79. data/lib/sequel/adapters/sqlite.rb +60 -18
  80. data/lib/sequel/adapters/tinytds.rb +1 -1
  81. data/lib/sequel/adapters/trilogy.rb +117 -0
  82. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  83. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  84. data/lib/sequel/ast_transformer.rb +6 -0
  85. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  86. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  87. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  88. data/lib/sequel/connection_pool/single.rb +6 -8
  89. data/lib/sequel/connection_pool/threaded.rb +14 -8
  90. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  91. data/lib/sequel/connection_pool.rb +55 -31
  92. data/lib/sequel/core.rb +28 -18
  93. data/lib/sequel/database/connecting.rb +27 -3
  94. data/lib/sequel/database/dataset.rb +16 -6
  95. data/lib/sequel/database/misc.rb +69 -14
  96. data/lib/sequel/database/query.rb +73 -2
  97. data/lib/sequel/database/schema_generator.rb +46 -53
  98. data/lib/sequel/database/schema_methods.rb +18 -2
  99. data/lib/sequel/dataset/actions.rb +108 -14
  100. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  101. data/lib/sequel/dataset/features.rb +20 -0
  102. data/lib/sequel/dataset/misc.rb +12 -2
  103. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  104. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  105. data/lib/sequel/dataset/query.rb +171 -44
  106. data/lib/sequel/dataset/sql.rb +182 -47
  107. data/lib/sequel/dataset.rb +4 -0
  108. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  109. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  110. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  111. data/lib/sequel/extensions/async_thread_pool.rb +439 -0
  112. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  113. data/lib/sequel/extensions/blank.rb +8 -0
  114. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  115. data/lib/sequel/extensions/connection_validator.rb +16 -11
  116. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  117. data/lib/sequel/extensions/core_refinements.rb +36 -11
  118. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  119. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  120. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  121. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  122. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  123. data/lib/sequel/extensions/index_caching.rb +5 -1
  124. data/lib/sequel/extensions/inflector.rb +9 -1
  125. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  126. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  127. data/lib/sequel/extensions/migration.rb +11 -2
  128. data/lib/sequel/extensions/named_timezones.rb +26 -6
  129. data/lib/sequel/extensions/pagination.rb +1 -1
  130. data/lib/sequel/extensions/pg_array.rb +32 -4
  131. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  132. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  133. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  134. data/lib/sequel/extensions/pg_enum.rb +2 -3
  135. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  136. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  137. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  138. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  139. data/lib/sequel/extensions/pg_inet.rb +10 -11
  140. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  141. data/lib/sequel/extensions/pg_interval.rb +45 -19
  142. data/lib/sequel/extensions/pg_json.rb +13 -15
  143. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  144. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +11 -24
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +21 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/query.rb +2 -0
  151. data/lib/sequel/extensions/s.rb +2 -1
  152. data/lib/sequel/extensions/schema_caching.rb +1 -1
  153. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  154. data/lib/sequel/extensions/server_block.rb +10 -13
  155. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  156. data/lib/sequel/extensions/sql_comments.rb +110 -3
  157. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  158. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  159. data/lib/sequel/extensions/string_agg.rb +1 -1
  160. data/lib/sequel/extensions/string_date_time.rb +19 -23
  161. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  162. data/lib/sequel/model/associations.rb +345 -101
  163. data/lib/sequel/model/base.rb +51 -27
  164. data/lib/sequel/model/dataset_module.rb +3 -0
  165. data/lib/sequel/model/errors.rb +10 -1
  166. data/lib/sequel/model/inflections.rb +1 -1
  167. data/lib/sequel/model/plugins.rb +5 -0
  168. data/lib/sequel/plugins/association_proxies.rb +2 -0
  169. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +87 -15
  172. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  173. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  174. data/lib/sequel/plugins/column_encryption.rb +728 -0
  175. data/lib/sequel/plugins/composition.rb +10 -4
  176. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  177. data/lib/sequel/plugins/constraint_validations.rb +10 -6
  178. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  179. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  180. data/lib/sequel/plugins/dirty.rb +1 -1
  181. data/lib/sequel/plugins/enum.rb +124 -0
  182. data/lib/sequel/plugins/finder.rb +4 -2
  183. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  184. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  185. data/lib/sequel/plugins/json_serializer.rb +39 -24
  186. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  187. data/lib/sequel/plugins/list.rb +3 -1
  188. data/lib/sequel/plugins/many_through_many.rb +109 -10
  189. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  190. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  191. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  192. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  193. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  194. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
  195. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  196. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  197. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  198. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  199. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  200. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  201. data/lib/sequel/plugins/serialization.rb +9 -3
  202. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  203. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  204. data/lib/sequel/plugins/sql_comments.rb +189 -0
  205. data/lib/sequel/plugins/static_cache.rb +39 -1
  206. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  207. data/lib/sequel/plugins/subclasses.rb +28 -11
  208. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  209. data/lib/sequel/plugins/timestamps.rb +1 -1
  210. data/lib/sequel/plugins/unused_associations.rb +521 -0
  211. data/lib/sequel/plugins/update_or_create.rb +1 -1
  212. data/lib/sequel/plugins/validate_associated.rb +22 -12
  213. data/lib/sequel/plugins/validation_helpers.rb +46 -12
  214. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  215. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  216. data/lib/sequel/sql.rb +1 -1
  217. data/lib/sequel/timezones.rb +12 -14
  218. data/lib/sequel/version.rb +1 -1
  219. metadata +132 -38
@@ -12,7 +12,9 @@
12
12
  #
13
13
  # How accurate this count is depends on the number of rows
14
14
  # added/deleted from the table since the last time it was
15
- # analyzed.
15
+ # analyzed. If the table has not been vacuumed or analyzed
16
+ # yet, this can return 0 or -1 depending on the PostgreSQL
17
+ # version in use.
16
18
  #
17
19
  # To load the extension into the database:
18
20
  #
@@ -0,0 +1,367 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The pg_multirange extension adds support for the PostgreSQL 14+ multirange
4
+ # types to Sequel. PostgreSQL multirange types are similar to an array of
5
+ # ranges, where a match against the multirange is a match against any of the
6
+ # ranges in the multirange.
7
+ #
8
+ # When PostgreSQL multirange values are retrieved, they are parsed and returned
9
+ # as instances of Sequel::Postgres::PGMultiRange. PGMultiRange mostly acts
10
+ # like an array of Sequel::Postgres::PGRange (see the pg_range extension).
11
+ #
12
+ # In addition to the parser, this extension comes with literalizers
13
+ # for PGMultiRanges, so they can be used in queries and as bound variables.
14
+ #
15
+ # To turn an existing array of Ranges into a PGMultiRange, use Sequel.pg_multirange.
16
+ # You must provide the type of multirange when creating the multirange:
17
+ #
18
+ # Sequel.pg_multirange(array_of_date_ranges, :datemultirange)
19
+ #
20
+ # To use this extension, load it into the Database instance:
21
+ #
22
+ # DB.extension :pg_multirange
23
+ #
24
+ # See the {schema modification guide}[rdoc-ref:doc/schema_modification.rdoc]
25
+ # for details on using multirange type columns in CREATE/ALTER TABLE statements.
26
+ #
27
+ # This extension makes it easy to add support for other multirange types. In
28
+ # general, you just need to make sure that the subtype is handled and has the
29
+ # appropriate converter installed. For user defined
30
+ # types, you can do this via:
31
+ #
32
+ # DB.add_conversion_proc(subtype_oid){|string| }
33
+ #
34
+ # Then you can call
35
+ # Sequel::Postgres::PGMultiRange::DatabaseMethods#register_multirange_type
36
+ # to automatically set up a handler for the range type. So if you
37
+ # want to support the timemultirange type (assuming the time type is already
38
+ # supported):
39
+ #
40
+ # DB.register_multirange_type('timerange')
41
+ #
42
+ # This extension integrates with the pg_array extension. If you plan
43
+ # to use arrays of multirange types, load the pg_array extension before the
44
+ # pg_multirange extension:
45
+ #
46
+ # DB.extension :pg_array, :pg_multirange
47
+ #
48
+ # The pg_multirange extension will automatically load the pg_range extension.
49
+ #
50
+ # Related module: Sequel::Postgres::PGMultiRange
51
+
52
+ require 'delegate'
53
+ require 'strscan'
54
+
55
+ module Sequel
56
+ module Postgres
57
+ class PGMultiRange < DelegateClass(Array)
58
+ include Sequel::SQL::AliasMethods
59
+
60
+ # Converts strings into PGMultiRange instances.
61
+ class Parser < StringScanner
62
+ def initialize(source, converter)
63
+ super(source)
64
+ @converter = converter
65
+ end
66
+
67
+ # Parse the multirange type input string into a PGMultiRange value.
68
+ def parse
69
+ raise Sequel::Error, "invalid multirange, doesn't start with {" unless get_byte == '{'
70
+ ranges = []
71
+
72
+ unless scan(/\}/)
73
+ while true
74
+ raise Sequel::Error, "unfinished multirange" unless range_string = scan_until(/[\]\)]/)
75
+ ranges << @converter.call(range_string)
76
+
77
+ case sep = get_byte
78
+ when '}'
79
+ break
80
+ when ','
81
+ # nothing
82
+ else
83
+ raise Sequel::Error, "invalid multirange separator: #{sep.inspect}"
84
+ end
85
+ end
86
+ end
87
+
88
+ raise Sequel::Error, "invalid multirange, remaining data after }" unless eos?
89
+ ranges
90
+ end
91
+ end
92
+
93
+ # Callable object that takes the input string and parses it using Parser.
94
+ class Creator
95
+ # The database type to set on the PGMultiRange instances returned.
96
+ attr_reader :type
97
+
98
+ def initialize(type, converter=nil)
99
+ @type = type
100
+ @converter = converter
101
+ end
102
+
103
+ # Parse the string using Parser with the appropriate
104
+ # converter, and return a PGMultiRange with the appropriate database
105
+ # type.
106
+ def call(string)
107
+ PGMultiRange.new(Parser.new(string, @converter).parse, @type)
108
+ end
109
+ end
110
+
111
+ module DatabaseMethods
112
+ # Add the default multirange conversion procs to the database
113
+ def self.extended(db)
114
+ db.instance_exec do
115
+ raise Error, "multiranges not supported on this database" unless server_version >= 140000
116
+
117
+ extension :pg_range
118
+ @pg_multirange_schema_types ||= {}
119
+
120
+ register_multirange_type('int4multirange', :range_oid=>3904, :oid=>4451)
121
+ register_multirange_type('nummultirange', :range_oid=>3906, :oid=>4532)
122
+ register_multirange_type('tsmultirange', :range_oid=>3908, :oid=>4533)
123
+ register_multirange_type('tstzmultirange', :range_oid=>3910, :oid=>4534)
124
+ register_multirange_type('datemultirange', :range_oid=>3912, :oid=>4535)
125
+ register_multirange_type('int8multirange', :range_oid=>3926, :oid=>4536)
126
+
127
+ if respond_to?(:register_array_type)
128
+ register_array_type('int4multirange', :oid=>6150, :scalar_oid=>4451, :scalar_typecast=>:int4multirange)
129
+ register_array_type('nummultirange', :oid=>6151, :scalar_oid=>4532, :scalar_typecast=>:nummultirange)
130
+ register_array_type('tsmultirange', :oid=>6152, :scalar_oid=>4533, :scalar_typecast=>:tsmultirange)
131
+ register_array_type('tstzmultirange', :oid=>6153, :scalar_oid=>4534, :scalar_typecast=>:tstzmultirange)
132
+ register_array_type('datemultirange', :oid=>6155, :scalar_oid=>4535, :scalar_typecast=>:datemultirange)
133
+ register_array_type('int8multirange', :oid=>6157, :scalar_oid=>4536, :scalar_typecast=>:int8multirange)
134
+ end
135
+
136
+ [:int4multirange, :nummultirange, :tsmultirange, :tstzmultirange, :datemultirange, :int8multirange].each do |v|
137
+ @schema_type_classes[v] = PGMultiRange
138
+ end
139
+
140
+ procs = conversion_procs
141
+ add_conversion_proc(4533, PGMultiRange::Creator.new("tsmultirange", procs[3908]))
142
+ add_conversion_proc(4534, PGMultiRange::Creator.new("tstzmultirange", procs[3910]))
143
+
144
+ if respond_to?(:register_array_type) && defined?(PGArray::Creator)
145
+ add_conversion_proc(6152, PGArray::Creator.new("tsmultirange", procs[4533]))
146
+ add_conversion_proc(6153, PGArray::Creator.new("tstzmultirange", procs[4534]))
147
+ end
148
+ end
149
+ end
150
+
151
+ # Handle PGMultiRange values in bound variables
152
+ def bound_variable_arg(arg, conn)
153
+ case arg
154
+ when PGMultiRange
155
+ arg.unquoted_literal(schema_utility_dataset)
156
+ else
157
+ super
158
+ end
159
+ end
160
+
161
+ # Freeze the pg multirange schema types to prevent adding new ones.
162
+ def freeze
163
+ @pg_multirange_schema_types.freeze
164
+ super
165
+ end
166
+
167
+ # Register a database specific multirange type. This can be used to support
168
+ # different multirange types per Database. Options:
169
+ #
170
+ # :converter :: A callable object (e.g. Proc), that is called with the PostgreSQL range string,
171
+ # and should return a PGRange instance.
172
+ # :oid :: The PostgreSQL OID for the multirange type. This is used by Sequel to set up automatic type
173
+ # conversion on retrieval from the database.
174
+ # :range_oid :: Should be the PostgreSQL OID for the multirange subtype (the range type). If given,
175
+ # automatically sets the :converter option by looking for scalar conversion
176
+ # proc.
177
+ #
178
+ # If a block is given, it is treated as the :converter option.
179
+ def register_multirange_type(db_type, opts=OPTS, &block)
180
+ oid = opts[:oid]
181
+ soid = opts[:range_oid]
182
+
183
+ if has_converter = opts.has_key?(:converter)
184
+ raise Error, "can't provide both a block and :converter option to register_multirange_type" if block
185
+ converter = opts[:converter]
186
+ else
187
+ has_converter = true if block
188
+ converter = block
189
+ end
190
+
191
+ unless (soid || has_converter) && oid
192
+ range_oid, subtype_oid = from(:pg_range).join(:pg_type, :oid=>:rngmultitypid).where(:typname=>db_type.to_s).get([:rngmultitypid, :rngtypid])
193
+ soid ||= subtype_oid unless has_converter
194
+ oid ||= range_oid
195
+ end
196
+
197
+ db_type = db_type.to_s.dup.freeze
198
+
199
+ if soid
200
+ raise Error, "can't provide both a converter and :range_oid option to register" if has_converter
201
+ raise Error, "no conversion proc for :range_oid=>#{soid.inspect} in conversion_procs" unless converter = conversion_procs[soid]
202
+ end
203
+
204
+ raise Error, "cannot add a multirange type without a convertor (use :converter or :range_oid option or pass block)" unless converter
205
+ creator = Creator.new(db_type, converter)
206
+ add_conversion_proc(oid, creator)
207
+
208
+ @pg_multirange_schema_types[db_type] = db_type.to_sym
209
+
210
+ singleton_class.class_eval do
211
+ meth = :"typecast_value_#{db_type}"
212
+ scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, db_type.sub('multirange', 'range'))}"
213
+ define_method(meth){|v| typecast_value_pg_multirange(v, creator, scalar_typecast_method)}
214
+ private meth
215
+ end
216
+
217
+ @schema_type_classes[db_type] = PGMultiRange
218
+ nil
219
+ end
220
+
221
+ private
222
+
223
+ # Recognize the registered database multirange types.
224
+ def schema_multirange_type(db_type)
225
+ @pg_multirange_schema_types[db_type] || super
226
+ end
227
+
228
+ # Set the :ruby_default value if the default value is recognized as a multirange.
229
+ def schema_post_process(_)
230
+ super.each do |a|
231
+ h = a[1]
232
+ db_type = h[:db_type]
233
+ if @pg_multirange_schema_types[db_type] && h[:default] =~ /\A#{db_type}\(.*\)\z/
234
+ h[:ruby_default] = get(Sequel.lit(h[:default]))
235
+ end
236
+ end
237
+ end
238
+
239
+ # Given a value to typecast and the type of PGMultiRange subclass:
240
+ # * If given a PGMultiRange with a matching type, use it directly.
241
+ # * If given a PGMultiRange with a different type, return a PGMultiRange
242
+ # with the creator's type.
243
+ # * If given an Array, create a new PGMultiRange instance for it, typecasting
244
+ # each instance using the scalar_typecast_method.
245
+ def typecast_value_pg_multirange(value, creator, scalar_typecast_method=nil)
246
+ case value
247
+ when PGMultiRange
248
+ return value if value.db_type == creator.type
249
+ when Array
250
+ # nothing
251
+ else
252
+ raise Sequel::InvalidValue, "invalid value for multirange type: #{value.inspect}"
253
+ end
254
+
255
+ if scalar_typecast_method && respond_to?(scalar_typecast_method, true)
256
+ value = value.map{|v| send(scalar_typecast_method, v)}
257
+ end
258
+ PGMultiRange.new(value, creator.type)
259
+ end
260
+ end
261
+
262
+ # The type of this multirange (e.g. 'int4multirange').
263
+ attr_accessor :db_type
264
+
265
+ # Set the array of ranges to delegate to, and the database type.
266
+ def initialize(ranges, db_type)
267
+ super(ranges)
268
+ @db_type = db_type.to_s
269
+ end
270
+
271
+ # Append the multirange SQL to the given sql string.
272
+ def sql_literal_append(ds, sql)
273
+ sql << db_type << '('
274
+ joiner = nil
275
+ conversion_meth = nil
276
+ each do |range|
277
+ if joiner
278
+ sql << joiner
279
+ else
280
+ joiner = ', '
281
+ end
282
+
283
+ unless range.is_a?(PGRange)
284
+ conversion_meth ||= :"typecast_value_#{db_type.sub('multi', '')}"
285
+ range = ds.db.send(conversion_meth, range)
286
+ end
287
+
288
+ ds.literal_append(sql, range)
289
+ end
290
+ sql << ')'
291
+ end
292
+
293
+ # Return whether the value is inside any of the ranges in the multirange.
294
+ def cover?(value)
295
+ any?{|range| range.cover?(value)}
296
+ end
297
+ alias === cover?
298
+
299
+ # Don't consider multiranges with different database types equal.
300
+ def eql?(other)
301
+ if PGMultiRange === other
302
+ return false unless other.db_type == db_type
303
+ other = other.__getobj__
304
+ end
305
+ __getobj__.eql?(other)
306
+ end
307
+
308
+ # Don't consider multiranges with different database types equal.
309
+ def ==(other)
310
+ return false if PGMultiRange === other && other.db_type != db_type
311
+ super
312
+ end
313
+
314
+ # Return a string containing the unescaped version of the multirange.
315
+ # Separated out for use by the bound argument code.
316
+ def unquoted_literal(ds)
317
+ val = String.new
318
+ val << "{"
319
+
320
+ joiner = nil
321
+ conversion_meth = nil
322
+ each do |range|
323
+ if joiner
324
+ val << joiner
325
+ else
326
+ joiner = ', '
327
+ end
328
+
329
+ unless range.is_a?(PGRange)
330
+ conversion_meth ||= :"typecast_value_#{db_type.sub('multi', '')}"
331
+ range = ds.db.send(conversion_meth, range)
332
+ end
333
+
334
+ val << range.unquoted_literal(ds)
335
+ end
336
+
337
+ val << "}"
338
+ end
339
+
340
+ # Allow automatic parameterization.
341
+ def sequel_auto_param_type(ds)
342
+ "::#{db_type}"
343
+ end
344
+ end
345
+ end
346
+
347
+ module SQL::Builders
348
+ # Convert the object to a Postgres::PGMultiRange.
349
+ def pg_multirange(v, db_type)
350
+ case v
351
+ when Postgres::PGMultiRange
352
+ if v.db_type == db_type
353
+ v
354
+ else
355
+ Postgres::PGMultiRange.new(v, db_type)
356
+ end
357
+ when Array
358
+ Postgres::PGMultiRange.new(v, db_type)
359
+ else
360
+ # May not be defined unless the pg_range_ops extension is used
361
+ pg_range_op(v)
362
+ end
363
+ end
364
+ end
365
+
366
+ Database.register_extension(:pg_multirange, Postgres::PGMultiRange::DatabaseMethods)
367
+ end
@@ -4,12 +4,9 @@
4
4
  # types to Sequel. PostgreSQL range types are similar to ruby's
5
5
  # Range class, representating an array of values. However, they
6
6
  # are more flexible than ruby's ranges, allowing exclusive beginnings
7
- # and endings (ruby's range only allows exclusive endings), and
8
- # unbounded beginnings and endings (which ruby's range does not
9
- # support).
7
+ # and endings (ruby's range only allows exclusive endings).
10
8
  #
11
- # This extension integrates with Sequel's native postgres and jdbc/postgresql adapters, so
12
- # that when range type values are retrieved, they are parsed and returned
9
+ # When PostgreSQL range values are retreived, they are parsed and returned
13
10
  # as instances of Sequel::Postgres::PGRange. PGRange mostly acts
14
11
  # like a Range, but it's not a Range as not all PostgreSQL range
15
12
  # type values would be valid ruby ranges. If the range type value
@@ -19,8 +16,7 @@
19
16
  # exception will be raised.
20
17
  #
21
18
  # In addition to the parser, this extension comes with literalizers
22
- # for both PGRange and Range that use the standard Sequel literalization
23
- # callbacks, so they work on all adapters.
19
+ # for PGRange and Range, so they can be used in queries and as bound variables.
24
20
  #
25
21
  # To turn an existing Range into a PGRange, use Sequel.pg_range:
26
22
  #
@@ -237,23 +233,9 @@ module Sequel
237
233
 
238
234
  private
239
235
 
240
- # Handle arrays of range types in bound variables.
241
- def bound_variable_array(a)
242
- case a
243
- when PGRange, Range
244
- "\"#{bound_variable_arg(a, nil)}\""
245
- else
246
- super
247
- end
248
- end
249
-
250
236
  # Recognize the registered database range types.
251
- def schema_column_type(db_type)
252
- if type = @pg_range_schema_types[db_type]
253
- type
254
- else
255
- super
256
- end
237
+ def schema_range_type(db_type)
238
+ @pg_range_schema_types[db_type] || super
257
239
  end
258
240
 
259
241
  # Set the :ruby_default value if the default value is recognized as a range.
@@ -290,7 +272,7 @@ module Sequel
290
272
  when Range
291
273
  PGRange.from_range(value, parser.db_type)
292
274
  when String
293
- parser.call(value)
275
+ parser.call(typecast_check_string_length(value, 100))
294
276
  else
295
277
  raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}"
296
278
  end
@@ -499,6 +481,11 @@ module Sequel
499
481
  end
500
482
  end
501
483
 
484
+ # Allow automatic parameterization for ranges with types.
485
+ def sequel_auto_param_type(ds)
486
+ "::#{db_type}" if db_type
487
+ end
488
+
502
489
  private
503
490
 
504
491
  # Escape common range types. Instead of quoting, just backslash escape all
@@ -1,7 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
  #
3
3
  # The pg_range_ops extension adds support to Sequel's DSL to make
4
- # it easier to call PostgreSQL range functions and operators.
4
+ # it easier to call PostgreSQL range and multirange functions and operators.
5
5
  #
6
6
  # To load the extension:
7
7
  #
@@ -11,10 +11,11 @@
11
11
  #
12
12
  # r = Sequel.pg_range_op(:range)
13
13
  #
14
- # If you have also loaded the pg_range extension, you can use
15
- # Sequel.pg_range as well:
14
+ # If you have also loaded the pg_range or pg_multirange extensions, you can use
15
+ # Sequel.pg_range or Sequel.pg_multirange as well:
16
16
  #
17
17
  # r = Sequel.pg_range(:range)
18
+ # r = Sequel.pg_multirange(:range)
18
19
  #
19
20
  # Also, on most Sequel expression objects, you can call the pg_range
20
21
  # method:
@@ -46,13 +47,25 @@
46
47
  # r.upper_inc # upper_inc(range)
47
48
  # r.lower_inf # lower_inf(range)
48
49
  # r.upper_inf # upper_inf(range)
50
+ #
51
+ # All of the above methods work for both ranges and multiranges, as long
52
+ # as PostgreSQL supports the operation. The following methods are also
53
+ # supported:
54
+ #
55
+ # r.range_merge # range_merge(range)
56
+ # r.unnest # unnest(range)
57
+ # r.multirange # multirange(range)
58
+ #
59
+ # +range_merge+ and +unnest+ expect the receiver to represent a multirange
60
+ # value, while +multi_range+ expects the receiver to represent a range value.
49
61
  #
50
- # See the PostgreSQL range function and operator documentation for more
62
+ # See the PostgreSQL range and multirange function and operator documentation for more
51
63
  # details on what these functions and operators do.
52
64
  #
53
- # If you are also using the pg_range extension, you should load it before
54
- # loading this extension. Doing so will allow you to use PGArray#op to get
55
- # an RangeOp, allowing you to perform range operations on range literals.
65
+ # If you are also using the pg_range or pg_multirange extension, you should
66
+ # load them before loading this extension. Doing so will allow you to use
67
+ # PGRange#op and PGMultiRange#op to get a RangeOp, allowing you to perform
68
+ # range operations on range literals.
56
69
  #
57
70
  # Related module: Sequel::Postgres::RangeOp
58
71
 
@@ -77,9 +90,12 @@ module Sequel
77
90
  :overlaps => ["(".freeze, " && ".freeze, ")".freeze].freeze,
78
91
  }.freeze
79
92
 
80
- %w'lower upper isempty lower_inc upper_inc lower_inf upper_inf'.each do |f|
93
+ %w'lower upper isempty lower_inc upper_inc lower_inf upper_inf unnest'.each do |f|
81
94
  class_eval("def #{f}; function(:#{f}) end", __FILE__, __LINE__)
82
95
  end
96
+ %w'range_merge multirange'.each do |f|
97
+ class_eval("def #{f}; RangeOp.new(function(:#{f})) end", __FILE__, __LINE__)
98
+ end
83
99
  OPERATORS.each_key do |f|
84
100
  class_eval("def #{f}(v); operator(:#{f}, v) end", __FILE__, __LINE__)
85
101
  end
@@ -127,6 +143,18 @@ module Sequel
127
143
  end
128
144
  end
129
145
  end
146
+
147
+ # :nocov:
148
+ if defined?(PGMultiRange)
149
+ # :nocov:
150
+ class PGMultiRange
151
+ # Wrap the PGRange instance in an RangeOp, allowing you to easily use
152
+ # the PostgreSQL range functions and operators with literal ranges.
153
+ def op
154
+ RangeOp.new(self)
155
+ end
156
+ end
157
+ end
130
158
  end
131
159
 
132
160
  module SQL::Builders
@@ -160,7 +188,7 @@ end
160
188
  if defined?(Sequel::CoreRefinements)
161
189
  module Sequel::CoreRefinements
162
190
  refine Symbol do
163
- include Sequel::Postgres::RangeOpMethods
191
+ send INCLUDE_METH, Sequel::Postgres::RangeOpMethods
164
192
  end
165
193
  end
166
194
  end
@@ -136,6 +136,15 @@ module Sequel
136
136
  ds.quote_schema_table_append(sql, db_type)
137
137
  end
138
138
  end
139
+
140
+ # Allow automatic parameterization if all values support it.
141
+ def sequel_auto_param_type(ds)
142
+ if db_type && all?{|v| nil == v || ds.send(:auto_param_type, v)}
143
+ s = String.new << "::"
144
+ ds.quote_schema_table_append(s, db_type)
145
+ s
146
+ end
147
+ end
139
148
  end
140
149
 
141
150
  # Class for row-valued/composite types that are treated as hashes.
@@ -208,6 +217,15 @@ module Sequel
208
217
  ds.quote_schema_table_append(sql, db_type)
209
218
  end
210
219
  end
220
+
221
+ # Allow automatic parameterization if all values support it.
222
+ def sequel_auto_param_type(ds)
223
+ if db_type && all?{|_,v| nil == v || ds.send(:auto_param_type, v)}
224
+ s = String.new << "::"
225
+ ds.quote_schema_table_append(s, db_type)
226
+ s
227
+ end
228
+ end
211
229
  end
212
230
 
213
231
  ROW_TYPE_CLASSES = [HashRow, ArrayRow].freeze
@@ -482,6 +500,7 @@ module Sequel
482
500
  row_type(db_type, v)
483
501
  end
484
502
  private meth
503
+ alias_method(meth, meth)
485
504
  end
486
505
 
487
506
  nil
@@ -518,26 +537,9 @@ module Sequel
518
537
 
519
538
  private
520
539
 
521
- # Format composite types used in bound variable arrays.
522
- def bound_variable_array(arg)
523
- case arg
524
- when ArrayRow
525
- "\"(#{arg.map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
526
- when HashRow
527
- arg.check_columns!
528
- "\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
529
- else
530
- super
531
- end
532
- end
533
-
534
540
  # Make the column type detection handle registered row types.
535
- def schema_column_type(db_type)
536
- if type = @row_schema_types[db_type]
537
- type
538
- else
539
- super
540
- end
541
+ def schema_composite_type(db_type)
542
+ @row_schema_types[db_type] || super
541
543
  end
542
544
  end
543
545
  end
@@ -210,7 +210,7 @@ end
210
210
  if defined?(Sequel::CoreRefinements)
211
211
  module Sequel::CoreRefinements
212
212
  refine Symbol do
213
- include Sequel::Postgres::PGRowOp::ExpressionMethods
213
+ send INCLUDE_METH, Sequel::Postgres::PGRowOp::ExpressionMethods
214
214
  end
215
215
  end
216
216
  end
@@ -74,7 +74,9 @@ module Sequel
74
74
  raise(Sequel::Error, "method #{method.inspect} did not return a dataset") unless @dataset.is_a?(Dataset)
75
75
  self
76
76
  end
77
+ # :nocov:
77
78
  ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
79
+ # :nocov:
78
80
  end
79
81
  end
80
82
 
@@ -51,9 +51,10 @@ module Sequel::S
51
51
 
52
52
  # :nocov:
53
53
  if RUBY_VERSION >= '2.0.0'
54
+ include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
54
55
  # :nocov:
55
56
  refine Object do
56
- include Sequel::S
57
+ send include_meth, Sequel::S
57
58
  end
58
59
  end
59
60
  end
@@ -52,7 +52,7 @@ module Sequel
52
52
  # Dump the cached schema to the filename given in Marshal format.
53
53
  def dump_schema_cache(file)
54
54
  sch = {}
55
- @schemas.each do |k,v|
55
+ @schemas.sort.each do |k,v|
56
56
  sch[k] = v.map do |c, h|
57
57
  h = Hash[h]
58
58
  h.delete(:callable_default)