module Sequel::Postgres::PGRange::DatabaseMethods
Public Class Methods
Add the conversion procs to the database and extend the datasets to correctly literalize ruby Range
values.
# File lib/sequel/extensions/pg_range.rb 136 def self.extended(db) 137 db.instance_exec do 138 @pg_range_schema_types ||= {} 139 extend_datasets(DatasetMethods) 140 register_range_type('int4range', :oid=>3904, :subtype_oid=>23) 141 register_range_type('numrange', :oid=>3906, :subtype_oid=>1700) 142 register_range_type('tsrange', :oid=>3908, :subtype_oid=>1114) 143 register_range_type('tstzrange', :oid=>3910, :subtype_oid=>1184) 144 register_range_type('daterange', :oid=>3912, :subtype_oid=>1082) 145 register_range_type('int8range', :oid=>3926, :subtype_oid=>20) 146 if respond_to?(:register_array_type) 147 register_array_type('int4range', :oid=>3905, :scalar_oid=>3904, :scalar_typecast=>:int4range) 148 register_array_type('numrange', :oid=>3907, :scalar_oid=>3906, :scalar_typecast=>:numrange) 149 register_array_type('tsrange', :oid=>3909, :scalar_oid=>3908, :scalar_typecast=>:tsrange) 150 register_array_type('tstzrange', :oid=>3911, :scalar_oid=>3910, :scalar_typecast=>:tstzrange) 151 register_array_type('daterange', :oid=>3913, :scalar_oid=>3912, :scalar_typecast=>:daterange) 152 register_array_type('int8range', :oid=>3927, :scalar_oid=>3926, :scalar_typecast=>:int8range) 153 end 154 [:int4range, :numrange, :tsrange, :tstzrange, :daterange, :int8range].each do |v| 155 @schema_type_classes[v] = PGRange 156 end 157 158 procs = conversion_procs 159 add_conversion_proc(3908, Parser.new("tsrange", procs[1114])) 160 add_conversion_proc(3910, Parser.new("tstzrange", procs[1184])) 161 if defined?(PGArray::Creator) 162 add_conversion_proc(3909, PGArray::Creator.new("tsrange", procs[3908])) 163 add_conversion_proc(3911, PGArray::Creator.new("tstzrange", procs[3910])) 164 end 165 end 166 end
Public Instance Methods
Handle Range
and PGRange
values in bound variables
# File lib/sequel/extensions/pg_range.rb 169 def bound_variable_arg(arg, conn) 170 case arg 171 when PGRange 172 arg.unquoted_literal(schema_utility_dataset) 173 when Range 174 PGRange.from_range(arg).unquoted_literal(schema_utility_dataset) 175 else 176 super 177 end 178 end
Freeze the pg range schema types to prevent adding new ones.
# File lib/sequel/extensions/pg_range.rb 181 def freeze 182 @pg_range_schema_types.freeze 183 super 184 end
Register a database specific range type. This can be used to support different range types per Database
. Options:
- :converter
-
A callable object (e.g. Proc), that is called with the start or end of the range (usually a string), and should return the appropriate typecasted object.
- :oid
-
The PostgreSQL OID for the range type. This is used by the
Sequel
postgres adapter to set up automatic type conversion on retrieval from the database. - :subtype_oid
-
Should be the PostgreSQL OID for the range's subtype. If given, automatically sets the :converter option by looking for scalar conversion proc.
If a block is given, it is treated as the :converter option.
# File lib/sequel/extensions/pg_range.rb 198 def register_range_type(db_type, opts=OPTS, &block) 199 oid = opts[:oid] 200 soid = opts[:subtype_oid] 201 202 if has_converter = opts.has_key?(:converter) 203 raise Error, "can't provide both a block and :converter option to register_range_type" if block 204 converter = opts[:converter] 205 else 206 has_converter = true if block 207 converter = block 208 end 209 210 unless (soid || has_converter) && oid 211 range_oid, subtype_oid = from(:pg_range).join(:pg_type, :oid=>:rngtypid).where(:typname=>db_type.to_s).get([:rngtypid, :rngsubtype]) 212 soid ||= subtype_oid unless has_converter 213 oid ||= range_oid 214 end 215 216 db_type = db_type.to_s.dup.freeze 217 218 if converter = opts[:converter] 219 raise Error, "can't provide both a block and :converter option to register" if block 220 else 221 converter = block 222 end 223 224 if soid 225 raise Error, "can't provide both a converter and :subtype_oid option to register" if has_converter 226 raise Error, "no conversion proc for :subtype_oid=>#{soid.inspect} in conversion_procs" unless converter = conversion_procs[soid] 227 end 228 229 parser = Parser.new(db_type, converter) 230 add_conversion_proc(oid, parser) 231 232 @pg_range_schema_types[db_type] = db_type.to_sym 233 234 singleton_class.class_eval do 235 meth = :"typecast_value_#{db_type}" 236 define_method(meth){|v| typecast_value_pg_range(v, parser)} 237 private meth 238 end 239 240 @schema_type_classes[:"#{opts[:type_symbol] || db_type}"] = PGRange 241 nil 242 end
Private Instance Methods
Handle arrays of range types in bound variables.
# File lib/sequel/extensions/pg_range.rb 247 def bound_variable_array(a) 248 case a 249 when PGRange, Range 250 "\"#{bound_variable_arg(a, nil)}\"" 251 else 252 super 253 end 254 end
Recognize the registered database range types.
# File lib/sequel/extensions/pg_range.rb 257 def schema_column_type(db_type) 258 if type = @pg_range_schema_types[db_type] 259 type 260 else 261 super 262 end 263 end
Set the :ruby_default value if the default value is recognized as a range.
# File lib/sequel/extensions/pg_range.rb 266 def schema_post_process(_) 267 super.each do |a| 268 h = a[1] 269 db_type = h[:db_type] 270 if @pg_range_schema_types[db_type] && h[:default] =~ /\A'([^']+)'::#{db_type}\z/ 271 default = $1 272 if convertor = conversion_procs[h[:oid]] 273 h[:ruby_default] = convertor.call(default) 274 end 275 end 276 end 277 end
Typecast value correctly to a PGRange
. If already an PGRange
instance with the same db_type, return as is. If a PGRange
with a different subtype, return a new PGRange
with the same values and the expected subtype. If a Range
object, create a PGRange
with the given db_type. If a string, assume it is in PostgreSQL output format and parse it using the parser.
# File lib/sequel/extensions/pg_range.rb 286 def typecast_value_pg_range(value, parser) 287 case value 288 when PGRange 289 if value.db_type.to_s == parser.db_type 290 value 291 elsif value.empty? 292 PGRange.empty(parser.db_type) 293 else 294 PGRange.new(value.begin, value.end, :exclude_begin=>value.exclude_begin?, :exclude_end=>value.exclude_end?, :db_type=>parser.db_type) 295 end 296 when Range 297 PGRange.from_range(value, parser.db_type) 298 when String 299 parser.call(value) 300 else 301 raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}" 302 end 303 end