sequel 5.39.0 → 5.65.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +326 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +57 -25
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +13 -13
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/migration.rdoc +12 -6
  10. data/doc/model_hooks.rdoc +1 -1
  11. data/doc/object_model.rdoc +8 -8
  12. data/doc/opening_databases.rdoc +18 -11
  13. data/doc/postgresql.rdoc +16 -8
  14. data/doc/querying.rdoc +5 -3
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/release_notes/5.41.0.txt +25 -0
  17. data/doc/release_notes/5.42.0.txt +136 -0
  18. data/doc/release_notes/5.43.0.txt +98 -0
  19. data/doc/release_notes/5.44.0.txt +32 -0
  20. data/doc/release_notes/5.45.0.txt +34 -0
  21. data/doc/release_notes/5.46.0.txt +87 -0
  22. data/doc/release_notes/5.47.0.txt +59 -0
  23. data/doc/release_notes/5.48.0.txt +14 -0
  24. data/doc/release_notes/5.49.0.txt +59 -0
  25. data/doc/release_notes/5.50.0.txt +78 -0
  26. data/doc/release_notes/5.51.0.txt +47 -0
  27. data/doc/release_notes/5.52.0.txt +87 -0
  28. data/doc/release_notes/5.53.0.txt +23 -0
  29. data/doc/release_notes/5.54.0.txt +27 -0
  30. data/doc/release_notes/5.55.0.txt +21 -0
  31. data/doc/release_notes/5.56.0.txt +51 -0
  32. data/doc/release_notes/5.57.0.txt +23 -0
  33. data/doc/release_notes/5.58.0.txt +31 -0
  34. data/doc/release_notes/5.59.0.txt +73 -0
  35. data/doc/release_notes/5.60.0.txt +22 -0
  36. data/doc/release_notes/5.61.0.txt +43 -0
  37. data/doc/release_notes/5.62.0.txt +132 -0
  38. data/doc/release_notes/5.63.0.txt +33 -0
  39. data/doc/release_notes/5.64.0.txt +50 -0
  40. data/doc/release_notes/5.65.0.txt +21 -0
  41. data/doc/schema_modification.rdoc +1 -1
  42. data/doc/security.rdoc +9 -9
  43. data/doc/sql.rdoc +28 -16
  44. data/doc/testing.rdoc +22 -11
  45. data/doc/transactions.rdoc +6 -6
  46. data/doc/virtual_rows.rdoc +2 -2
  47. data/lib/sequel/adapters/ado/access.rb +1 -1
  48. data/lib/sequel/adapters/ado.rb +17 -17
  49. data/lib/sequel/adapters/amalgalite.rb +3 -5
  50. data/lib/sequel/adapters/ibmdb.rb +2 -2
  51. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  52. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  53. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  54. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  55. data/lib/sequel/adapters/jdbc.rb +16 -18
  56. data/lib/sequel/adapters/mysql.rb +80 -67
  57. data/lib/sequel/adapters/mysql2.rb +54 -49
  58. data/lib/sequel/adapters/odbc.rb +6 -2
  59. data/lib/sequel/adapters/oracle.rb +4 -3
  60. data/lib/sequel/adapters/postgres.rb +83 -40
  61. data/lib/sequel/adapters/shared/access.rb +11 -1
  62. data/lib/sequel/adapters/shared/db2.rb +30 -0
  63. data/lib/sequel/adapters/shared/mssql.rb +58 -7
  64. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  65. data/lib/sequel/adapters/shared/oracle.rb +76 -0
  66. data/lib/sequel/adapters/shared/postgres.rb +418 -174
  67. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  68. data/lib/sequel/adapters/shared/sqlite.rb +103 -11
  69. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  70. data/lib/sequel/adapters/sqlite.rb +60 -18
  71. data/lib/sequel/adapters/tinytds.rb +1 -1
  72. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  73. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  74. data/lib/sequel/ast_transformer.rb +6 -0
  75. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  76. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  77. data/lib/sequel/connection_pool/single.rb +6 -8
  78. data/lib/sequel/connection_pool/threaded.rb +8 -8
  79. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  80. data/lib/sequel/connection_pool.rb +47 -30
  81. data/lib/sequel/core.rb +28 -18
  82. data/lib/sequel/database/connecting.rb +26 -2
  83. data/lib/sequel/database/misc.rb +69 -14
  84. data/lib/sequel/database/query.rb +73 -2
  85. data/lib/sequel/database/schema_generator.rb +45 -52
  86. data/lib/sequel/database/schema_methods.rb +17 -1
  87. data/lib/sequel/dataset/actions.rb +107 -13
  88. data/lib/sequel/dataset/features.rb +20 -0
  89. data/lib/sequel/dataset/misc.rb +12 -2
  90. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  91. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  92. data/lib/sequel/dataset/query.rb +118 -16
  93. data/lib/sequel/dataset/sql.rb +177 -47
  94. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  95. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  96. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  97. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  98. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  99. data/lib/sequel/extensions/blank.rb +8 -0
  100. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  101. data/lib/sequel/extensions/core_refinements.rb +36 -11
  102. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  103. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  104. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  105. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  106. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  107. data/lib/sequel/extensions/inflector.rb +9 -1
  108. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  109. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  110. data/lib/sequel/extensions/migration.rb +7 -2
  111. data/lib/sequel/extensions/named_timezones.rb +26 -6
  112. data/lib/sequel/extensions/pagination.rb +1 -1
  113. data/lib/sequel/extensions/pg_array.rb +23 -3
  114. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  115. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  116. data/lib/sequel/extensions/pg_enum.rb +1 -1
  117. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  118. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  119. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  120. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  121. data/lib/sequel/extensions/pg_inet.rb +10 -11
  122. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  123. data/lib/sequel/extensions/pg_interval.rb +45 -19
  124. data/lib/sequel/extensions/pg_json.rb +13 -15
  125. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  126. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  127. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  128. data/lib/sequel/extensions/pg_range.rb +10 -23
  129. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  130. data/lib/sequel/extensions/pg_row.rb +19 -13
  131. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  132. data/lib/sequel/extensions/query.rb +2 -0
  133. data/lib/sequel/extensions/s.rb +2 -1
  134. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  135. data/lib/sequel/extensions/server_block.rb +8 -12
  136. data/lib/sequel/extensions/sql_comments.rb +110 -3
  137. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  138. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  139. data/lib/sequel/extensions/string_agg.rb +1 -1
  140. data/lib/sequel/extensions/string_date_time.rb +19 -23
  141. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  142. data/lib/sequel/model/associations.rb +345 -101
  143. data/lib/sequel/model/base.rb +51 -27
  144. data/lib/sequel/model/errors.rb +10 -1
  145. data/lib/sequel/model/inflections.rb +1 -1
  146. data/lib/sequel/model/plugins.rb +5 -0
  147. data/lib/sequel/plugins/association_proxies.rb +2 -0
  148. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  149. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  150. data/lib/sequel/plugins/auto_validations.rb +87 -15
  151. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  152. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  153. data/lib/sequel/plugins/column_encryption.rb +728 -0
  154. data/lib/sequel/plugins/composition.rb +10 -4
  155. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  156. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  157. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  158. data/lib/sequel/plugins/dirty.rb +1 -1
  159. data/lib/sequel/plugins/enum.rb +124 -0
  160. data/lib/sequel/plugins/finder.rb +4 -2
  161. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  162. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  163. data/lib/sequel/plugins/json_serializer.rb +39 -24
  164. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  165. data/lib/sequel/plugins/list.rb +3 -1
  166. data/lib/sequel/plugins/many_through_many.rb +109 -10
  167. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  168. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  169. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  170. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  171. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  172. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  173. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  174. data/lib/sequel/plugins/serialization.rb +9 -3
  175. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  176. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  177. data/lib/sequel/plugins/sql_comments.rb +189 -0
  178. data/lib/sequel/plugins/static_cache.rb +1 -1
  179. data/lib/sequel/plugins/subclasses.rb +28 -11
  180. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  181. data/lib/sequel/plugins/timestamps.rb +1 -1
  182. data/lib/sequel/plugins/unused_associations.rb +521 -0
  183. data/lib/sequel/plugins/update_or_create.rb +1 -1
  184. data/lib/sequel/plugins/validate_associated.rb +22 -12
  185. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  186. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  187. data/lib/sequel/sql.rb +1 -1
  188. data/lib/sequel/timezones.rb +12 -14
  189. data/lib/sequel/version.rb +1 -1
  190. metadata +101 -43
@@ -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_column_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
237
  def schema_column_type(db_type)
252
- if type = @pg_range_schema_types[db_type]
253
- type
254
- else
255
- super
256
- end
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,19 +537,6 @@ 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
541
  def schema_column_type(db_type)
536
542
  if type = @row_schema_types[db_type]
@@ -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
@@ -6,6 +6,17 @@
6
6
  # the current database). The main interface is through
7
7
  # Sequel::Database#dump_schema_migration.
8
8
  #
9
+ # The schema_dumper extension is quite limited in what types of
10
+ # database objects it supports. In general, it only supports
11
+ # dumping tables, columns, primary key and foreign key constraints,
12
+ # and some indexes. It does not support most table options, CHECK
13
+ # constraints, partial indexes, database functions, triggers,
14
+ # security grants/revokes, and a wide variety of other useful
15
+ # database properties. Be aware of the limitations when using the
16
+ # schema_dumper extension. If you are dumping the schema to restore
17
+ # to the same database type, it is recommended to use your database's
18
+ # dump and restore programs instead of the schema_dumper extension.
19
+ #
9
20
  # To load the extension:
10
21
  #
11
22
  # DB.extension :schema_dumper
@@ -172,7 +183,7 @@ END_MIG
172
183
  if options[:single_pk] && schema_autoincrementing_primary_key?(schema)
173
184
  type_hash = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
174
185
  [:table, :key, :on_delete, :on_update, :deferrable].each{|f| type_hash[f] = schema[f] if schema[f]}
175
- if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"}
186
+ if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"} || type_hash == {:type=>"INTEGER"}
176
187
  type_hash.delete(:type)
177
188
  elsif options[:same_db] && type_hash == {:type=>type_literal_generic_bignum_symbol(type_hash).to_s}
178
189
  type_hash[:type] = :Bignum
@@ -214,7 +225,7 @@ END_MIG
214
225
  col_opts[:null] = false if schema[:allow_null] == false
215
226
  if table = schema[:table]
216
227
  [:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
217
- col_opts[:type] = type unless type == Integer || type == 'integer'
228
+ col_opts[:type] = type unless type == Integer || type == 'integer' || type == 'INTEGER'
218
229
  gen.foreign_key(name, table, col_opts)
219
230
  else
220
231
  gen.column(name, type, col_opts)
@@ -88,12 +88,10 @@ module Sequel
88
88
  module UnthreadedServerBlock
89
89
  # Set a default server/shard to use inside the block.
90
90
  def with_server(default_server, read_only_server=default_server)
91
- begin
92
- set_default_server(default_server, read_only_server)
93
- yield
94
- ensure
95
- clear_default_server
96
- end
91
+ set_default_server(default_server, read_only_server)
92
+ yield
93
+ ensure
94
+ clear_default_server
97
95
  end
98
96
 
99
97
  private
@@ -131,12 +129,10 @@ module Sequel
131
129
  # Set a default server/shard to use inside the block for the current
132
130
  # thread.
133
131
  def with_server(default_server, read_only_server=default_server)
134
- begin
135
- set_default_server(default_server, read_only_server)
136
- yield
137
- ensure
138
- clear_default_server
139
- end
132
+ set_default_server(default_server, read_only_server)
133
+ yield
134
+ ensure
135
+ clear_default_server
140
136
  end
141
137
 
142
138
  private